Hermes Agent 使用指南
18 章 · 约 304 千字
官方文档「使用指南」:命令行、界面、配置、会话、安全、多账号与跨平台。
目录
- CLI 界面
- TUI
- Hermes Desktop
- 配置
- 配置模型
- Sessions(会话)
- 安全
- Secrets
- Profiles:运行多个 Agent
- Bot 模式
- Hermes Agent — Docker
- Local Models
- 检查点与 /rollback
- Import from Other Agents
- Which File Does What?
- 消息网关
- Windows(原生)指南
- Windows (WSL2) 指南
CLI 界面
Hermes Agent 的 CLI 是一个完整的终端用户界面(TUI),而非 Web UI。它支持多行编辑、斜杠命令自动补全、对话历史、中断并重定向,以及流式工具输出。专为常驻终端的用户而生。
提示
Hermes 还提供了一个现代 TUI,支持模态覆盖层、鼠标选择和非阻塞输入。使用 hermes --tui 启动——参见 TUI 指南。
运行 CLI
# 启动交互式会话(默认)
hermes
# 单次查询模式(非交互式)
hermes chat -q "Hello"
# 使用指定模型
hermes chat --model "anthropic/claude-sonnet-4"
# 使用指定提供商
hermes chat --provider nous # 使用 Nous Portal
hermes chat --provider openrouter # 强制使用 OpenRouter
# 使用指定工具集
hermes chat --toolsets "web,terminal,skills"
# 启动时预加载一个或多个 skill
hermes -s hermes-agent-dev,github-auth
hermes chat -s github-pr-workflow -q "open a draft PR"
# 恢复之前的会话
hermes --continue # 恢复最近的 CLI 会话(-c)
hermes --resume <session_id> # 通过 ID 恢复指定会话(-r)
# 详细模式(调试输出)
hermes chat --verbose
# 隔离的 git worktree(用于并行运行多个 agent)
hermes -w # 在 worktree 中以交互模式运行
hermes -w -z "Fix issue #123" # 在 worktree 中以单次查询模式运行界面布局
Hermes CLI 横幅、对话流和固定输入提示符,以稳定的文档图示形式呈现,而非脆弱的文字艺术。
欢迎横幅一目了然地显示当前模型、终端后端、工作目录、可用工具和已安装的 skill。
状态栏
一个持久状态栏位于输入区域上方,实时更新:
☤ claude-sonnet-4-20250514 │ 12.4K/200K │ [██████░░░░] 6% │ $0.06 │ 15m| | 元素 | 描述
| 模型名称 | 当前模型(超过 26 个字符时截断)
| Token 计数 | 已使用的上下文 token 数 / 最大上下文窗口
| 上下文进度条 | 带颜色阈值编码的可视填充指示器
| 费用 | 预估会话费用(未知或零价格模型显示 n/a)
| 🗜️ N | 上下文压缩次数——当前运行会话被自动压缩的次数。首次压缩触发后显示。
| ▶ N | 活跃后台任务数——当前会话中仍在运行的 /bg prompt(提示词)数量。至少有一个任务进行中时显示。
| 时长 | 会话已用时间
| ⚠ YOLO | YOLO 模式警告——当 HERMES_YOLO_MODE 开启时显示(通过启动时的 hermes --yolo 或会话中的 /yolo 切换)。与横幅行警告保持同步,确保你不会忘记自己处于自动批准模式。
状态栏会根据终端宽度自适应——≥ 76 列时显示完整布局,52–75 列时显示紧凑布局,低于 52 列时显示最简布局(模型 + 时长,以及 YOLO 徽章(如已激活))。
上下文颜色编码:
| | 颜色 | 阈值 | 含义
| 绿色 | < 50% | 空间充足
| 黄色 | 50–80% | 趋于饱满
| 橙色 | 80–95% | 接近上限
| 红色 | ≥ 95% | 即将溢出——考虑使用 /compress
使用 /usage 查看详细分解,包括各类别费用(输入 vs 输出 token)。
会话恢复显示
恢复之前的会话时(hermes -c 或 hermes --resume <id>),横幅与输入提示符之间会出现一个"Previous Conversation"面板,显示对话历史的简洁摘要。详情及配置说明参见会话——恢复时的对话摘要。
快捷键
| | 按键 | 操作
| Enter | 发送消息
| Alt+Enter、Ctrl+J 或 Shift+Enter | 换行(多行输入)。Shift+Enter 需要终端能够将其与 Enter 区分——见下文。在 Windows Terminal 中,Alt+Enter 被终端捕获(切换全屏);请改用 Ctrl+Enter 或 Ctrl+J。
| Alt+V | 在终端支持时从剪贴板粘贴图片
| Ctrl+V | 粘贴文本,并尝试附加剪贴板中的图片
| Ctrl+B | 语音模式启用时开始/停止录音(voice.record_key,默认:ctrl+b)
| Ctrl+G | 在 $EDITOR(vim/nvim/nano/VS Code 等)中打开当前输入缓冲区。保存并退出后,编辑后的文本将作为下一条 prompt 发送——适合编写长篇多段落 prompt。
| Ctrl+X Ctrl+E | 外部编辑器的 Emacs 风格备用绑定(与 Ctrl+G 行为相同)。
| Ctrl+C | 中断 agent(2 秒内双击强制退出)
| F6 | 打开全屏实时子智能体监视器,保留输入草稿。方向键选择,Enter 查看近期日志,s 引导,x 请求停止并确认。
| F7 | 将实时子智能体栏切换为单行摘要或恢复多行预览,不改变输入焦点。
| Ctrl+D | 退出
| Ctrl+Z | 将 Hermes 挂起到后台(仅 Unix)。在 shell 中运行 fg 恢复。
| Tab | 接受自动建议(ghost text)或自动补全斜杠命令
多行粘贴预览。 粘贴多行内容时,CLI 会显示一行简洁的单行预览([pasted: 47 lines, 1,842 chars — press Enter to send]),而非将全部内容倾倒到滚动缓冲区。实际发送的仍是完整内容;这只是显示上的优化。
最终响应中的 Markdown 剥离。 CLI 会从 agent 的最终回复中剥离最冗长的 Markdown 围栏以及 **粗体** / *斜体* 包装,使其在终端中呈现为可读的纯文本,而非原始源码。代码块和列表会被保留。这不影响 gateway 平台或工具结果——它们保留 Markdown 以供原生渲染。
斜杠命令
输入 / 查看自动补全下拉菜单。Hermes 支持大量 CLI 斜杠命令、动态 skill 命令和用户自定义快捷命令。
常用示例:
| | 命令 | 描述
| /help | 显示命令帮助
| /model | 显示或更改当前模型
| /tools | 列出当前可用工具
| /skills browse | 浏览 skill 中心和官方可选 skill
| /bg <prompt> | 在独立后台会话中运行一个 prompt
| /btw <question> | 在不打断当前对话的情况下,就当前对话提出顺带问题
| /skin | 显示或切换当前 CLI 皮肤
| /voice on | 启用 CLI 语音模式(按 Ctrl+B 录音)
| /voice tts | 切换 Hermes 回复的语音播放
| /reasoning high | 提高推理强度
| /title My Session | 为当前会话命名
| /status | 显示会话信息——模型/配置/token/时长——以及本地会话摘要块(近期轮次数、常用工具、涉及文件、最新用户 prompt + 助手回复)。纯本地计算,不调用 LLM。
| /sessions | 在经典 CLI 中直接打开交互式会话选择器(与 TUI 使用同一界面)。输入过滤,方向键导航,Enter 恢复。
完整的内置 CLI 和消息列表,参见斜杠命令参考。
语音模式的设置、提供商、静音调节以及消息/Discord 语音用法,参见语音模式。
提示
命令不区分大小写——/HELP 与 /help 效果相同。已安装的 skill 也会自动成为斜杠命令。
快捷命令
你可以定义自定义命令,无需调用 LLM 即可立即执行 shell 命令。这些命令在 CLI 和消息平台(Telegram、Discord 等)中均可使用。
# ~/.hermes/config.yaml
quick_commands:
status:
type: exec
command: systemctl status hermes-agent
gpu:
type: exec
command: nvidia-smi --query-gpu=utilization.gpu,memory.used --format=csv,noheader
restart:
type: alias
target: /gateway restart然后在任意聊天中输入 /status、/gpu 或 /restart。更多示例参见配置指南。
启动时预加载 Skill
如果你已知道本次会话需要哪些 skill,可在启动时传入:
hermes -s hermes-agent-dev,github-auth
hermes chat -s github-pr-workflow -s github-authHermes 会在第一轮对话前将每个指定的 skill 加载到会话 prompt 中。该标志在交互模式和单次查询模式下均有效。
Skill 斜杠命令
~/.hermes/skills/ 中每个已安装的 skill 都会自动注册为斜杠命令。skill 名称即为命令名:
/gif-search funny cats
/axolotl help me fine-tune Llama 3 on my dataset
/github-pr-workflow create a PR for the auth refactor
# 仅输入 skill 名称即可加载它,让 agent 询问你的需求:
/excalidraw人格设定
设置预定义人格以改变 agent 的语气:
/personality pirate
/personality kawaii
/personality concise内置人格包括:helpful、concise、technical、creative、teacher、kawaii、catgirl、pirate、shakespeare、surfer、noir、uwu、philosopher、hype。
你也可以在 ~/.hermes/config.yaml 中定义自定义人格:
personalities:
helpful: "You are a helpful, friendly AI assistant."
kawaii: "You are a kawaii assistant! Use cute expressions..."
pirate: "Arrr! Ye be talkin' to Captain Hermes..."
# 添加你自己的!多行输入
有两种方式输入多行消息:
- Alt+Enter、Ctrl+J 或 Shift+Enter — 插入新行
- 反斜杠续行 — 在行尾加 \ 继续输入:
❯ Write a function that:\
1. Takes a list of numbers\
2. Returns the sum信息
支持粘贴多行文本——使用上述任意换行键,或直接粘贴内容。
在使用 Kitty 键盘协议的终端中,数字小键盘上的 Alt+Enter 也会插入换行符,即使光标紧邻折叠的粘贴内容。带修饰键的小键盘导航键与对应的非小键盘按键行为一致。
Shift+Enter 兼容性
大多数终端默认对 Enter 和 Shift+Enter 发送相同的字节序列,因此应用程序无法区分它们。Hermes 仅在终端通过 Kitty 键盘协议或 xterm 的 modifyOtherKeys 模式发送不同序列时才能识别 Shift+Enter。
| | 终端 | 状态
| Kitty、foot、WezTerm、Ghostty | 默认启用独立的 Shift+Enter
| iTerm2(近期版本)、Alacritty、VS Code terminal、Warp | 在设置中启用 Kitty 协议后支持
| Windows Terminal Preview 1.25+ | 在设置中启用 Kitty 协议后支持
| macOS Terminal.app、Windows Terminal 稳定版 | 不支持——Shift+Enter 与 Enter 无法区分
当终端无法区分时,Alt+Enter 和 Ctrl+J 在所有终端中均可正常使用。特别是在 Windows Terminal 中,Alt+Enter 被终端捕获(切换全屏),永远不会传递给 Hermes——请直接使用 Ctrl+Enter(传递为 Ctrl+J)或 Ctrl+J 来换行。
中断 Agent
你可以在任意时刻中断 agent:
- 输入新消息 + Enter,在 agent 工作时——中断并处理你的新指令
- Ctrl+C——中断当前操作(2 秒内双击强制退出)
- 正在进行的终端命令会立即被终止(SIGTERM,1 秒后 SIGKILL)
- 中断期间输入的多条消息会合并为一条 prompt
繁忙输入模式
display.busy_input_mode 配置项控制在 agent 工作时按下 Enter 的行为:
| | 模式 | 行为
| "interrupt"(默认) | 你的消息中断当前操作并立即处理
| "queue" | 你的消息被静默排队,在 agent 完成后作为下一轮发送
| "steer" | 你的消息通过 /steer 注入当前运行,在下一次工具调用后到达 agent——不中断,不开启新轮次
# ~/.hermes/config.yaml
display:
busy_input_mode: "steer" # 或 "queue" 或 "interrupt"(默认)"queue" 模式适合在不意外取消进行中工作的情况下准备后续消息。"steer" 模式适合在不中断的情况下在任务执行中途重定向 agent——例如在它还在编辑代码时说"顺便也检查一下测试"。未知值会回退到 "interrupt"。
"steer" 有两个自动回退:如果 agent 尚未启动,或附有图片,消息会回退到 "queue" 行为,确保内容不丢失。
你也可以在 CLI 中动态更改:
/busy queue
/busy steer
/busy interrupt
/busy status首次提示
第一次在 Hermes 工作时按下 Enter,Hermes 会打印一行提示,说明 /busy 选项("(tip) Your message interrupted the current run…")。每次安装只触发一次——config.yaml 中 onboarding.seen.busy_input_prompt 下的标志会锁定它。删除该键可再次看到提示。
挂起到后台
在 Unix 系统上,按 Ctrl+Z 将 Hermes 挂起到后台——与任何终端进程一样。shell 会打印确认信息:
Hermes Agent has been suspended. Run `fg` to bring Hermes Agent back.在 shell 中输入 fg 即可从中断处恢复会话。Windows 不支持此功能。
工具进度显示
CLI 在 agent 工作时显示动态反馈:
思考动画(API 调用期间):
◜ (。•́︿•̀。) pondering... (1.2s)
◠ (⊙_⊙) contemplating... (2.4s)
✧٩(ˊᗜˋ*)و✧ got it! (3.1s)工具执行信息流:
┊ 💻 terminal `ls -la` (0.3s)
┊ 🔍 web_search (1.2s)
┊ 📄 web_extract (2.1s)使用 /verbose 循环切换显示模式:off → new → all → verbose。该命令也可为消息平台启用——参见配置。
工具预览长度
display.tool_preview_length 配置项控制工具调用预览行(如文件路径、终端命令)中显示的最大字符数。默认值为 0,表示无限制——显示完整路径和命令。
# ~/.hermes/config.yaml
display:
tool_preview_length: 80 # 将工具预览截断为 80 个字符(0 = 无限制)这在终端较窄或工具参数包含很长文件路径时非常有用。
会话管理
恢复会话
退出 CLI 会话时,会打印恢复命令:
Resume this session with:
hermes --resume 20260225_143052_a1b2c3
Session: 20260225_143052_a1b2c3
Duration: 12m 34s
Messages: 28 (5 user, 18 tool calls)恢复选项:
hermes --continue # 恢复最近的 CLI 会话
hermes -c # 简写形式
hermes -c "my project" # 恢复命名会话(谱系中最新的)
hermes --resume 20260225_143052_a1b2c3 # 通过 ID 恢复指定会话
hermes --resume "refactoring auth" # 通过标题恢复
hermes -r 20260225_143052_a1b2c3 # 简写形式恢复会从 SQLite 中还原完整的对话历史。agent 能看到所有之前的消息、工具调用和响应——就像从未离开一样。
在聊天中使用 /title My Session Name 为当前会话命名,或从命令行使用 hermes sessions rename <id> <title>。使用 hermes sessions list 浏览历史会话。
会话存储
CLI 会话存储在 Hermes 的 SQLite 状态数据库 ~/.hermes/state.db 中。数据库保存:
- 会话元数据(ID、标题、时间戳、token 计数器)
- 消息历史
- 跨压缩/恢复会话的谱系
- session_search 使用的全文搜索索引
部分消息适配器还会在数据库旁保存各平台的转录文件,但 CLI 本身从 SQLite 会话存储中恢复。
上下文压缩
长对话在接近上下文限制时会自动摘要:
# 在 ~/.hermes/config.yaml 中
compression:
enabled: true
threshold: 0.50 # 默认在上下文限制的 50% 时压缩
# 摘要模型在 auxiliary 下配置:
auxiliary:
compression:
model: "" # 留空则使用主聊天模型(默认)。或指定一个廉价快速的模型,如 "google/gemini-3-flash-preview"。压缩触发时,中间轮次会被摘要,同时始终保留前 3 轮和后 20 轮。
后台会话
在独立的后台会话中运行 prompt,同时继续使用 CLI 进行其他工作:
/bg Analyze the logs in /var/log and summarize any errors from todayHermes 立即确认任务并将提示符还给你:
🔄 Background task #1 started: "Analyze the logs in /var/log and summarize..."
Task ID: bg_143022_a1b2c3工作原理
每个 /bg prompt 会在守护线程中生成一个完全独立的 agent 会话:
- 隔离对话——后台 agent 不了解当前会话的历史。它只接收你提供的 prompt。
- 相同配置——后台 agent 继承当前会话的模型、提供商、工具集、推理设置和回退模型。
- 非阻塞——前台会话保持完全交互。你可以聊天、运行命令,甚至启动更多后台任务。
- 多任务——你可以同时运行多个后台任务。每个任务都有编号 ID。
结果
后台任务完成时,结果会以面板形式出现在终端中:
╭─ ☤ Hermes (background #1) ──────────────────────────────────╮
│ Found 3 errors in syslog from today: │
│ 1. OOM killer invoked at 03:22 — killed process nginx │
│ 2. Disk I/O error on /dev/sda1 at 07:15 │
│ 3. Failed SSH login attempts from 192.168.1.50 at 14:30 │
╰──────────────────────────────────────────────────────────────╯如果任务失败,你会看到错误通知。如果配置中启用了 display.bell_on_complete,任务完成时终端会响铃。
使用场景
- 长时间研究——"/bg research the latest developments in quantum error correction",同时继续编写代码
- 文件处理——"/bg analyze all Python files in this repo and list any security issues",同时继续对话
- 并行调查——同时启动多个后台任务,从不同角度探索问题
信息
后台会话不会出现在主对话历史中。它们是独立会话,拥有各自的任务 ID(如 bg_143022_a1b2c3)。
静默模式
默认情况下,CLI 以静默模式运行,该模式会:
- 抑制工具的详细日志
- 启用 kawaii 风格的动态反馈
- 保持输出简洁易读
如需调试输出:
hermes chat --verboseTUI
TUI 是 Hermes 的现代前端——一个终端 UI(用户界面),与 Classic CLI 共享同一 Python 运行时。相同的 agent、相同的会话、相同的斜杠命令;交互界面更简洁、响应更流畅。
这是以交互方式运行 Hermes 的推荐方式。
启动
# 启动 TUI
hermes --tui
# 恢复最近的 TUI 会话(若无则回退到最近的 classic 会话)
hermes --tui -c
hermes --tui --continue
# 通过 ID 或标题恢复指定会话
hermes --tui -r 20260409_000000_aa11bb
hermes --tui --resume "my t0p session"
# 直接运行源码——跳过预构建步骤(供 TUI 贡献者使用)
hermes --tui --dev也可以通过环境变量启用:
export HERMES_TUI=1
hermes # 现在使用 TUI
hermes chat # 同上Classic CLI 仍作为默认方式保留。CLI 界面中记录的所有内容——斜杠命令、快捷命令、skill 预加载、personality、多行输入、中断——在 TUI 中均完全一致。
为什么选择 TUI
- 即时首帧 — banner 在应用加载完成前就已渲染,因此 Hermes 启动时终端不会出现卡顿感。
- 非阻塞输入 — 会话就绪前即可输入并排队消息。agent 上线后立即发送第一条 prompt(提示词)。
- 丰富的浮层面板 — 模型选择器、会话选择器、审批和澄清提示均以模态面板形式渲染,而非内联流程。
- 实时会话面板 — 工具和 skill 在初始化过程中逐步填充。
- 鼠标友好的选择 — 拖拽高亮时使用统一背景色,而非 SGR 反色。使用终端的常规复制手势即可复制。
- 备用屏幕渲染 — 差量更新意味着流式传输时无闪烁,退出后无滚动历史残留。
- 编辑器增强 — 长片段的内联折叠粘贴、Cmd+V / Ctrl+V 文本粘贴(带剪贴板图片回退)、括号粘贴安全保护,以及图片/文件路径附件规范化。
同样的 skins 和 personalities 均适用。会话中途使用 /skin ares、/personality pirate 切换,UI 实时重绘。完整的可定制键列表及其对 classic 与 TUI 的适用范围,请参阅 Skins & Themes——TUI 支持 banner 调色板、UI 颜色、prompt 字形/颜色、会话显示、补全菜单、选区背景色、tool_prefix 和 help_header。
可折叠的 banner 区块
TUI 启动 banner 将运行时信息分为四个可折叠区块,每个区块标题旁渲染 ▸ / ▾ 折叠箭头:
| | 区块 | 默认状态
| Tools | 展开
| Skills | 折叠
| System Prompt | 折叠
| MCP Servers | 折叠
点击区块标题(或其折叠箭头)的任意位置即可切换展开/折叠状态。Tools 列表默认展开,因为它是会话开始时最常查看的区块;Skills、System Prompt 和 MCP Servers 默认折叠,即使安装了大量 skill 或接入了多个 MCP server,banner 也能保持紧凑。状态仅对当前 banner 实例有效,下次启动将重置为默认值。
环境要求
- Node.js ≥ 20 — TUI 作为从 Python CLI 启动的子进程运行。hermes doctor 会验证此项。
- TTY — 与 classic CLI 一样,通过管道传入 stdin 或在非交互式环境中运行时,将回退到单次查询模式。
首次启动时,Hermes 会将 TUI 的 Node 依赖安装到 ui-tui/node_modules(一次性操作,耗时数秒)。后续启动速度很快。拉取新版 Hermes 后,若源文件比 dist 更新,TUI bundle 将自动重新构建。
外部预构建
发行版若附带预构建 bundle(如 Nix、系统包),可将 Hermes 指向该 bundle:
export HERMES_TUI_DIR=/path/to/prebuilt/ui-tui
hermes --tui该目录必须包含 dist/entry.js。
快捷键
快捷键与 Classic CLI 完全一致。仅有以下行为差异:
- Ctrl+T — 将输入框上方的实时子智能体栏展开为完整 /agents 列表;Enter/t 查看实时日志,d 查看详细信息,e 引导,x 停止选中的工作者。可见行数随终端高度调整,关闭后保留输入草稿。
- F7 — 在多行预览和单行摘要之间切换,保留输入焦点,不写入配置。
- 鼠标拖拽 — 以统一选区背景色高亮文本。
- Cmd+V / Ctrl+V — 优先尝试普通文本粘贴,然后回退到 OSC52/原生剪贴板读取,最后在剪贴板或粘贴内容解析为图片时进行图片附件操作。
- /terminal-setup — 安装本地 VS Code / Cursor / Windsurf 终端绑定,以在 macOS 上获得更好的 Cmd+Enter 和撤销/重做一致性。
- 斜杠自动补全 — 以带描述的浮动面板形式展开,而非内联下拉菜单。
- Ctrl+X — 当排队消息被高亮(在 agent 仍在运行时发送的消息)时,从队列中删除该消息。Esc 取消编辑并取消高亮,但不删除。
- Ctrl+G / Ctrl+X Ctrl+E — 在 $EDITOR 中打开当前输入缓冲区,用于多行/长 prompt 编写;保存并退出后,内容将作为 prompt 发送回来。
斜杠命令
所有斜杠命令均可正常使用。部分命令由 TUI 独有——它们会产生更丰富的输出或以浮层而非内联面板形式渲染:
| | 命令 | TUI 行为
| /help | 带分类命令的浮层,可用方向键导航
| /sessions | 模态会话选择器——预览、标题、token 总量、内联恢复
| /model | 按提供商分组的模态模型选择器,带费用提示
| /skin | 实时预览——浏览时主题变更即时生效
| /details | 切换详细工具调用详情(全局或按区块)
| /usage | 丰富的 token / 费用 / 上下文面板
| /agents(别名 /tasks) | 可观测性浮层——带终止/暂停控制的实时子 agent 树、按分支的费用/token/文件汇总、逐轮历史记录
| /reload | 将 ~/.hermes/.env 重新读入运行中的 TUI 进程,使新添加的 API 密钥无需重启即可生效
| /mouse [on|off|toggle|wheel|buttons|all] | 在运行时选择鼠标跟踪预设(同时持久化到 config.yaml 的 display.mouse_tracking)。wheel(1000+1006)保留滚轮滚动而不产生悬停事件,避免在 tmux 中向 prompt 行发送"No image in clipboard"垃圾信息;buttons 添加 1002 以支持终端侧拖拽选择;all 是带悬停 UI 的默认值。
其他所有斜杠命令(包括已安装的 skill、快捷命令和 personality 切换)与 classic CLI 完全一致。请参阅斜杠命令参考。
LaTeX 数学渲染
TUI 的 Markdown 渲染管线支持内联 LaTeX 数学:$E = mc^2$ 和 $$\frac{a}{b}$$ 渲染为 Unicode 格式的数学表达式,而非原始 TeX 源码。支持内联和块级数学;不支持的语法将回退为显示包裹在代码 span 中的原始 TeX,以保持可复制性。
此功能始终开启,无需配置。Classic CLI 保留原始 TeX。
浅色终端检测
TUI 自动检测浅色终端并相应切换到浅色主题。检测分三层进行:
- HERMES_TUI_THEME 环境变量——最高优先级。可选值:light、dark,或原始 6 位背景十六进制色值(如 ffffff、1a1a2e)。
- COLORFGBG 环境变量——xterm 衍生终端使用的经典"背景色查询"提示。
- 通过 OSC 11 探测终端背景——适用于不设置 COLORFGBG 的现代终端(Ghostty、Warp、iTerm2、WezTerm、Kitty)。
若要无论终端如何都永久使用浅色主题:
export HERMES_TUI_THEME=light忙碌指示器样式
状态栏忙碌指示器可插拔——默认在 agent 工作期间每 2.5 秒轮换一次 Hermes 的 kawaii 表情调色板。通过配置或 /indicator 斜杠命令选择不同样式:
display:
tui_status_indicator: kaomoji # kaomoji | emoji | unicode | ascii或在会话中:/indicator emoji(等)。各样式附带匹配的字形宽度,轮换时状态栏其余部分不会抖动。
自动恢复
默认情况下,hermes --tui 每次启动都会开启新会话。若要自动重新连接到最近的 TUI 会话(在终端或 SSH 连接意外断开时很有用),可选择启用:
export HERMES_TUI_RESUME=1 # 最近的 TUI 会话
# 或:
export HERMES_TUI_RESUME=<session-id> # 指定会话取消设置该变量,或在每次启动时显式传入 --resume <id> 以覆盖。
状态栏
TUI 的状态栏实时跟踪 agent 状态:
| | 状态 | 含义
| starting agent… | 会话 ID 已激活;工具和 skill 仍在上线中。可以输入——消息将排队,就绪后发送。
| ready | Agent 空闲,等待输入。
| thinking… / running… | Agent 正在推理或运行工具。
| interrupted | 当前轮次已取消;按 Enter 重新发送。
| forging session… / resuming… | 初始连接或 --resume 握手中。
各 skin 的状态栏颜色和阈值与 classic CLI 共享——请参阅 Skins 了解自定义方式。
状态栏还显示:
- 工作目录及 git 分支 — ~/projects/hermes-agent (docs/two-week-gap-sweep)。在旁边的终端执行 git checkout 时,分支后缀会更新(mtime 缓存),TUI 反映的是实际活跃分支,而非启动时的分支。
- 每条 prompt 的耗时 — 轮次运行时显示 ⏱ 12s/3m 45s(实时),轮次完成后冻结为 ⏲ 32s / 3m 45s。第一个数字是自上次用户消息以来的时间;第二个是会话总时长。每次新 prompt 时重置。
- 🗜️ N — 当前会话被自动压缩的次数。首次压缩触发后显示。
- ▶ N — 当前会话中正在运行的 /bg 任务数量。至少有一个任务在执行时显示。
- ⚠ YOLO — 每当 YOLO 模式开启时(hermes --yolo、/yolo 或 HERMES_YOLO_MODE=1)显示的可见警告。同一徽章也出现在启动 banner 中,确保你不会在未注意到的情况下启动自动审批会话。
配置
TUI 遵循所有标准 Hermes 配置:~/.hermes/config.yaml、profile、personality、skin、快捷命令、凭证池、内存提供商、工具/skill 启用状态。不存在 TUI 专属配置文件。
少数键专门用于调整 TUI 界面:
display:
skin: default # 任意内置或自定义 skin
personality: helpful
details_mode: collapsed # hidden | collapsed | expanded — 全局折叠面板默认值
sections: # 可选:按区块覆盖(任意子集)
thinking: expanded # 始终展开
tools: expanded # 始终展开
activity: collapsed # 重新启用 activity 面板(默认隐藏)
mouse_tracking: all # off | wheel | buttons | all(或 true/false 以向后兼容)
# wheel — 1000+1006(滚轮+点击;无拖拽,无悬停——
# 在 tmux 内推荐使用,可消除悬停事件导致的
# prompt 行"No image in clipboard"垃圾信息)
# buttons — 添加 1002 以支持终端侧拖拽选择
# all — 添加 1003 以支持悬停(滚动条悬停翻页、
# 链接 mouseenter 等)运行时切换:
- /details [hidden|collapsed|expanded|cycle] — 设置全局模式
- /details <section> [hidden|collapsed|expanded|reset] — 覆盖单个区块
(区块:thinking、tools、subagents、activity)
默认可见性
TUI 附带有主见的按区块默认值,将轮次以实时转录形式流式展示,而非一堆折叠箭头:
- thinking — 展开。推理过程随模型输出内联流式显示。
- tools — 展开。工具调用及其结果以展开状态渲染。
- subagents — 沿用全局 details_mode(默认折叠在箭头下——在实际发生委托之前保持安静)。
- activity — 隐藏。环境元信息(gateway 提示、终端一致性提醒、后台通知)对日常使用来说是噪音。工具失败仍会在失败的工具行内联渲染;当所有面板均隐藏时,环境错误/警告通过浮动警告兜底显示。
按区块覆盖优先于区块默认值和全局 details_mode。调整布局的方式:
- display.sections.thinking: collapsed — 将 thinking 折叠到箭头下
- display.sections.tools: collapsed — 将工具调用折叠到箭头下
- display.sections.activity: collapsed — 重新启用 activity 面板
- 运行时使用 /details <section> <mode>
在 display.sections 中显式设置的内容优先于默认值,因此现有配置保持不变。
会话
会话在 TUI 和 classic CLI 之间共享——两者均写入同一个 ~/.hermes/state.db。可以在一个界面开始会话,在另一个界面恢复。会话选择器显示来自两个来源的会话,并带有来源标签。
会话生命周期、搜索、压缩和导出,请参阅会话。
TUI 如何与其 gateway 通信
默认情况下,TUI 会在进程内启动自己的 gateway,因此每个 TUI 实例是自包含的——无需任何配置。
你可能会在代码或日志中看到 HERMES_TUI_GATEWAY_URL 环境变量。它是 Web 仪表板的内部接线细节,并非面向用户的远程连接开关。当你打开仪表板的 "Chat" 标签页(hermes dashboard → /chat)时,仪表板的 Web 服务器会派生一个内嵌的 TUI 子进程,并注入 HERMES_TUI_GATEWAY_URL,让该子进程通过本地回环 WebSocket(/api/ws)连接到仪表板自己的进程内 tui_gateway。/api/ws 端点仅存在于仪表板服务器内部(hermes_cli/web_server.py),并绑定到该进程的生命周期和认证。
不存在通用的"将任意 TUI 指向任意独立 gateway 端口"的模式。特别是,OpenAI 兼容 API 服务器(hermes gateway / api_server 平台)不提供 /api/ws——它是模型后端接口(/v1/chat/completions、/v1/models 等),并刻意不暴露 TUI 的 JSON-RPC 控制通道。将 HERMES_TUI_GATEWAY_URL 设置为该端口将返回 404。
如果你希望多个界面共享同一组会话,请使用共享的 ~/.hermes/state.db(参见会话)或 Web 仪表板的内嵌聊天(参见 Web Dashboard)——而不是手动设置 gateway URL。
回退到 Classic CLI
不带 --tui 启动 hermes 将继续使用 classic CLI。若要让某台机器默认使用 TUI,在 shell profile 中设置 HERMES_TUI=1。若要回退,取消设置即可。
如果 TUI 启动失败(无 Node、缺少 bundle、TTY 问题),Hermes 会打印诊断信息并回退——而不是让你陷入困境。
另请参阅
- CLI 界面 — 完整的斜杠命令和快捷键参考(共享)
- 会话 — 恢复、分支和历史记录
- Skins & Themes — 自定义 banner、状态栏和浮层主题
- 语音模式 — 在两种界面中均可使用
- 配置 — 所有配置键
Hermes Desktop
The Hermes desktop app is a native app built around the same agent you get from the CLI and the gateway — same config, same API keys, same sessions, same skills, same memory. It is not a separate product or a lightweight clone; it uses the same Hermes Agent core and settings, and drives it through a modern & thoughtfully designed UI. If you have used hermes in a terminal, everything you set up there is already here, and anything you do here shows up there.
It runs on macOS (Apple Silicon), Windows, and Linux — see Platform Support for the full matrix.
Which interface is which?
Hermes has several front ends that all talk to the same agent:
- Desktop App (this page) — a native application with a purpose-built UI for chat, configuration, and management.
- CLI (hermes) and TUI (hermes --tui) — terminal interfaces.
- Web Dashboard (hermes dashboard) — a browser admin panel; its optional Chat tab embeds the TUI through a pseudo-terminal.
Pick whichever fits the moment. They share state, so you can start a session in one and resume it in another.
Install
Download the app from the Hermes Desktop product page, or follow the installation instructions for Hermes Desktop.
If you already have Hermes installed, simply run
hermes desktopThat uses your current config, keys, sessions, and skills.
What's in the app
The desktop app is organized as a chat-first window with a left sidebar for navigation. It's built to allow managing multiple simultaneous agent conversations, configuring messaging providers, creating artifacts, browsing projects' folder structures, and working on multiple projects at once.
Sidebar selection follows the focused chat pane. Opening or focusing a session tab clears a page's highlight, including contributed pages such as Kanban, even when the workspace retains that page's route.
Chat
The center of the app. You get:
- Streaming responses with live tool activity and structured tool-call summaries as the agent works. When a tool works on an image (for example vision_analyze on a file under the backend's filesystem), expand its activity row to see the image and click it to open it full-size; on a remote connection the image is fetched from the gateway, not from the machine running the app.
- Markdown line breaks follow Markdown semantics: two trailing spaces create a hard line break; an ordinary newline stays a soft break. Media and preview extraction preserve text outside removed attachment spans, including first-line code indentation and unfinished fenced-code spacing. Code display and Copy preserve leading blank lines, trailing spaces, and terminal blank lines from the Markdown parser.
- The same conversation history as every other Hermes surface — sessions started here resume in the CLI/TUI and vice versa.
- Drag-and-drop files anywhere in the chat area to attach them to your next message.
- Independent background drafts — hidden chat tabs can update their drafts without moving the caret or selection in the visible composer.
- Unsent drafts survive a lost session — a draft you typed into a chat whose session no longer exists (deleted elsewhere, or a stale tab after a profile rename or wiped backend) is carried into the fresh chat the app falls back to, with an inline Restored your unsent message strip above the input. Undo puts the text back where it was; nothing is sent, navigated, or focused on your behalf, and the strip appears once per lost draft.
- Directive chip actions — hover an actionable reference (such as a URL) to reveal its action pill. A short grace period lets you move from the chip to the pill before it dismisses. The pill stays available while you move within it; after leaving, unrelated pointer movement does not delay dismissal. Clicking its action preserves the draft selection.
- A right-hand preview rail — render web pages, files, and tool outputs side by side while you keep chatting.
- Hide vs. Close for stateful panes — a zone holding a Browser (or HTML preview) or the Terminal pane offers Hide instead of Minimize. Hiding collapses the zone to its restore rail but keeps the pane's body mounted: a live page keeps its unsaved form input, timers, scroll position, and the agent's drive_preview automation target; a terminal keeps its running shell and scrollback; and Restore shows the same content without a reload. The hidden body is inert — it never takes keyboard shortcuts or steals composer focus. Close (the tab's ×) is what actually releases the page or shell.
- Comment mode in the in-app browser — click Annotate in the preview browser bar, then click any element (or drag a box) on the live page and type a note; each saved comment stays as a numbered pin on the page. Saving a pin never sends a turn — when you're done, Add N comments attaches a cropped screenshot per pin and a short prompt naming each comment to the composer, and you still hit send yourself. Each element comment carries its CSS selector, its markup, and the computed styles that matter for layout, so the agent can find the element in your source instead of guessing from the picture. Password and hidden field values, and any attribute that looks like a key or token, are redacted on the page before the markup leaves it. Larger batches arrive grouped by which part of the page each comment sits in, so twenty-odd comments become a handful of pieces of work rather than one task each — and because the groups are separate DOM subtrees they usually touch separate files, which is what makes handing them to parallel workers safe. Pin numbers hold steady if you delete one, and switching chats clears the stack.
- Composer history and queue editing — press the up/down arrow keys in an empty composer to recall and reuse previous prompts, and edit messages you've queued up before they're sent. Pressing Stop (or Esc) while turns are queued pauses the queue and expands it above the composer; resume it from there, or send, edit, and delete individual entries. The queue itself lives on the gateway, so it survives an app or gateway restart and every window attached to the same chat sees the same list. If the gateway restarts while a turn is mid-flight, that turn cannot be finished or safely repeated: the queue shows it as Turn lost during restart with a single Discard button, and the turns queued behind it wait until you discard it. Your text stays in the transcript to resend. (The gateway CLI has the same control as /discard <admission_id>.)
- Task progress above the composer — expand the Tasks header to inspect each phase. Long lists stay bounded above the input; scroll inside the expanded list to reach the final tasks without moving the conversation.
- A conversation timeline rail — long chats get a slim rail of markers along the edge of the transcript, one per prompt. Hover it to pop open the list of prompts, click one to jump straight to that point in the conversation. After a jump, Show earlier at the top of the transcript keeps paging backwards from that prompt all the way to the start of the session. (It appears once the chat has a handful of turns.)
- Reading-position memory — returning to a session restores its saved distance from the bottom instead of always jumping to the latest message. Sessions left at the bottom continue following new output. Use Scroll to bottom to return to the live edge. Positions are kept in this Desktop installation's local storage; they are not synchronized through the backend.
- Find in page — press Cmd/Ctrl+F to open a find bar that searches the rendered chat transcript. Enter / Shift+Enter (or Cmd/Ctrl+G / Cmd/Ctrl+Shift+G while the bar is open) step through matches; Esc closes it.
Async cron and delegation completions appear as collapsed timeline disclosures. Open the completion label to read the result body (including job output) as Markdown; long reports scroll within the disclosure. Task instructions and delivery envelopes are not shown as report content.
Status bar
The bar along the bottom of the chat shows live session state and exposes quick controls without opening Settings:
- Per-session YOLO toggle — flip YOLO on or off for just this session (matching the TUI). YOLO bypasses the dangerous-command approval prompts, so know what you're turning off — see Security → YOLO Mode.
- Context-usage meter — a live "% full" meter of the session's context window. Click it to open the Context Usage popover with a token breakdown by category (system prompt, tool definitions, skills, memory, rules, MCP, subagent definitions, and the conversation itself) so you can see exactly what's eating the window before compression kicks in.
- Cache hit rate and tokens per second — off by default; turn them on from the right-click menu. Cache hit rate is the share of this session's prompt tokens served from the provider's prompt cache (cached tokens cost less, so higher is cheaper — you can watch a session get cheaper as it warms up). Tokens per second is output throughput averaged over the last 10 model calls. Both update live during a turn.
- Customizable items — right-click the status bar (Show in status bar) to choose what appears: the context meter, cache hit rate, tokens per second, workspace, model, approvals, turn/session timers, terminal, Command Center, backend version, and more — or hide the bar entirely (Cmd/Ctrl+Shift+S toggles it). The workspace item's menu offers Open containing folder only when the focused session runs on this computer; a session on a remote gateway keeps its folder on that machine, so use Reveal in filetree instead. When the OS file manager cannot find a path, the app says so instead of doing nothing.
Chatting against a Hermes instance on another machine instead of the bundled local backend? See Connecting to a remote backend below — and for the full picture of how the remote-hosted dashboard connection works (the auth gate, the /api/ws chat socket, and WebSocket close-code triage), see Web Dashboard → Connecting Hermes Desktop to a remote backend.
Fonts
Two independent font settings live in Settings → Appearance, both stored per profile in config.yaml:
- Chat Font (desktop.font_family) — chat and the rest of the app's UI. Readability faces such as OpenDyslexic or Atkinson Hyperlegible work as soon as they are installed on the system; the active theme's stack stays behind your pick so missing glyphs still render. Blank means the theme's font.
- Terminal Font (terminal.font_family) — the embedded terminal pane; Nerd Fonts render shell icons here. Blank means the bundled JetBrains Mono.
Repository discovery
Hermes Desktop discovers local Git repositories for the Projects sidebar by scanning your home directory to a bounded depth. You can change this per profile in Settings → Workspace, or in config.yaml:
desktop:
repo_scan_enabled: true
repo_scan_roots: []
repo_scan_exclude_paths: []- Set repo_scan_enabled: false to stop the filesystem scan completely. Existing disk-discovery cache rows for that profile are cleared; explicit projects and repositories inferred from intentional Hermes sessions remain available.
- Set repo_scan_roots to a list of folders to restrict scanning. An empty list preserves the default home-directory scan.
- Set repo_scan_exclude_paths to folders whose complete subtrees should be skipped.
Changing any of these values invalidates only that profile's disk-discovery cache and starts a policy-compliant refresh. Hide from sidebar remains a separate per-item curation action.
With Group by → Projects, each project row previews its three most recent sessions. A project with more ends in a Show all N sessions row that expands the rest in place; the sidebar menu's Show all sessions toggle removes the preview cap for every project at once.
Choosing a model
The model picker lives in the composer, just left of the microphone. Click it to switch the model; hover a model row for its options (thinking, effort, fast). Next to it, a reasoning pill shows the active model's effort level (Med, High, …) and opens the same options directly, so you can change effort without finding the model's row. The pill is hidden for models whose catalog reports no reasoning control. When the route clamps a Hermes-internal step (ultra is sent as the route's strongest level, e.g. max), the pill shows both ends (Ultra→Max) and its tooltip spells out the same wording as the CLI, Ultra (sends Max on this route), so the level you see is the level that is sent. When the gateway flags a switch as risky (a large cached context, an expensive model, a data-training tier), the app asks first in a dialog: Switch anyway applies it, Keep current model (or Esc) leaves everything as it was.
The microphone is dictation; hover it and the other voice toggles fan out above it — Read replies aloud and the wake word ear. A toggle that is on shows as a solid disc. Starting a full voice conversation stays on the primary button to the right. In the HUD and in narrow tiles the same controls fold into one menu behind the mic instead. When dictation talks to the speech-to-text provider directly (client-direct voice), the request honours the same stt.openai.timeout budget (default 60 s) as the gateway's own transcription client, so a slow endpoint fails with "Transcription timed out" instead of leaving the mic stuck on transcribing.
- The composer picker is sticky UI state and never touches your default. It's remembered locally (per device) and follows across new chats and restarts instead of snapping back to the default — pick a model once and the next Cmd/Ctrl+N opens on it. With a live chat, switching models scopes the change to that current chat; either way the selection rides along when the session is created/switched and is never written to the profile default — with one exception: on a fresh profile that has no model.default/model.provider configured yet, the first pick is persisted so the app has a real default instead of falling through to a stray API-key env var on restart. Persistence follows the same rule as /model (model.persist_switch_by_default); use Settings → Model to change the default deliberately. (Switching profiles reseeds to that profile's own default.)
- Set the default in Settings → Model → Main model. Choose a provider and model in their labeled rows, then click Apply below the model selector. Controls align to the right on wide windows and stack below their labels on narrow windows. The Defaults section has separate reasoning and speed controls where the model supports them. That "main" model is your per-profile global default, which new chats, crons, subagents, and auxiliary tasks start from. Each profile keeps its own default.
- Per-model effort/fast presets. Each model remembers its own reasoning effort and fast-mode choice in the desktop app, re-applied to the session whenever you pick that model. These presets are a desktop convenience and don't change crons or subagents.
- Favorite models. Click the star on the left of a model row (or shift-click the row, the same gesture that pins a chat in the sidebar; Shift+Enter from the search box) to lift it into a Favorites section at the top of the picker. Starring never selects the model or closes the menu, so the same gesture undoes it. The section only appears once something is starred. Favorites show in the composer, in session tiles, and anywhere else a model is picked, because they are one stored preference. They persist per device, keep the order you starred them, and ignore the Edit models shortlist (a star IS an explicit "always show me this one"); a favorite for a provider that is not connected is kept and reappears when it reconnects. Searching is unaffected — a query lists every match in its provider's place.
- Mid-chat switches reset the prompt cache. Switching the model inside a live chat means the next message re-reads the whole conversation at full input price (provider prompt caches are keyed to the model). Fine occasionally; on a long chat, a fresh chat on the new model is often cheaper than bouncing back and forth.
Subagent models and limits
Open Settings → Model → Subagents to choose a delegated agent's provider and model and adjust its turn limit, concurrency, timeout, and reasoning effort. Provider and model dropdowns have separate labeled rows, with controls aligned to the right on wide windows and stacked below their labels on narrow windows. The catalog and saved changes belong to the profile selected under Applies to.
Provider and model changes remain a local draft until you click Apply. Cancel, leaving the page, or changing the settings profile discards an unapplied route. A provider switch requires a model; select Use parent model explicitly for a provider-only override, and make sure that provider supports the parent's model.
Inherit from main agent uses the parent's model and credentials. Custom model (use parent credentials) overrides only the model. Existing direct endpoints are shown as Direct endpoint override. Applying a different route clears the superseded delegation endpoint, its stored key and its wire protocol together, without removing other request settings. If the catalog is unavailable, use Custom provider... and Custom model… to enter a route manually, or click Retry without losing your draft.
File browser
Explore and preview the working directory without leaving the app — useful for following along as the agent reads, writes, and edits files. Set the initial project directory with hermes desktop --cwd <path> (or the HERMES_DESKTOP_CWD environment variable).
Artifacts
Preview links above the composer are session suggestions, not a task-completion checklist. Dismissing one keeps historical tool rows from bringing it back after navigation or reload. A new successful tool completion can offer the file again. Read-only file inspection and failed writes do not create suggestions. Files with the same name show enough directory context to distinguish them; dismissing a suggestion does not delete its file or transcript. Changing a /goal does not erase a conversation's artifacts.
When connected to a remote gateway, opening a file artifact downloads it through that gateway, using the artifact’s originating profile and session. Relative paths resolve against the session’s saved working directory; home-relative paths use the gateway’s home, never the Desktop machine’s home. Windows-style relative paths are recognized alongside forward-slash paths, and file URIs retain drive and network-share information for the gateway to interpret. Missing sessions or working directories produce an error rather than selecting a different local file.
If Desktop closes before an image message is acknowledged, reopen its original chat. The Restore image draft button above the composer restores the saved text and image attachment together; press Send to retry the original submission. It never sends automatically or replaces a newer draft.
The Artifacts view collects what your sessions generate — images, files, and links — into one searchable, browsable gallery. Open it from the sidebar, the command palette (Artifacts — Browse generated outputs), or a nav.artifacts shortcut you bind yourself. It indexes recent session outputs automatically; every artifact shows which session produced it with a jump back to that chat, and images and files open in a preview with download / open-in-browser / copy actions.
Windows, tabs & panes
The app is built for working on several things at once:
- Tabs — Cmd/Ctrl+T opens a new session tab; Ctrl+Tab / Ctrl+Shift+Tab cycle sessions, and Ctrl+1…9 jump to a recent session by position. Cmd/Ctrl+W closes the focused tab — except over an interactive terminal, where it keeps its shell meaning (word erase) — and Cmd/Ctrl+Shift+T reopens the last closed one.
- Multiple windows — Cmd/Ctrl+Shift+N opens a new window, and any session can be popped out via its context menu (New window) or from the command palette. A popped-out window renders that single chat without the global sidebar — handy for parking a long-running session on another monitor. Live agent output streams into every window showing the session.
- Panes — Cmd/Ctrl+B toggles the left sidebar, Cmd/Ctrl+J the right one, and Cmd/Ctrl+\ swaps which side the sidebars sit on.
Interface mode
The layout editor (titlebar button, or Cmd/Ctrl+Shift+\) opens with an Interface mode choice — also under Settings → Appearance → Window & layout and as Simple mode in the command palette. It changes what is shown, not what Hermes can do.
- Advanced (default) is the app as you have set it up. Users who have not explicitly selected Simple stay in Advanced.
- Simple is chat-first: the statusbar, profile rail, terminal, file browser and review panes, the technical tool-call view, inline code diffs, and the Artifacts / Scheduled jobs rows rest out of the way (Capabilities and Messaging stay — they are how you set Hermes up); thinking starts collapsed; session rows show the title, a preview and when they were last active. The titlebar keeps Settings and the layout editor. Simple's layout picker offers Sidebar left or Sidebar right. The templates and saved layouts are Advanced.
A layout says what is on screen, not just where things sit: applying one opens every pane it places and closes the ones it leaves resting, so Ctrl+**, **Cmd/Ctrl+J** and **Cmd/Ctrl+G** always agree with what you see. *Basic* is sessions and chat with the terminal resting as a collapsed rail under the chat and the file browser and review resting in a right column — **Ctrl+ opens the terminal under the chat, Cmd/Ctrl+J opens the tree on the right. Focus keeps files and review as tabs behind the chat, with the same terminal rail. Default, Terminal deck and Quad open everything they place. A layout you save remembers which of its panes were closed.
Each mode remembers its own arrangement: pane positions, sizes, active tabs, hidden tabs, dismissals, collapsed sides and floating-card positions. Returning to a mode restores that arrangement rather than reapplying a preset. Simple starts with the sidebar on the left; moving it to the right does not change Advanced. Conversations, drafts, previews and running work stay shared.
Existing layouts are retained on upgrade. Advanced continues using the original storage keys. If you already explicitly selected Simple, its current layout is copied into Simple's separate storage without deleting the originals. An older arrangement that was overwritten before this separation cannot be reconstructed.
Simple shadows your display preferences instead of overwriting them. Every keybind still works in Simple — Ctrl+`, Cmd/Ctrl+J and Cmd/Ctrl+G open the terminal, file browser and review for the current session, and the next launch is quiet again. With more than one profile the profile rail stays, since it is then the only way to switch. First-run onboarding sets the mode from the layout you pick: Basic starts in Simple, Elite in Advanced; skipping leaves it on Advanced.
Minimize to tray
Enable Settings → Appearance → Window layout → Minimize to tray to hide minimized windows from the taskbar or Dock while their sessions keep running. The setting is off by default and applies only to this device.
With the setting enabled and a tray available, closing the main window with X or Alt+F4 hides it without stopping Hermes or destroying its session. Closing secondary windows still closes those windows. Use Show Hermes in the system tray (the menu bar on macOS) to restore hidden windows. Quit Hermes from the tray menu and Cmd+Q still quit, including the normal active-work confirmation. If the tray is unavailable, closing the main window behaves normally.
On macOS, the Dock icon hides only when no ordinary Hermes window remains visible. On Linux, a registered StatusNotifier tray host is required; desktops without one keep normal minimize behavior. If the host disappears, hidden windows are restored.
Terminal
A real terminal lives in the right sidebar, next to the file browser:
- Ctrl+** shows the terminal (opening one if none exist); **Ctrl+Shift+ spawns an additional one. Multiple terminals stack in a tab rail — Ctrl+Shift+↓/↑ walk between them, Ctrl+Shift+W closes the active one.
- Shells persist while hidden. Closing or hiding the panel doesn't kill your shell — every open terminal stays mounted with its scrollback and running processes intact until you explicitly close it.
- Add to chat — select terminal output and send it into the composer as context for your next message.
Live subagents
While delegated workers are live, a Subagents frame appears above the composer with their count, task names, elapsed time, and latest activity. It previews up to three workers; expand the header for the roster, then select a worker for details and Steer / Stop controls. Each frame belongs to its chat, including in split panes. Steering acknowledges that guidance is queued for a checkpoint, not that the child has already read it. See Monitoring subagents.
Git review & worktrees
For sessions running inside a Git repository, the app has a built-in source-control surface:
- Review pane — Cmd/Ctrl+G toggles the working-tree review pane: branch and ahead/behind status, changed files (list or tree view), and diffs scoped to Uncommitted, Branch, or Last turn (just what the agent changed in its most recent turn). Stage/unstage files, revert changes, write a commit message (or Generate commit message), then Commit or Commit & Push — and Create PR via the GitHub CLI (gh), or hand the whole thing to the agent with Ask Hermes to open PR. You can also create and switch branches from here. The Last turn scope shows everything changed since the most recent turn in this repo began (including any commits the agent made mid-turn); it is empty until a turn has run here. The Branch and Last turn scopes are read-only — stage, revert, and commit only apply to uncommitted changes.
- Worktrees — Cmd/Ctrl+Shift+B (or New worktree on a project in the sidebar) creates a Git worktree on a new branch so an agent can work on a parallel copy of the repo without touching your checkout. Worktrees show up as their own lanes under the project; removing one offers to delete the worktree directory (the branch stays) or just hide the lane and leave it on disk, with a force option when it has uncommitted changes. A chat also follows the agent: when you ask it to make a worktree and work in it, the chat moves to that lane at the end of the turn and the sidebar re-scopes with it. Only a workspace you deliberately switched the chat to (the folder picker, or a project switch) stays put.
Memory Graph
The Memory Graph (command palette → Memory Graph, or the status-bar item) is an interactive map of what Hermes has learned for you — skills and memories laid out as a zoomable node graph with a timeline, filterable by All / Used / Learned. A share control exports the map layout as a compact code you can paste to someone else (layout only — none of your memory or skill text is included) and imports codes the same way.
Quick Entry
Quick Entry is a small always-available composer summoned by a global hotkey from anywhere on your system — fire off a prompt without switching to (or even opening) the main window. Enable it in Settings → Advanced → Quick Entry; the default shortcut is Ctrl/Cmd+Shift+Space and you can set your own (it needs at least one modifier). If another app already owns the chord, the settings row tells you so you can pick a different one.
Voice
Talk to Hermes and hear it back, the same voice mode available elsewhere. On macOS the OS will prompt once for microphone access.
HUD mode
⌘/Ctrl+Shift+H (or the titlebar button) detaches the chat into a chrome-free, always-on-top floating bar that sits over whatever you are working in. The app window steps aside; the HUD keeps your live conversation and a composer. Where you park it is context — the bar's position tells Hermes which app and screen you're asking about, so "this", "here", and "that page" resolve to what's underneath it.
- Moving the bar — on macOS and Windows, press and hold anywhere on the composer for a beat, then drag. On Linux/X11, hold Ctrl and drag with the primary mouse button for an immediate grab (including over selected text); press-and-hold remains available too. Keep the grab held while invoking your desktop switch shortcut to carry the HUD onto another virtual desktop. On native Wayland the composer bar is a compositor drag handle (the only way to move it, because an app cannot place its own window).
- Resizing — drag any edge or corner of the bar; the opposite edge stays anchored. Native Wayland exposes the right and bottom edges because the compositor does not allow apps to position top-level windows themselves.
- Reset layout — the discard control on the bar restores the default size and (on X11 / macOS / Windows) position. Use this if a persisted size leaves the HUD unusable.
- Tap to summon — enable Tap to summon HUD under Settings → Keyboard Shortcuts → HUD gesture, then tap and release ⌘+Option on macOS or Ctrl+Alt on Windows/Linux X11. Release both keys within half a second without another key or mouse action. On Windows, use left Alt; right Alt is reserved for AltGr layouts. This opens or focuses the HUD from another app; it does not toggle it closed, record audio, or send a message. Off by default and saved only on this device. macOS requires Input Monitoring permission; the page offers recovery controls only when permission or another error needs attention. Linux Wayland does not expose this gesture, so keep using the in-app HUD shortcut there.
- Snap to pointer — ⌘/Ctrl+Shift+G (a global hotkey, works from any app) jumps the HUD to wherever your cursor is. On native Wayland this is a no-op — the compositor owns placement.
- Exiting — click the exit button on the bar, press ⌘/Ctrl+Shift+H again, or press ⌘/Ctrl+W while the HUD has focus. The app window comes back in front with your session and the caret in its composer.
Linux / Wayland
On a local Wayland session (XDG_SESSION_TYPE=wayland, or WAYLAND_DISPLAY set) Hermes launches with --ozone-platform=wayland so Electron does not fall back to XWayland. The platform has to be on the process command line before application JavaScript loads. An explicit --ozone-platform, desktop.ozone_platform_hint: x11, or an ozone platform in desktop.electron_flags still wins. Drag, click-through, and resize work on the native Wayland path.
On Hyprland (including Omarchy) the HUD is floated and pinned through the compositor's IPC after it maps — otherwise Hyprland tiles it like any other window, always-on-top is ignored, and compositor drag does nothing. No extra window rule is required.
A few compositors (notably COSMIC) ignore always-on-top for native Wayland windows. To restore pinning there, run the app under XWayland:
desktop:
ozone_platform_hint: x11That bridges to ELECTRON_OZONE_PLATFORM_HINT at launch (an explicit env var still wins). The trade: X11 cannot restore a window that has ignored the mouse, so the HUD stays a solid window instead of click-through. Some KDE setups also report keyboard breakage with the X11 ozone backend — leave the hint on auto unless you need always-on-top.
WSLg (Windows GPU from WSL2)
Under local WSLg, Hermes launches with --ozone-platform=wayland to avoid the XWayland maximized-window offset and shifted mouse hit-testing (microsoft/wslg#1015). The platform must be selected at process launch, before Electron loads application JavaScript. Explicit --ozone-platform=x11 and desktop.ozone_platform_hint: x11 remain available. The app draws its own minimize, maximize and close controls on WSLg.
When hermes gui runs inside WSL2 with /dev/dxg present and Mesa's d3d12_dri.so installed, the launcher sets GALLIUM_DRIVER=d3d12 for Electron so rendering uses the Windows GPU instead of the llvmpipe software rasterizer; an explicit GALLIUM_DRIVER, MESA_LOADER_DRIVER_OVERRIDE, LIBGL_ALWAYS_SOFTWARE, or LIBGL_DRIVERS_PATH in your environment is left untouched (for example GALLIUM_DRIVER=llvmpipe hermes gui keeps software rendering).
Launch flags and the renderer heap ceiling
Two desktop.* keys reach Chromium at launch on every path — hermes desktop, the Start-menu shortcut and the Linux .desktop entry alike (the app reads them from config.yaml before its first window opens):
desktop:
electron_flags: ["--ozone-platform=x11"] # extra Chromium switches; a single string is split on spaces
renderer_max_old_space_mb: 2048 # V8 heap ceiling for the chat renderer; 0 = Chromium defaultrenderer_max_old_space_mb is applied as --js-flags=--max-old-space-size=N and merged with any --js-flags you already pass, so neither overwrites the other. Set it when a very long, tool-heavy session drives the renderer past what the machine can spare: the renderer then hits its own limit and reloads (bounded to three reloads per minute) instead of freezing the whole machine.
Both keys must be written exactly as shown — two spaces of indentation under a top-level desktop: key, and four spaces before the - of a block list:
desktop:
electron_flags:
- "--ozone-platform=x11"
- "--js-flags=--expose-gc"The pre-window reader is a small YAML subset, not the full parser the rest of Hermes uses, because it has to run before the app loads anything. Other indentations are valid YAML but are ignored here; when that happens the app logs desktop.electron_flags / desktop.renderer_max_old_space_mb were ignored at startup and launches with Chromium's defaults.
Settings & onboarding
Manage providers, models, tools, and credentials from a real UI instead of editing YAML. First-run onboarding gets you to your first message in seconds. The settings panes cover providers/keys, model selection, toolset configuration, MCP servers, the gateway, and session management.
- Providers settings pane — a dedicated place to manage inference providers, with an Accounts / API-keys UX for signing in and storing credentials per provider. Accounts, API keys, and Custom Endpoints share the Settings Applies to selection: credential reads and edits, OAuth account removal, custom-endpoint save/test, and sign-in launched here target the selected profile, not the active chat profile. The sign-in flow keeps that target through credential saving and model selection. Changing Applies to discards unsaved credential drafts. Closing sign-in cancels polling and ignores late results; a credential write already sent may still finish in its original profile. Externally managed CLI credentials use their own CLI and are not covered by this profile selector. Its Local Models view installs and manages an on-device llama.cpp runtime — see Local Models.
- Every provider and model in the menus — the GUI surfaces the full provider list and every model that hermes model knows about, so you pick from the same catalog the CLI sees rather than a curated subset.
- Custom endpoints with an API mode — Settings → Providers → Custom Endpoints has an API Mode selector (Auto-detect, Chat Completions, Responses API, Anthropic Messages) — the same choice hermes model offers for a custom provider. It is saved as providers.<id>.api_mode in config.yaml, so a Responses-only or Anthropic-compatible host is no longer called on /chat/completions. Test checks the transport you will actually use, not just /v1/models: it sends a one-token request to the pinned mode's route (or to the mode Auto-detect resolves to) and fails with the transport named when the host does not serve it. Test also keeps the alias metadata a gateway advertises in /v1/models (canonical_model, reasoning_effort): picking an alias such as gpt-5.6-sol-high saves the canonical model and pins its effort under agent.reasoning_overrides.
- xAI Grok OAuth — Grok is a first-class OAuth provider in the launcher; sign in through the browser flow like the other OAuth providers.
- Tool-backend installs from the GUI — run a tool backend's post-setup install steps directly from the app instead of dropping to a terminal. In the terminal backend picker, selecting a backend marked Needs setup asks for confirmation first; declining leaves the current backend selected.
- Terminal font picker — choose an installed font in Settings → Appearance. Nerd Fonts such as MesloLGS NF render Powerlevel10k separators and icons in both interactive and agent terminals; the setting is saved per profile.
- Reasoning Blocks — Settings → Chat → Reasoning Blocks (display.show_reasoning in config.yaml) shows or hides the model's thinking in the transcript (off shows answers only). Open chats update as soon as the setting saves. Typing /reasoning hide or /reasoning show in the composer flips the same setting and the open transcript follows immediately.
- Reopen Last Chat on Launch — by default the app picks up where you left off on cold start. Turn it off in Settings → Appearance (or set display.resume_last_session: false in config.yaml) to always begin with a fresh chat. Deep links and explicit destinations are never overridden either way.
- Auxiliary-model warning — if you switch the main model to a new provider while auxiliary tasks (titling, summarization, and similar helpers) are still pinned to another provider, the app warns you so you don't unknowingly split work across two providers.
- Per-task reasoning effort — each row under Settings → Model → Auxiliary models has a reasoning selector next to its provider/model pick: a level, Off, or inherit · main model effort (the default, which removes the task's override). It is saved as auxiliary.<task>.reasoning_effort in config.yaml, the same key hermes model writes, and shows in the row's summary when set. Use it to run frequent helpers such as compression or titling at low or no reasoning while the main agent stays at high.
- VS Code Marketplace themes — beyond the built-in theme presets, the appearance settings include a live VS Code Marketplace search: pick any color theme and the app downloads, converts, and installs it as a desktop theme. The same importer is available from the command palette (Install theme), and imported themes can be removed again from the appearance settings.
- Keep computer awake — Settings → Advanced → Keep computer awake stops the machine from sleeping so long or overnight agent runs keep going (the display can still dim). While working holds the machine awake only while a turn is in flight and lets it sleep normally once the run finishes or fails; Always holds it whenever Hermes is open. While working is the default on a computer where nobody has picked a mode. If you turned it Off and a timed sleep interrupts a turn, Hermes offers Keep awake while working once on wake. Closing the lid or choosing Sleep still sleeps the machine, and on battery Windows may still sleep after its own timeout. This is a per-computer setting.
First-run onboarding has been redesigned on a unified overlay design system, and you can pick Choose provider later to skip provider setup and get into the app first.
Per-profile settings: the "Applies to" scope
When you have two or more profiles, the config-backed settings pages — Model, Workspace, Safety, Memory & Context, Voice, Chat, Advanced, and Tools & Keys — plus Providers → Custom Endpoints and the Messaging overlay show a shared Applies to chip row at the top. It selects which profile your edits target:
- The default selection follows the active profile, which behaves exactly as before — edit the profile you're using.
- Pick another profile to view and edit its settings without switching the whole app; the selection persists as you move between settings pages.
- Switching the app's active profile resets the selector, so edits can't silently keep landing on a previously selected profile.
- With fewer than two profiles the chip row is hidden entirely.
(The Gateways page handles profiles differently — via its Per-profile overrides subsection — and the Capabilities and Scheduled Jobs views have their own scope selectors.)
Management panes
The app also surfaces the broader Hermes management surface so you don't have to drop to a terminal:
- Skills — browse, install, and manage skills. The Skills tab lists your installed skills with enable/disable toggles, and below them the full built-in optional-skills catalog that ships with Hermes — each entry has a one-click Install button that flips the row into the installed list once it finishes. The public Skills Hub's Install in Hermes buttons open hermes://skill/install?identifier=... links that install through the same pipeline after you confirm (see skill links).
- Memory graph (Star Map) — type /journey (aliases /learning, /memory-graph) in chat to open an interactive constellation of learned skills and memories over time, with a playback scrubber. Nodes can be edited or deleted right from the panel (skills are archived, memories removed). See Learning Journey.
- Cron — view and manage scheduled jobs. With All profiles on, the list aggregates every profile's jobs; a job's run history and actions (pause, resume, edit, delete) always go to the profile that owns the job, whichever profile is active.
- Profiles — switch between Hermes profiles (isolated config/skills/sessions).
- Messaging — set up gateway channels. Telegram has a Quick setup card: click Create with QR, scan the code (or open the link) in Telegram, and Hermes creates the bot, detects your user ID for the allowlist, saves the credentials, and restarts the gateway for you. Any credential save, clear, or enable toggle keeps a Restart now banner on the page until the gateway has actually restarted; if a restart fails, the banner stays so you can retry or restart manually.
- Agents and Command Center — orchestration surfaces for multi-agent work.
Bot Mode (built in)
Bot Mode ships with the app and is on by default: a "one chat per agent"
roster where every Hermes profile appears as a bot with its
own avatar (geometric face, uploaded image, AI-generated portrait, or a pixel
pet), its own canonical Bot Chat conversation, and its own Routines
(recurring tasks backed by Hermes cron). The roster lives in the left
sidebar as a tab next to your conversations — a Sessions | Bots tab
strip — rather than a second pane stacked below the session list. Installs
that picked up the older stacked layout are re-homed into the tab strip
automatically, once; if you've hand-placed panes yourself, your layout is
left alone. The Cronjobs (Routines) pane docks beside the chat only
while the Bots tab is active and disappears when you switch back to
Sessions (older desktop builds keep it always visible).
Create new agents from the roster —
Name / Title / Description plus an Advanced disclosure with the full
capabilities surface (model, SOUL, skills, toolsets, MCP servers) — group
them into sections, and open group chats where several bots deliberate.
Group chats appear as standalone Discord-style rows in the roster — stacked
member avatars, member count, a preview of the latest room line, and the
"needs you" badge — interleaved with the bot rows in the same pin+recency
ordering. Clicking a group row opens the room as a tab that takes over the
main chat window (older desktop builds fall back to opening it inside the
bots side panel).
Bots message each other: type @researcher have a look at this in any chat
and the active bot hands the message off and reports back, and bots reach
each other's Bot Chats directly (hermes -p <bot> chat). The backend teaches
each bot's canonical Bot Chat session the messaging protocol
automatically (config agent.bot_mode_protocol, default on) — including
when a teammate bot opens it headlessly from the CLI — so bot-to-bot
replies and handoffs work without touching your SOUL.md, and your regular
sessions stay untouched.
Bot Mode's sessions — each bot's canonical Bot Chat and every group-chat
member session — are always hidden from the global Sessions sidebar. They
live in the Bots pane (roster rows, room views, and each bot's session
browser) instead of interleaving with your own conversations.
Bots you don't use can be tucked away: right-click a bot row → Hide
Bot. Hidden bots leave the roster but keep working — @mentions still
resolve and group-chat membership is untouched. An eye toggle appears in
the Bots header whenever at least one bot is hidden; click it to reveal
hidden bots dimmed in place (right-click → Unhide Bot brings one back),
and the eye shows a dot when a hidden bot has unread activity. Hidden
state is stored in the bot's profile, so it follows the bot across
machines.
Don't want it? Flip its Desktop switch off in Capabilities → Plugins → Bots — the roster,
routines pane, and composer middleware unregister live, no restart needed.
Full guide — creating agents (including the multi-machine Create on
picker), the roster across connections, bot-to-bot mentions, and how group
chats decide who replies: Bot Mode: A Roster of Agents.
Keyboard & navigation
- Command palette — press Cmd+K or Cmd+P (Ctrl+K / Ctrl+P on Windows/Linux) to jump to actions and navigate the app from the keyboard: open any page or settings section, jump to a session by title or id, switch model/theme/color mode, spawn a terminal, restart the gateway, update Hermes, and more.
- Rebindable shortcuts — Settings → Keyboard Shortcuts (or Cmd/Ctrl+/) opens the shortcuts panel where you can remap almost every binding — profile switching, session navigation, view toggles, and any shortcuts contributed by desktop plugins. Duplicate assignments are flagged as conflicts. A few defaults worth knowing: Cmd/Ctrl+N new session, Cmd/Ctrl+. Command Center, Cmd/Ctrl+, Settings, Cmd/Ctrl+Shift+F search sessions, Cmd/Ctrl+1–9 switch to the Nth tab of the pane under the pointer (or the focused pane) and switch profiles when no pane has a tab strip, Shift+X toggle light/dark.
- Custom zoom shortcuts — zoom the interface in half-step increments for finer control over text size.
- UI language switcher — change the app's interface language in-app: English, Simplified Chinese (zh-Hans), Traditional Chinese (zh-Hant), Japanese, Arabic (RTL), and Russian.
Sessions & profiles
- Session-list overhaul — a reworked session list with archiving and general session hygiene to keep the list manageable as it grows.
- Search sessions by id — find a specific session directly by its id.
- Concurrent multi-profile sessions — run sessions across multiple profiles at the same time, and reference a session in another profile with cross-profile @session links.
- Export / import a profile — share a whole setup as a single file. ⌘K → Export profile… (or right-click a profile square in the rail) writes a .tar.gz with skills, memory, persona, crons, plugins, and settings; API keys are stripped. Exporting from the desktop also bundles your appearance and interface — skin, light/dark mode, custom themes, the profile's rail color, and your window layout — so an imported profile arrives looking the way the sender had it. Import via ⌘K → Import profile… or the button beside the rail's +; it applies the overlay and drops you into the new profile. The same archive works with /export / /import in chat and hermes profile export / import from a shell. See Export and import a profile file.
Updating
The app checks for updates in the background and offers a one-click update when one is ready.
The background check asks the GitHub API for the branch tip. Anonymous GitHub requests are limited to 60 per hour per network address, so on a shared connection (office NAT, VPN, proxy) the check can report GitHub API rate limit reached even though this machine made almost none of them. To spend a 5,000/hour budget instead, the check uses the first credential it finds, in this order:
- GITHUB_TOKEN, then GH_TOKEN, from the environment the app was launched from — read on each request, never stored.
- The GitHub CLI's own login (gh auth token). This is the rung that helps an app started from the Dock, Finder, or a desktop launcher, which inherits a minimal environment without your shell's variables. gh is looked up on PATH and in the usual install locations (Homebrew, /usr/local/bin, ~/.local/bin, the Windows GitHub CLI installer); the answer is cached until the app restarts, so gh runs at most once per session.
- Anonymous.
A credential GitHub rejects (HTTP 401 — expired or revoked) is logged once in desktop.log, naming its source and never the token, and the request is retried anonymously. Applying an update uses git, not the API, and is unaffected.
During a local update, detailed build output streams into the active profile's
logs/update.log, including detached --gateway updates. It stays out of the
terminal but is available for troubleshooting before the build finishes. The
Windows hand-off counts new output in this log as progress; a child that produces
no output is still subject to the idle watchdog. Process liveness alone does not
reset that watchdog, and cancelling an update does not wait for its build to finish.
The desktop app and the Hermes backend it talks to update on separate clocks — the app package on your machine, the backend wherever it runs. When more than one update target exists (a remote gateway, or several registered gateways), the update affordances (Update now on the About panel, the ⌘K Update Hermes row, and the update-ready toast) update everything: the connected backend first, then every other eligible registered gateway (Hermes Cloud entries are platform-managed and skipped), and the desktop app itself last, since applying the client update relaunches the app. Single-machine installs keep the one-button experience.
After any backend update, the app also re-checks its own version and warns with a one-click Update desktop app action if the GUI is still behind — so updating a remote backend can never silently leave you on a stale desktop build.
The manual update process also works with the GUI.
Uninstalling
For installations managed by the app, open Settings → About → Danger zone and pick how much to remove:
- Uninstall Chat GUI only — removes the desktop app and its data; the Hermes agent, your config, and your chats stay. (Same as hermes uninstall --gui.)
- Uninstall GUI + agent, keep my data — removes the app and the agent but keeps config, chats, and secrets for a future reinstall. (Same as hermes uninstall.)
- Uninstall everything — removes the app, the agent, and all user data. (Same as hermes uninstall --full.)
The app closes to finish the job (the cleanup runs after it exits so it can remove the running app bundle and its own venv). The agent-removing options are hidden automatically when no local agent is installed.
These controls are hidden for Nix, bundled packages, and other externally owned installations. Remove those through their package manager or the operating system instead. The app checks its own local package ownership, independently of a remote backend's update status. If ownership cannot be confirmed, no uninstall actions are offered.
For self-managed installations, you can do the same from the terminal — hermes uninstall --gui for the GUI alone, or hermes uninstall / hermes uninstall --full for the agent too.
Preview GUI cleanup with hermes uninstall --gui --dry-run. It lists the removal targets and returns without prompting or removing anything, including when --yes is also supplied.
备注
Running hermes uninstall --gui from a source checkout (a hermes desktop dev build) removes apps/desktop/{dist,release,node_modules} and the desktop build stamp. The workspace-root node_modules stays installed because it is shared with the TUI, dashboard and other workspaces. Rebuild the GUI with hermes desktop if you need it again.
hermes update keeps a desktop app current once it has been built in the checkout, even on a server that never opens it. On a headless Linux host, or when the desktop app's own Node dependencies fail to install (for example a compiler too old for node-pty), the update still builds the TUI and web UI, reports the desktop build as owed, and suggests hermes uninstall --gui. After that uninstall, later updates skip the desktop app.
CLI reference: hermes desktop
To launch via the CLI, simply run hermes desktop. By default it installs workspace Node dependencies, builds the current OS's unpacked Electron app, then launches that packaged artifact.
On Linux, launches refresh $XDG_DATA_HOME/applications/hermes.desktop (by default ~/.local/share/applications/hermes.desktop) so Hermes appears in the application menu. To preserve a hand-edited entry, disable refreshes:
hermes config set desktop.manage_launcher_entry falseA missing entry is still created; the flag only stops hermes desktop from rewriting an entry that already exists.
When you start Hermes from the application grid or menu (the launcher sets DESKTOP_STARTUP_ID), the entry is written only after the window is on screen. If the app exits before a window appears, nothing is written that time; the next terminal launch, updater relaunch, or grid launch that shows a window installs it. Some GNOME Shell versions lose track of an app whose .desktop file changes while it is still starting (they keep it in that state until the startup notification completes or times out, not until the process exits), and that can crash the whole session later; waiting for the window avoids it. Terminal launches and the updater's relaunch still write the entry immediately.
| | Flag | Description
| --skip-build | Skip npm install/package and launch the existing unpacked app from apps/desktop/release
| --force-build | Force a full rebuild even if the content stamp matches
| --build-only | Build the desktop app but do not launch it (used by hermes update)
| --source | Launch via electron . against apps/desktop/dist instead of the packaged app
| --cwd PATH | Initial project directory for desktop chat sessions (sets HERMES_DESKTOP_CWD)
| --hermes-root PATH | Override the Hermes source root the app uses (sets HERMES_DESKTOP_HERMES_ROOT)
| --ignore-existing | Skip the installed Hermes runtime so no local backend starts; offer connect or install
| --fake-boot | Enable deterministic boot delays for validating the startup UI
How it works
The packaged app ships the Electron shell and a native React chat surface. On first launch it can install the Hermes Agent runtime into HERMES_HOME (~/.hermes, or %LOCALAPPDATA%\hermes on Windows) — the same layout a CLI install uses, which is why the two are interchangeable. Bundled apps use their included backend. Without a bundled backend, resolution honours HERMES_DESKTOP_HERMES_ROOT, then the development checkout, then an explicit HERMES_DESKTOP_HERMES command override for packagers such as Nix, and finally a usable managed install. The command override takes precedence over the managed install so a Nix desktop cannot silently launch an older mutable runtime. The React renderer talks to the profile's gateway: on first use of a local profile the app runs hermes gateway ensure, which attaches to the gateway that serves that profile or, when there is none, starts it (an already-installed systemd/launchd service where one exists, otherwise a detached daemon; ensure never installs a service). One gateway per host serves every profile unless a profile is marked gateway.standalone: true, and Desktop dials it over /api/ws with one-use tickets — the same process hermes chat, the TUI and messaging channels use. That gateway is not tied to the app's lifetime: it keeps running after you quit and has no idle shutdown; stop it with hermes gateway stop (or the gateway controls in Settings). The desktop app is self-contained: it needs no separate hermes serve process and never opens or requires the web dashboard. Install, backend-resolution, and self-update logic live in the Electron main process.
Connecting to a remote backend
By default the app starts and manages its own local backend. You can instead point it at a Hermes backend running on another machine — a VPS, a home server, or a Mini behind Tailscale.
Everything connection-related lives on one settings page: Settings → Gateways. (Older builds split this across separate Gateway and Connections pages — those are now unified, and old ?tab=connections deep links redirect to the unified page.)
Settings → Gateways → Connection mode offers the alternatives to the local gateway:
- Remote gateway — enter the URL of a hermes serve backend you run yourself and sign in. This is the mode the rest of this section walks through.
- Hermes Cloud — sign in once to Hermes Cloud and pick from the agents on your account; no URL to paste. The app discovers your agents (with an organization picker if your account spans several orgs), and connecting to one switches the session over automatically. The status bar shows the cloud connection while it's active.
Gateway connections are machine-level: the Gateways page manages which gateway backends this desktop can connect to, and profiles are discovered from the gateways you connect. Sessions select one gateway at a time, while the adjacent profile rail selects a profile discovered on that gateway.
The multi-connection registry
Further down the same Settings → Gateways page, Registered gateways manages a named list of every Hermes gateway the app knows about — the local runtime, any number of remote gateways (LAN, Tailscale, internet), Hermes Cloud instances, and SSH hosts — all persisted together in one place. You can jump there from the plug button at the right end of the sidebar profile rail (Connect another Hermes gateway…) or via ⌘K → Gateways. The full guide, including the union agent roster, @name-device handles, fleet-wide updates, and the plugin SDK surface, is at Connecting Desktop to Many Hermes Instances.
- Every connection needs a unique name (a device name such as "Homelab" or "Work laptop"). When the same profile name exists on several registered gateways, surfaces disambiguate it as @profile-device (e.g. @research-homelab).
- Switch gateways from the Sessions sidebar. A named gateway selector appears when more than one gateway is registered and handles any registry size without making gateways look like profiles. The adjacent profile rail then shows only that gateway's agents and remembers the last profile used there; large profile sets condense independently.
- Hide the profile rail. If your profiles are bots rather than workspaces, the colored strip of profile squares at the foot of the sidebar duplicates the sessions list. Turn it off from the Sessions view menu (Profile rail), the shell's right-click menu, or ⌘K → Toggle profile rail. While it is hidden, a Profiles dropdown appears in the status bar next to the gateway selector with the same choices — this gateway's profiles, other gateways' agents, New profile, Import profile…, Manage profiles… — so switching profiles always has a door.
- Choose what opens after a restart. Open on launch keeps the backward-compatible Primary gateway default, or can resume the Last used gateway after it connects successfully. This preference is stored outside the application bundle and survives Desktop updates.
- Add / edit / remove / test connections from the panel. The Add flow offers all four kinds — Local, Hermes Cloud, Remote gateway, and SSH (the Local button is disabled while the app-managed local entry exists, and a hint points cloud adds at the sign-in/discovery flow above). The local entry is managed by the app and cannot be removed. Test probes the connection's own HTTP and WebSocket legs directly.
- Duplicates are rejected at save time: only one local entry ever; remote and cloud entries are deduplicated on the normalized URL (trimmed, trailing slashes stripped, lowercased — across both kinds); SSH entries on the normalized user@host:port plus remote profile.
- Existing settings are imported automatically the first time you run a build with the registry: your current global connection and any legacy per-profile overrides become named entries. The legacy settings file is left untouched, so older builds keep working.
- Cloud entries come from the Hermes Cloud sign-in/discovery flow above, not from a hand-typed URL.
- Tokens are stored encrypted with the OS keyring (with an explicit plain-text opt-in on keyring-less Linux).
Side-by-side routing is live: each registered gateway dials its own backends and sockets on demand (keyed per connection + profile), the plugin SDK exposes the union agent roster (host.agents() / host.ensureAgent()), and Update all instances on the Gateways page dispatches hermes update to every eligible gateway at once — Hermes Cloud entries are skipped (the platform updates them), and each instance reports its own result.
The remote backend is a running hermes serve process
"Remote backend" means a hermes serve server running on the remote machine — that is the process the desktop app connects to. Nothing in this section works unless that backend is actually up and reachable. The desktop app does not start it for you; you (or a systemd service) keep hermes serve running on the remote host, and the app attaches to it. If you also use messaging channels (Telegram, Discord, etc.), the gateway is a separate long-running process you start independently — see the note after the setup steps.
The connection has two halves: on the backend you protect it with an auth provider, and in the app you enter the backend's URL and sign in. Binding the backend to a non-loopback address automatically engages its auth gate, and the provider you configure is what lets the desktop app through.
Pick a provider based on where the backend lives:
- OAuth (Nous Portal) — preferred for anything reachable beyond your own machine. Logins are verified against your Nous account, so this is the option suitable for a VPS, a public host, or any remote backend. Register the dashboard with hermes dashboard register (or the Portal /local-dashboards page) to provision its OAuth client, then sign in from the app with Sign in with Nous Research. A self-hosted OIDC provider works the same way if you run your own identity provider.
- Username/password — local / trusted-network use only. The simplest option when the backend is on the same trusted LAN or reachable only over a VPN (e.g. Tailscale). It protects a single shared credential with no external identity provider, so do not use it for a dashboard exposed to the public internet — reach for OAuth there instead.
The rest of this section shows the username/password path because it's the quickest to stand up on a trusted network; for the OAuth path see Web Dashboard → Default provider: Nous Research.
On the backend (the remote machine)
Set a username and password, then start the backend bound to a reachable address. The credentials live in ~/.hermes/.env (the secrets file, mode 0600):
# 1. Set the dashboard login credentials.
cat >> ~/.hermes/.env <<'EOF'
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=choose-a-strong-password
# Recommended: a stable signing secret so sessions survive restarts.
# Without it a random key is generated per boot and you'll be logged out
# on every restart.
HERMES_DASHBOARD_BASIC_AUTH_SECRET=$(openssl rand -base64 32)
EOF
chmod 600 ~/.hermes/.env
# 2. Run the backend bound to a reachable address. The non-loopback bind
# engages the auth gate; the username/password provider handles login.
hermes serve --host 0.0.0.0 --port 9119Keep that hermes serve process running for as long as you want the desktop app to be able to connect — if it stops, the app can no longer reach the backend. Run it under systemd, tmux, or your process manager of choice so it survives logout and reboots.
Separately, make sure the gateway is running on the remote host if you rely on messaging channels — the hermes serve backend is what the desktop app talks to, but your Telegram/Discord/Slack gateway sessions are a different process that you start and keep running on their own. See Messaging for gateway setup.
Prefer not to keep a plaintext password at rest? Set HERMES_DASHBOARD_BASIC_AUTH_PASSWORD_HASH to a scrypt hash instead — compute it with python -c "from plugins.dashboard_auth.basic import hash_password; print(hash_password('PW'))". Full configuration surface (config.yaml keys, every env var, the rate limiter): Web Dashboard → Username/password provider.
Running the backend as a systemd service? Give the unit EnvironmentFile=%h/.hermes/.env so the credentials are in the environment at boot.
注意
The backend reads and writes your .env (API keys, secrets) and can run agent commands. The username/password setup shown above is for a trusted network — never expose a password-protected backend directly to the open internet; put it behind a VPN. Tailscale is the clean option: bind to the machine's tailscale IP (--host <tailscale-ip>) and use http://<tailscale-ip>:9119 as the Remote URL so only your tailnet can reach it. To reach a backend over the public internet, use the OAuth (Nous Portal) provider instead.
In the app
Settings → Gateways → Remote gateway:
- Remote URL — http://<backend-host>:9119 (path prefixes like /hermes work if you front it with a reverse proxy)
- Sign in — the app detects which provider the backend advertises and adapts the button. For a username/password backend it shows a Sign in button that opens a credential form (enter the credentials from step 1). For an OAuth backend it shows Sign in with <provider> (e.g. Sign in with Nous Research), which runs the provider's browser sign-in. Either way the app ends up with an authenticated session against the backend.
- Save and reconnect — switches the desktop shell onto the remote backend. The session refreshes automatically; you stay signed in across restarts when HERMES_DASHBOARD_BASIC_AUTH_SECRET is set.
You can also set the backend URL without the UI via the HERMES_DESKTOP_REMOTE_URL environment variable before launching the app (it overrides the in-app setting); you still sign in from the Gateways settings panel.
Per-profile remote hosts
The remote gateway host is configured per profile, so each profile can point at its own remote backend (or stay on its local one). Switching profiles switches which remote host the app connects to.
Troubleshooting
Window context unavailable on Windows ARM64
Check that the installed get-windows package includes a working
win32-arm64 native binding. Without one, read_window_below and HUD window
context cannot enumerate other apps' windows. The error and HUD log preserve
the underlying failure reason alongside this troubleshooting guidance.
If the binding is unavailable, use the x64 desktop build under Windows
emulation, or a custom build with a matching native binding. Changing the agent
backend or granting macOS screen permissions cannot fix a missing Windows
binding; enumeration runs on the computer hosting the desktop app. This
diagnostic does not add native ARM64 window enumeration support.
- Sign-in fails with 401 / "Invalid credentials" — the username or password doesn't match the backend's HERMES_DASHBOARD_BASIC_AUTH_USERNAME / HERMES_DASHBOARD_BASIC_AUTH_PASSWORD. The backend returns the same generic error for an unknown user and a wrong password (no enumeration oracle), so double-check both. Confirm the gate is on with curl -s http://<host>:9119/api/status | jq '.auth_required, .auth_providers' — it should report true and include "basic".
- No "Sign in" button — it asks for a session token instead — the backend's username/password provider isn't active. /api/status won't list "basic" in auth_providers. Make sure both the username and a password (or password hash) are set in ~/.hermes/.env and that the dashboard process actually loaded them.
- Signed out on every restart — set HERMES_DASHBOARD_BASIC_AUTH_SECRET to a stable value. Without it the token-signing key is regenerated per boot, invalidating all sessions.
- Connection refused / times out — the backend bound to 127.0.0.1 (the default) or a firewall/VPN is blocking the port. Bind to 0.0.0.0 or the tailscale IP and open the port to your trusted network.
For the same setup from the web-dashboard angle, see Web Dashboard → Connecting Hermes Desktop to a remote backend; the env vars are catalogued under Environment Variables → Web Dashboard & Hermes Desktop.
Extending the desktop app
The desktop app is contribution-driven — panes, pages, sidebar nav, status-bar
items, palette commands, keybinds, and themes all register through one SDK, and
you can add your own. A plugin is a single ESM file dropped in
$HERMES_HOME/desktop-plugins/<id>/plugin.js; the app loads it within seconds and
hot-reloads every save. Manage installed plugins live in Capabilities → Plugins.
See Desktop Plugin SDK for the full
reference. (This is separate from the web dashboard plugin system.)
A desktop plugin is not sandboxed: it runs inside the app with the app's own
authority (gateway RPC, the native bridge, other plugins' storage). Only load
files you wrote or reviewed; for catalog installs the protection is the
catalog trust model (human-reviewed,
SHA-pinned) plus an import allowlist — not isolation. A plugin whose
register() throws is rolled back and shown as Failed with the error on its
row; ⌘K → Reload desktop plugins re-reads every installed plugin.js,
including one an installer replaced in place.
Capabilities → Plugins is the one place for everything that extends
Hermes: one row per plugin, with two switch columns.
- A plugin can extend this app, the agent, or both — the badge on
each row says which, inferred from what the package contains (plugin.yaml
→ agent half, plugin.js → desktop half). A plugin with both halves is one
row, never two.
- Desktop column — the half loaded into this app. It is app-level: the
same switch, the same value, whichever profile, gateway, or remote machine
the window is looking at. Desktop code loads from exactly one place,
~/.hermes/desktop-plugins/; the desktop half of a unified agent+desktop
package is copied there by the app when the package is installed (and
follows its updates and uninstall), so switching profiles never loads,
unloads, or re-scopes a pane. Toggles apply live.
- Agent column — the half installed in the selected profile's backend
(agent plugins: user, git, project, pip and
portable installs), with an Update chip when a catalog pin moved. The
profile selector lives in this column's header because it governs only
this column; with a single profile there is no selector at all.
Repo-bundled built-ins (platform adapters, provider plugins) are not
listed: they ship enabled and are configured from their own surfaces. The
exceptions are the bundled lifecycle plugins with no surface of their own
(disk-cleanup, security-guidance), which appear here so they can be
toggled like any other agent plugin.
- A half the plugin does not ship shows a dash. A desktop half whose agent
half is not installed in the selected profile shows Install here,
which pre-fills the install dialog from the package's origin (catalog entry
or git remote) for that profile only. Optional extras such as the
Accent Picker
install from the plugin catalog.
- Uninstall — every plugin installed under the selected profile's
plugins/ folder (user or git install) has a trash button beside its name.
It asks for confirmation, then deletes the plugin's files and install
metadata from that profile — the same operation as
hermes plugins remove <name> — and prunes the app-level copy of a unified
package's desktop half. Restart the gateway to unload the plugin's code.
Repo-bundled and pip-installed (entrypoint) plugins have no trash button:
the first cannot be removed, the second goes with its Python package.
A standalone desktop plugin (a folder you dropped into
~/.hermes/desktop-plugins/ with no agent package) gets the same trash
button; confirming deletes that folder on this computer and unloads the
plugin immediately, no gateway involved.
Discovery sits underneath: the live Plugin Catalog
picker installs reviewed entries at their pinned commit into the selected
profile, and Install from Git takes any other repository through the same
review-then-install dialog; its optional Pin to commit field installs one
exact 40-character commit SHA (private repos included), and pinned plugins
carry a pinned @ <sha8> badge in the list. Old Settings → Plugins links
redirect here.
Troubleshooting
Reconnect without restarting the app
If a Desktop chat or bot stops responding while the connection still shows Connected, select that bot/profile or gateway, open the status bar's gateway menu, and click Reconnect gateway. Reconnect stays available for open, connecting, and disconnected transports. It redials the active route without restarting Desktop or deliberately closing other routes' sockets. In-flight requests on the selected socket can be interrupted; this is an explicit recovery action, not a backend or model restart.
The app vanished after hermes update
An earlier update that replaced the checkout without keeping apps/desktop/release/ leaves no packaged app to launch. As long as HERMES_HOME/desktop-build-stamp.json (written only by a successful Desktop build) still exists, the next hermes update notices the missing app and rebuilds it. To rebuild by hand: hermes desktop --build-only --force-build. On Windows the ZIP fallback also keeps the built app, its renderer bundle and its Electron node_modules across the swap.
The local backend stopped in the background
If the local Hermes backend process exits after it was ready, Desktop restarts it on its own and shows a Hermes stopped working in the background notice; the chat reconnects once the replacement is up. HERMES_HOME/logs/desktop.log records the exit code together with the backend's last output lines (Hermes backend exited (1) followed by Recent backend output:), so the reason it died is in the log even when the app recovered by itself. A backend that keeps dying within seconds of every restart points at the backend itself — look at the traceback in that tail. After three such restarts within two minutes Desktop stops respawning and shows a keeps crashing notice instead of cycling; relaunch the app once the cause is fixed.
Failed turns name the failing layer
When a turn fails, the chat renders an error card that names which layer
failed — provider/model, custom endpoint, streaming connection,
authentication, billing, gateway, local runtime, or disk — instead of a
generic error toast. The card offers recovery actions matched to the failure:
- Retry — re-runs the failed turn in place (hidden when retrying would
deterministically reproduce the failure, e.g. a content-policy rejection).
When a rate-limit or usage-limit response names when the limit lifts
(Retry-After header or a resets_at field), the card shows Limit resets
at HH:mm (in 1h 05m) next to Retry so you know when a retry will work; the
CLI/TUI print the same line under the error. The hint itself is
informational, but the card also offers Retry when the limit resets
(HH:mm): click it and the app retries that turn once at the reset time
with a live countdown and a Cancel control. The schedule lives only in
the open window — switching sessions, sending another message, or closing
the app drops it, and nothing retries unattended.
- Switch provider — for provider, endpoint, auth, and billing failures,
opens the composer's live model menu so you can move this chat to another
provider/model right away (Settings → Models only changes the default for new
chats). When no chat surface is on screen it falls back to Settings → Models.
- Open logs — opens HERMES_HOME/logs in your file manager. On a remote
or Cloud connection the button reads Open Desktop logs: it opens the
local Desktop-side logs (transport evidence), since the failed turn's
gateway/agent logs live on the remote machine.
- Send diagnostics — uploads a redacted debug bundle to Nous-internal
storage after an explicit consent prompt (same pipeline as
hermes debug share --nous; secrets are always redacted, the bundle is
viewable by Nous staff only and auto-deletes after 14 days). On success you
get a private view link to paste into your support thread, plus quick links
to GitHub Issues, Nous Portal Support, and Discord. On a remote or Cloud
connection the backend bundles its own agent/gateway logs and the local
Desktop log is attached alongside, so support sees both halves.
- Copy error details — copies a compact plain-text summary (layer, code,
provider/model, error message) you can paste into a bug report or Discord.
The layer comes from the same error classifier the agent's retry loop uses,
so it reflects the real failure semantics, not a guess from the message text.
Older backends that predate the descriptor still render the card with a
generic title and the Retry / Open logs / Copy error details actions.
Boot logs land in HERMES_HOME/logs/desktop.log (it includes backend output and recent Python tracebacks) — check it first if the app reports a boot failure. You can also tail it from the CLI:
hermes logs gui -fFor a canonical source installation, Desktop checks and runs the installation
launcher. PM selects its interpreter and dependency generation. A missing
bootstrap marker does not force installation when that launcher works.
On Linux, Chromium's own errors go to HERMES_HOME/logs/desktop-chromium.log, and a crash of the shell itself leaves a minidump under the app's Crashpad/ directory (inside Electron's user-data directory, next to connection.json). If the window vanishes with SIGTRAP in the journal, the FATAL: line in that log names the check that fired; attach it to the bug report. Nothing is uploaded.
If Python dependencies are damaged, run the installation's hermes pm repair.
Then restart Desktop. Do not delete guessed venv paths or PM facts. For
damaged application files, repair through the
installation owner.
# Reset a stuck macOS microphone prompt
tccutil reset Microphone com.nousresearch.hermesWindows: the SSH client is missing or broken
On Windows the app runs SSH through the built-in OpenSSH client (%SystemRoot%\System32\OpenSSH\ssh.exe). If that client is not installed, it falls back to Git for Windows' bundled usr\bin\ssh.exe and then to whatever ssh is on PATH. If the built-in client is installed but broken (for example, every ssh.exe exits with code 255 after a Windows update), boot stops on an error naming the client instead of retrying. To use a different client, set it in config.yaml and restart the app:
desktop:
ssh_path: 'C:\Program Files\Git\usr\bin\ssh.exe'Use single quotes or no quotes so the backslashes stay literal. The key goes two spaces under desktop:, like the launch keys above. It has no effect on macOS or Linux.
"The host key has CHANGED since you last connected" (SSH remote)
If your SSH remote was reinstalled or its host key rotated, SSH fails closed
and Desktop latches an error overlay instead of retrying (retrying can never
succeed until the stale key is cleared). Verify the change is expected, then
remove the old entry and retry from the overlay:
ssh-keygen -R <host>Click Retry (or re-apply the connection in Settings → Gateway) after
clearing the entry — the latch resets and the next boot dials fresh.
"Build desktop app" stuck on Electron download
The build downloads the Electron runtime (~114 MB) from github.com/electron/electron/releases. If the installer hangs on the Build desktop app step with the live output repeating retrying attempt=…, GitHub is being blocked or throttled on your network (firewall, proxy, or region).
The build does not fall back to a mirror on its own and does not retry a failed download: a build that fails for any reason leaves the previous app untouched (stage-and-swap, see Updating), the update itself fails, and hermes desktop --build-only --force-build (or the next hermes update) runs the build again. If the failure was a corrupt cached Electron zip, delete it from @electron/get's cache (~/.cache/electron/ on Linux, ~/Library/Caches/electron/ on macOS, %LOCALAPPDATA%\electron\Cache on Windows) before rebuilding. @electron/get SHASUM-checks every download, but the checksums come from the same host, so a mirror you point it at is trusted for both.
To use a mirror (e.g. a corporate one, or npmmirror.com, the de-facto Electron community mirror), set ELECTRON_MIRROR before installing or rebuild manually — the build honors it and never overrides one you've set:
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ \
bash -c 'cd "$HOME/.hermes/hermes-agent/apps/desktop" && CSC_IDENTITY_AUTO_DISCOVERY=false npm run pack'Other native downloads (e.g. the get-windows prebuilt on Windows) that need a mirror: put the npm keys in $HERMES_HOME/npmrc (%LOCALAPPDATA%\hermes\npmrc on Windows, ~/.hermes/npmrc elsewhere) — for example node_get_windows_binary_host_mirror=https://<mirror>/sindresorhus/get-windows/releases/download/. Every npm ci/npm run the updater spawns (desktop, web and TUI builds) points NPM_CONFIG_USERCONFIG at that file when it exists, so the config survives hermes update; the repo-root .npmrc is git-tracked and gets autostashed on every update, and ~/.npmrc may be missed because the desktop hand-off inherits the GUI's environment. An NPM_CONFIG_USERCONFIG you set yourself is never overridden.
If get-windows is missing or half-installed: the build no longer fails — it prints [stage-native-deps] get-windows not installed ... read_window_below will be unavailable in this build and ships without the read_window_below tool. When the package directory exists but is not loadable (a Windows in-place update interrupted by a running Hermes window, TAR_ENTRY_ERROR in the install log), the same warning names the directory, and the next hermes desktop --force-build or update removes it before its npm install so the package is re-extracted — close every Hermes window and gateway first so the extract is not interrupted again. A package whose native binding or macOS helper is missing is likewise shipped without window enumeration rather than failing the build.
To clear a corrupt cached zip by hand:
rm -f "$HOME/Library/Caches/electron"/electron-*.zip # macOS
rm -f "$HOME/.cache/electron"/electron-*.zip # LinuxBuilding from source
If you want to hack on the app itself, install workspace deps from the repo root once, then run the dev server from apps/desktop:
npm install # from repo root — links apps/desktop, web, apps/shared
cd apps/desktop
npm run dev # Vite renderer + Electron, which boots the Python backendPoint the app at a specific checkout, or sandbox it from your real config:
HERMES_DESKTOP_HERMES_ROOT=/path/to/clone npm run dev
HERMES_HOME=$HOME/.hermes/cache/scratch/throwaway npm run dev
npm run dev:fake-boot # exercise the startup overlay with deterministic delaysBuild installers:
npm run dist:mac # DMG + zip
npm run dist:win # NSIS + MSI
npm run dist:linux # AppImage + deb + rpm
npm run pack # unpacked app under release/ (no installer)macOS/Windows signing and notarization run automatically when the relevant credentials are present in the environment (CSC_LINK / CSC_KEY_PASSWORD / APPLE_* for macOS, WIN_CSC_* for Windows).
The opt-in HUD modifier-tap helper is built with the Electron bundle and packaged outside ASAR. macOS uses the existing Xcode command-line tools prerequisite. Windows builds use the C# compiler included with the operating system's .NET Framework; no Clang or developer SDK is required. Windows packaging fails rather than silently omitting the helper. Linux builds need a C compiler and X11/XInput development headers (libx11-dev and libxi-dev on Debian/Ubuntu); without those optional Linux prerequisites, packaging continues without modifier-tap support. Build on the target OS; Linux also requires the target architecture. Installed users do not need a developer toolchain. Settings distinguishes a missing helper from startup failure and an unsupported desktop session; the X11/Wayland warning is not shown for a missing or failed Windows helper.
macOS permissions and local rebuilds (TCC)
Silence every folder prompt with one switch. macOS prompts per-category
(Desktop, then Downloads, then Documents, ...) as Hermes touches each folder.
A single Full Disk Access grant covers all of them, permanently — and
with Hermes' stable signing identities it survives every update:
- System Settings → Privacy & Security → Full Disk Access (or run
open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles")
- Enable your terminal app — and Hermes.app if you use Desktop.
- Fully quit and relaunch them once.
hermes doctor reports whether the current terminal context already has the
grant, and hermes setup shows this tip on macOS when it doesn't.
macOS remembers permission grants (Full Disk Access, Desktop/Downloads/Documents,
Accessibility, Automation, microphone) against the app's code-signing identity,
not its path. Locally built and self-updated apps are signed with a stable
identifier-pinned ad-hoc signature, so grants persist across updates.
One-time note: grants made to builds before the identifier-pinned signing
fix (PR #73681) carry the old cdhash-pinned requirement. macOS keeps showing the
toggle as ON for those stale grants but still re-prompts, because the stored
grant no longer matches the rebuilt binary — and the modern prompt has no Allow
button, so it looks like there is nothing to re-check. If that happens, reset
the stale grant once and re-grant:
tccutil reset ScreenCapture com.nousresearch.hermes # repeat per servicethen toggle the fresh entry ON in System Settings and fully quit & relaunch
Hermes. Grants are stable from then on.
For the strongest guarantee — a certificate-anchored identity, the same
mechanism yabai/skhd users rely on — create a self-signed code-signing
certificate once and tell Hermes to use it. The one-shot command does
everything (creates the certificate in your login keychain, grants codesign
access, writes the config, and re-signs the packaged app):
hermes desktop --setup-tcc-identityOr do it manually:
- Keychain Access → Certificate Assistant → Create a Certificate…
- Name: Hermes Local Signing, Identity Type: Self-Signed Root,
Certificate Type: Code Signing.
- In Keychain Access, double-click the new certificate → Trust → set
Code Signing to Always Trust (an imported self-signed certificate is
not a valid signing identity until it is trusted for code signing —
security find-identity -v -p codesigning should list it afterwards).
- hermes config set desktop.macos_signing_identity "Hermes Local Signing"
Use --identity <name> with the command to create/use a differently named
certificate (default: Hermes Local Signing). The command is idempotent —
re-run it after updates to re-point the config and re-sign the rebuilt app.
The next update re-signs the rebuilt app with that certificate; every TCC grant
survives. No Apple Developer account is required. Notarized release builds are
detected and never re-signed.
One-time note: changing the signing identity (including the first update after
this fix) changes the app's identity once, so macOS will re-prompt one final
time. Grants are stable from then on. If a permission gets stuck, reset it with
tccutil reset All com.nousresearch.hermes and re-grant.
See also
- CLI Guide — the terminal interface
- TUI — the modern terminal UI used by hermes --tui and the dashboard chat tab
- Web Dashboard — browser admin panel with an embedded chat tab
- Configuration — config that the desktop app reads and writes
- Windows (Native) — native Windows install path
配置
本页的 Python 依赖命令使用 PM 准备的源码环境。
依赖变更后,请重新激活该 checkout 并重启 Hermes。
所有设置均存储在 ~/.hermes/ 目录中,便于访问。
目录结构
~/.hermes/
├── config.yaml # 设置(模型、终端、TTS、压缩等)
├── .env # API 密钥和机密
├── auth.json # OAuth provider 凭据(Nous Portal 等)
├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)
├── memories/ # 持久记忆(MEMORY.md、USER.md)
├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)
├── cron/ # 定时任务
├── sessions/ # Gateway 会话
└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)管理配置
hermes config # 查看当前配置
hermes config edit # 在编辑器中打开 config.yaml
hermes config set KEY VAL # 设置特定值
hermes config check # 检查缺失选项(更新后使用)
hermes config migrate # 交互式添加缺失选项
# 示例:
hermes config set model anthropic/claude-opus-4
hermes config set terminal.backend docker
hermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env提示
hermes config set 命令会自动将值路由到正确的文件 —— API 密钥保存到 .env,其他所有内容保存到 config.yaml。
配置优先级
设置按以下顺序解析(优先级从高到低):
- CLI 参数 —— 例如 hermes chat --model anthropic/claude-sonnet-4(单次调用覆盖)
- ~/.hermes/config.yaml —— 所有非机密设置的主配置文件
- ~/.hermes/.env —— 环境变量的回退;机密(API 密钥、token、密码)必须放这里
- 内置默认值 —— 未设置任何内容时的硬编码安全默认值
经验法则
机密(API 密钥、bot token、密码)放入 .env。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 config.yaml。当两者都设置时,config.yaml 对非机密设置优先。
环境变量替换
可以在 config.yaml 中使用 ${VAR_NAME} 语法引用环境变量:
auxiliary:
vision:
api_key: ${GOOGLE_API_KEY}
base_url: ${CUSTOM_VISION_URL}
delegation:
api_key: ${DELEGATION_KEY}单个值中可以有多个引用:url: "${HOST}:${PORT}"。如果引用的变量未设置,占位符将保持原样(${UNDEFINED_VAR} 保持不变)。仅支持 ${VAR} 语法 —— 裸 $VAR 不会被展开。
关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 AI Providers。
Provider 超时
可以为 provider 设置 providers.<id>.request_timeout_seconds 作为全局请求超时,以及 providers.<id>.models.<model>.timeout_seconds 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 HERMES_API_TIMEOUT 环境变量。
还可以设置 providers.<id>.stale_timeout_seconds 用于非流式陈旧调用检测器,以及 providers.<id>.models.<model>.stale_timeout_seconds 作为特定模型的覆盖值。此值优先于旧版 HERMES_API_CALL_STALE_TIMEOUT 环境变量。
不设置这些值将保持旧版默认值(HERMES_API_TIMEOUT=1800s、HERMES_API_CALL_STALE_TIMEOUT=90s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(bedrock_converse 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 cli-config.yaml.example 中的注释示例。
终端后端配置
Hermes 支持七种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区、Vercel Sandbox,或 Singularity/Apptainer 容器。
terminal:
backend: local # local | docker | ssh | modal | daytona | vercel_sandbox | singularity
cwd: "." # Gateway/cron 工作目录(CLI 始终使用启动目录)
timeout: 180 # 每条命令的超时时间(秒)
env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)
singularity_image: "docker://nikolaik/python-nodejs:python3.11-nodejs20" # Singularity 后端的容器镜像
modal_image: "nikolaik/python-nodejs:python3.11-nodejs20" # Modal 后端的容器镜像
daytona_image: "nikolaik/python-nodejs:python3.11-nodejs20" # Daytona 后端的容器镜像对于 Modal、Daytona 和 Vercel Sandbox 等云沙箱,container_persistent: true 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。
后端概览
| | 后端 | 命令运行位置 | 隔离性 | 最适合
| local | 直接在您的机器上 | 无 | 开发、个人使用
| docker | 单个持久 Docker 容器(跨会话、/new、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD
| ssh | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件
| modal | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估
| daytona | Daytona 工作区 | 完全(云容器) | 托管云开发环境
| vercel_sandbox | Vercel Sandbox | 完全(云 microVM) | 带快照文件系统持久化的云执行
| singularity | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器
Local 后端
默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。
terminal:
backend: local注意
Agent 拥有与您的用户账户相同的文件系统访问权限。使用 hermes tools 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。
Docker 后端
在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。
单个持久容器,而非每条命令一个容器。 Hermes 在首次使用时启动一个长期运行的容器,并通过 docker exec 将每个终端、文件和 execute_code 调用路由到同一容器中 —— 跨会话、/new、/reset 和 delegate_task 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 /workspace 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的容器生命周期。
terminal:
backend: docker
docker_image: "nikolaik/python-nodejs:python3.11-nodejs20"
docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace
docker_run_as_host_user: false # 参见下方"以宿主用户身份运行容器"
docker_forward_env: # 转发到容器的环境变量
- "GITHUB_TOKEN"
docker_volumes: # 宿主目录挂载
- "/home/user/projects:/workspace/projects"
- "/home/user/data:/data:ro" # :ro 表示只读
docker_extra_args: # 附加到 `docker run` 的额外标志
- "--gpus=all"
- "--network=host"
# 资源限制
container_cpu: 1 # CPU 核心数(0 = 不限制)
container_memory: 5120 # MB(0 = 不限制)
container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)
container_persistent: true # 跨会话持久化 /workspace 和 /rootterminal.docker_extra_args(也可通过 TERMINAL_DOCKER_EXTRA_ARGS='["--gpus=all"]' 覆盖)允许传递 Hermes 未作为一级键公开的任意 docker run 标志 —— --gpus、--network、--add-host、替代 --security-opt 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 docker run 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、--user、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。
要求: 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 $PATH 以及常见的 macOS 安装位置(/usr/local/bin/docker、/opt/homebrew/bin/docker、Docker Desktop 应用包)。开箱即用支持 Podman:设置 HERMES_DOCKER_BINARY=podman(或完整路径)以在两者都安装时强制使用它。
容器生命周期: Hermes 为每个终端和文件工具调用重用单个长期运行的容器(docker run -d ... sleep 2h),跨会话、/new、/reset 和 delegate_task 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 docker exec 运行,因此工作目录更改、已安装的包以及 /workspace 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。
通过 delegate_task(tasks=[...]) 生成的并行子 agent 共享这一个容器 —— 并发的 cd、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 register_task_env_overrides() 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。
安全加固:
- --cap-drop ALL,仅添加回 DAC_OVERRIDE、CHOWN、FOWNER
- --security-opt no-new-privileges
- --pids-limit 256
- /tmp(512MB)、/var/tmp(256MB)、/run(64MB)的大小限制 tmpfs
凭据转发: docker_forward_env 中列出的环境变量首先从您的 shell 环境解析,然后回退到 ~/.hermes/.env。技能也可以声明 required_environment_variables,这些变量会自动合并。
SSH 后端
通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。
terminal:
backend: ssh
persistent_shell: true # 保持长期运行的 bash 会话(默认:true)必需的环境变量:
TERMINAL_SSH_HOST=my-server.example.com
TERMINAL_SSH_USER=ubuntu可选:
| | 变量 | 默认值 | 描述
| TERMINAL_SSH_PORT | 22 | SSH 端口
| TERMINAL_SSH_KEY | (系统默认) | SSH 私钥路径
| TERMINAL_SSH_PERSISTENT | true | 启用持久 shell
工作原理: 使用 BatchMode=yes 和 StrictHostKeyChecking=accept-new 在初始化时连接。持久 shell 在远程主机上保持单个 bash -l 进程存活,通过临时文件进行通信。需要 stdin_data 或 sudo 的命令会自动回退到单次模式。
Modal 后端
在 Modal 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。
terminal:
backend: modal
container_cpu: 1 # CPU 核心数
container_memory: 5120 # MB(5GB)
container_disk: 51200 # MB(50GB)
container_persistent: true # 快照/恢复文件系统必需: MODAL_TOKEN_ID + MODAL_TOKEN_SECRET 环境变量,或 ~/.modal.toml 配置文件。
持久化: 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 ~/.hermes/modal_snapshots.json 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。
凭据文件: 自动从 ~/.hermes/ 挂载(OAuth token 等),并在每条命令前同步。
Daytona 后端
在 Daytona 托管工作区中运行命令。支持停止/恢复以实现持久化。
terminal:
backend: daytona
container_cpu: 1 # CPU 核心数
container_memory: 5120 # MB → 转换为 GiB
container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)
container_persistent: true # 停止/恢复而非删除必需: DAYTONA_API_KEY 环境变量。
持久化: 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 hermes-{task_id} 模式。
磁盘限制: Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。
Vercel Sandbox 后端
在 Vercel Sandbox 云 microVM 中运行命令。Hermes 使用普通的终端和文件工具接口;没有 Vercel 特定的面向模型的工具。
terminal:
backend: vercel_sandbox
vercel_runtime: node24 # node24 | node22 | python3.13
cwd: /vercel/sandbox # 默认工作区根目录
container_persistent: true # 快照/恢复文件系统
container_disk: 51200 # 仅共享默认值;不支持自定义磁盘必需安装: 安装可选 SDK 扩展:
python -c "import pm; pm.sync_venv(['vercel'], explicit=True)"必需认证: 使用 VERCEL_TOKEN、VERCEL_PROJECT_ID 和 VERCEL_TEAM_ID 三者全部配置访问令牌认证。这是在 Render、Railway、Docker 及类似宿主上部署和正常长期运行 Hermes 进程的受支持设置。
对于一次性本地开发,Hermes 也接受短期 Vercel OIDC token:
VERCEL_OIDC_TOKEN="$(vc project token <project-name>)" hermes chat在已链接的 Vercel 项目目录中,可以省略项目名称:
VERCEL_OIDC_TOKEN="$(vc project token)" hermes chatOIDC token 是短期的,不应作为文档化的部署路径使用。
运行时: terminal.vercel_runtime 支持 node24、node22 和 python3.13。未设置时,Hermes 默认使用 node24。
持久化: 当 container_persistent: true 时,Hermes 在清理期间对沙箱文件系统进行快照,并从该快照为同一任务恢复后续沙箱。快照内容可以包括复制到沙箱中的 Hermes 同步凭据、技能和缓存文件。这仅保留文件系统状态;不保留活跃沙箱身份、PID 空间、shell 状态或正在运行的后台进程。
后台命令: terminal(background=true) 使用 Hermes 的通用非本地后台进程流程。您可以在沙箱存活期间通过普通进程工具生成、轮询、等待、查看日志和终止进程。Hermes 不提供清理或重启后的原生 Vercel 分离进程恢复。
磁盘大小: Vercel Sandbox 目前不支持 Hermes 的 container_disk 资源旋钮。将 container_disk 保持未设置或使用共享默认值 51200;非默认值会导致诊断和后端创建失败,而不是被静默忽略。
Singularity/Apptainer 后端
在 Singularity/Apptainer 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。
terminal:
backend: singularity
singularity_image: "docker://nikolaik/python-nodejs:python3.11-nodejs20"
container_cpu: 1 # CPU 核心数
container_memory: 5120 # MB
container_persistent: true # 可写覆盖层跨会话持久化要求: $PATH 中有 apptainer 或 singularity 二进制文件。
镜像处理: Docker URL(docker://...)自动转换为 SIF 文件并缓存。现有 .sif 文件直接使用。
临时目录: 按顺序解析:TERMINAL_SCRATCH_DIR → TERMINAL_SANDBOX_DIR/singularity → /scratch/$USER/hermes-agent(HPC 惯例)→ ~/.hermes/sandboxes/singularity。
隔离: 使用 --containall --no-home 实现完全命名空间隔离,不挂载宿主 home 目录。
常见终端后端问题
如果终端命令立即失败或终端工具报告为已禁用:
- Local —— 无特殊要求。入门时最安全的默认选项。
- Docker —— 运行 docker version 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 hermes config set terminal.backend local。
- SSH —— TERMINAL_SSH_HOST 和 TERMINAL_SSH_USER 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。
- Modal —— 需要 MODAL_TOKEN_ID 环境变量或 ~/.modal.toml。运行 hermes doctor 检查。
- Daytona —— 需要 DAYTONA_API_KEY。Daytona SDK 处理服务器 URL 配置。
- Singularity —— 需要 $PATH 中有 apptainer 或 singularity。HPC 集群上常见。
如有疑问,将 terminal.backend 设回 local 并首先验证命令在那里运行。
拆卸时远程到宿主文件同步
对于 SSH、Modal 和 Daytona 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件同步回宿主,存放在 ~/.hermes/cache/remote-syncs/<session-id>/ 下。
- 触发时机:会话关闭、/new、/reset、gateway 消息超时、子 agent 使用远程后端时 delegate_task 子 agent 完成。
- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。
- 远程沙箱可能在您查找时已被拆除;本地 ~/.hermes/cache/remote-syncs/… 副本是 agent 更改内容的权威记录。
- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 file_sync_max_mb(默认 100)的文件。如果您期望更大的工件返回,请调高该值。
terminal:
file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件
file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 scp 或 modal volume put 每个工件。
Docker 卷挂载
使用 Docker 后端时,docker_volumes 允许您与容器共享宿主目录。每个条目使用标准 Docker -v 语法:host_path:container_path[:options]。
terminal:
backend: docker
docker_volumes:
- "/home/user/projects:/workspace/projects" # 读写(默认)
- "/home/user/datasets:/data:ro" # 只读
- "/home/user/.hermes/cache/documents:/output" # Gateway 可见的导出适用于:
- 向 agent 提供文件(数据集、配置、参考代码)
- 从 agent 接收文件(生成的代码、报告、导出)
- 共享工作区,您和 agent 都访问相同的文件
如果您使用消息 gateway 并希望 agent 通过 MEDIA:/... 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 /home/user/.hermes/cache/documents:/output。
- 在 Docker 中将文件写入 /output/...
- 在 MEDIA: 中发出宿主路径,例如:MEDIA:/home/user/.hermes/cache/documents/report.txt
- 不要发出 /workspace/... 或 /output/...,除非该确切路径在宿主上对 gateway 进程也存在
注意
YAML 重复键会静默覆盖之前的键。如果您已有 docker_volumes: 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 docker_volumes: 键。
也可以通过环境变量设置:TERMINAL_DOCKER_VOLUMES='["/host:/container"]'(JSON 数组)。
Docker 凭据转发
默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 terminal.docker_forward_env。
terminal:
backend: docker
docker_forward_env:
- "GITHUB_TOKEN"
- "NPM_TOKEN"Hermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 hermes config set 保存的 ~/.hermes/.env。
注意
docker_forward_env 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。
以宿主用户身份运行容器
默认情况下,Docker 容器以 root(UID 0)身份运行。在 /workspace 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 sudo chown 才能从宿主编辑器编辑它们。terminal.docker_run_as_host_user 标志解决了这个问题:
terminal:
backend: docker
docker_run_as_host_user: true # 默认:false启用后,Hermes 将 --user $(id -u):$(id -g) 附加到 docker run 命令,使写入绑定挂载目录(/workspace、/root、docker_volumes 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 apt install 或写入 /root/.npm 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 HOME 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。
保持 false(默认)以获得向后兼容的行为。当您的工作流主要是"编辑挂载的宿主文件"且厌倦了 sudo chown -R 时,请开启此选项。
可选:将启动目录挂载到 /workspace
Docker 沙箱默认保持隔离。Hermes 不会将您当前的宿主工作目录传入容器,除非您明确选择加入。
在 config.yaml 中启用:
terminal:
backend: docker
docker_mount_cwd_to_workspace: true启用后:
- 如果您从 ~/projects/my-app 启动 Hermes,该宿主目录将绑定挂载到 /workspace
- Docker 后端从 /workspace 开始
- 文件工具和终端命令都能看到相同的挂载项目
禁用时,/workspace 保持沙箱所有,除非您通过 docker_volumes 显式挂载内容。
安全权衡:
- false 保留沙箱边界
- true 使沙箱直接访问您启动 Hermes 的目录
仅在您有意希望容器处理实时宿主文件时才选择加入。
持久 Shell
默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用持久 shell 后,单个长期运行的 bash 进程在 execute() 调用之间保持存活,使状态在命令之间保持。
这对 SSH 后端最有用,它还消除了每条命令的连接开销。持久 shell 对 SSH 默认启用,对本地后端禁用。
terminal:
persistent_shell: true # 默认 —— 为 SSH 启用持久 shell禁用:
hermes config set terminal.persistent_shell false跨命令保持的内容:
- 工作目录(cd /tmp 对下一条命令生效)
- 导出的环境变量(export FOO=bar)
- Shell 变量(MY_VAR=hello)
优先级:
| | 级别 | 变量 | 默认值
| 配置 | terminal.persistent_shell | true
| SSH 覆盖 | TERMINAL_SSH_PERSISTENT | 遵循配置
| Local 覆盖 | TERMINAL_LOCAL_PERSISTENT | false
每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:
export TERMINAL_LOCAL_PERSISTENT=true备注
需要 stdin_data 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。
有关每个后端的详细信息,请参阅代码执行和 README 的终端部分。
技能设置
技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 config.yaml 的 skills.config 命名空间下。
skills:
config:
myplugin:
path: ~/myplugin-data # 示例 —— 每个技能定义自己的键技能设置的工作原理:
- hermes config migrate 扫描所有已启用的技能,找到未配置的设置,并提供提示
- hermes config show 在"技能设置"下显示所有技能设置及其所属技能
- 技能加载时,其解析的配置值会自动注入到技能上下文中
手动设置值:
hermes config set skills.config.myplugin.path ~/myplugin-data有关在您自己的技能中声明配置设置的详细信息,请参阅创建技能 — 配置设置。
Agent 创建技能写入的守卫
当 agent 使用 skill_manage 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器默认关闭 —— 合法触及 ~/.ssh/ 或提及 $OPENAI_API_KEY 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:
skills:
guard_agent_created: true # 默认:false开启后,任何被标记的 skill_manage 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。
内存配置
memory:
memory_enabled: true
user_profile_enabled: true
memory_char_limit: 2200 # ~800 tokens
user_char_limit: 1375 # ~500 tokens文件读取安全
控制单次 read_file 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 offset 和 limit 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。
file_read_max_chars: 100000 # 默认 —— ~25-35K tokens如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:
# 大上下文模型(200K+)
file_read_max_chars: 200000
# 小型本地模型(16K 上下文)
file_read_max_chars: 30000Agent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。
工具输出截断限制
三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:
tool_output:
max_bytes: 50000 # 终端输出上限(字符)
max_lines: 2000 # read_file 分页上限
max_line_length: 2000 # read_file 行号视图中的每行上限- max_bytes —— 当 terminal 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 [OUTPUT TRUNCATED] 通知。默认 50000(典型分词器约 12-15K tokens)。
- max_lines —— 单次 read_file 调用的 limit 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 2000。
- max_line_length —— read_file 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 ... [truncated]。默认 2000。
对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:
# 大上下文模型(200K+)
tool_output:
max_bytes: 150000
max_lines: 5000
# 小型本地模型(16K 上下文)
tool_output:
max_bytes: 20000
max_lines: 500全局工具集禁用
要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 agent.disabled_toolsets 下列出其名称:
agent:
disabled_toolsets:
- memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入
- web # 任何地方都不使用 web_search / web_extract这在每个平台的工具配置(由 hermes tools 写入的 platform_toolsets)之后应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个"到处关闭 X"的单一开关而不是编辑 hermes tools UI 中的 15+ 个平台行时,请使用此选项。
留空列表或省略键不会产生任何效果。
Git Worktree 隔离
启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:
worktree: true # 始终创建 worktree(与 hermes -w 相同)
# worktree: false # 默认 —— 仅在传递 -w 标志时启用后,每个 CLI 会话在 .worktrees/ 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。
您还可以通过仓库根目录中的 .worktreeinclude 列出要复制到 worktree 的 gitignore 文件:
# .worktreeinclude
.env
.venv/
node_modules/上下文压缩
Hermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。
所有压缩设置都在 config.yaml 中(无环境变量)。
完整参考
compression:
enabled: true # 开启/关闭压缩
threshold: 0.50 # 在上下文限制的此百分比时压缩
target_ratio: 0.20 # 保留为最近尾部的阈值分数
tail_mode: legacy # 尾部保留策略:"legacy"(0.20×窗口的逐字尾部)或 "lean"(截取 2.5% 窗口、10K-25K 上下限的精简尾部,摘要中附带分块摘录、锚点索引与 session_search 恢复指引 — 压缩后保留 token 约减少 3 倍)
protect_last_n: 20 # 保持未压缩的最少最近消息数
hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文
context_timeout_seconds: 120 # Agent 侧 compress_context 无进展超时(秒)—— 见下文
context_total_ceiling_seconds: 600 # Agent 侧 compress_context 预提交等待上限(秒;已开始的 SessionDB 提交不会被放弃,超限会记录日志并告警)
# 摘要模型/provider 在 auxiliary: 下配置:
auxiliary:
compression:
model: "" # 空 = 使用主聊天模型。覆盖为例如 "google/gemini-3-flash-preview" 以获得更便宜/更快的压缩。
provider: "auto" # Provider:"auto"、"openrouter"、"nous"、"codex"、"main" 等
base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)旧版配置迁移
带有 compression.summary_model、compression.summary_provider 和 compression.summary_base_url 的旧版配置在首次加载时自动迁移到 auxiliary.compression.*(配置版本 17)。无需手动操作。
hygiene_hard_message_limit 是仅限 gateway 的预压缩安全阀。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 5000 —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。
context_timeout_seconds(默认 120)是 agent 侧 compress_context(对话循环、预检压缩、手动 /compress)的无进展超时,语义与 gateway 会话预压缩(session hygiene)的 inactivity 预算相同:摘要模型仍在流式出 token 时会延长等待;仅当完全无输出时才跳过压缩并保留原消息。设为 0 可关闭。Gateway 会话预压缩仍使用自己的 hygiene_timeout_seconds,不会被双重包装。
context_total_ceiling_seconds(默认 600)限制即使仍有 token 推进时的 agent 侧预提交等待时间(摘要 / 流式阶段),并会被钳制为至少等于 context_timeout_seconds。确切保证:**摘要阶段受该上限约束;提交阶段若超出上限则记录日志并向用户告警。**一旦 worker 已进入 compression commit fence 且 SessionDB 变更正在进行,提交绝不会被中途放弃(那会导致 transcript 分叉),但等待不再是静默的:若提交超过上限,Hermes 会记录超时(WARNING,重复时升级为 ERROR),通过用户可见的警告通道发送一次性提醒,并以有界增量继续等待直到提交完成。
Gateway 热重载压缩和上下文长度
从最近的版本开始,在运行中的 gateway 上编辑 config.yaml 中的 model.context_length 或任何 compression.* 键将在下一条消息时生效 —— 无需 gateway 重启、/reset 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。
常见设置
默认(自动检测)—— 无需配置:
compression:
enabled: true
threshold: 0.50使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 auxiliary.compression.provider: openrouter + model: google/gemini-2.5-flash)。
强制特定 provider(基于 OAuth 或 API 密钥):
auxiliary:
compression:
provider: nous
model: gemini-3-flash适用于任何 provider:nous、openrouter、codex、anthropic、main 等。
自定义端点(自托管、Ollama、zai、DeepSeek 等):
auxiliary:
compression:
model: glm-4.7
base_url: https://api.z.ai/api/coding/paas/v4指向自定义 OpenAI 兼容端点。使用 OPENAI_API_KEY 进行认证。
三个旋钮的交互方式
| | auxiliary.compression.provider | auxiliary.compression.base_url | 结果
| auto(默认) | 未设置 | 自动检测最佳可用 provider
| nous / openrouter / 等 | 未设置 | 强制使用该 provider,使用其认证
| 任意 | 已设置 | 直接使用自定义端点(忽略 provider)
摘要模型上下文长度要求
摘要模型必须具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将在没有摘要的情况下被丢弃,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。
会话卡死监视器(Session Stall Watchdog)
Gateway 运行一个仅通知的卡死监视器(agent.session_stall_timeout,默认 300 秒,0 = 禁用)。当一个忙碌的会话存在待处理的入站后续消息,且 agent 的共享活动时钟空闲达到该时长时,gateway 会记录一条 WARNING 日志并向用户发送一次性通知:
⚠️ Agent session appears stalled (last activity N min ago). Try /new to reset.语义:
- 仅通知。 监视器绝不会终止当前轮次 —— 对比 agent.gateway_timeout(长时间无活动后取消运行)。卡死通知只是告诉您 agent 看起来卡住了,由您决定(/new、/stop 或继续等待)。
- 每个卡死周期只通知一次。 待处理入站消息被消化或活动恢复时闩锁清除,因此恢复后再次卡死的会话会再次通知。
- 进度仅来自共享活动快照(工具调用、API 流式进度、压缩心跳)。待处理入站消息是通知门槛,不是进度时钟。
agent:
session_stall_timeout: 300 # 秒;0 禁用监视器上下文引擎
上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 compressor 引擎使用有损摘要(参见上下文压缩)。插件引擎可以用替代策略替换它。
context:
engine: "compressor" # 默认 —— 内置有损摘要使用插件引擎(例如,用于无损上下文管理的 LCM):
context:
engine: "lcm" # 必须与插件名称匹配插件引擎永远不会自动激活 —— 您必须将 context.engine 显式设置为插件名称。可用引擎可以通过 hermes plugins → Provider Plugins → Context Engine 浏览和选择。
有关内存插件的类似单选系统,请参阅内存 Providers。
迭代预算
当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:500 轮)。Hermes 不会在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。
取而代之的是,当预算真正耗尽(500/500)时,Hermes 注入一条消息要求模型收尾,并允许一次宽限调用以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。
agent:
max_turns: 500 # 每次对话轮次的最大迭代次数(默认:500)
api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)当迭代预算完全耗尽时,CLI 向用户显示通知:⚠ Iteration budget reached (500/500) — response may be incomplete。
agent.api_max_retries 控制 Hermes 在回退 provider 切换启动之前对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 3 —— 总共四次尝试。如果您配置了回退 providers 并希望更快地故障转移,请将其降至 0,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。
API 超时
Hermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。
| | 超时 | 默认值 | 本地 providers | 配置/环境变量
| Socket 读取超时 | 120s | 自动提升至 1800s | HERMES_STREAM_READ_TIMEOUT
| 陈旧流检测 | 180s | 自动禁用 | HERMES_STREAM_STALE_TIMEOUT
| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | providers.<id>.stale_timeout_seconds 或 HERMES_API_CALL_STALE_TIMEOUT
| API 调用(非流式) | 1800s | 不变 | providers.<id>.request_timeout_seconds / timeout_seconds 或 HERMES_API_TIMEOUT
Socket 读取超时控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 HERMES_STREAM_READ_TIMEOUT,无论端点检测如何,始终使用该值。
陈旧流检测终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。
陈旧非流检测终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 providers.<id>.stale_timeout_seconds、providers.<id>.models.<model>.stale_timeout_seconds 或 HERMES_API_CALL_STALE_TIMEOUT,即使在本地端点上也会遵守该显式值。
周期性的提供商等待提示仅在至少 60 秒没有响应活动后显示。Codex Responses 的等待状态描述的是没有流事件的时间,而不是生成的总时长:持续收到流事件(包括推理内容)时不会显示等待警告。如果事件停止,提示会显示多久没有收到流事件,而不会声称尚未收到任何响应;事件恢复后提示会清除。当重连进入新的首事件看门狗阶段时,等待状态也会跟随该阶段。这只影响状态显示,不会延长独立的调用总时长限制,也不会更改看门狗超时。Chat Completions 流同样会在数据块恢复时及时清除无输出警告,但不会覆盖本地模型加载状态。
上下文压力警告
与迭代预算压力分开,上下文压力跟踪对话距压缩阈值有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。
| | 进度 | 级别 | 发生的事情
| ≥ 60% 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知
| ≥ 85% 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生
在 CLI 中,上下文压力在工具输出流中显示为进度条:
◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction在消息平台上,发送纯文本通知:
◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).如果自动压缩被禁用,警告会告诉您上下文可能被截断。
上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。
凭据池策略
当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:
credential_pool_strategies:
openrouter: round_robin # 均匀循环使用密钥
anthropic: least_used # 始终选择使用最少的密钥选项:fill_first(默认)、round_robin、least_used、random。完整文档请参阅凭据池。
Prompt 缓存
当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。
对于原生 Anthropic、OpenRouter 和 Nous Portal 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(ttl: "1h")的 cache_control 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 hermes 会话和分叉子 agent 被重用。
Qwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 xAI prompt 缓存。
不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。
辅助模型
Hermes 使用"辅助"模型处理图像分析、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(auxiliary.*.provider: "auto"),Hermes 将每个辅助任务路由到您的主聊天模型 —— 与您在 hermes model 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 auxiliary.<task>.provider 和 auxiliary.<task>.model(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉分析)。(网页提取不是辅助任务:web_extract 和浏览器快照会确定性截断长内容,并将完整文本存储供 read_file 分页读取 —— 不涉及 LLM。)
为什么 "auto" 使用您的主模型
早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。auto 现在对所有人使用主模型,config.yaml 中的每任务覆盖仍然优先(见下方完整辅助配置参考)。
交互式配置辅助模型
无需手动编辑 YAML,运行 hermes model 并从菜单中选择**"配置辅助模型"**。您将获得交互式的每任务选择器:
$ hermes model
→ Configure auxiliary models
[ ] vision currently: auto / main model
[ ] title_generation currently: openrouter / google/gemini-3-flash-preview
[ ] compression currently: auto / main model
[ ] approval currently: auto / main model
[ ] triage_specifier currently: auto / main model
[ ] kanban_decomposer currently: auto / main model
[ ] profile_describer currently: auto / main model选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 config.yaml 中的 auxiliary.<task>.*。与主模型选择器相同的机制 —— 无需学习额外语法。
视频教程
通用配置模式
Hermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:
| | 键 | 作用 | 默认值
| provider | 用于认证和路由的 provider | "auto"
| model | 请求的模型 | provider 的默认值
| base_url | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置
当设置 base_url 时,Hermes 忽略 provider 并直接调用该端点(使用 api_key 或 OPENAI_API_KEY 进行认证)。当仅设置 provider 时,Hermes 使用该 provider 的内置认证和基础 URL。
辅助任务的可用 providers:auto、main,以及provider 注册表中的任何 provider —— openrouter、nous、openai-codex、copilot、copilot-acp、anthropic、gemini、qwen-oauth、zai、kimi-coding、kimi-coding-cn、minimax、minimax-cn、minimax-oauth、deepseek、nvidia、xai、xai-oauth、ollama-cloud、alibaba、bedrock、huggingface、arcee、xiaomi、kilocode、opencode-zen、opencode-go、ai-gateway、azure-foundry —— 或您 custom_providers 列表中任何命名的自定义 provider(例如 provider: "beans")。
MiniMax OAuth
minimax-oauth 通过浏览器 OAuth 登录(无需 API 密钥)。运行 hermes model 并选择 MiniMax (OAuth) 进行认证。辅助任务自动使用 MiniMax-M2.7-highspeed。参阅 MiniMax OAuth 指南。
xAI Grok OAuth
xai-oauth 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 hermes model 并选择 xAI Grok OAuth (SuperGrok / Premium+) 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 xAI Grok OAuth 指南,如果 Hermes 在远程主机上,请参阅 SSH/远程主机上的 OAuth。
"main" 仅用于辅助任务
"main" provider 选项表示"使用我的主 agent 使用的任何 provider" —— 它仅在 auxiliary:、compression: 和 fallback_model: 配置中有效。它不是顶级 model.provider 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 model: 部分设置 provider: custom。所有主模型 provider 选项请参阅 AI Providers。
如果端点直接拒绝推理字段(例如 OpenAI 兼容中继后面的纯聊天模型返回 400 Unrecognized request argument supplied: reasoning_effort),辅助调用会去掉所有推理字段重试一次,因此该任务(例如会话标题)仍会以端点的默认行为完成。
后台审查有所不同: 与主会话使用同一模型的审查分支始终继承主会话的推理强度;auxiliary.background_review.reasoning_effort 在这条路径上不会生效,即使显式指定了主会话的 provider/model 也一样。推理设置、系统 prompt、完整会话快照和工具定义保持逐字节一致,以复用 prompt 缓存前缀。没有用于同模型审查的独立推理强度开关。详见同模型审查的推理强度。路由到其他模型时的独立问题见 #94825。
完整辅助配置参考
auxiliary:
# 图像分析(vision_analyze 工具 + 浏览器截图)
vision:
provider: "auto" # "auto"、"openrouter"、"nous"、"codex"、"main" 等
model: "" # 例如 "openai/gpt-4o"、"google/gemini-2.5-flash"
base_url: "" # 自定义 OpenAI 兼容端点(覆盖 provider)
api_key: "" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)
timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时
download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加
# 危险命令审批分类器
approval:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30 # 秒
# 上下文压缩超时(与 compression.* 配置分开)
compression:
timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间
# fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider
# - provider: nous
# model: deepseek/deepseek-chat
# - provider: openrouter
# model: google/gemini-2.5-flash
# base_url: ""
# api_key: ""
# 技能中心 —— 技能匹配和搜索
skills_hub:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30
# MCP 工具调度
mcp:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30
# Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或
# 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此
# 槽位将单行描述扩展为具体规格并将
# 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展
# 很短,不需要推理深度。
triage_specifier:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 120提示
每个辅助任务都有可配置的 timeout(秒)。默认值:vision 120s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 download_timeout(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。
信息
上下文压缩有自己的 compression: 块用于阈值,以及 auxiliary.compression: 块用于模型/provider 设置 —— 参阅上方的上下文压缩。主备用链使用顶层的 fallback_providers: 列表 —— 参阅备用提供商。三者都遵循相同的 provider/model/base_url 模式。
辅助任务的每任务回退链
每个辅助任务都可以选择性地定义一个 fallback_chain —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:
auxiliary:
compression:
provider: openrouter
model: openai/gpt-4o-mini
fallback_chain:
- provider: nous
model: deepseek/deepseek-chat
- provider: openrouter
model: google/gemini-2.5-flash当主要辅助 provider(openrouter / openai/gpt-4o-mini)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 fallback_chain。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。
每个条目支持与任何辅助任务配置相同的三个旋钮:
| | 键 | 描述
| provider | Provider 名称(nous、openrouter、anthropic、gemini、main 等)
| model | 该 provider 的模型名称
| base_url | (可选)自定义 OpenAI 兼容端点
fallback_chain 适用于任何辅助任务 —— compression、vision、approval、skills_hub、mcp 等。
OpenRouter 路由和辅助任务的 Pareto Code
当辅助任务解析到 OpenRouter(显式或通过 provider: "main" 而您的主 agent 在 OpenRouter 上)时,主 agent 的 provider_routing 和 openrouter.min_coding_score 设置不会传播 —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 Pareto Code 路由器,请通过 extra_body 按任务设置:
auxiliary:
compression:
provider: openrouter
model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器
extra_body:
provider: # OpenRouter provider 路由偏好
order: [anthropic, google] # 按此顺序尝试这些 providers
sort: throughput # 或 "price" | "latency"
# only: [anthropic] # 限制到特定 provider
# ignore: [deepinfra] # 排除特定 providers
plugins: # OpenRouter Pareto Code 路由器旋钮
- id: pareto-router
min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 extra_body,因此 openrouter.ai/docs 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。
更改视觉模型
使用 GPT-4o 而非 Gemini Flash 进行图像分析:
auxiliary:
vision:
model: "openai/gpt-4o"或通过环境变量(在 ~/.hermes/.env 中):
AUXILIARY_VISION_MODEL=openai/gpt-4oProvider 选项
这些选项适用于辅助任务配置(auxiliary:、compression:、fallback_model:),而非您的主 model.provider 设置。
| | Provider | 描述 | 要求
| "auto" | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | —
| "openrouter" | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | OPENROUTER_API_KEY
| "nous" | 强制 Nous Portal | hermes auth
| "codex" | 强制 Codex OAuth(ChatGPT 账户)。请显式设置 model(例如 gpt-5.4)。 | hermes model → ChatGPT or Codex Subscription
| "minimax-oauth" | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | hermes model → MiniMax (OAuth)
| "xai-oauth" | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | hermes model → xAI Grok OAuth (SuperGrok / Premium+)
| "main" | 使用您的活跃自定义/主端点。可以来自 OPENAI_BASE_URL + OPENAI_API_KEY 或通过 hermes model / config.yaml 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。仅限辅助任务 —— 对 model.provider 无效。 | 自定义端点凭据 + 基础 URL
当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 GMI_API_KEY 后,gmi 有效:
auxiliary:
compression:
provider: "gmi"
model: "anthropic/claude-opus-4.6"对于 GMI 辅助路由,使用 GMI 的 /v1/models 端点返回的确切模型 ID。
常见设置
使用直接自定义端点(比 provider: "main" 对本地/自托管 API 更清晰):
auxiliary:
vision:
base_url: "http://localhost:1234/v1"
api_key: "local-key"
model: "qwen2.5-vl"base_url 优先于 provider,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 api_key 或回退到 OPENAI_API_KEY;它不会为该自定义端点重用 OPENROUTER_API_KEY。两者都未设置时,只有当 base_url 与主端点的源(scheme、主机和端口)完全相同时,才会重用主模型的密钥。主端点是会话当前运行的端点(/model 切换后即为切换后的端点),并且只与它自己的密钥配对,绝不会使用另一个端点的密钥。
使用 OpenAI API 密钥进行视觉:
# 在 ~/.hermes/.env 中:
# OPENAI_BASE_URL=https://api.openai.com/v1
# OPENAI_API_KEY=sk-...
auxiliary:
vision:
provider: "main"
model: "gpt-4o" # 或 "gpt-4o-mini" 更便宜使用 OpenRouter 进行视觉(路由到任何模型):
auxiliary:
vision:
provider: "openrouter"
model: "openai/gpt-4o" # 或 "google/gemini-2.5-flash" 等使用 Codex OAuth(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):
auxiliary:
vision:
provider: "codex" # 使用您的 ChatGPT OAuth token
model: "gpt-5.4" # Codex 路径没有隐式默认模型使用 MiniMax OAuth(浏览器登录,无需 API 密钥):
model:
default: MiniMax-M2.7
provider: minimax-oauth
base_url: https://api.minimax.io/anthropic运行 hermes model 并选择 MiniMax (OAuth) 自动登录并设置此项。对于中国区域,基础 URL 将是 https://api.minimaxi.com/anthropic。完整演练请参阅 MiniMax OAuth 指南。
使用本地/自托管模型:
auxiliary:
vision:
provider: "main" # 使用您的活跃自定义端点
model: "my-local-model"provider: "main" 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 beans)、内置 provider(如 openrouter)还是旧版 OPENAI_BASE_URL 端点。
提示
如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。
注意
视觉需要多模态模型。 如果您设置 provider: "main",请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。
环境变量(旧版)
辅助模型也可以通过环境变量配置。但是,config.yaml 是首选方法 —— 它更易于管理,并支持所有选项,包括 base_url 和 api_key。
| | 设置 | 环境变量
| Vision provider | AUXILIARY_VISION_PROVIDER
| Vision 模型 | AUXILIARY_VISION_MODEL
| Vision 端点 | AUXILIARY_VISION_BASE_URL
| Vision API 密钥 | AUXILIARY_VISION_API_KEY
压缩和回退模型设置仅限 config.yaml。(AUXILIARY_WEB_EXTRACT_* 变量已废弃 —— 网页提取不再使用辅助 LLM。)
提示
运行 hermes config 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。
原生视觉嵌入预算(顶层 vision:)
与 auxiliary.vision(选择描述器模型)不同:当主模型支持视觉时,vision_analyze 和浏览器截图会把真实像素嵌入工具结果,并在之后的每一轮请求中重复发送。vision.embed_target_bytes(默认 262144,限制在 64 KiB..4 MiB 之间)控制单次嵌入的大小;vision.max_calls_per_image 限制同一张图片在一个会话中可被嵌入的次数(未设置 = 委派子代理内为 3,主代理不限;0 = 不限)。参见 视觉 → 原生嵌入伴随整个会话。
推理努力程度
控制模型在响应前进行多少"思考":
agent:
reasoning_effort: "" # 空 = 中等。选项:none、minimal、low、medium、high、xhigh、max、ultra未设置时(默认),推理努力程度默认为"medium" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。
您也可以在运行时使用 /reasoning 命令更改推理努力程度:
/reasoning # 显示当前努力程度和显示状态
/reasoning high # 将推理努力程度设为 high
/reasoning none # 禁用推理
/reasoning show # 在每次响应上方显示模型思考
/reasoning hide # 隐藏模型思考工具使用强制
某些模型偶尔会将预期操作描述为文本而不是进行工具调用("我会运行测试..."而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。
agent:
tool_use_enforcement: "auto" # "auto" | true | false | ["model-substring", ...]| | 值 | 行为
| "auto"(默认) | 对匹配以下模型启用:gpt、codex、gemini、gemma、grok。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。
| true | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。
| false | 始终禁用,无论模型如何。
| ["gpt", "codex", "qwen", "llama"] | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。
注入的内容
启用后,系统提示词中可能会添加三层指导:
-
通用工具使用强制(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。
-
OpenAI 执行纪律(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布"完成"。
-
Google 操作指导(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。
这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 "auto" 排除它们。
何时开启
如果您使用的模型不在默认自动列表中,并注意到它经常描述它会做什么而不是实际去做,请设置 tool_use_enforcement: true 或将模型子字符串添加到列表中:
agent:
tool_use_enforcement: ["gpt", "codex", "gemini", "grok", "my-custom-model"]TTS 配置
tts:
provider: "edge" # "edge" | "elevenlabs" | "openai" | "minimax" | "mistral" | "gemini" | "xai" | "neutts"
speed: 1.0 # 全局速度倍数(所有 provider 的回退)
edge:
voice: "en-US-AriaNeural" # 322 种声音,74 种语言
speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)
elevenlabs:
voice_id: "pNInz6obpgDQGcFmaJgB"
model_id: "eleven_multilingual_v2"
openai:
model: "gpt-4o-mini-tts"
voice: "alloy" # alloy、echo、fable、onyx、nova、shimmer
speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)
base_url: "https://api.openai.com/v1" # 覆盖 OpenAI 兼容 TTS 端点
minimax:
speed: 1.0 # 语音速度倍数
# base_url: "" # 可选:覆盖 OpenAI 兼容 TTS 端点
mistral:
model: "voxtral-mini-tts-2603"
voice_id: "c69964a6-ab8b-4f8a-9465-ec0925096ec8" # Paul - Neutral(默认)
gemini:
model: "gemini-2.5-flash-preview-tts" # 或 gemini-2.5-pro-preview-tts
voice: "Kore" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等
xai:
voice_id: "eve" # xAI TTS 声音
language: "en" # ISO 639-1
sample_rate: 24000
bit_rate: 128000 # MP3 比特率
# base_url: "https://api.x.ai/v1"
neutts:
ref_audio: ''
ref_text: ''
model: neuphonic/neutts-air-q4-gguf
device: cpu这控制 text_to_speech 工具和语音模式中的口语回复(CLI 中的 /voice tts 或消息 gateway)。
速度回退层次: provider 特定速度(例如 tts.edge.speed)→ 全局 tts.speed → 1.0 默认值。设置全局 tts.speed 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。
显示设置
display:
tool_progress: all # off | new | all | verbose
tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令
platforms: {} # 每平台显示覆盖(见下文)
interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送
skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)
personality: "kawaii" # 旧版外观字段,仍在某些摘要中显示
compact: false # 紧凑输出模式(减少空白)
resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)
bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)
show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)
streaming: false # 将 token 实时流式传输到终端
show_cost: false # 在 CLI 状态栏中显示估计 $ 成本
timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀
tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)
runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚
enabled: false
fields: ["model", "context_pct", "cwd"]
file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚
language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu文件变更验证器
当 display.file_mutation_verifier 为 true(默认)时,每当本轮中 write_file 或 patch 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了"批量并行补丁,一半静默失败,模型总结成功"这类过度声明,而无需您在每次编辑后手动运行 git status。
示例页脚:
⚠️ File-mutation verifier: 3 file edit(s) FAILED this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm what actually landed.
• concepts/automatic-organization.md — [patch] Could not find match for old_string
• concepts/lora.md — [patch] Could not find match for old_string
• concepts/rag-pipeline.md — [patch] Could not find match for old_string设置 file_mutation_verifier: false(或 HERMES_FILE_MUTATION_VERIFIER=0)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。
静态消息的 UI 语言
display.language 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、"审批已过期"、"目标已清除")。它不翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。
支持的值:en(默认)、zh(简体中文)、zh-hant(繁体中文)、ja(日语)、de(德语)、es(西班牙语)、fr(法语)、tr(土耳其语)、uk(乌克兰语)、af(南非荷兰语)、ko(韩语)、it(意大利语)、ga(爱尔兰语)、pt(葡萄牙语)、ru(俄语)、hu(匈牙利语)。未知值回退到英文。
您也可以使用 HERMES_LANGUAGE 环境变量按会话设置,它会覆盖配置值。
display:
language: zh # CLI 审批提示以中文显示| | 模式 | 您看到的内容
| off | 静默 —— 仅最终响应
| new | 仅在工具更改时显示工具指示器
| all | 每次工具调用附带简短预览(默认)
| verbose | 完整参数、结果和调试日志
在 CLI 中,使用 /verbose 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 /verbose,请在上方的 display 部分设置 tool_progress_command: true。该命令将循环切换模式并保存到配置。
运行时元数据页脚(仅限 gateway)
当 display.runtime_footer.enabled: true 时,Hermes 在每个 gateway 轮次的最终消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。
display:
runtime_footer:
enabled: true
fields: ["model", "context_pct", "cwd"] # 支持字段:model、context_pct、cwd/footer 斜杠命令在任何会话中运行时切换此功能。
附加到 Telegram/Discord/Slack 回复的示例页脚:
— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042只有轮次的最终消息获得页脚;中间更新保持干净。
每平台进度覆盖
不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 display.platforms 设置每平台模式:
display:
tool_progress: all # 全局默认
platforms:
signal:
tool_progress: 'off' # 在 Signal 上静默进度
telegram:
tool_progress: verbose # 在 Telegram 上详细进度
slack:
tool_progress: 'off' # 在共享 Slack 工作区中保持安静没有覆盖的平台回退到全局 tool_progress 值。有效平台键:telegram、discord、slack、signal、whatsapp、matrix、mattermost、email、sms、dingtalk、feishu、wecom、weixin、bluebubbles、qqbot,以及任意插件平台的名称(例如 Home Assistant 插件的 homeassistant)。旧版 display.tool_progress_overrides 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 display.platforms。
interim_assistant_messages 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 tool_progress 无关,不需要 gateway 流式传输。
隐私
privacy:
redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)当 redact_pii 为 true 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:
| | 字段 | 处理方式
| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 user_<12-char-sha256>
| 用户 ID | 哈希为 user_<12-char-sha256>
| 聊天 ID | 数字部分哈希,保留平台前缀(telegram:<hash>)
| 主频道 ID | 数字部分哈希
| 用户名/昵称 | 不受影响(用户选择的,公开可见)
平台支持: 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(<@user_id>)需要 LLM 上下文中的真实 ID。
哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。
语音转文字(STT)
stt:
provider: "local" # "local" | "groq" | "openai" | "mistral"
local:
model: "base" # tiny、base、small、medium、large-v3
openai:
model: "whisper-1" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe | gpt-transcribe
# model: "whisper-1" # 旧版回退键仍受支持Provider 行为:
- local 使用在您机器上运行的 faster-whisper。使用 python -c "import pm; pm.sync_venv(['stt-whisper'], explicit=True)" 单独安装。静音幻觉防护默认开启:Silero VAD 过滤器让静音/噪声不会进入 Whisper,跨窗口条件预测被禁用,并且模型自己标记为"很可能不是语音"且低置信度的片段会被丢弃。设置 stt.local.vad: false 可用原始行为转录非语音音频(音乐、环境声)。
- groq 使用 Groq 的 Whisper 兼容端点,读取 GROQ_API_KEY。
- openai 使用 OpenAI 语音 API,读取 VOICE_TOOLS_OPENAI_KEY。
如果请求的 provider 不可用,Hermes 按此顺序自动回退:local → groq → openai。
Groq 和 OpenAI 模型覆盖由环境变量驱动:
STT_GROQ_MODEL=whisper-large-v3-turbo
STT_OPENAI_MODEL=whisper-1
GROQ_BASE_URL=https://api.groq.com/openai/v1
STT_OPENAI_BASE_URL=https://api.openai.com/v1语音模式(CLI)
voice:
record_key: "ctrl+b" # CLI 内的按键通话键
max_recording_seconds: 120 # 长录音的硬停止
auto_tts: false # /voice on 时自动启用口语回复
beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音
silence_threshold: 200 # 语音检测的 RMS 阈值
silence_duration: 3.0 # 自动停止前的静默秒数在 CLI 中使用 /voice on 启用麦克风模式,使用 record_key 开始/停止录音,使用 /voice tts 切换口语回复。端到端设置和平台特定行为请参阅语音模式。
流式传输
将 token 实时流式传输到终端或消息平台,而不是等待完整响应。
CLI 流式传输
display:
streaming: true # 实时将 token 流式传输到终端
show_reasoning: true # 同时流式传输推理/思考 token(可选)启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。
Gateway 流式传输(Telegram、Discord、Slack)
streaming:
enabled: true # 启用渐进式消息编辑
transport: edit # "edit"(渐进式消息编辑)或 "off"
edit_interval: 0.3 # 消息编辑之间的秒数
buffer_threshold: 40 # 强制编辑刷新前的字符数
cursor: " ▉" # 流式传输期间显示的光标
fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。
对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 display.interim_assistant_messages: true。
溢出处理: 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。
新的最终消息(Telegram): Telegram 的 editMessageText 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 fresh_final_after_seconds > 0 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 0,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。
备注
主开关 streaming.enabled 默认为 false——在你启用之前不会有任何流式传输。启用后,是否流式传输按平台决定:Telegram 默认带有 display.platforms.telegram.streaming: true(流式传输),Discord 为 display.platforms.discord.streaming: false(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 Channels 开关中或直接在 ~/.hermes/config.yaml 中调整这些按平台的开关。
群聊会话隔离
控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:
group_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话- true 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。
- false 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。
- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。
- 线程与其父频道保持隔离;使用 true 时,每个参与者在线程内也获得自己的会话。
有关行为详情和示例,请参阅会话和 Discord 指南。
未授权 DM 行为
控制当未知用户发送私信时 Hermes 的行为:
unauthorized_dm_behavior: pair
whatsapp:
unauthorized_dm_behavior: ignore- pair 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。
- ignore 静默丢弃未授权的 DM。
- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。
快速命令
定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。
quick_commands:
status:
type: exec
command: systemctl status hermes-agent
disk:
type: exec
command: df -h /
update:
type: exec
command: hermes update
gpu:
type: exec
command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader
restart:
type: alias
target: /gateway restart用法:在 CLI 或任何消息平台中输入 /status、/disk、/update、/gpu 或 /restart。exec 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。alias 命令重写为配置的斜杠命令目标。
- 30 秒超时 —— 长时间运行的命令被终止并显示错误消息
- 优先级 —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称
- 自动补全 —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中
- 类型 —— 支持的类型为 exec 和 alias;其他类型显示错误
- 到处可用 —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant
仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。
人类延迟
在消息平台中模拟类人响应节奏:
human_delay:
mode: "off" # off | natural | custom
min_ms: 800 # 最小延迟(自定义模式)
max_ms: 2500 # 最大延迟(自定义模式)代码执行
配置 execute_code 工具:
code_execution:
mode: project # project(默认)| strict
timeout: 300 # 最大执行时间(秒)
max_tool_calls: 50 # 代码执行中的最大工具调用次数mode 控制脚本的工作目录和 Python 解释器:
- project(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(pandas、torch、项目包)和相对路径(.env、./data.csv)自然解析,与 terminal() 看到的一致。
- strict —— 脚本在临时暂存目录中以 sys.executable(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。
环境清理(删除 *_API_KEY、*_TOKEN、*_SECRET、*_PASSWORD、*_CREDENTIAL、*_PASSWD、*_AUTH)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。
Web 搜索后端
web_search 和 web_extract 工具支持五种后端 provider。在 config.yaml 中或通过 hermes tools 配置后端:
web:
backend: firecrawl # firecrawl | searxng | parallel | tavily | exa
# 或使用每功能键混合 provider(例如免费搜索 + 付费提取):
search_backend: "searxng"
extract_backend: "firecrawl"| | 后端 | 环境变量 | 搜索 | 提取
| Firecrawl(默认) | FIRECRAWL_API_KEY | ✔ | ✔
| SearXNG | SEARXNG_URL | ✔ | —
| Parallel | PARALLEL_API_KEY | ✔ | ✔
| Tavily | TAVILY_API_KEY | ✔ | ✔
| Exa | EXA_API_KEY | ✔ | ✔
后端选择: 如果未设置 web.backend,后端从可用的 API 密钥自动检测。如果仅设置了 SEARXNG_URL,使用 SearXNG。如果仅设置了 EXA_API_KEY,使用 Exa。如果仅设置了 TAVILY_API_KEY,使用 Tavily。如果仅设置了 PARALLEL_API_KEY,使用 Parallel。否则 Firecrawl 是默认值。
SearXNG 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 SEARXNG_URL 设置为您的实例(例如 http://localhost:8080)。SearXNG 仅限搜索;web_extract 需要单独的提取 provider(设置 web.extract_backend)。Docker 设置说明请参阅 Web 搜索设置指南。
自托管 Firecrawl: 设置 FIRECRAWL_API_URL 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 USE_DB_AUTHENTICATION=*** 以禁用认证)。
Parallel 搜索模式: 设置 PARALLEL_SEARCH_MODE 控制搜索行为 —— fast、one-shot 或 agentic(默认:agentic)。
Exa: 在 ~/.hermes/.env 中设置 EXA_API_KEY。支持 category 过滤(company、research paper、news、people、personal site、pdf)和域名/日期过滤器。
浏览器
配置浏览器自动化行为:
browser:
inactivity_timeout: 120 # 自动关闭空闲会话前的秒数
command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)
record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/
# 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的
# Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。
cdp_url: ""
# 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系
# 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。
# 在 Camofox 和默认本地 agent 浏览器模式下忽略。
dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept
dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)
camofox:
managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录
user_id: "" # 可选的外部管理 Camofox userId
session_key: "" # Hermes 创建标签页时发送的可选会话密钥
adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页对话框策略:
- must_respond(默认)—— 捕获对话框,在 browser_snapshot.pending_dialogs 中显示,等待 agent 调用 browser_dialog(action=...)。在 dialog_timeout_s 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。
- auto_dismiss —— 捕获,立即关闭。Agent 仍然在事后的 browser_snapshot.recent_dialogs 中看到对话框记录,closed_by="auto_policy"。
- auto_accept —— 捕获,立即接受。适用于有激进 beforeunload 提示的页面。
完整对话框工作流请参阅浏览器功能页面。
浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅浏览器功能页面。
时区
使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。
timezone: "America/New_York" # IANA 时区(默认:"" = 服务器本地时间)支持的值:任何 IANA 时区标识符(例如 America/New_York、Europe/London、Asia/Kolkata、UTC)。留空或省略以使用服务器本地时间。
代理时钟、cron 调度和时间相关工具在所有操作系统上都遵循此时区。通过 execute_code 运行的代码在 Linux 和 macOS 上也会以 TZ 的形式继承它;在 Windows 上这些子进程保留操作系统配置的时区(Windows C 运行时只解析 POSIX 形式的 TZ 字符串,IANA 名称会产生错误的 UTC 偏移量),因此若子脚本必须以此时区显示本地时间,请直接设置 Windows 系统时区。
Discord
为消息 gateway 配置 Discord 特定行为:
discord:
require_mention: true # 在服务器频道中需要 @提及才能响应
free_response_channels: "" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应
auto_thread: true # 在频道中 @提及时自动创建线程- require_mention —— 为 true(默认)时,bot 仅在服务器频道中被 @BotName 提及时响应。DM 始终无需提及即可工作。
- free_response_channels —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。
- auto_thread —— 为 true(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。
安全
机密脱敏和网站黑名单:
security:
redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)
website_blocklist: # 参见下方网站黑名单部分
enabled: false
domains: []
shared_files: []- redact_secrets —— 为 true 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。默认关闭 —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 true 以开启。
使用 tirith 扫描命令现在是可选插件,参见内容级命令检查。
网站黑名单
阻止 agent 的 web 和浏览器工具访问特定域名:
security:
website_blocklist:
enabled: false # 启用 URL 阻止(默认:false)
domains: # 被阻止的域名模式列表
- "*.internal.company.com"
- "admin.example.com"
- "*.local"
shared_files: # 从外部文件加载额外规则
- "/etc/hermes/blocked-sites.txt"启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 web_search、web_extract、browser_navigate 以及任何访问 URL 的工具。
域名规则支持:
- 精确域名:admin.example.com
- 通配符子域名:*.internal.company.com(阻止所有子域名)
- TLD 通配符:*.local
共享文件每行包含一条域名规则(空行和 # 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。
策略缓存 30 秒,因此配置更改无需重启即可快速生效。
智能审批
控制 Hermes 如何处理潜在危险命令:
approvals:
mode: smart # smart | manual | off| | 模式 | 行为
| smart(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。
| manual | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。
| off | 跳过所有审批检查。等同于 HERMES_YOLO_MODE=true。谨慎使用。
智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。
注意
设置 approvals.mode: off 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。
检查点
破坏性文件操作之前的自动文件系统快照。详情请参阅检查点与回滚。
checkpoints:
enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。
max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)委托
为委托工具配置子 agent 行为:
delegation:
# model: "google/gemini-3-flash-preview" # 覆盖模型(空 = 继承父级)
# provider: "openrouter" # 覆盖 provider(空 = 继承父级)
# base_url: "http://localhost:1234/v1" # 直接 OpenAI 兼容端点(优先于 provider)
# api_key: "local-key" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)
# api_mode: "" # base_url 的线路协议:"chat_completions"、"codex_responses" 或 "anthropic_messages"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。
max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。
max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。
orchestrator_enabled: true # 全局终止开关。为 false 时,role="orchestrator" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。子 agent provider:model 覆盖: 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 delegation.provider 和 delegation.model 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。
直接端点覆盖: 如果您想要明显的自定义端点路径,请设置 delegation.base_url、delegation.api_key 和 delegation.model。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 delegation.provider。如果省略 delegation.api_key,Hermes 仅回退到 OPENAI_API_KEY。
线路协议(api_mode): Hermes 从 delegation.base_url 自动检测线路协议(例如以 /anthropic 结尾的路径 → anthropic_messages;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 delegation.api_mode 显式设置为 chat_completions、codex_responses 或 anthropic_messages 之一。留空(默认)以保持自动检测。
委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:openrouter、nous、copilot、zai、kimi-coding、minimax、minimax-cn。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。
优先级: 配置中的 delegation.base_url → 配置中的 delegation.provider → 父 provider(继承)。配置中的 delegation.model → 父模型(继承)。仅设置 model 而不设置 provider 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。
宽度和深度: max_concurrent_children 限制每批并行运行的子 agent 数量(默认 3,下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。当模型提交的 tasks 数组超过上限时,delegate_task 返回工具错误解释限制,而不是静默截断。max_spawn_depth 控制委托树深度(截断到 1-3)。在默认 1 时,委托是扁平的:子级无法生成孙级,传递 role="orchestrator" 静默降级为 leaf。提升到 2 使编排器子级可以生成叶子孙级;3 用于三级树。Agent 通过 role="orchestrator" 按调用选择编排;orchestrator_enabled: false 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 max_spawn_depth: 3 和 max_concurrent_children: 3 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅子 Agent 委托 → 深度限制和嵌套编排。
澄清
配置澄清提示行为:
clarify:
timeout: 120 # 等待用户澄清响应的秒数上下文文件(SOUL.md、AGENTS.md)
Hermes 使用两种不同的上下文范围:
| | 文件 | 用途 | 范围
| SOUL.md | 主要 agent 身份 —— 定义 agent 是谁(系统提示词第 #1 槽位) | ~/.hermes/SOUL.md 或 $HERMES_HOME/SOUL.md
| .hermes.md / HERMES.md | 项目特定指令(最高优先级) | 向上走到 git 根目录
| AGENTS.md | 项目特定指令、编码规范 | 递归目录遍历
| CLAUDE.md | Claude Code 上下文文件(也会检测) | 仅工作目录
| .cursorrules | Cursor IDE 规则(也会检测) | 仅工作目录
| .cursor/rules/*.mdc | Cursor 规则文件(也会检测) | 仅工作目录
- SOUL.md 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。
- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。
- 项目上下文文件使用优先级系统 —— 仅加载一种类型(第一个匹配优先):.hermes.md → AGENTS.md → CLAUDE.md → .cursorrules。SOUL.md 始终独立加载。
- AGENTS.md 是分层的:如果子目录也有 AGENTS.md,所有都会合并。
- 如果 SOUL.md 不存在,Hermes 会自动生成默认的 SOUL.md。
- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。
另请参阅:
- 个性与 SOUL.md
- 上下文文件
工作目录
| | 上下文 | 默认值
| CLI(hermes) | 运行命令的当前目录
| 消息 gateway | 主目录 ~(用 MESSAGING_CWD 覆盖)
| Docker / Singularity / Modal / SSH | 容器或远程机器内用户的主目录
覆盖工作目录:
# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:
MESSAGING_CWD=/home/myuser/projects # Gateway 会话
TERMINAL_CWD=/workspace # 所有终端会话配置模型
Hermes 使用两类模型槽位:
- 主模型 — agent 的思考核心。每条用户消息、每个工具调用循环、每次流式响应都经由该模型处理。
- 辅助模型 — agent 卸载给较小模型的边缘任务。包括上下文压缩、视觉(图像分析)、网页摘要、审批评分、MCP 工具路由、会话标题生成和技能搜索。每项任务有独立槽位,可单独覆盖。
本页介绍如何通过仪表板配置上述两类模型。如需使用配置文件或 CLI,请跳至底部的其他方法。
最快路径:Nous Portal
Nous Portal 在单一订阅下提供 300+ 个模型。全新安装后,运行 hermes setup --portal 即可登录并一键将 Nous 设为提供商。使用 hermes portal info 查看当前配置。
Models 页面
打开仪表板,点击侧边栏中的 Models。页面分为两个区域:
- Model Settings — 顶部面板,用于为各槽位分配模型。
- 使用分析 — 按排名显示所选时间段内运行过会话的所有模型,包含 token 数量、费用和能力标签。
顶部卡片为 Model Settings 面板。主行始终显示 agent 将为新会话启动的模型。点击 Change 打开选择器。
设置主模型
点击主模型行上的 Change:
选择器分为两列:
- 左列 — 已认证的提供商。仅显示已配置的提供商(已设置 API key、完成 OAuth 或定义了自定义端点)。若某提供商未出现,请前往 Keys 添加凭据。
- 右列 — 所选提供商的精选模型列表。这些是 Hermes 针对该提供商推荐的 agentic 模型,而非原始的 /models 接口返回结果(OpenRouter 的原始列表包含 400+ 个模型,涵盖 TTS、图像生成器和重排序器)。
在过滤框中输入提供商名称、slug 或模型 ID 进行筛选。
选择模型后点击 Switch,Hermes 会将其写入 ~/.hermes/config.yaml 的 model 部分。此操作仅对新会话生效 — 已打开的聊天标签页将继续使用启动时的模型。如需在当前聊天中热切换,请在聊天内使用 /model 斜杠命令。
设置辅助模型
点击 Show auxiliary 展开 11 个任务槽位:
每个辅助任务默认为 auto,即 Hermes 对该任务也使用主模型。当某个边缘任务需要更便宜或更快的模型时,可单独覆盖该槽位。
常见覆盖模式
| | 任务 | 何时覆盖
| Title Gen(标题生成) | 几乎总是。$0.10/M 的 flash 模型生成会话标题的效果与 Opus 相当。默认配置在 OpenRouter 上将此项设为 google/gemini-3-flash-preview。
| Vision(视觉) | 当主模型是不支持视觉的编程模型时(如 Kimi、DeepSeek)。将其指向 google/gemini-2.5-flash 或 gpt-4o-mini。
| Compression(压缩) | 当你在用 Opus/M2.7 的推理 token 来摘要上下文时。快速聊天模型以 1/50 的成本即可完成此工作。
| Approval(审批) | 用于 approval_mode: smart — 由快速/廉价模型(haiku、flash、gpt-5-mini)决定是否自动批准低风险命令。此处使用昂贵模型是浪费。
| Web Extract(网页提取) | 当你大量使用 web_extract 时。逻辑同压缩 — 摘要任务不需要推理能力。
| Skills Hub(技能中心) | hermes skills search 使用此槽位。通常保持 auto 即可。
| MCP | MCP 工具路由。通常保持 auto 即可。
单任务覆盖
点击任意辅助行上的 Change,打开相同的选择器,操作方式相同 — 选择提供商和模型,点击 Switch。该行将从 auto (use main model) 更新为 provider · model。
全部重置为 auto
如果调整过度想重新开始,点击辅助区域顶部的 Reset all to auto。所有槽位将恢复使用主模型。
"Use as" 快捷方式
页面上每张模型卡片都有 Use as 下拉菜单。这是快捷路径 — 从分析数据中选择一个模型,点击 Use as,一键将其分配到主槽位或任意辅助任务:
下拉菜单包含:
- Main model — 与点击主行上的 Change 效果相同。
- All auxiliary tasks — 将此模型分配给全部 11 个辅助槽位。适合将所有边缘任务统一切换到廉价 flash 模型的场景。
- 单项任务选项 — Vision、Web Extract、Compression 等。每项任务当前分配的模型标记为 current。
当模型卡片当前已分配到某个槽位时,会显示 main 或 aux · <task> 标签,方便一眼看出历史模型的使用情况。
写入 config.yaml 的内容
通过仪表板保存时,Hermes 写入 ~/.hermes/config.yaml:
主模型:
model:
provider: openrouter
default: anthropic/claude-opus-4.7
base_url: '' # cleared on provider switch
api_mode: chat_completions辅助覆盖示例(视觉任务使用 gemini-flash):
auxiliary:
vision:
provider: openrouter
model: google/gemini-2.5-flash
base_url: ''
api_key: ''
timeout: 120
extra_body: {}
download_timeout: 30辅助任务处于 auto(默认):
auxiliary:
compression:
provider: auto
model: ''
base_url: ''
# ... other fields unchangedprovider: auto 加 model: '' 表示 Hermes 对该任务使用主模型。
何时生效?
- CLI(hermes chat):下次执行 hermes chat 时生效。
- Gateway(Telegram、Discord、Slack 等):下一个新会话生效。现有会话保持原有模型。如需强制所有会话使用新配置,重启 gateway(hermes gateway restart)。
- 仪表板聊天标签页(/chat):下一个新 PTY 生效。当前打开的聊天保持原有模型 — 在聊天内使用 /model 进行热切换。
更改不会使运行中会话的 prompt 缓存失效。这是有意为之:在会话内切换主模型需要重置缓存(系统 prompt 包含模型特定内容),该操作保留给聊天内的显式 /model 斜杠命令。
故障排查
选择器中显示"No authenticated providers"
Hermes 仅列出具有有效凭据的提供商。检查侧边栏中的 Keys — 应存在以下之一:API key、成功的 OAuth 或自定义端点 URL。若所需提供商不在列表中,运行 hermes setup 进行配置,或前往 Keys 添加环境变量。
主模型在运行中的聊天里未发生变化
符合预期。仪表板写入 config.yaml,新会话读取该文件。当前打开的聊天是一个活跃的 agent 进程 — 它保持启动时的模型。在聊天内使用 /model <name> 对该会话进行热切换。
辅助覆盖"未生效"
检查以下三点:
- 是否启动了新会话? 现有聊天不会重新读取配置。
- provider 是否设置为非 auto 的值? 若字段显示 auto,该任务仍在使用主模型。点击 Change 选择实际的提供商。
- 提供商是否已认证? 若将 minimax 分配给某任务但没有 MiniMax API key,该任务将回退到 openrouter 默认值,并在 agent.log 中记录警告。
我选择了模型,但 Hermes 切换了提供商
在 OpenRouter(或任何聚合器)上,裸模型名称会优先在聚合器内解析。因此 OpenRouter 上的 claude-sonnet-4 会解析为 anthropic/claude-sonnet-4.6,保持在你的 OpenRouter 认证下。但若在原生 Anthropic 认证下输入 claude-sonnet-4,则会保持为 claude-sonnet-4-6。若出现意外的提供商切换,请确认当前提供商是否符合预期 — 选择器始终在对话框顶部显示当前主模型。
其他方法
CLI 斜杠命令
在任意 hermes chat 会话内:
/model gpt-5.4 --provider openrouter # 仅当前会话
/model gpt-5.4 --provider openrouter --global # 同时持久化到 config.yaml--global 与仪表板 Change 按钮效果相同,并额外在当前会话内原地切换模型。
自定义别名
为常用模型定义短名称,然后在 CLI 或任意消息平台中使用 /model <alias>:
# ~/.hermes/config.yaml
model_aliases:
fav:
model: claude-sonnet-4.6
provider: anthropic
grok:
model: grok-4
provider: x-ai或通过 shell 命令(简写形式,provider/model):
hermes config set model.aliases.fav anthropic/claude-opus-4.6
hermes config set model.aliases.grok x-ai/grok-4然后在聊天中使用 /model fav 或 /model grok。用户别名会覆盖内置短名称(sonnet、kimi、opus 等)。完整参考请见自定义模型别名。
hermes model 子命令
hermes model # 交互式提供商 + 模型选择器(切换默认值的标准方式)hermes model 引导你选择提供商、完成认证(OAuth 流程会打开浏览器;API key 提供商会提示输入密钥),然后从该提供商的精选目录中选择具体模型。选择结果写入 ~/.hermes/config.yaml 的 model.provider 和 model.model 字段。保存新模型后会紧接一步推理强度选择(minimal … ultra、Disable reasoning,或 Skip 保留当前值),写入 agent.reasoning_effort;目录标记为无推理控制的模型会跳过该步。提供商列表中还有 Reasoning effort for the current model... 一行,可只更改推理强度。
Configure auxiliary models... 打开各辅助任务(视觉、压缩、审批、委派等)的侧模型选择器。每个任务在选完提供商 → 模型后同样会进入推理强度步骤,存为 auxiliary.<task>.reasoning_effort(或 delegation.reasoning_effort),并多一行 Provider default 把强度交给提供商决定。设计上没有 reasoning_effort 键的任务(MoA 槽位、记忆查询改写)会跳过该步。
如需在不启动选择器的情况下列出提供商/模型,请使用仪表板或下方的 REST 端点。查看 CLI 当前实际使用的配置:hermes config get model 和 hermes status。
直接编辑配置文件
编辑 ~/.hermes/config.yaml 后重启相关服务。完整 schema 请见配置参考。
REST API
仪表板使用以下三个端点,可用于脚本化操作:
# 列出已认证的提供商及精选模型列表
curl -H "X-Hermes-Session-Token: $TOKEN" http://localhost:PORT/api/model/options
# 读取当前主模型及辅助任务分配
curl -H "X-Hermes-Session-Token: $TOKEN" http://localhost:PORT/api/model/auxiliary
# 设置主模型
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
-d '{"scope":"main","provider":"openrouter","model":"anthropic/claude-opus-4.7"}' \
http://localhost:PORT/api/model/set
# 覆盖单个辅助任务
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
-d '{"scope":"auxiliary","task":"vision","provider":"openrouter","model":"google/gemini-2.5-flash"}' \
http://localhost:PORT/api/model/set
# 将一个模型分配给所有辅助任务
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
-d '{"scope":"auxiliary","task":"","provider":"openrouter","model":"google/gemini-2.5-flash"}' \
http://localhost:PORT/api/model/set
# 将所有辅助任务重置为 auto
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
-d '{"scope":"auxiliary","task":"__reset__","provider":"","model":""}' \
http://localhost:PORT/api/model/setsession token 在启动时注入仪表板 HTML,每次服务器重启后轮换。如需对运行中的仪表板编写脚本,可从浏览器开发者工具中获取(window.__HERMES_SESSION_TOKEN__)。
Sessions(会话)
Hermes Agent 自动将每次对话保存为一个 session。Session 支持对话恢复、跨 session 搜索以及完整的对话历史管理。
Session 的工作原理
每次对话——无论来自 CLI、Telegram、Discord、Slack、WhatsApp、Signal、Matrix、Teams 还是其他任何消息平台——都会以完整消息历史的形式存储为一个 session。Session 记录在:
- SQLite 数据库(~/.hermes/state.db)——包含 FTS5 全文搜索的结构化 session 元数据,以及完整消息历史
SQLite 数据库存储:
- Session ID、来源平台、用户 ID
- Session 标题(唯一、人类可读的名称)
- 模型名称和配置
- 系统 prompt(提示词)快照
- 完整消息历史(角色、内容、工具调用、工具结果)
- Token 计数(输入/输出)
- 时间戳(started_at、ended_at)
- 父 session ID(用于压缩触发的 session 分割)
哪些内容计入上下文
Hermes 存储 session 历史以便恢复对话,但不会在每次对话时重新发送所有历史字节。每轮对话中,模型看到的是:所选系统 prompt、当前对话窗口,以及 Hermes 为该轮显式注入的内容。
媒体附件作为轮次范围内的输入处理:
- 图片可以原生附加到下一次模型调用,或在当前模型不支持原生视觉时预先分析为文字描述。
- 音频在配置了语音转文字时会被转录为文本。
- 文本文档可以将提取的文本包含在内;其他文档类型通常以本地保存路径和简短说明来表示。
- 附件路径和提取/派生的文本可能出现在对话记录中,但原始图片、音频或二进制文件字节不会被反复复制到后续 prompt 中。
例如,如果用户发送一张图片并要求 Hermes 制作表情包,Hermes 可能会用视觉能力检查该图片一次并运行图像处理脚本。后续轮次不会自动将原始 JPEG 带入上下文,只携带写入对话的内容,例如用户的请求、简短的图片描述、本地缓存路径或最终的助手回复。
上下文增长最常见的原因不是媒体文件本身,而是冗长的文本:粘贴的转录、完整日志、大型工具输出、长 diff、重复的状态报告以及详细的证明转储。优先使用摘要、文件路径、重点摘录和工具支持的查找,而不是将大型内容复制到聊天中。
提示
当 session 变长时使用 /compress,用 /new 开启新线程,仅在需要从存储中删除旧的已结束 session 时才使用 hermes sessions prune。压缩会减少活跃上下文,而不是隐私删除。向 /new 传入名称(例如 /new payments-refactor)可以预先设置新 session 的初始标题——便于之后通过 /resume <name> 或 /sessions 选择器找到它。
Session 来源
每个 session 都标记了其来源平台:
| | 来源 | 描述
| cli | 交互式 CLI(hermes 或 hermes chat)
| telegram | Telegram 消息
| discord | Discord 服务器/私信
| slack | Slack 工作区
| whatsapp | WhatsApp 消息
| signal | Signal 消息
| matrix | Matrix 房间和私信
| mattermost | Mattermost 频道
| email | 电子邮件(IMAP/SMTP)
| sms | 通过 Twilio 的短信
| dingtalk | 钉钉消息
| feishu | 飞书/Lark 消息
| wecom | 企业微信
| weixin | 微信(个人版)
| bluebubbles | 通过 BlueBubbles macOS 服务器的 Apple iMessage
| qqbot | QQ Bot(腾讯 QQ)通过官方 API v2
| homeassistant | Home Assistant 事件(插件)
| webhook | 传入 webhook
| api-server | API 服务器请求
| acp | ACP 编辑器集成
| cron | 定时 cron 任务
| batch | 批处理运行
CLI Session 恢复
使用 --continue 或 --resume 从 CLI 恢复之前的对话:
继续上次 Session
# 恢复最近的 CLI session
hermes --continue
hermes -c
# 或使用 chat 子命令
hermes chat --continue
hermes chat -c这会从 SQLite 数据库中查找最近的 cli session 并加载其完整对话历史。
按名称恢复
如果你已为 session 设置了标题(见下方Session 命名),可以按名称恢复:
# 恢复一个命名 session
hermes -c "my project"
# 如果存在谱系变体(my project、my project #2、my project #3),
# 会自动恢复最新的一个
hermes -c "my project" # → 恢复 "my project #3"恢复特定 Session
# 按 ID 恢复特定 session
hermes --resume 20250305_091523_a1b2c3d4
hermes -r 20250305_091523_a1b2c3d4
# 按标题恢复
hermes --resume "refactoring auth"
# 或使用 chat 子命令
hermes chat --resume 20250305_091523_a1b2c3d4Session ID 在退出 CLI session 时显示,也可通过 hermes sessions list 查找。
恢复时的对话摘要
恢复 session 时,Hermes 会在输入提示符前以样式化面板显示之前对话的紧凑摘要:
恢复模式会在返回实时提示符前显示一个紧凑摘要面板,包含最近的用户和助手轮次。
摘要内容:
- 显示用户消息(金色 ●)和助手回复(绿色 ◆)
- 截断长消息(用户 300 字符,助手 200 字符/3 行)
- 折叠工具调用为带工具名称的计数(例如 [3 tool calls: terminal, web_search])
- 隐藏系统消息、工具结果和内部推理
- 最多显示最近 10 轮,并以"... N earlier messages ..."指示器标注
- 使用暗色样式与活跃对话区分
要禁用摘要并保留最简单的单行行为,在 ~/.hermes/config.yaml 中设置:
display:
resume_display: minimal # 默认值: full提示
Session ID 格式为 YYYYMMDD_HHMMSS_<hex>——CLI/TUI session 使用 6 位十六进制后缀(例如 20250305_091523_a1b2c3),gateway session 使用 8 位后缀(例如 20250305_091523_a1b2c3d4)。可以按 ID(完整或唯一前缀)或按标题恢复——-c 和 -r 均支持两种方式。
跨平台切换
在 CLI session 中使用 /handoff <platform> 将实时对话转移到消息平台的主频道。Agent 会从 CLI 停止的地方精确接续——相同的 session id、完整的角色感知对话记录、工具调用一并保留。
# 在 CLI session 内
/handoff telegram执行过程:
-
CLI 验证 <platform> 已启用且已设置主频道(在目标聊天中运行一次 /sethome 即可配置)。
-
CLI 将 session 标记为待处理并阻塞轮询 gateway。如果 agent 正在处理轮次,则拒绝操作——请等待当前响应完成后再执行。
-
Gateway 监视器认领切换请求,并向目标适配器请求新线程:
- Telegram — 开启新的论坛话题(如果在聊天中启用了 Bot API 9.4+ Topics 模式则为私信话题,或论坛超级群组话题)。
- Discord — 在主文字频道下创建 1440 分钟自动归档的线程。
- Slack — 发布一条种子消息并使用其 ts 作为线程锚点。
- Matrix — 发布一条种子消息并使用其事件 id 作为线程根(m.thread 关系)。
- WhatsApp / Signal / SMS — 无原生线程,回退到直接使用主频道。
-
Gateway 将目标键重新绑定到你现有的 CLI session id,然后伪造一个合成用户轮次,要求 agent 确认并总结。回复会出现在新线程中。
-
Gateway 确认成功后,CLI 打印 /resume 提示并干净退出:
↻ Handoff complete. The session is now active on telegram.
Resume it on this CLI later with: /resume my-session-title-
从此时起,对话在该平台上继续。在新线程中回复——该频道中任何已授权的用户共享同一 session,之后线程中任何真实用户消息都能无缝加入,因为线程 session 的键不含 user_id。
恢复到 CLI: 当你想回到桌面时,只需运行 /resume <title>(或在 shell 中运行 hermes -r "<title>"),从平台停止的地方继续。
故障模式:
- 未配置主频道 → CLI 拒绝并提示 /sethome。
- 平台未启用/gateway 未运行 → CLI 在 60 秒后超时并显示明确消息,CLI session 保持完整。
- 线程创建失败(权限不足、话题模式未开启)→ 直接回退到主频道并仍然完成切换;没有线程隔离,但切换本身有效。
- adapter.send 失败(速率限制、临时 API 错误)→ 切换标记为失败并附带原因;行被清除以便重试。
值得注意的限制: 对于无线程能力的多用户群组主频道平台,合成轮次以私信风格 session 为键。这对自私信主频道(典型设置)有效,但对真正的共享群聊并不理想。线程支持覆盖 Telegram / Discord / Slack / Matrix——这是最常见的情况——因此大多数设置不会遇到此问题。
Session 命名
为 session 设置人类可读的标题,便于查找和恢复。
自动生成标题
Hermes 在第一次交换后自动为每个 session 生成简短的描述性标题(3–7 个词)。这在后台线程中使用快速辅助模型运行,不增加延迟。浏览 hermes sessions list 或 hermes sessions browse 时可以看到自动生成的标题。
自动命名每个 session 只触发一次,如果你已手动设置标题则跳过。
手动设置标题
在任何聊天 session(CLI 或 gateway)中使用 /title 斜杠命令:
/title my research project标题立即生效。如果 session 尚未在数据库中创建(例如在发送第一条消息之前运行 /title),则会排队等待 session 启动后应用。
也可以从命令行重命名现有 session:
hermes sessions rename 20250305_091523_a1b2c3d4 "refactoring auth module"标题规则
- 唯一——不能有两个 session 共享同一标题
- 最多 100 个字符——保持列表输出整洁
- 净化处理——控制字符、零宽字符和 RTL 覆盖字符会被自动去除
- 普通 Unicode 均可——emoji、CJK 字符、带重音字符均支持
压缩时的自动谱系
当 session 的上下文被压缩(通过 /compress 手动或自动触发)时,Hermes 会创建一个新的续接 session。如果原 session 有标题,新 session 会自动获得带编号的标题:
"my project" → "my project #2" → "my project #3"按名称恢复时(hermes -c "my project"),会自动选取谱系中最新的 session。
在消息平台中使用 /title
/title 命令在所有 gateway 平台(Telegram、Discord、Slack、WhatsApp)中均可使用:
- /title My Research — 设置 session 标题
- /title — 显示当前标题
Session 管理命令
Hermes 通过 hermes sessions 提供完整的 session 管理命令集:
列出 Session
# 列出最近的 session(默认:最近 20 个)
hermes sessions list
# 按平台过滤
hermes sessions list --source telegram
# 显示更多 session
hermes sessions list --limit 50当 session 有标题时,输出显示标题、预览和相对时间戳:
Title Preview Last Active ID
────────────────────────────────────────────────────────────────────────────────────────────────
refactoring auth Help me refactor the auth module please 2h ago 20250305_091523_a
my project #3 Can you check the test failures? yesterday 20250304_143022_e
— What's the weather in Las Vegas? 3d ago 20250303_101500_f当没有 session 有标题时,使用更简单的格式:
Preview Last Active Src ID
──────────────────────────────────────────────────────────────────────────────────────
Help me refactor the auth module please 2h ago cli 20250305_091523_a
What's the weather in Las Vegas? 3d ago tele 20250303_101500_f导出 Session
hermes sessions export 是所有导出格式的统一入口,用 --format 选择:
| | 格式 | 输出 | 适用场景
| jsonl(默认) | 每个 session 一个 JSON 对象 | 备份、机器可读的往返格式
| md / qmd | 每个 session 一个 Markdown/Quarto 文件 + manifest | 可读归档、笔记
| html | 单个独立页面(多 session 带侧边栏) | 分享、浏览
| trace | Claude Code JSONL | HF Agent Trace Viewer、--upload
另有 --only user-prompts 只导出你的 prompt(jsonl 或 md)。
所有格式共享同一套选择方式:--session-id 导出单个 session,或使用与 prune / archive 相同的完整过滤器进行批量导出 — --older-than / --newer-than / --before / --after(时长如 5h/2d/1w、纯数字天数或 ISO 时间戳)、--source、--title、--model、--provider、--cwd、--min/--max-messages、--min/--max-tokens、--min/--max-cost、--min/--max-tool-calls、--user、--chat-id、--chat-type、--branch、--end-reason。--dry-run 可预览匹配集而不写入。--redact 在任意格式下从导出内容中清除密钥(API key、token、凭据)— 任何打算分享的导出都建议加上。注意:带过滤器的批量导出只匹配已结束的 session;不带过滤器的 export 会导出所有 session(包括活跃的)。
JSONL(默认)
# 将所有 session 导出到 JSONL 文件
hermes sessions export backup.jsonl
# 导出特定平台的 session
hermes sessions export telegram-history.jsonl --source telegram
# 导出单个 session
hermes sessions export session.jsonl --session-id 20250305_091523_a1b2c3d4
# 从导出内容中脱敏 API key/token/凭据
hermes sessions export backup.jsonl --redact导出文件每行包含一个 JSON 对象,包含完整的 session 元数据和每一条已存储的消息,每条都带有 active/compacted 标记。其中包括原地压缩归档的轮次,以及被 rewind 或编辑移除的消息。导入该文件(dashboard 的 session 导入)时,这些行会恢复为已归档的历史,而不会成为活跃的模型上下文。CLI 和消息平台的 /save json 快照同样如此。请把备份视为包含该 session 曾经有过的全部内容;要分享对话,请用 --redact 导出显示格式(--format md 或 html,只包含 session 显示的历史)。每个 session 的备份在内存中构建,因此已存储行数超过 sessions.max_export_messages 的 session 会被拒绝。
HTML
--format html 生成一个完全独立的 HTML 文件 — 无远程依赖 — 带样式化的消息气泡、可折叠的工具输出,多 session 导出时还带侧边栏导航:
# 将一个 session 导出为独立 HTML 页面
hermes sessions export --format html --session-id 20250305_091523_a1b2c3d4 transcript.html
# 将最近一周的所有 Telegram session 导出到一个文件,并脱敏
hermes sessions export --format html --newer-than 1w --source telegram --redact archive.html只导出 Prompt
--only user-prompts 只导出你写的 prompt — 不含助手回复、工具输出或系统上下文。适合构建 prompt 库或回顾你问过什么:
# 每个 prompt 一条 JSONL 记录(session id、序号、时间戳、文本)
hermes sessions export prompts.jsonl --session-id 20250305_091523_a1b2c3d4 --only user-prompts
# Markdown 格式,直接输出到 stdout
hermes sessions export - --session-id 20250305_091523_a1b2c3d4 --only user-prompts --format md支持 --format jsonl(默认)或 md,批量导出时同样支持全部过滤器,也可与 --redact 组合。
Trace(HF Agent Trace Viewer)
--format trace 生成 Claude Code JSONL — Hugging Face Hub 的 Agent Trace Viewer 可自动识别的转录格式。可以写入本地文件,或加 --upload 推送到你自己的私有 hermes-traces 数据集(读取 HF_TOKEN):
# 最近一个 session 的 trace,输出到 stdout
hermes sessions export --format trace
# 将一个 session 导出为本地 trace 文件
hermes sessions export --format trace --session-id 20250305_091523_a1b2c3d4 trace.jsonl
# 直接上传到你的私有 HF traces 数据集
hermes sessions export --format trace --session-id 20250305_091523_a1b2c3d4 --uploadTrace 导出默认强制脱敏(它们本来就是要离开本机的);--no-redact 需人工审查后才建议使用。--upload 默认私有,除非加 --public。带过滤器的批量 trace 导出会为每个 session 写一个 <id>.trace.jsonl。
Markdown / QMD
当你想在隐藏或删除旧 session 之前保留一份可读的文件归档时,传入 --format md 或 --format qmd。Markdown/QMD 导出会为每个 session 写入一个文件到目录中(默认:~/.hermes/session-exports)。
# 将单个 session 导出为 Markdown
hermes sessions export --format md --session-id 20250305_091523_a1b2c3d4
# 将压缩链(compression lineage)导出为一个逻辑文档
hermes sessions export --format md --session-id 20250305_091523_a1b2c3d4 --lineage logical
# 预览 90 天前已结束的 session,不写入文件
hermes sessions export --format md --older-than 90 --dry-run
# 将 2 周前已结束的 Telegram session 导出为 QMD 文件
hermes sessions export --format qmd --older-than 2w --source telegram
# 导出长的 Claude session,并脱敏
hermes sessions export --format md --model sonnet --min-messages 50 --redact
# 导出并在校验通过后删除一个明确指定的 session
hermes sessions export --format md --session-id 20250305_091523_a1b2c3d4 --delete-after-verified --yesMarkdown/QMD 导出为每个 session 写入一个 .md 或 .qmd 文件,并附带一个 manifest.jsonl,记录文件路径、消息数量、lineage id 和 SHA-256。批量导出必须带至少一个过滤条件,不带过滤条件的批量导出会被拒绝。--delete-after-verified 仅限与 --session-id 搭配使用,且必须加 --yes。--redact 会在写入前从消息内容和工具输出中清除密钥(API key、token、凭据)— 任何打算分享的导出都建议加上。
删除 Session
# 删除特定 session(需确认)
hermes sessions delete 20250305_091523_a1b2c3d4
# 不需确认直接删除
hermes sessions delete 20250305_091523_a1b2c3d4 --yes删除一个仍在运行中的对话所使用的 session 并不会结束该对话:它下一次保存时会以同一个 id 重建 session,并写入完整的内存中对话记录。如果希望该 session 彻底消失,请先关闭对话。
重命名 Session
# 设置或更改 session 的标题
hermes sessions rename 20250305_091523_a1b2c3d4 "debugging auth flow"
# 多词标题在 CLI 中不需要引号
hermes sessions rename 20250305_091523_a1b2c3d4 debugging auth flow如果标题已被另一个 session 使用,则显示错误。
清理旧 Session
# 删除 90 天前已结束的 session(默认)
hermes sessions prune
# 自定义时间阈值
hermes sessions prune --older-than 30
# 仅清理特定平台的 session
hermes sessions prune --source telegram --older-than 60
# 跳过确认
hermes sessions prune --older-than 30 --yes信息
清理仅删除已结束的 session(已被显式结束或自动重置的 session)。活跃 session 永远不会被清理。
被压缩拆分成多个 session 的对话作为一个整体清理:只要后续任一段仍保留,较早的段就会保留。
Session 统计
hermes sessions stats输出:
Total sessions: 142
Total messages: 3847
cli: 89 sessions
telegram: 38 sessions
discord: 15 sessions
Database size: 12.4 MB如需更深入的分析——token 用量、费用估算、工具分解和活动模式——请使用 hermes insights。
Session 搜索工具
Agent 内置了 session_search 工具,使用 SQLite 的 FTS5 引擎对所有历史对话进行全文搜索,并允许 agent 滚动浏览找到的任何 session。无需 LLM 调用、无需摘要、无截断。每种调用形式都从数据库返回实际消息。
三种调用形式
工具根据你设置的参数推断意图,没有 mode 参数。
1. 发现——传入 query:
session_search(query="auth refactor", limit=3)运行 FTS5,按 session 谱系去重,返回前 N 个 session。每个结果包含:
- session_id、title、when、source
- snippet — FTS5 高亮的匹配摘录
- bookend_start — session 的前 3 条用户+助手消息(目标/开场)
- messages — FTS5 匹配点前后各 ±5 条消息,锚点消息有标记(命中上下文)
- bookend_end — session 的最后 3 条用户+助手消息(结论/决策)
- match_message_id、messages_before、messages_after
书签+窗口共同重建目标→命中→结论,无需加载完整对话记录。在真实 session 数据库上的典型耗时:15–50ms。
2. 滚动——传入 session_id + around_message_id:
session_search(session_id="20260510_174648_805cc2", around_message_id=590803, window=10)返回以锚点为中心的 ±window 条消息窗口。无 FTS5,无书签——只是切片。在发现调用后需要比默认 ±5 窗口更多上下文时使用。
- 向前滚动:将 messages[-1].id 作为 around_message_id 传回
- 向后滚动:将 messages[0].id 作为 around_message_id 传回
- 边界消息在两个窗口中均出现,作为定向标记
- 当 messages_before 或 messages_after 小于 window 时,表示已到达 session 的开头或结尾
每次滚动调用的典型耗时:1–2ms。
3. 浏览——无参数:
session_search()按时间顺序返回最近的 session(标题、预览、时间戳)。当用户询问"我在做什么"而未指定主题时很有用。
FTS5 查询语法
关键词模式支持标准 FTS5 查询语法:
- 简单关键词:docker deployment(FTS5 默认为 AND)
- 短语:"exact phrase"
- 布尔:docker OR kubernetes、python NOT java
- 前缀:deploy*
可选参数
- sort — newest 或 oldest,在 FTS5 排名之上排序。省略则仅按相关性排序(默认;适合探索性召回)。对于"我们在哪里停下了 X"的问题使用 newest,对于"X 是怎么开始的"的问题使用 oldest。
- role_filter — 逗号分隔的角色列表。发现模式默认为 user,assistant(工具输出通常是噪音)。传入 user,assistant,tool 以包含工具输出(调试工具行为),或传入 tool 仅搜索工具输出。
使用时机
Agent 被提示在以下情况自动使用 session 搜索:
"当用户引用过去对话中的内容,或你怀疑存在相关的先前上下文时,在要求用户重复之前先使用 session_search 召回。"
典型触发词:「我们之前做过这个」、「还记得吗」、「上次」、「正如我提到的」,或任何当前窗口中没有的项目/人物/概念的引用。
各平台 Session 跟踪
Gateway Session
在消息平台上,session 通过从消息来源构建的确定性 session 键来标识:
| | 聊天类型 | 默认键格式 | 行为
| Telegram 私信 | agent:main:telegram:dm:<chat_id> | 每个私信聊天一个 session
| Discord 私信 | agent:main:discord:dm:<chat_id> | 每个私信聊天一个 session
| WhatsApp 私信 | agent:main:whatsapp:dm:<canonical_identifier> | 每个私信用户一个 session(存在映射时 LID/手机号别名合并为一个身份)
| 群聊 | agent:main:<platform>:group:<chat_id>:<user_id> | 当平台暴露用户 ID 时,群内每用户独立 session
| 群组线程/话题 | agent:main:<platform>:group:<chat_id>:<thread_id> | 所有线程参与者共享 session(默认)。设置 thread_sessions_per_user: true 则每用户独立。
| 频道 | agent:main:<platform>:channel:<chat_id>:<user_id> | 当平台暴露用户 ID 时,频道内每用户独立 session
当 Hermes 无法获取共享聊天的参与者标识符时,回退为该房间共享一个 session。
共享与隔离的群组 Session
默认情况下,Hermes 在 config.yaml 中使用 group_sessions_per_user: true。这意味着:
- Alice 和 Bob 可以在同一个 Discord 频道中与 Hermes 对话,而不共享对话历史
- 一个用户的长时间工具密集型任务不会污染另一个用户的上下文窗口
- 中断处理也保持每用户独立,因为运行中的 agent 键与隔离的 session 键匹配
如果你想要一个共享的"房间大脑",设置:
group_sessions_per_user: false这会将群组/频道恢复为每个房间一个共享 session,保留共享的对话上下文,但也共享 token 费用、中断状态和上下文增长。
会话连续性
Gateway 不会因空闲时间或每日时间边界而重置对话。需要新对话时使用 /new
或 /reset;上下文压缩仍会自动运行。旧的 session_reset 配置、重置策略覆盖和
重置计时环境变量均被忽略。缓存中的 agent 可以释放资源,但不会替换持久化对话。
重启恢复的新鲜度限制仅约束自动继续执行,不会清除用户发送消息时加载的历史。
存储位置
| | 内容 | 路径 | 描述
| SQLite 数据库 | ~/.hermes/state.db | 所有 session 元数据 + 带 FTS5 的消息
| Gateway 消息 | ~/.hermes/state.db | SQLite——所有 session 消息的权威存储
| Gateway 路由索引 | ~/.hermes/sessions/sessions.json | 将 session 键映射到活跃 session ID(来源元数据、过期标志)
SQLite 数据库使用 WAL 模式支持并发读取和单写入,非常适合 gateway 的多平台架构。
遗留 JSONL 对话记录
在 state.db 成为权威存储之前创建的 session 可能在 ~/.hermes/sessions/ 中留有
*.jsonl 文件。Hermes 不再写入或读取这些文件。在确认对应 session 存在于
state.db 后可安全删除。
数据库 Schema
state.db 中的关键表:
- sessions — session 元数据(id、source、user_id、model、title、时间戳、token 计数)。标题有唯一索引(允许 NULL 标题,只有非 NULL 标题必须唯一)。
- messages — 完整消息历史(role、content、tool_calls、tool_name、token_count)
- messages_fts — 用于跨消息内容全文搜索的 FTS5 虚拟表
Session 过期与清理
自动清理
- Gateway 会话会持续保留;请使用 /new 或 /reset 显式开始新会话
- 重置前,agent 保存即将过期 session 中的记忆和技能
- 可选自动清理:当 sessions.auto_prune 为 true 时,在 CLI/gateway 启动时清理早于 sessions.retention_days(默认 90)天的已结束 session
- 实际删除了行的清理操作完成后,如果距离上次成功执行 VACUUM 已达到 sessions.min_vacuum_interval_days(默认 30)天,state.db 会执行 VACUUM 以回收磁盘空间(SQLite 在普通 DELETE 后不会缩小文件)
- 清理最多每 sessions.min_interval_hours(默认 24)小时运行一次;上次运行时间戳记录在 state.db 内部,因此在同一 HERMES_HOME 下的所有 Hermes 进程间共享
默认为关闭——session 历史对 session_search 召回很有价值,静默删除可能会让用户感到意外。在 ~/.hermes/config.yaml 中启用:
sessions:
auto_prune: true # 选择启用——默认为 false
retention_days: 90 # 保留已结束 session 的天数
vacuum_after_prune: true # 清理后回收磁盘空间
min_vacuum_interval_days: 30 # 数据库重写的最短间隔天数
min_interval_hours: 24 # 清理间隔不短于此值活跃 session 永远不会被自动清理,无论时间多长。被压缩拆分成多个 session 的对话同样如此:只要后续任一段仍保留,其较早的段就会保留,并在整个对话符合条件后一起清理。
手动清理
# 清理 90 天前的 session
hermes sessions prune
# 删除特定 session
hermes sessions delete <session_id>
# 清理前先导出(备份)
hermes sessions export backup.jsonl
hermes sessions prune --older-than 30 --yes提示
数据库增长缓慢(典型情况:数百个 session 约 10–15 MB),session 历史为跨历史对话的 session_search 召回提供支持,因此自动清理默认关闭。如果你运行繁重的 gateway/cron 工作负载且 state.db 明显影响性能(已观察到的故障模式:约 1000 个 session 的 384 MB state.db 导致 FTS5 插入和 /resume 列表变慢),则启用它。使用 hermes sessions prune 进行一次性清理,无需开启自动清理。
安全
Hermes Agent 采用纵深防御安全模型。本页涵盖所有安全边界——从命令审批到容器隔离,再到消息平台上的用户授权。
概述
安全模型共有七层:
- 用户授权 — 谁可以与 Agent 通信(允许列表、DM 配对)
- 危险命令审批 — 针对破坏性操作的人工审核环节
- 容器隔离 — Docker/Singularity/Modal 沙箱及加固配置
- MCP 凭据过滤 — MCP 子进程的环境变量隔离
- 上下文文件扫描 — 检测项目文件中的 prompt(提示词)注入
- 跨会话隔离 — 会话之间无法访问彼此的数据或状态;cron 任务存储路径已针对路径遍历攻击进行加固
- 输入清理 — 终端工具后端中的工作目录参数会经过允许列表验证,以防止 shell 注入
危险命令审批
在执行任何命令之前,Hermes 会将其与一份精心维护的危险模式列表进行比对。若匹配,用户必须明确批准。
审批模式
审批系统支持三种模式,通过 ~/.hermes/config.yaml 中的 approvals.mode 配置:
approvals:
mode: smart # smart | manual | off
timeout: 300 # 等待用户响应的秒数(默认:300)| | 模式 | 行为
| smart(默认) | 使用辅助 LLM 评估风险。低风险命令(如 python -c "print('hello')")仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级为手动提示。
| manual | 始终提示用户审批危险命令。
| off | 禁用所有审批检查——等同于使用 --yolo 运行。所有命令无需提示即可执行。
注意
设置 approvals.mode: off 将禁用所有安全提示。仅在受信任的环境(CI/CD、容器等)中使用。
YOLO 模式
YOLO 模式会绕过当前会话中所有危险命令审批提示。可通过以下三种方式激活:
- CLI 标志:使用 hermes --yolo 或 hermes chat --yolo 启动会话
- 斜杠命令:在会话中输入 /yolo 以切换开/关
- 环境变量:设置 HERMES_YOLO_MODE=1
/yolo 命令是一个切换开关——每次使用都会翻转模式的开/关状态:
> /yolo
⚡ YOLO mode ON — all commands auto-approved. Use with caution.
> /yolo
⚠ YOLO mode OFF — dangerous commands will require approval.YOLO 模式在 CLI 和 gateway 会话中均可使用。在内部,它会设置 HERMES_YOLO_MODE 环境变量,该变量在每次命令执行前都会被检查。
当 YOLO 激活时,Hermes 会显示两个持久的视觉提醒,以确保用户不会忘记审批提示已被绕过:
- 当 YOLO 已激活时,会话开始时显示一条红色横幅:⚠ YOLO mode — all approval prompts bypassed。YOLO 关闭时隐藏,以保持默认横幅整洁。
- 状态栏中所有宽度层级均显示 ⚠ YOLO 片段,随着 YOLO 的切换实时更新(富文本渲染器和纯文本回退均支持)。
危险
YOLO 模式会禁用会话中所有危险命令安全检查——但硬性黑名单除外(见下文)。仅在完全信任所生成命令的情况下使用(例如,在一次性环境中经过充分测试的自动化脚本)。
对于破坏性会话斜杠命令(/clear、/new / /reset、/undo、/exit --delete),CLI 在执行前也会提示确认。参见斜杠命令——破坏性命令的确认提示。
硬性黑名单(始终生效的底线)
某些命令极具破坏性——不可逆的文件系统清除、fork 炸弹、直接写入块设备——无论以下任何情况,Hermes 都拒绝执行:
- --yolo / /yolo 已开启
- approvals.mode: off
- Cron 任务以无头 approve 模式运行
- 用户明确点击"始终允许"
黑名单是 --yolo 之下的底线。它在审批层看到命令之前就会触发,且没有任何覆盖标志。当前涵盖的模式(非详尽列表;与 tools/approval.py::UNRECOVERABLE_BLOCKLIST 保持同步):
| | 模式 | 为何列为硬性规则
| rm -rf / 及明显变体 | 清除文件系统根目录
| rm -rf --no-preserve-root / | 明确表示"我就是要删根目录"的变体
| :(){ :|:& };: (bash fork 炸弹) | 使主机挂起直至重启
| mkfs.* 作用于已挂载的根设备 | 格式化运行中的系统
| dd if=/dev/zero of=/dev/sd* | 清零物理磁盘
| 将不受信任的 URL 通过管道传给 sh(作用于根文件系统顶层) | 远程代码执行攻击面过大,无法批准
若触发黑名单,工具调用会向 Agent 返回一条说明性错误,且不执行任何操作。如果某个合法工作流确实需要这些命令(例如,你是一个清除并重装流水线的操作者),请在 Agent 外部运行。
审批超时
当危险命令提示出现时,用户有一段可配置的时间来响应。若在超时内未响应,命令将默认被拒绝(故障关闭)。
在 ~/.hermes/config.yaml 中配置超时:
approvals:
timeout: 300 # 秒(默认:300)触发审批的条件
以下模式会触发审批提示(定义于 tools/approval.py):
| | 模式 | 描述
| rm -r / rm --recursive | 递归删除
| rm ... / | 在根路径下删除
| chmod 777/666 / o+w / a+w | 全局/其他用户可写权限
| chmod --recursive 配合不安全权限 | 递归全局/其他用户可写(长标志)
| chown -R root / chown --recursive root | 递归 chown 为 root
| mkfs | 格式化文件系统
| dd if= | 磁盘复制
| > /dev/sd | 写入块设备
| DROP TABLE/DATABASE | SQL DROP
| DELETE FROM(不含 WHERE) | 不含 WHERE 的 SQL DELETE
| TRUNCATE TABLE | SQL TRUNCATE
| > /etc/ | 覆盖系统配置
| systemctl stop/restart/disable/mask | 停止/重启/禁用系统服务
| kill -9 -1 | 杀死所有进程
| pkill -9 | 强制杀死进程
| Fork 炸弹模式 | Fork 炸弹
| bash -c / sh -c / zsh -c / ksh -c | 通过 -c 标志执行 shell 命令(包括组合标志如 -lc)
| python -e / perl -e / ruby -e / node -c | 通过 -e/-c 标志执行脚本
| curl ... | sh / wget ... | sh | 将远程内容通过管道传给 shell
| bash <(curl ...) / sh <(wget ...) | 通过进程替换执行远程脚本
| tee 写入 /etc/、~/.ssh/、~/.hermes/.env | 通过 tee 覆盖敏感文件
| > / >> 写入 /etc/、~/.ssh/、~/.hermes/.env | 通过重定向覆盖敏感文件
| xargs rm | xargs 配合 rm
| find -exec rm / find -delete | find 配合破坏性操作
| cp/mv/install 写入 /etc/ | 复制/移动文件到系统配置目录
| sed -i / sed --in-place 作用于 /etc/ | 就地编辑系统配置
| pkill/killall hermes/gateway | 防止自我终止
| gateway run 配合 &/disown/nohup/setsid | 防止在服务管理器外启动 gateway
信息
容器绕过:在 docker、singularity、modal、daytona 或 vercel_sandbox 后端运行时,危险命令检查会被跳过,因为容器本身就是安全边界。容器内的破坏性命令不会危害宿主机。
审批流程(CLI)
在交互式 CLI 中,危险命令会显示内联审批提示:
⚠️ DANGEROUS COMMAND: recursive delete
rm -rf /tmp/old-project
[o]nce | [s]ession | [a]lways | [d]eny
Choice [o/s/a/D]:四个选项:
- once — 仅允许本次执行
- session — 在本次会话剩余时间内允许此模式
- always — 添加到永久允许列表(保存至 config.yaml)
- deny(默认) — 阻止该命令
审批流程(Gateway/消息平台)
在消息平台上,Agent 会将危险命令详情发送到聊天中,并等待用户回复:
- 回复 yes、y、approve、ok 或 go 以批准
- 回复 no、n、deny 或 cancel 以拒绝
运行 gateway 时,HERMES_EXEC_ASK=1 环境变量会自动设置。
永久允许列表
通过"always"批准的命令会保存到 ~/.hermes/config.yaml:
# 永久允许的危险命令模式
command_allowlist:
- rm
- systemctl这些模式在启动时加载,并在所有后续会话中静默批准。
提示
使用 hermes config edit 查看或删除永久允许列表中的模式。
用户授权(Gateway)
运行消息 gateway 时,Hermes 通过分层授权系统控制谁可以与机器人交互。
授权检查顺序
_is_user_authorized() 方法按以下顺序检查:
- 每平台允许所有用户标志(如 DISCORD_ALLOW_ALL_USERS=true)
- DM 配对已批准列表(通过配对码批准的用户)
- 平台专属允许列表(如 TELEGRAM_ALLOWED_USERS=12345,67890)
- 全局允许列表(GATEWAY_ALLOWED_USERS=12345,67890)
- 全局允许所有用户(GATEWAY_ALLOW_ALL_USERS=true)
- 默认:拒绝
平台允许列表
在 ~/.hermes/.env 中以逗号分隔的值设置允许的用户 ID:
# 平台专属允许列表
TELEGRAM_ALLOWED_USERS=123456789,987654321
DISCORD_ALLOWED_USERS=111222333444555666
WHATSAPP_ALLOWED_USERS=15551234567
SLACK_ALLOWED_USERS=U01ABC123
# 跨平台允许列表(对所有平台均检查)
GATEWAY_ALLOWED_USERS=123456789
# 每平台允许所有用户(谨慎使用)
DISCORD_ALLOW_ALL_USERS=true
# 全局允许所有用户(极度谨慎使用)
GATEWAY_ALLOW_ALL_USERS=true注意
若未配置任何允许列表且未设置 GATEWAY_ALLOW_ALL_USERS,则所有用户均被拒绝。Gateway 在启动时会记录警告:
No user allowlists configured. All unauthorized users will be denied.
Set GATEWAY_ALLOW_ALL_USERS=true in ~/.hermes/.env to allow open access,
or configure platform allowlists (e.g., TELEGRAM_ALLOWED_USERS=your_id).DM 配对系统
为实现更灵活的授权,Hermes 提供了基于验证码的配对系统。无需预先提供用户 ID,未知用户会收到一次性配对码,由机器人所有者通过 CLI 批准。
工作原理:
- 未知用户向机器人发送 DM
- 机器人回复一个 8 位配对码
- 机器人所有者在 CLI 上运行 hermes pairing approve <platform> <code>
- 该用户在该平台上获得永久批准
在 ~/.hermes/config.yaml 中控制未授权私信的处理方式:
unauthorized_dm_behavior: pair
whatsapp:
unauthorized_dm_behavior: ignore- pair 为默认值。未授权的 DM 会收到配对码回复。
- ignore 静默丢弃未授权的 DM。
- 平台部分会覆盖全局默认值,因此可以在 Telegram 上保持配对,同时让 WhatsApp 保持静默。
安全特性(基于 OWASP + NIST SP 800-63-4 指南):
| | 特性 | 详情
| 验证码格式 | 8 位字符,来自 32 位无歧义字母表(不含 0/O/1/I)
| 随机性 | 密码学安全(secrets.choice())
| 验证码有效期 | 1 小时过期
| 速率限制 | 每用户每 10 分钟 1 次请求
| 待处理上限 | 每平台最多 3 个待处理验证码
| 锁定 | 5 次失败的批准尝试 → 1 小时锁定
| 文件安全 | 所有配对数据文件执行 chmod 0600
| 日志 | 验证码永不记录到 stdout
配对 CLI 命令:
# 列出待处理和已批准的用户
hermes pairing list
# 批准配对码
hermes pairing approve telegram ABC12DEF
# 撤销用户访问权限
hermes pairing revoke telegram 123456789
# 清除所有待处理验证码
hermes pairing clear-pending存储: 配对数据存储于 ~/.hermes/pairing/,按平台分为独立的 JSON 文件:
- {platform}-pending.json — 待处理的配对请求
- {platform}-approved.json — 已批准的用户
- _rate_limits.json — 速率限制和锁定追踪
容器隔离
使用 docker 终端后端时,Hermes 对每个容器应用严格的安全加固。
Docker 安全标志
每个容器均使用以下标志运行(定义于 tools/environments/docker.py):
_SECURITY_ARGS = [
"--cap-drop", "ALL", # 丢弃所有 Linux capabilities
"--cap-add", "DAC_OVERRIDE", # root 可写入绑定挂载目录
"--cap-add", "CHOWN", # 包管理器需要文件所有权
"--cap-add", "FOWNER", # 包管理器需要文件所有权
"--security-opt", "no-new-privileges", # 阻止权限提升
"--pids-limit", "256", # 限制进程数量
"--tmpfs", "/tmp:rw,nosuid,size=512m", # 有大小限制的 /tmp
"--tmpfs", "/var/tmp:rw,noexec,nosuid,size=256m", # 禁止执行的 /var/tmp
"--tmpfs", "/run:rw,noexec,nosuid,size=64m", # 禁止执行的 /run
]资源限制
容器资源可在 ~/.hermes/config.yaml 中配置:
terminal:
backend: docker
docker_image: "nikolaik/python-nodejs:python3.11-nodejs20"
docker_forward_env: [] # 仅显式允许列表;空值可防止密钥进入容器
container_cpu: 1 # CPU 核心数
container_memory: 5120 # MB(默认 5GB)
container_disk: 51200 # MB(默认 50GB,需要 XFS 上的 overlay2)
container_persistent: true # 跨会话持久化文件系统文件系统持久化
- 持久模式(container_persistent: true):从 ~/.hermes/sandboxes/docker/<task_id>/ 绑定挂载 /workspace 和 /root
- 临时模式(container_persistent: false):工作区使用 tmpfs——清理后所有内容丢失
提示
对于生产 gateway 部署,使用 docker、modal、daytona 或 vercel_sandbox 后端,将 Agent 命令与宿主机系统隔离。这样可以完全消除危险命令审批的需要。
注意
若向 terminal.docker_forward_env 添加名称,这些变量会被有意注入容器供终端命令使用。这对于任务专属凭据(如 GITHUB_TOKEN)很有用,但也意味着容器内运行的代码可以读取并泄露这些变量。
终端后端安全对比
| | 后端 | 隔离 | 危险命令检查 | 适用场景
| local | 无——在宿主机上运行 | ✅ 是 | 开发、受信任用户
| ssh | 远程机器 | ✅ 是 | 在独立服务器上运行
| docker | 容器 | ❌ 跳过(容器即边界) | 生产 gateway
| singularity | 容器 | ❌ 跳过 | HPC 环境
| modal | 云沙箱 | ❌ 跳过 | 可扩展的云隔离
| daytona | 云沙箱 | ❌ 跳过 | 持久化云工作区
| vercel_sandbox | 云微虚拟机 | ❌ 跳过 | 带快照持久化的云执行
环境变量透传
execute_code 和 terminal 都会从子进程中剥离敏感环境变量,以防止 LLM 生成的代码泄露凭据。但是,声明了 required_environment_variables 的技能(skill)确实需要访问这些变量。
工作原理
两种机制允许特定变量通过沙箱过滤器:
1. 技能作用域透传(自动)
当技能通过 skill_view 或 /skill 命令加载,且声明了 required_environment_variables 时,环境中实际已设置的这些变量会自动注册为透传变量。尚未设置(仍处于待配置状态)的变量不会被注册。
# 在技能的 SKILL.md frontmatter 中
required_environment_variables:
- name: TENOR_API_KEY
prompt: Tenor API key
help: Get a key from https://developers.google.com/tenor加载此技能后,TENOR_API_KEY 会透传到 execute_code、terminal(本地)以及远程后端(Docker、Modal)——无需手动配置。
Docker & Modal
在 v0.5.1 之前,Docker 的 forward_env 与技能透传是独立的系统。现在它们已合并——技能声明的环境变量会自动转发到 Docker 容器和 Modal 沙箱,无需手动添加到 docker_forward_env。
2. 基于配置的透传(手动)
对于未被任何技能声明的环境变量,将其添加到 config.yaml 中的 terminal.env_passthrough:
terminal:
env_passthrough:
- MY_CUSTOM_KEY
- ANOTHER_TOKEN凭据文件透传(OAuth token 等)
某些技能需要在沙箱中访问文件(而非仅环境变量)——例如,Google Workspace 将 OAuth token 存储为活跃 profile 的 HERMES_HOME 下的 google_token.json。技能在 frontmatter 中声明这些文件:
required_credential_files:
- path: google_token.json
description: Google OAuth2 token (created by setup script)
- path: google_client_secret.json
description: Google OAuth2 client credentials加载后,Hermes 会检查这些文件是否存在于活跃 profile 的 HERMES_HOME 中,并将其注册为挂载:
- Docker:只读绑定挂载(-v host:container:ro)
- Modal:在沙箱创建时挂载,并在每次命令前同步(处理会话中途的 OAuth 配置)
- 本地:无需操作(文件已可访问)
也可以在 config.yaml 中手动列出凭据文件:
terminal:
credential_files:
- google_token.json
- my_custom_oauth_token.json路径相对于 ~/.hermes/。文件在容器内挂载到 /root/.hermes/。
各沙箱的过滤规则
| | 沙箱 | 默认过滤 | 透传覆盖
| execute_code | 阻止名称中包含 KEY、TOKEN、SECRET、PASSWORD、CREDENTIAL、PASSWD、AUTH 的变量;仅允许安全前缀变量通过 | ✅ 透传变量绕过两项检查
| terminal(本地) | 阻止明确的 Hermes 基础设施变量(提供商密钥、gateway token、工具 API 密钥) | ✅ 透传变量绕过黑名单
| terminal(Docker) | 默认不传入宿主机环境变量 | ✅ 透传变量 + docker_forward_env 通过 -e 转发
| terminal(Modal) | 默认不传入宿主机环境/文件 | ✅ 凭据文件挂载;环境变量通过同步透传
| MCP | 阻止所有变量,仅允许安全系统变量 + 显式配置的 env | ❌ 不受透传影响(改用 MCP env 配置)
安全注意事项
- 透传仅影响你或你的技能明确声明的变量——任意 LLM 生成代码的默认安全态势不变
- 凭据文件以只读方式挂载到 Docker 容器中
- Skills Guard 在安装前会扫描技能内容中的可疑环境变量访问模式
- 缺失/未设置的变量永远不会被注册(不存在的内容无法泄露)
- Hermes 基础设施密钥(提供商 API 密钥、gateway token)不应添加到 env_passthrough——它们有专用机制。声明此类名称时会被拒绝;已声明的名称若之后被某个平台适配器占用(例如在技能加载后才注册的插件适配器),从那时起便不再转发
MCP 凭据处理
MCP(Model Context Protocol)服务器子进程接收经过过滤的环境,以防止意外泄露凭据。
安全环境变量
从宿主机传递到 MCP stdio 子进程的变量仅限以下几项:
PATH, HOME, USER, LANG, LC_ALL, TERM, SHELL, TMPDIR以及所有 XDG_* 变量。所有其他环境变量(API 密钥、token、密钥)均被剥离。
在 MCP 服务器的 env 配置中显式定义的变量会被透传:
mcp_servers:
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_..." # 仅此变量被传递凭据脱敏
MCP 工具的错误消息在返回给 LLM 之前会经过清理。以下模式会被替换为 [REDACTED]:
- GitHub PAT(ghp_...)
- OpenAI 风格密钥(sk-...)
- Bearer token
- token=、key=、API_KEY=、password=、secret= 参数
网站访问策略
你可以限制 Agent 通过其 Web 和浏览器工具可访问的网站。这对于防止 Agent 访问内部服务、管理面板或其他敏感 URL 非常有用。
# 在 ~/.hermes/config.yaml 中
security:
website_blocklist:
enabled: true
domains:
- "*.internal.company.com"
- "admin.example.com"
shared_files:
- "/etc/hermes/blocked-sites.txt"当请求被阻止的 URL 时,工具会返回一条错误,说明该域名已被策略阻止。黑名单在 web_search、web_extract、browser_navigate 及所有支持 URL 的工具中均强制执行。
完整详情请参见配置指南中的网站黑名单。
SSRF 防护
所有支持 URL 的工具(网页搜索、网页提取、视觉、浏览器)在获取 URL 之前都会进行验证,以防止服务器端请求伪造(SSRF)攻击。被阻止的地址包括:
- 私有网络(RFC 1918):10.0.0.0/8、172.16.0.0/12、192.168.0.0/16
- 回环地址:127.0.0.0/8、::1
- 链路本地地址:169.254.0.0/16(包括 169.254.169.254 处的云元数据)
- CGNAT / 共享地址空间(RFC 6598):100.64.0.0/10(Tailscale、WireGuard VPN)
- 云元数据主机名:metadata.google.internal、metadata.goog
- 保留地址、多播地址和未指定地址
SSRF 防护对面向互联网的使用始终有效,DNS 失败被视为阻止(故障关闭)。重定向链在每一跳都会重新验证,以防止基于重定向的绕过。
有意允许私有 URL
某些场景确实需要访问私有/内部 URL——将 home.arpa 解析到 RFC 1918 空间的家庭网络、仅限局域网的 Ollama/llama.cpp 端点、内部 wiki、云元数据调试等。对于这些情况,提供了一个全局选项:
security:
allow_private_urls: true # 默认:false开启后,Web 工具、浏览器、视觉 URL 获取和 gateway 媒体下载不再拒绝 RFC 1918 / 回环 / 链路本地 / CGNAT / 云元数据目标。这是一个有意为之的信任边界——仅在 Agent 针对本地网络执行任意 prompt 注入 URL 属于可接受风险的机器上启用。面向公众的 gateway 应保持关闭。
主机子字符串防护(即使底层 IP 是公共的,也能阻止 Unicode 同形字域名欺骗)无论此设置如何均保持开启。
本地代理的 fake-ip 地址段
以 fake-ip 模式工作的 TUN 代理(Mihomo/Clash fake-ip、Surge 增强模式)会对不在其过滤器内的
每个域名返回自己地址段中的地址——默认是 198.18.0.0/15(RFC 2544 基准测试段)。这些地址是代理
的哨兵地址,而不是内网主机,因此私网 IP 守卫会在这种机器上拦掉全部出网抓取:web_extract、平台
附件下载、浏览器链路都会以 URL targets a private or internal network address 失败,而请求根本
没有发出。声明该地址段即可放行哨兵地址:
security:
fake_ip_ranges:
- 198.18.0.0/15默认为空,且比 allow_private_urls 更窄:只有被声明的地址段获得豁免,且应当是本地代理自己拥有的
地址段(连接仍然发往代理,由代理自行解析真实目标),回环、RFC 1918、链路本地、CGNAT 和云元数据
目标依然被拦截。
内容级命令检查
危险命令检测器还会标记两类仅靠破坏性动词列表无法发现的内容:
- curl/wget 请求体中携带机密:机密命名的变量(-d "k=$OPENAI_API_KEY")或凭据文件(-F file=@.env、-T ~/.ssh/id_rsa、--post-file=/etc/passwd),或把凭据文件通过管道传给上传中的 curl/wget。Authorization 请求头属于正常 API 用法,不会被标记。
- 不可见或双向 Unicode 控制字符(零宽空格、从右到左覆盖、隔离符),它们会让你批准的命令与实际执行的命令不同。表情符号连接序列不会被标记。
两者都和其他危险模式一样进入正常审批流程。
早期版本在这里内置了外部 tirith 扫描器,现已移除;升级会删除 security.tirith_* 设置,且不会启用任何替代项。
上下文文件注入防护
上下文文件(AGENTS.md、.cursorrules、SOUL.md)在被纳入系统 prompt 之前会扫描 prompt 注入。扫描器检查以下内容:
- 指示忽略/无视先前指令的内容
- 含有可疑关键词的隐藏 HTML 注释
- 尝试读取密钥(.env、credentials、.netrc)
- 通过 curl 泄露凭据
- 不可见 Unicode 字符(零宽空格、双向覆盖)
被阻止的文件会显示警告:
[BLOCKED: AGENTS.md contained potential prompt injection (prompt_injection). Content not loaded.]生产部署最佳实践
Gateway 部署检查清单
- 设置明确的允许列表 — 生产环境中切勿使用 GATEWAY_ALLOW_ALL_USERS=true
- 使用容器后端 — 在 config.yaml 中设置 terminal.backend: docker
- 限制资源上限 — 设置合适的 CPU、内存和磁盘限制
- 安全存储密钥 — 将 API 密钥保存在具有适当文件权限的 ~/.hermes/.env 中
- 启用 DM 配对 — 尽可能使用配对码,而非硬编码用户 ID
- 审查命令允许列表 — 定期审计 config.yaml 中的 command_allowlist
- 设置 MESSAGING_CWD — 不要让 Agent 在敏感目录中操作
- 以非 root 用户运行 — 切勿以 root 身份运行 gateway
- 监控日志 — 检查 ~/.hermes/logs/ 中的未授权访问尝试
- 保持更新 — 定期运行 hermes update 以获取安全补丁
保护 API 密钥
# 为 .env 文件设置适当权限
chmod 600 ~/.hermes/.env
# 为不同服务使用独立密钥
# 切勿将 .env 文件提交到版本控制网络隔离
为获得最高安全性,请在独立的机器或虚拟机上运行 gateway。在 config.yaml 中设置 terminal.backend: ssh,然后通过 ~/.hermes/.env 中的环境变量提供主机详情:
# ~/.hermes/config.yaml
terminal:
backend: ssh# ~/.hermes/.env
TERMINAL_SSH_HOST=agent-worker.local
TERMINAL_SSH_USER=hermes
TERMINAL_SSH_KEY=~/.ssh/hermes_agent_keySSH 连接详情保存在 .env(而非 config.yaml)中,以避免随 profile 导出时被检入或共享。这样可以将 gateway 的消息连接与 Agent 的命令执行分离。
供应链安全公告检查
Hermes 内置了一个公告扫描器,用于标记活跃 venv 中与已知受损版本目录匹配的 Python 包(例如 2026 年 5 月的 mistralai 2.4.6 供应链投毒事件)。实现位于 hermes_cli/security_advisories.py。
运行方式:
- CLI 启动横幅。 若有任何公告匹配,会打印一行警告,并指向 hermes doctor 获取完整修复方案。
- hermes doctor。 显示所有活跃公告的版本详情和 2-4 步修复说明。
- Gateway 启动。 记录到 gateway.log;第一条交互消息会附带简短的操作者横幅。
每条公告都有一个稳定 ID。阅读并处理后,可以永久忽略它:
hermes doctor --ack <advisory-id>确认信息持久化到 config.security.acked_advisories,重启后仍有效。旧公告不会从目录中删除——保留它们可以确保新安装的用户收到关于历史受损版本的警告,这些版本可能仍缓存在私有镜像中。
检查本身仅使用标准库,每条公告执行一次 importlib.metadata.version() 查找,因此在每次启动时运行是安全的。
可选依赖的懒加载安装
PM 通过 pyproject.toml 中的 extras 管理可选 Python 功能。
源码安装选择 all extra,原生桌面包预装目标平台支持的所有 extras。
这两种集合并不相同。
当功能请求缺失的 extra 时,pm.ensure_import("extra-name") 使用与插件准入相同的依赖事务:
- 检查平台支持和 security.allow_lazy_installs。
- 在候选环境中统一准备核心依赖、现有 extras 和已启用插件的依赖。
- 没有插件成员时保持提交的锁文件不变;有成员时从先前选择开始解析,然后执行冻结同步。
- 验证候选环境后才发布新选择。失败会保留原环境,不会自动禁用或删除其他插件。
- 若当前进程仍使用旧环境,则提示重启,不在进程中直接替换已导入的库。
已发布的源码、锁文件和签名载荷保持不变。新增依赖使用包外的可写存储。
插件依赖共享完整 Python 环境,不是相互隔离的沙箱。
兼容的传递依赖可以更新,但声明的约束和精确固定版本仍有效。
失败通过 pm.InstallError 和同步记录报告。
关闭按需安装:
hermes config set security.allow_lazy_installs false已安装的依赖仍可使用。显式安装命令与按需安装不同。
关闭懒加载且存在包内冻结功能列表时,请求的 Python extra 名称仍受该列表限制。
该设置不是禁止显式插件准入或手动包管理命令的沙箱。
官方 Docker 镜像还通过内部策略关闭按需安装,仅更改配置不能覆盖它。
用 hermes tools 和 hermes doctor 检查缺失需求。
不要向签名载荷或系统 Python 执行 pip 安装。详见包管理。
Secrets
Hermes 可以在进程启动时从外部密钥管理器拉取 API 密钥,而不是将其存储在 ~/.hermes/.env 中。密钥管理器的引导令牌存放在 .env 中;其他所有提供商密钥(OpenAI、Anthropic、OpenRouter 等)可以保留在管理器中并集中轮换。
支持的后端:
- Bitwarden Secrets Manager — 使用 bws CLI,懒加载安装,免费套餐可用。
更多后端(Vault、AWS Secrets Manager、1Password CLI)可以轻松接入同一接口——只需在 agent/secret_sources/ 中添加一个模块并实现一个 CLI 处理器。如有特定需求,欢迎提交请求。
Profiles:运行多个 Agent
在同一台机器上运行多个独立的 Hermes agent——每个 agent 拥有各自的配置、API 密钥、记忆、会话、技能和 gateway 状态。
什么是 profile?
profile 是一个独立的 Hermes 主目录。每个 profile 拥有自己的目录,其中包含各自的 config.yaml、.env、SOUL.md、记忆、会话、技能、cron 任务和状态数据库。profile 让你可以为不同用途运行独立的 agent——编程助手、个人机器人、研究 agent——而不会混淆 Hermes 状态。
创建 profile 后,它会自动成为独立的命令。创建名为 coder 的 profile,你立即就拥有了 coder chat、coder setup、coder gateway start 等命令。
Profile、agent 与 bot
这几个术语描述的是 Hermes 的不同部分:
-
Profile 是一个助手的配置和数据的持久化主目录。它在多次对话和重启之间保持同一份状态。
-
Agent 是使用该配置和状态运行中的 Hermes 助手。"Hermes Agent" 同时也是产品的名称。
-
Bot 模式中的 Bot 是以具名条目出现在桌面应用 Bot 模式花名册中的 profile,带有头像和一个持久的 Bot Chat。同一个 profile 仍然可以从 CLI 访问。每个 Bot 都是一个 profile,但并非每个 profile 都是 Bot:当 Bot 模式把它的花名册展示信息(标题、头像、分区、隐藏状态)写入该 profile 的元数据并固定其规范 Bot Chat 时,这个 profile 才成为 Bot。一个只在 CLI、Docker 或 gateway 中使用、从未加入花名册的 profile 仍然只是普通 profile。
-
消息平台 bot 是 Telegram、Discord、Slack 等平台上的一个账号,通过 gateway 连接到 Hermes。它的 bot token 标识的是那个平台账号。
-
子 agent(subagent) 是由 delegate_task 为某个任务派生出的子助手,拥有全新的对话。独立的对话不等于独立的 profile。
快速开始
hermes profile create coder # 创建 profile + "coder" 命令别名
coder setup # 配置 API 密钥和模型
coder chat # 开始对话就这些。coder 现在是拥有独立配置、记忆和状态的 Hermes profile。
创建 profile
空白 profile
hermes profile create mybot创建一个预置了内置技能的全新 profile。运行 mybot setup 配置 API 密钥、模型和 gateway token。
如果你计划将此 profile 用作 kanban(看板)工作节点(或希望 kanban 编排器将任务路由到它),在创建时传入 --description "<角色>" 以便编排器了解其能力:
hermes profile create researcher --description "Reads source code and external docs, writes findings."你也可以稍后通过 hermes profile describe 设置或自动生成描述——完整路由模型请参阅 Kanban 指南。
仅克隆配置(--clone)
hermes profile create work --clone将当前 profile 的 config.yaml、.env、SOUL.md、skills 以及精选记忆文件 memories/MEMORY.md 和 memories/USER.md 复制到新 profile——记忆与 SOUL.md 一样被视为 agent 身份的一部分。会话、state.db、cron 任务及其他内容均从空白开始。若也想要空白记忆,请不加 --clone 创建 profile,或在创建后删除这两个文件;文件不存在时 agent 不会回退读取其他 profile 的记忆。编辑 ~/.hermes/profiles/work/.env 可使用不同的 API 密钥,编辑 ~/.hermes/profiles/work/SOUL.md 可设置不同的人格。
克隆全部内容(--clone-all)
hermes profile create backup --clone-all复制所有内容——配置、API 密钥、人格、记忆、技能、cron 任务、插件。会排除每个 profile 自己的历史数据(会话历史、state.db、backups/、state-snapshots/、checkpoints/),这些数据属于源 profile 且可能达到数十 GB。若要包含历史的完整备份,请使用 hermes profile export 或 hermes backup。
OAuth 登录是共享的,而不是复制的
Anthropic(Claude Pro/Max)、OpenAI Codex 和 xAI 的 OAuth 登录使用一次性刷新令牌——复制一份并不会得到第二个凭证,而是同一个凭证有了两个持有者,第一个刷新它的 profile 会让其他所有副本失效。因此 --clone-all(以及仪表盘的凭证镜像)不会把这些 OAuth 条目复制到克隆中。新 profile 继续从根目录的 ~/.hermes/auth.json 读取登录,任何 profile 中执行的令牌刷新都会写回根目录,所有 profile 都保持登录状态。静态 API 密钥照常复制。若要让某个 profile 拥有自己独立的 OAuth 登录,请在其中运行 hermes -p <name> auth add <provider>。该登录保存在这个 profile 自己的 auth.json 中(即使根目录还没有该提供商的登录也是如此),此后该 profile 使用它而不是根目录的登录;根目录的 auth.json 不会被修改。如果登录无法保存,命令会报错,而不会打印 Added。
从指定 profile 克隆
hermes profile create work --clone-from coder--clone-from <source> 会直接选择源 profile,并隐含执行 config/skills/SOUL 克隆。若要完整复制该源 profile,请与 --clone-all 组合使用:
hermes profile create work-backup --clone-from coder --clone-allHoncho 记忆 + profiles
启用 Honcho 后,克隆操作会自动为新 profile 创建专属 AI 对等体,同时共享同一用户工作区。每个 profile 构建各自的观察记录和身份标识。详见 Honcho——多 agent / Profiles。
使用 profile
命令别名
每个 profile 在 ~/.local/bin/<name> 自动获得一个命令别名:
coder chat # 与 coder agent 对话
coder setup # 配置 coder 的设置
coder gateway start # 启动 coder 的 gateway
coder doctor # 检查 coder 的健康状态
coder skills list # 列出 coder 的技能
coder config set model.default anthropic/claude-sonnet-4别名支持所有 hermes 子命令——底层实际上是 hermes -p <name>。
-p 标志
你也可以通过任意命令显式指定 profile:
hermes -p coder chat
hermes --profile=coder doctor
hermes chat -p coder -q "hello" # 可在任意位置使用粘性默认值(hermes profile use)
hermes profile use coder
hermes chat # 现在指向 coder
hermes tools # 配置 coder 的工具
hermes profile use default # 切换回默认设置默认值后,普通 hermes 命令将指向该 profile。类似于 kubectl config use-context。
了解当前所在 profile
CLI 始终显示当前活跃的 profile:
- 提示符:显示 coder ❯ 而非 ❯
- 启动横幅:启动时显示 Profile: coder
- hermes profile:显示当前 profile 名称、路径、模型、gateway 状态
Profile vs 工作区 vs 沙箱
profile 常与工作区或沙箱混淆,但它们是不同的概念:
- profile 为 Hermes 提供独立的状态目录:config.yaml、.env、SOUL.md、会话、记忆、日志、cron 任务和 gateway 状态。
- 工作区或工作目录是终端命令的起始位置,由 terminal.cwd 单独控制。
- 沙箱用于限制文件系统访问。profile 不对 agent 进行沙箱隔离。
在默认的 local 终端后端,agent 仍拥有与你的用户账户相同的文件系统访问权限。profile 不会阻止其访问 profile 目录之外的文件夹。
如果你希望 profile 默认在特定项目文件夹中启动,请在该 profile 的 config.yaml 中设置绝对路径的 terminal.cwd:
terminal:
backend: local
cwd: /absolute/path/to/project在 local 后端使用 cwd: "." 表示"Hermes 启动时所在的目录",而非"profile 目录"。
另请注意:
- SOUL.md 可以引导模型,但不能强制限定工作区边界。
- SOUL.md 的更改在新会话中会生效。现有会话可能仍在使用旧的 prompt(提示词)状态。
- 询问模型"你在哪个目录?"并不是可靠的隔离测试。如果你需要工具有可预测的起始目录,请显式设置 terminal.cwd。
运行 gateway
每个 profile 以独立进程运行各自的 gateway,使用各自的 bot token:
coder gateway start # 启动 coder 的 gateway
assistant gateway start # 启动 assistant 的 gateway(独立进程)不同的 bot token
每个 profile 有各自的 .env 文件。在各文件中配置不同的 Telegram/Discord/Slack bot token:
# 编辑 coder 的 token
nano ~/.hermes/profiles/coder/.env
# 编辑 assistant 的 token
nano ~/.hermes/profiles/assistant/.env安全性:token 锁
如果两个 profile 意外使用了相同的 bot token,第二个 gateway 将被阻止并显示明确的错误信息,指出冲突的 profile。支持 Telegram、Discord、Slack、WhatsApp 和 Signal。
持久化服务
coder gateway install # 创建 hermes-gateway-coder systemd/launchd 服务
assistant gateway install # 创建 hermes-gateway-assistant 服务每个 profile 拥有独立的服务名称,各自独立运行。
在官方 Docker 镜像中
各 profile 的 gateway 由 s6-overlay(容器中的 PID 1)监管,因此 hermes profile create <name> 会自动在 /run/service/gateway-<name>/ 注册 s6 服务槽。hermes -p <name> gateway start/stop/restart 会调度到 s6-svc 而非直接启动裸进程——崩溃后自动重启,docker restart 会保留之前运行的 gateway 集合。详见 各 profile gateway 监管。
配置 profile
每个 profile 拥有各自的:
- config.yaml — 模型、提供商、工具集及所有设置
- .env — API 密钥、bot token
- SOUL.md — 人格与指令
coder config set model.default anthropic/claude-sonnet-4
echo "You are a focused coding assistant." > ~/.hermes/profiles/coder/SOUL.md如果你希望此 profile 默认在特定项目中工作,还需设置其 terminal.cwd:
coder config set terminal.cwd /absolute/path/to/project更新
hermes update 拉取一次代码(共享),并自动将新的内置技能同步到所有 profile:
hermes update
# → Code updated (12 commits)
# → Skills synced: default (up to date), coder (+2 new), assistant (+2 new)用户修改过的技能不会被覆盖。
管理 profile
hermes profile list # 显示所有 profile 及其状态
hermes profile show coder # 显示某个 profile 的详细信息
hermes profile rename coder dev-bot # 重命名(更新别名;移除旧名称的网关服务)
hermes profile export coder # 导出为 coder.tar.gz
hermes profile import coder.tar.gz # 从归档文件导入删除 profile
hermes profile delete coder此操作将停止 gateway、移除 systemd/launchd 服务、移除命令别名并删除所有 profile 数据。系统会要求你输入 profile 名称以确认。
使用 --yes 跳过确认:hermes profile delete coder --yes
备注
你无法删除默认 profile(~/.hermes)。如需删除所有内容,请使用 hermes uninstall。
Tab 补全
# Bash
eval "$(hermes completion bash)"
# Zsh
eval "$(hermes completion zsh)"将该行添加到 ~/.bashrc 或 ~/.zshrc 以启用持久补全。支持补全 -p 后的 profile 名称、profile 子命令及顶级命令。
工作原理
profile 使用 HERMES_HOME 环境变量。运行 coder chat 时,包装脚本在启动 hermes 前将 HERMES_HOME 设置为 ~/.hermes/profiles/coder。由于代码库中 119+ 个文件通过 get_hermes_home() 解析路径,Hermes 状态会自动限定在 profile 目录范围内——包括配置、会话、记忆、技能、状态数据库、gateway PID、日志和 cron 任务。
这与终端工作目录是分开的。工具执行从 terminal.cwd 开始(或在 local 后端使用 cwd: "." 时从启动目录开始),而非自动从 HERMES_HOME 开始。
默认 profile 就是 ~/.hermes 本身。无需迁移——现有安装的工作方式完全不变。
将 profile 作为发行版共享
你在一台机器上构建的 profile 可以打包为 git 仓库,并通过一条命令安装到另一台机器——你自己的工作站、团队成员的笔记本,或社区用户的环境。共享包包含 SOUL、配置、技能、cron 任务和 MCP 连接。凭据、记忆和会话保持各机器独立。
# 从 git 仓库安装完整 agent
hermes profile install github.com/you/research-bot --alias
# 当作者发布新版本时更新(保留你的记忆和 .env)
hermes profile update research-bot完整指南请参阅 Profile 发行版:共享完整 Agent——包括编写、发布、更新语义、安全模型和使用场景。
Bot 模式
Bot 模式把你的 Hermes profile 变成一支具名 Bot 团队。每个 Bot 都有自己的角色、模型、记忆、技能和头像;Bot 之间可以运行周期性的例行任务、在群聊中共同商议,并直接互相发消息。花一次功夫搭建一个专精 Bot,它就永远留在那里,一键可达。
Bot 模式**内置于桌面应用**中,默认开启——无需安装。它在左侧边栏中以 Bots 标签页的形式出现,紧挨着 Sessions;当 Bots 标签页处于激活状态时,一个 Routines 面板会停靠在对话旁边。
Bot 就是 profile
这里没有新概念需要学习:Bot 就是 一个 Hermes profile——位于 ~/.hermes/profiles/<name>/ 下的独立配置、记忆、技能、凭据和聊天记录。Bot 模式只是这个基本单元之上的一层 UI,所以你在其中做的一切在 CLI 里同样可见:hermes -p <bot> chat 打开的是同一个 agent,Bot 的例行任务也会出现在 hermes cron list 中。没有核心补丁,没有后台守护进程,也不需要额外的存储。
Bots 面板
花名册中每个 agent profile 各占一行:头像、最新消息预览和时间戳。
- 点击一个 Bot 进入它的对话——每个 Bot 都有一个唯一的、持久的规范 Bot Chat 对话,在该 Bot 诞生的那一刻就被创建(并置顶)。点击一行总是打开那个 Bot Chat(也就是该行预览的那段对话),即便你为这个 Bot 还开着其他标签页;那些标签页会保留在它旁边。在标签栏中,Bot Chat 以 Bot 的名字作为标题,因此两个同时打开的 Bot 一眼就能分清。
- Active now(当前活跃)——花名册的活跃过滤器包含当前聚焦的实时轮次的所有者、过去 90 秒内有过写入的 Bot,以及最近有 worker 心跳的 Bot。仅仅连接着某个 gateway 并不代表某个 Bot 正在工作。
- 搜索会随输入实时过滤花名册。
- 隐藏一个 Bot——右键点击一行 → Hide Bot,把不常用的 Bot 从花名册和 Active-now 状态栏中移出。隐藏只影响显示:@提及依然能解析,群聊成员关系不受影响,例行任务照常运行。一旦至少有一个 Bot 被隐藏,面板标题栏会出现一个眼睛图标切换开关——点击它可就地以变暗的样式显示隐藏的 Bot,再右键 → Unhide Bot 即可恢复。隐藏的 Bot 不会弹出提示,但仍会静默累积未读消息,眼睛图标上会出现一个小红点提示有新动态。隐藏状态保存在该 Bot 的 profile 元数据中,因此它会跟随这个 Bot 出现在连接到同一后端的每台桌面设备上。
规范 Bot Chat 是一个永不重置的对话
在 Bot 的规范对话中输入 /new(或 /reset)本应把这段关系分叉成一个临时会话——而这正是 Bot 模式承诺永远不会发生的事。输入框会把它重新路由为 /compact:获得全新的工作上下文,但保留同一段对话。同一 profile 下的普通会话仍然拥有完整的 /new 自由。
从侧边栏归档一个 Bot Chat 会让它退役:下一次点击这个 Bot 会开启一段全新的对话,它将成为新的规范 Bot Chat。退役的对话保持归档并隐藏——它的历史仍保存在数据库中,但不再能从这个 Bot 或归档视图中打开。自动的空闲归档清理(sessions.auto_archive)永远不会让 Bot Chat 退役;只有显式归档才会。
把 Bot 整理进分区
分区(section)是你自己创建的文件夹——Clients、Team,怎么合适怎么来——作为自动按 gateway 分组之外的第二个维度。没有创建任何分区时,花名册仍是原来那份普通列表。
- 创建分区:从面板的 + 菜单 → New section,或右键点击一个 Bot → Move to section → New section…(创建的同时就把这个 Bot 归入其中)。
- 归档一个 Bot:把它的行拖到某个分区上——悬停时目标会高亮,按 Esc 取消拖动——或者右键 → Move to section 再选择一个。Remove from section 会把它放回 Unassigned。
- 重命名、重新排序或删除分区:通过分区标题的右键菜单(或悬停时出现的 ⋯);双击标题即可重命名。标题可以像 gateway 标题那样折叠。
- 删除分区永远不会删除 Bot——它们会回到 Unassigned,弹出的提示会提供 Undo。不会要求确认。
成员关系保存在每个 Bot 的 profile 元数据(ui_meta)中,因此一个 Bot 的分区会跟随它出现在连接到同一后端的每台桌面上。当花名册显示多个 gateway 时,分区会嵌套在各个 gateway 的分组之内。
创建一个 Bot
在花名册中点击 New Agent。最快的路径只需三个字段——Name、Title、Description——几秒钟内 Bot 就会诞生,并在它新建的 Bot Chat 中发出第一条自我介绍消息。
一个 Advanced 折叠面板会展开完整的能力配置界面:
- 从现有 profile 克隆——从另一个 Bot 的配置、技能、SOUL 和记忆起步,或者选择 Fresh profile 从零开始。
- Create empty——完全跳过内置技能,得到一个最小化的 profile。
- 模型与 provider 锁定——为 Bot 指定专属模型。Hermes 支持的任意 provider/model 组合都可以使用,不同的 Bot 可以并排运行在不同的模型上。留空则继承自启动 profile。
- 自定义 SOUL.md——Bot 的人格与常驻指令。
- 按技能、按工具集、按 MCP 服务器逐项启用——精确勾选这个专精 Bot 需要的能力。
- 从主 profile 复制 API 密钥——默认开启。每个 Bot 都拥有自己的凭据存储:静态 API 密钥会被复制进来,而一次性 OAuth 登录(Anthropic、OpenAI Codex、xAI)不会被复制——请用 hermes -p <name> auth add <provider> 在 Bot 内登录。详见每个 profile 各自拥有凭据。
选择它运行在哪台机器上("Create on")
当设置 → Connections 中注册了不止一个连接时,New Agent 对话框会新增一个 Create on 选择器。选定一台设备后,profile 就会在那台机器的后端上创建——你的窗口不会切换 gateway。这个新 Bot 随后会以 Connections Bot 的身份出现在花名册中(当同名 Bot 存在于多台机器上时,带有 @name-device 形式的 handle),与它对话会路由到它自己所在的机器。
在只有一个连接的常见情况下,这个选择器会被隐藏,Bot 会在你当前连接的机器上创建——和以前的行为完全一致。
远程创建的注意事项:
- 克隆源取自目标机器上的 profile(它的 default)——远程主机没有你本地的 profile 可供克隆。
- 实时的 Capabilities 标签页会锁定到目标机器的后端,因此你在创建过程中配置的技能、工具和 MCP 服务器都会落在这个 Bot 最终运行的机器上。(较旧的桌面版本会为远程目标回退到分阶段的 Skills/Tools/MCP 勾选列表;两者读取的都是目标机器的目录。)
- 取消对话框会丢弃草稿 profile,无论它是在哪台机器上创建的。
Edit Profile(右键点击一个 Bot)随时可以在这个实时 profile 上重新打开同一个界面:头像、标题、描述、模型锁定、技能、工具集、MCP 服务器,以及完整的 SOUL.md。
Duplicate(右键)会完整克隆一个 Bot——配置、技能、SOUL.md、记忆及外观。Delete Profile 会永久移除一个 Bot,需要经过与桌面 profile 菜单相同的破坏性操作确认;默认 profile 无法被删除。
头像
每个 Bot 都有一张脸:
- Blob 脸(默认)——从 Bot 名字派生出的确定性软体脸:同名同脸,永远不变。在 New Agent 中输入名字时,这张脸会实时跟随变化;点击 Randomize 重新生成,点击 Lock face 锁定你喜欢的那张脸(即使名字之后改变),或者固定六种轮廓之一(圆形、有机形、方形、圆突形、云朵形、太阳形),其余部分仍由名字派生。
- 几何脸——经典的 7 种形状 × 10 种颜色组合。在聚焦的实时轮次进行期间,拥有该轮次的 Bot 会侧身向上看,并显示三个动态的小点,轮次结束后再缓缓回到空闲姿态。所有权包含连接信息,因此不同 gateway 上的同名 Bot 不会借用彼此的姿态。后台 worker 保留原有的工作动画;照片、Blob 脸和印记(sigil)保留各自的渲染方式。
- 上传的图片——任何你喜欢的图片。
- AI 生成的肖像——配置了图像后端时,在原地生成(这走的是标准的 image.generate RPC,本地和远程 gateway 均可使用)。
- 像素宠物——来自 petdex 图鉴 的伙伴,在 Bot 工作时会在头像旁跳动。在终端中运行 hermes pets 即可浏览图鉴。
一个 Bot 的外观、标题和描述都保存在该 profile 的后端元数据中,因此同一个 Bot 在连接到该后端的每台桌面上看起来都一样。
Routines(例行任务)
Routines 面板把周期性任务挂载到负责它的 Bot 上——"每天早上帮我总结收件箱"就紧挨着负责这件事的 Bot。这个面板只在 Bots 标签页激活时才停靠在对话旁边,切回 Sessions 时会自动让开(较旧的桌面版本会始终显示它)。一个结构化的调度选择器会构建调度规则(先选频率,再填入真正重要的细节),Advanced 字段则暴露原始的 Hermes 调度字符串。
Routines 本质上就是命名空间为 [bot:<name>] <routine> 的普通 Hermes cron 任务——它们同样会出现在 hermes cron list 和核心 Cron 页面中。运行结果会写入该 Bot 自己的对话历史,所以结果正好出现在你本来就会找这个 Bot 交流的地方。
群组与群聊
在某个成员的轮次进行期间发送的消息(包括同一话题线程中的回复)会排在当前房间驱动之后等待。它们不会打断那个成员,也不会把未读消息标记为已读。Stop 会取消排队中的后续轮次,并让成员保持暂停直到恢复。房间安静下来后,会在前台等待结束后继续观察超时的成员 21 分钟;这个观察窗口不会延长轮次本身。房间平息后,未解决的成员失败仍会显示在折叠的 Activity 摘要中。展开 Activity 可查看轮次顺序;重新 @ 该成员即可重试。结果不明确的提交失败不会被自动重新提交。
右键点击一个本地 Bot → Manage groups,即可把它加入或移出任意数量的群聊。可以单独挑选已有的群,也可以直接内联创建一个新群。本地成员关系保存在该 Bot 的后端同步 profile 元数据中,因此它会跟随这个 profile 出现在各个桌面上;带有一个旧版群组的旧 profile 仍能正常工作。Connections Bot 通过 New Group Chat 选择器加入群聊,并在房间的共享状态中保留来源标识。
房间跟随的是你的 gateway,而不是某一台 Desktop。 每个房间的近期记录、成员、图片和名称都会被镜像到你的 Desktop 所连接的每一个 gateway 的共享 profile 元数据中,并带有按 gateway 划分的版本号,因此两台 Desktop 同时写入时会合并而不是互相覆盖。在另一台机器上打开 Hermes Desktop(局域网、Tailscale,任何地方都可以),连接到同一个 gateway,这个房间及其历史就会出现;仅有 gateway 的客户端也能看到它。房间携带一个持久的内部身份,因此重命名一个房间只会在各处改变它的显示名称,解散一个房间会在每个客户端上永久移除它——即便是当时离线的客户端也不例外——而重新创建一个同名群组会开启一个真正全新的房间。如果某个 gateway 挂掉或被移除,不会丢失任何数据:每台连接的 Desktop 都在本地保留完整的房间,并在重新连接时向任何 gateway 重新播种。(完整的编排日志留在每台 Desktop 的本地存储中;共享镜像只是一个有界的近期历史投影。)
群组的身份在创建时和创建后都可以编辑:
- New Group Chat 允许你在名称之外设置一张可选的房间图片——从设备上传一张,或(配置了图像后端时)生成一张,使用的是与 Bot 头像相同的流程。这张图片会出现在花名册行中(取代默认的群组图标),并在房间头部置于标题之前。
- 房间头部的齿轮按钮会打开 Group settings,你可以在这里随时重命名群组,或设置、替换、移除它的图片。重命名会把一切一并带走——房间日志、每个成员的房间会话、成员关系和图片——不会丢失任何历史。重命名为已被占用的名称会被拒绝,而不是悄悄加上后缀。
群组是与 Bot 私信同属一个按活跃度排序的花名册中的独立行。一个 Bot 即便属于多个群组,也只占花名册中的一行私信;而每个群组都拥有自己独立的一行,显示成员数、最新消息预览、时间戳和"需要你"状态。
使用房间旁边的 Move up 和 Move down 箭头可以选择它在各个房间中的位置。在第一次移动之前,原有的"置顶优先、按近期活跃度"排序保持不变。移动之后,房间顺序会保存在这台 Desktop 上并在重新加载后保留;新房间会排在各自置顶或未置顶分段内已显式排序的房间之后。移动不能跨越置顶边界,过滤也不会把隐藏的房间从已保存的顺序中丢弃。这些控件重新排序的是真正的 Group Chat 房间,而不是用户创建的 Bot 文件夹,也不会改变成员关系或 gateway 归属。
在任意群组行上点击 Open chat(2–6 个 Bot)会打开一个共享房间,整个群组在其中协作:
- 一条可见的对话。 公开消息和每个成员的回复按到达顺序保持可读,并带有发言者的名字和时间戳。开启另一个话题不会折叠之前的回复。Reply in thread 会在不打乱房间顺序的前提下延续那个话题;Activity 是一个辅助的状态视图,而不是消息的替代品。私有的 Bot Chat 保持独立。
- 你的消息会触发最多三轮串行的成员发言。被 @提及 的 Bot 会回应(如果没有人被提及,则所有人都会回应);每个 Bot 简短回复或选择跳过,当完整一轮都保持沉默时,房间就会安静下来。
- 队友可以用 @hermes 把工作转交给主 Bot,在较早保存的房间中同样有效;其他 gateway 上的 Bot 会保留带设备限定的标签(例如 @default-vera)。
- Bot 之间通过 @name 互相拉入对话,遇到真正需要判断的问题会用 @user 上报给你——这种情况下群组行会显示一个需要你的徽标。待处理的提问和命令审批同样会点亮这个徽标;解决最后一个提示只会清除提示类的关注,而不会清除独立的提及。提示会跟随重命名后的房间,而解散房间会让它们退役,即使某个成员正在进行中的轮询稍后才到达。
- 硬性上限(每次发送 10 条消息,3 轮)防止房间失控。
- 每个成员都保留自己独立、持久的房间会话,因此房间上下文会像其他对话一样持续保存。
- 不是每个 Bot 都会回复每一条消息。 是否发言由每个成员自己决定——一个 Bot 只有在有新内容可补充时才会回复,否则就跳过;@提及特定成员会把这一轮范围限定到他们身上。你可以预期被 @提及 的成员(或任何有话要说的成员)会发言,其余的保持安静。
- 关闭 Desktop 后房间仍会继续运行。 当一个房间的所有成员都位于同一个 gateway 上时,该 gateway 会通过一个持久的驱动器来负责轮次调度:关闭 Hermes Desktop(或失去它的连接)不会让讨论中途停止,Desktop 重新连接时只需从房间日志中补上进度即可。适用这种情况时,gateway 上的 groups.capabilities 会报告 driver: true。成员跨多台机器的房间则不同:每个成员的轮次运行在它自己的 gateway 上,Bot 之间的消息一节中描述的跨连接信使机制仍然适用于它们。
- 房间可以跨越多台机器。 New Group Chat 选择器可以从任意已注册的连接中挑选 Bot;每个成员的发言都运行在它自己的机器上,在它自己那台机器的房间会话里。跨机器的成员在房间和其他成员的对话记录中都带有设备徽标(dixie · Mac Mini),消除歧义的 @name-device handle 在房间提及中同样有效——因此两台机器上同名的 agent 永远不会混淆。
- 插件可以观察成员的工作。 持久的房间日志会记录 turn.started 和 turn.settled;成员在这两者之间做的事情(工具、审批、流式文本)会通过 on_room_member_activity 钩子投射给插件,并附带房间、成员和轮次坐标,因此社区客户端无需读取 Hermes 内部实现,就能在 Group Chat 之上构建工具卡片和实时成员状态。
Bot 之间的消息
Bot 之间发消息会带有署名,你也可以从任意对话中把工作转交出去:
- @提及——在任意对话中输入 @researcher have a look at this,输入框的 @ 自动补全会帮你选中正确的 Bot;发送时,这个提及会与当前花名册进行解析,当前活跃的 Bot 会被准确告知你指的是谁(profile、友好名称,以及跨连接 Bot 的设备信息)。随后该 Bot 会自己撰写消息,并通过 message_agent 发送出去——你的原文永远不会被原样转发,回复会带着那个 agent 的署名返回。一个邮箱地址或未知的 @ 会原样透传。运行在其他已连接机器上的 Bot 同样可以这样触达(见下方跨机器的 Bot):Desktop 会通过那个连接自己的 socket 转发消息。
- 重命名的 Bot 会同步保留标签——给一个 Bot 起一个友好名字(它对话头部的铅笔图标,或 hermes profile rename),它就可以用这个名字被 @标记:一个标题为 Research Buddy 的 Bot 会响应 @research-buddy(以及 @researchbuddy),无论是在普通对话还是群聊中都是如此。输入框的 @ 自动补全会提供重命名后的标签,同时输入旧的 profile 名称依然能匹配并继续生效。
- 私信——每个 Bot Chat 都携带 message_agent 工具:一个 Bot 通过调用 message_agent(target="researcher", message="…") 给队友发消息。这个工具会根据当前花名册校验目标,自动加上发送方的 Message from 🤖 <sender> (@<sender>): 署名前缀,并投递到队友的规范 Bot Chat 中。投递是发后即忘的:发送方会收到一个确认,完成自己的这一轮,而回复会作为一条后台完成通知稍后到达。这条消息作为真正的参数传递(不经过 shell 解释——引号、$(...) 和反引号都会原样到达),而且 Bot 会自己撰写消息,而不是转发你的原话。队友花名册——每个 profile 的名称和角色(来自标题/描述)——是每个 Bot Chat 系统提示的一部分,因此 Bot 在选择接收方之前就知道谁负责什么。这个工具只存在于 Bot 模式管理的安装上的规范 Bot Chat 会话中;普通对话、群聊成员会话和 CLI 会话都看不到它。
本地消息同样能送达一个在 Desktop 或 TUI 中保持打开的 Bot Chat。接收方后端保留所有权:它在现有的通知轮询器上读取持久化的入站消息,空闲时立即接纳,否则等到正在运行的轮次和已排队的人类提示完成后再处理。queued 确认表示消息已被持久接纳,而不是回复已完成。目标 profile 会在 runtime/bot_live_delivery/ 下保留投递 ID 和回执;settled 表示已完成。崩溃或被取消的导入轮次不会被自动重放,绑定到已离开的所有者的待处理工作会保留下来供检查,而不是被悄悄重新运行。不要重发结果未知的投递。不具备实时投递能力的较旧后端仍会返回原有的所有权拒绝;升级后请重启那个后端。
- 保持沉默——没有新内容可补充的 Bot 可以用某个有意沉默标记([SILENT]、NO_REPLY、……)来结束一轮。Bot Chat 会把这一轮保留在记录中但不渲染任何内容,给它发消息的队友会收到一个空回复而不是这个标记。失败的轮次以及仅仅提到某个标记的文字会原样显示。
后端会在构建提示词时自动把消息协议教给每个 Bot 的规范 Bot Chat 会话——包括队友从 CLI 无界面打开它的情况。只有规范 Bot Chat 会获得协议部分;你的普通会话和 SOUL.md 不受影响。这由 config.yaml 中的 agent.bot_mode_protocol 控制(默认:开启):
agent:
bot_mode_protocol: true # 向规范 Bot Chat 注入 Bot 间消息协议备注
Bot 间投递是按次调用的:接收方 Bot 会在它下一次运行时取走这条消息。中途打断一个正在对话的 Bot 目前还做不到,是未来的工作方向。
失败的轮次会安全重试
本地一次性投递会把活跃会话的拒绝代码与人类可读的消息分开保留。SESSION_NOT_OWNED 会产生 target_busy;无法读取的协调注册表不会被误标为另一个所有者。没有代码标记的较旧本地 CLI 仍使用原有的拒绝措辞。
一次失败的投递轮次最多重试一次,并且只在重试确实可能有帮助时才会重试。瞬时性失败(目标运行时离线、投递超时、provider 限流或服务端错误)会不加改动地重新运行同一个 Bot Chat 会话。上下文溢出失败同样会重新运行同一个会话——重试的这一轮会先通过标准的上下文压缩流程压缩超出阈值的记录,再调用模型,这样重试就能装进原本装不下的空间。认证、配额和配置类的失败永远不会自动重试:第二次尝试无法解决这些问题,只会白白消耗配额,因此这类失败会被立即上报。一次重试永远不会开启新会话——你的 Bot Chat 历史和上下文会保持完整。
当目标没有活跃的 Desktop 或 TUI 所有者时,本地投递会通过一次安静的 CLI 轮次打开该 profile 的规范 Bot Chat。这条传输路径优先使用与发送方运行时的 Python 解释器并列的 Hermes 入口,因此只要这个同级入口存在,某个服务 PATH 上不相关或较旧的 hermes 就不会抢先。--in ~ 选择工作目录;显式的 Bot Chat 标题会在目标 profile 的会话数据库中解析。
投递失败时:带类型的原因码
一次失败的 Bot 轮次或中继投递会全程携带一个机器可读的 reason 代码,与人类可读的错误文本并列:目标 gateway 会对失败进行分类(provider_auth_or_access、provider_quota_limit、provider_rate_limit、provider_server_error、context_overflow、missing_config、model_unavailable、runtime_offline、queued_expired、delivery_timeout、target_busy、unknown),Desktop 负责转发,发送方 agent 的完成通知会在错误文本前带上 [reason: <code>] 标签。调用方 agent 可以据此分支处理——"需要重新登录"还是"稍后重试"——而不必解析 provider 的自然语言描述。Desktop 的"需要关注"徽标使用的是同一套代码。
跨已连接机器的消息(Desktop 中继)
你在设置 → Connections 中注册的每一个 gateway——本地、远程 URL、SSH、Hermes Cloud、docker——都是 Desktop 持续保持打开的一条常驻连接,Bot 模式会自动利用这些连接来发消息。无需额外配置:
- 花名册会自行同步。 只要 Desktop 在运行,它就会定期告诉每个已连接的 gateway,其他连接上都有哪些 agent。每个 Bot Chat 的队友花名册随即会列出它们("在其他已连接机器上的队友"),包含名称、角色以及所在的机器——当 agent 出现、消失或被重命名时,这份花名册也会刷新(能力版本)。
- message_agent 可以直接触达它们。 你笔记本上的 Bot 可以用 message_agent(target="moxie", …) 给云端 agent 发消息,和给本地队友发消息完全一样。如果同一个 handle 在多台机器上都存在,用 target="moxie@<connection>" 消除歧义(这个工具的报错信息会告诉 Bot 确切的写法)。投递走的是 Desktop:发送方的 gateway 把消息入队,Desktop 把它中继到目标连接自己的 gateway,目标 Bot 在它自己的规范 Bot Chat 中运行一轮,回复以本地私信同样使用的那种后台完成通知的形式返回给发送方。发给不同 Bot 的消息会并行投递,因此一个 Bot 的长轮次永远不会拖延另一个 Bot 的邮件(也不会让它超过 bot_mode.envelope_ttl_seconds 而过期);发给同一个 Bot 的消息则按顺序、一次一轮地投递。
- Desktop 是信使。 只要一台同时认识两个连接的 Desktop 在运行,跨连接投递就能工作(它持有 socket 和凭据——gateway 之间彼此看不到对方的认证信息)。如果 Desktop 在投递途中被关闭,发送方的 Bot 会被告知回复没有送达,而不是被无限期挂起。若需要完全不经过 Desktop 的、永远在线的机器对机器消息,请注册一个 peer(见下方 hermes peer)——这两条路径可以共存。
Bot 发起的跨机器私信(hermes peer)
一台机器上的 Bot 可以在没有任何 Desktop 参与的情况下,给另一台机器的 gateway 上的 Bot 发消息。把对方的 gateway 注册为一个 peer(它的 API server URL + API_SERVER_KEY):
hermes peer add spark --url http://spark.lan:8377 --key <API_SERVER_KEY>
hermes peer list
hermes peer dm spark < ~/.hermes/cache/scratch/dm.txt # 消息内容来自一个文件(不经过 shell 解释)
hermes peer dm spark/researcher < ~/.hermes/cache/scratch/dm.txt # 多路复用 peer 上的指定 profile
hermes peer run spark --idempotency-key ticket-123 < ~/.hermes/cache/scratch/long-task.txt
hermes peer status spark run_abc123
hermes peer stop spark run_abc123hermes peer dm 会通过该 peer 已有的 API server,把消息投递进远程 agent 的规范 Bot Chat,在那里运行一轮 agent,并把回复打印到 stdout——这正是本地 hermes -p <bot> chat 命令的跨机器对应版本。
peer dm 只适合简短的查询和回执,因为它会一直占用一条 HTTP 连接直到轮次结束。对于较长的轮次,peer run 会立即返回一个 run_id;用 peer status 轮询它。这次运行会继承规范 Bot Chat 的记录,而一个稳定的 --idempotency-key 会让重试返回原来的那次运行,而不是开始重复的工作。用 peer stop 加上这个确切的运行 ID 来中断它,而不会误伤其他轮次。
一旦注册了 peer,教给每个 Bot Chat 的消息协议(agent.bot_mode_protocol)就会自动包含 peer 花名册,message_agent 也可以直接接受 peer 目标——message_agent(target="spark/researcher", …),或用 target="spark" 指向该 peer 的主 agent——这样你的 Bot 会自己了解到其他机器上存在队友,以及如何触达它们。注册或移除一个 peer 会在下一条消息时刷新每个 Bot Chat 的协议(能力版本)。
前提条件:peer 所在机器运行 api_server gateway 平台,并配置了强密码的 API_SERVER_KEY;能否触达取决于你的网络(局域网、Tailscale、VPN)。这个 key 是一项凭据,保存在 ~/.hermes/.env 中的 HERMES_PEER_<NAME>_KEY 下;peer 的名称/URL 保存在 config.yaml 的 bot_peers 下。
单向可达(NAT)
跨 gateway 的链接是 gateway 到 gateway 的直接连接——Desktop 只是观察者,不是中继。位于家庭 NAT 之后的 gateway 可以向公网 peer 主动拨出(笔记本 → VPS 可行),但反方向没有入站路由(VPS → 家庭会失败),除非你的网络提供了一条。如果你的 Group Chat 跨越了 NAT 边界,请把房间的权威放在每个参与者都能触达的主机上(通常是公网 VPS),或者用 Tailscale/VPN 打通网络。
转移托管房间的权威
权威接管是一项运维恢复流程,而不是原子化的交接。请在相应的 gateway 上使用现有的 JSON-RPC 方法 groups.promote 和 groups.demote。不存在 groups.peer.promote 或 groups.peer.demote 方法;groups.capabilities 会列出你的 gateway 支持的方法。
提升之前先隔离旧的写入方
在发送 confirm: true 之前,先确认之前的权威无法再提交,并在它被降级之前一直保持这道隔离。停止它写入房间的进程并阻止自动重启,或者使用等效的基础设施隔离手段。网络超时、Desktop 断开连接或 groups.stop 都不算证明:旧 gateway 可能仍在运行,而停止一个轮次并不会撤销房间权威。如果无法建立隔离,就不要提升。
-
检查副本覆盖度。 在替代 gateway 上,用 {"room_id":"ROOM_ID"} 检查 groups.replica_state,比较 last_seq 与 latest_seq。计划内接管前要求副本完整;提升本身并不会检查这一覆盖度。groups.replicate 在摄取完 groups.log 返回的页面后会报告 caught_up;仅仅注册了 peer 并不能证明替代方已拥有房间历史。追平状态描述的是最后一个已复制的页面,并不能证明旧写入方已经停止,或不存在更新的事件。对于计划内迁移,先让写入方静默,复制到最终游标,然后保持隔离。对于灾难恢复,要考虑到从未到达副本的历史。
-
只在旧写入方已被隔离时提升。 在替代方上:
{"jsonrpc":"2.0","id":1,"method":"groups.promote","params":{"room_id":"ROOM_ID","confirm":true,"reason":"planned-handover"}}room_id 和 confirm: true 是必需的;reason 可选,默认为 authority-unreachable。确认是你对"之前的权威无法再提交"这一事实的断言,而不是请求自动隔离它。缺少确认时该调用会返回错误 4118。成功的结果会报告 authority_gateway_id 和 authority_epoch(已复制的 epoch 加一)。
-
在旧权威恢复服务之前先将其降级。 在旧 gateway 上通过受控的恢复连接应用这个 RPC,同时保持其常规房间写入方处于隔离状态。把示例中的 gateway ID 和 epoch 替换为成功提升时返回的确切值:
{"jsonrpc":"2.0","id":2,"method":"groups.demote","params":{"room_id":"ROOM_ID","observed_gateway_id":"NEW_GATEWAY_ID","observed_epoch":2}}三个参数都是必需的。不要猜测未来的 epoch:降级需要的是存在更新权威的证据,而不是编造的值。它会记录 authority.lost 并采用观察到的谱系;重复相同的谱系是幂等的。如果旧主机不可用,请保持隔离,并在恢复其常规写入方之前完成这一步。
-
验证并重新连接。 在两个 gateway 上读取 groups.state,将 room.authority_gateway_id 和 room.authority_epoch 与提升结果进行比较。旧权威的发送必须被拒绝;把客户端指向替代方。降级只隔离写入;它不会合并历史,也不会自动把旧的权威存储变成同步副本。
在旧 gateway 仍可写入时进行提升,会让两个独立的 state.db 存储都接受消息并产生分叉的历史。替代方上更高的 epoch 并不会远程禁用旧的写入方;出现脑裂并不要求 epoch 相等。如果历史已经分叉,请隔离写入方并保留两份历史以供恢复,而不要假设提升、降级或重放会把它们合并。
跨机器的 Bot
当你在设置 → Connections 中注册了多个后端——本地运行时、远程 gateway、SSH 主机、Hermes Cloud 实例——花名册会持续显示来自每一个已连接来源的 Bot:SSH 来源会在不在远程主机上启动任何进程的前提下被盘点,暂时无法触达的机器会保留它们最后已知的行,而不是直接消失。当同一个 profile 名称存在于多个来源时,handle 会以 @name-device 的形式消除歧义(例如 @research-homelab)。一个 Bot 的对话、会话、记忆和例行任务都存放在拥有该 profile 的那台机器上。
点击一个 Connections Bot 不会把你的窗口切换到那台机器上——留在你当前的对话里 @提及 它、把它安排进某个群聊,或者用 Create on 选择器直接在它所在的机器上创建新的 agent。云端和本地 agent 就这样共用同一个花名册:注册你的 Hermes Cloud 实例和你的桌面(比如通过 Tailscale 或 SSH),它们的 Bot 就能互相发消息、共处同一个房间,每个 agent 的工作都运行在自己的机器上。跨这些机器的 Bot 间私信会自动走 Desktop 中继(见上文跨已连接机器的消息)。
完整的多连接指南请参阅将 Desktop 连接到多个 Hermes 实例。
本地 Bot 与 gateway(同时运行多少个 Bot)
Desktop 不会为每个 Bot 运行一个后端。每台主机只有一个 gateway 进程,负责所有本地 profile 的会话:当你打开一个本地 Bot 时,Desktop 会为该 profile 运行 hermes gateway ensure --json,它会附加到正在运行的 gateway;如果还没有进程服务该 profile,就启动它。随后 Desktop 通过 WebSocket(/api/ws)使用一次性 ticket 连接它。Desktop 端没有后端池,没有“同时保持多少个 Bot 预热”的设置,也没有空闲回收——打开另一个 Bot 从不需要等待槽位,读取另一个 Bot 的历史也只是向同一个 gateway 发一个请求。
gateway 的生命周期与 Desktop 窗口无关。关闭窗口或退出 Desktop 只会丢弃连接,绝不会停止 gateway,因此你的消息平台 bot、Routines 和进行中的轮次都会继续运行,Desktop 下次打开时会重新附加。要停止它,请显式运行 hermes gateway stop。如果 gateway 在 Desktop 运行期间重启(更新、崩溃,或 hermes gateway stop 之后再次打开),Desktop 会重新运行一次 gateway ensure 并重新连接。标记为 gateway.standalone: true 的 profile 会保留自己的 gateway;Desktop 以同样方式附加到它。
设置 → Connections 中的远程 gateway、SSH 主机和 Hermes Cloud 实例是独立的主机,拥有各自的进程;它们的 Bot 运行在那台机器上(参见上文跨机器的 Bot),而不是本机的 gateway 上。
关闭它
Bot 模式是一个内置的桌面插件。在 Capabilities → Plugins → Bots 中关闭它的 Desktop 开关——花名册、Routines 面板和输入框中间件会实时注销,无需重启。无论开关状态如何,你的 profile、会话和 cron 任务都不受影响;Bot 模式从不拥有你的数据,它只是负责渲染。
还有一个偏好设置可以把规范 Bot Chat 从常规侧边栏会话列表中隐藏,让它们只出现在 Bots 面板里。(这依赖核心的隐藏会话标记;在较旧的 gateway 上这些对话会照常保持可见。)
与 CLI 的对应关系
因为 Bot 本质上就是 profile,所以每个操作都有对应的终端命令:
| | 在 Bot 模式中 | 在终端中
| 与一个 Bot 对话 | hermes -p <bot> chat
| 一个 Bot 的文件、技能、记忆 | ~/.hermes/profiles/<bot>/
| 例行任务 | hermes cron list(任务名为 [bot:<name>] …)
| 创建 / 查看 profile | hermes profile create、hermes profile list
底层原理请参阅 Profiles,完整 CLI 参考请参阅 Profile Commands。
Hermes Agent — Docker
Docker 与 Hermes Agent 的交集有两种截然不同的方式:
- 在 Docker 中运行 Hermes — agent 本身在容器内运行(本页的主要内容)
- Docker 作为终端后端 — agent 在宿主机上运行,但将每条命令在单个持久化 Docker 沙箱容器中执行,该容器在工具调用、/new 和子 agent 之间保持存活,直至 Hermes 进程结束(参见 配置 → Docker 后端)
本页介绍选项 1。容器将所有用户数据(配置、API 密钥、会话、技能、记忆)存储在从宿主机挂载于 /opt/data 的单个目录中。镜像本身是无状态的,可通过拉取新版本进行升级而不会丢失任何配置。
镜像标签和更新
| | 标签 | 含义
| stable / latest | 通过完整稳定发布验收后提升的镜像
| main | 主分支开发构建
| X.Y.Z | 发布流程生成的版本化稳定镜像;精确部署使用摘要
镜像流程构建并测试 amd64 和 arm64。稳定发布复用已测试归档,不重新构建。
主分支推送只更新 main,不会提升 stable 或 latest。
应用代码位于 /opt/hermes,持久数据位于挂载的 /opt/data,两者分别管理。
快速开始
如果这是你第一次运行 Hermes Agent,请在宿主机上创建一个数据目录,并以交互方式启动容器以运行设置向导:
mkdir -p ~/.hermes
docker run -it --rm \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent setup这将进入设置向导,向导会提示你输入 API 密钥并将其写入 ~/.hermes/.env。你只需执行一次。强烈建议此时为 gateway 配置一个聊天系统。
以 gateway 模式运行
配置完成后,将容器作为持久化 gateway(Telegram、Discord、Slack、WhatsApp 等)在后台运行:
docker run -d \
--name hermes \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
-p 8642:8642 \
nousresearch/hermes-agent gateway run端口 8642 暴露 gateway 的 OpenAI 兼容 API 服务器和健康检查端点。如果你只使用聊天平台(Telegram、Discord 等),该端口是可选的;但如果你希望 dashboard 或外部工具访问 gateway,则必须开放。
注意:API 服务器需设置 API_SERVER_ENABLED=true 才会启用。若要在容器内将其暴露至 127.0.0.1 以外,还需设置 API_SERVER_HOST=0.0.0.0 和 API_SERVER_KEY(最少 8 个字符——可用 openssl rand -hex 32 生成)。示例:
docker run -d \
--name hermes \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
-p 8642:8642 \
-e API_SERVER_ENABLED=true \
-e API_SERVER_HOST=0.0.0.0 \
-e API_SERVER_KEY="$(openssl rand -hex 32)" \
-e API_SERVER_CORS_ORIGINS='*' \
nousresearch/hermes-agent gateway run在面向互联网的机器上开放任何端口都存在安全风险。除非你了解相关风险,否则不应这样做。
运行 dashboard
内置 Web dashboard 在同一容器内作为受 s6-rc 监管的服务与 gateway 并行运行。设置 HERMES_DASHBOARD=1 即可拉起它:
docker run -d \
--name hermes \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
-p 8642:8642 \
-p 9119:9119 \
-e HERMES_DASHBOARD=1 \
nousresearch/hermes-agent gateway runDashboard 由 s6 监管:若进程崩溃,s6-supervise 会在短暂退避后自动重启。Dashboard 的 stdout/stderr 会直接转发到 docker logs <container>;gateway 的主输出现在写入每个 profile 的 s6 日志文件,见下方的 per-profile 日志说明。
| | 环境变量 | 描述 | 默认值
| HERMES_DASHBOARD | 设为 1(或 true / yes)以启用受监管的 dashboard 服务 | (未设置——服务已注册但保持关闭)
| HERMES_DASHBOARD_HOST | dashboard HTTP 服务器的绑定地址 | 0.0.0.0
| HERMES_DASHBOARD_PORT | dashboard HTTP 服务器的端口 | 9119
| HERMES_DASHBOARD_INSECURE | 已弃用 / 空操作。 以前用于绕过鉴权门控;自 2026 年 6 月的安全加固起,它不再禁用鉴权。任何非回环绑定都必须配置鉴权提供方 | (被忽略——请改为配置提供方)
容器内的 dashboard 默认绑定 0.0.0.0,否则发布的 -p 9119:9119 端口将无法从宿主机访问。若你要把它限制在容器回环地址(例如 sidecar / 反向代理拓扑),请显式设置 HERMES_DASHBOARD_HOST=127.0.0.1。
当以下两项同时满足时,dashboard 的鉴权门控会自动启用:
- 绑定地址为非回环地址,且
- 注册了一个 DashboardAuthProvider 插件。
有三种内置方式可满足第二个条件:
- 用户名/密码 —— 最简单的自托管 / 局域网 / VPN 内部署方式:设置 HERMES_DASHBOARD_BASIC_AUTH_USERNAME + HERMES_DASHBOARD_BASIC_AUTH_PASSWORD(以及用于跨重启稳定 session 的 HERMES_DASHBOARD_BASIC_AUTH_SECRET)。不适合直接暴露到公网上。
- OAuth(Nous Portal) —— 适合托管/公网部署:设置 HERMES_DASHBOARD_OAUTH_CLIENT_ID 后,dashboard_auth/nous 提供者会自动激活。
- 自托管 OIDC —— 通过标准 OpenID Connect 接入你自己的身份提供商:设置 HERMES_DASHBOARD_OIDC_ISSUER + HERMES_DASHBOARD_OIDC_CLIENT_ID 后,dashboard_auth/self_hosted 提供者会激活。
无论选择哪种,调用方在访问受保护路由前都会先被重定向到登录页。完整说明见 Web Dashboard → 鉴权。
如果未注册提供者且绑定为非回环地址,dashboard 会在启动时
失败关闭,并给出指向缺失环境变量的具体错误信息。现在已不再
存在以无鉴权方式在公网绑定上提供 dashboard 的“逃生通道”:
HERMES_DASHBOARD_INSECURE=1 现在是一个已弃用的空操作(它会
打印告警并被忽略)。请改为配置鉴权提供方,或设置
HERMES_DASHBOARD_HOST=127.0.0.1 并通过 SSH 隧道 / Tailscale 访问。
为什么移除了 --insecure
无鉴权的公网 dashboard 是 2026 年 6 月 MCP 配置持久化攻击活动的入口:互联网扫描器访问到暴露的 dashboard(以及 OpenAI API 服务器),诱导 agent 植入 SSH 密钥后门。现在每个非回环绑定都强制启用鉴权门控。对于可信局域网 / homelab 主机,内置的用户名/密码提供方(HERMES_DASHBOARD_BASIC_AUTH_USERNAME + _PASSWORD)是满足该要求的零基础设施方式。
当独立的 dashboard 容器与宿主机共享 PID 与网络命名空间时(例如 network_mode: host,正如仓库自带的 docker-compose.yml 中的 dashboard 服务那样),是支持将 dashboard 作为独立容器运行的。其 gateway 存活检测需要与 gateway 进程共享 PID 命名空间,因此该限制仅适用于在隔离的 bridge 网络容器中、且未共享 PID 命名空间的 dashboard。
交互式运行(CLI 聊天)
对已有数据目录打开交互式聊天会话:
docker run -it --rm \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent或者,如果你已通过 Docker Desktop 等方式在运行中的容器内打开了终端,直接运行:
/opt/hermes/.venv/bin/hermes持久化卷
/opt/data 卷是所有 Hermes 状态的唯一数据来源。它映射到宿主机的 ~/.hermes/ 目录,包含:
| | 路径 | 内容
| .env | API 密钥和机密
| config.yaml | 所有 Hermes 配置
| SOUL.md | Agent 个性/身份
| sessions/ | 对话历史
| memories/ | 持久化记忆存储
| skills/ | 已安装的技能
| home/ | Hermes 工具子进程(git、ssh、gh、npm 及 skill CLI)的 per-profile HOME
| cron/ | 定时任务定义
| hooks/ | 事件 hook
| logs/ | 运行时日志
| skins/ | 自定义 CLI 皮肤
不可变安装树
在托管/发布的 Docker 镜像中,/opt/hermes 是安装好的应用树。它由 root 拥有,并且对运行时的 hermes 用户只读,因此 agent 回合、gateway 会话、dashboard 操作以及普通的 docker exec hermes hermes ... 命令都不能原地修改核心源码、打包的 .venv、node_modules 或 TUI bundle。
所有可变的 Hermes 状态都应位于 /opt/data 下:配置、.env、profiles、skills、memories、sessions、logs、dashboard 上传、plugins 以及其他用户管理的文件。官方镜像还会阻止在运行时向不可变的 /opt/hermes 树写入 .pyc 或执行 Hermes 的懒安装依赖流程。
如果运维人员确实需要修复或检查 /opt/data 之外的文件,请有意识地使用 root shell。hermes shim 默认会把 docker exec hermes hermes ... 降回运行时用户;只有在你明确需要 root 语义时,才临时设置 HERMES_DOCKER_EXEC_AS_ROOT=1。
某些 skill CLI 会把凭据写到 ~ 下,因此在官方 Docker 布局里要针对子进程 HOME 初始化,而不是只针对数据卷根目录。例如 xurl skill 会把 OAuth 状态存到 ~/.xurl;在容器里这对应 /opt/data/home/.xurl,因此手动认证时应使用 HOME=/opt/data/home xurl auth status 之类的调用。
注意
切勿同时对同一数据目录运行两个 Hermes gateway 容器——会话文件和记忆存储不支持并发写入。
多 profile 支持
Hermes 支持多个 profile——独立的 ~/.hermes/ 子目录,让你可以从单个安装运行独立的 agent(不同的 SOUL、skills、memory、sessions、credentials)。在官方 Docker 镜像内,s6 监管树把每个 profile 当作一等受监管服务,因此推荐部署方式是:一个容器承载多个 profile。
每个通过 hermes profile create <name> 创建的 profile 都会获得:
- 一个专用的 s6 服务槽位 /run/service/gateway-<name>/,运行时动态注册,无需重建镜像。
- 崩溃后的自动重启,由 s6-supervise 管理退避。
- 每个 profile 独立的轮转日志:${HERMES_HOME}/logs/gateways/<name>/current。
- 跨容器重启的状态持久化:启动协调器会读取该 profile 的 gateway_state.json,仅在上次记录状态为 running 时自动拉起。
容器内生命周期命令与宿主机上一致:
# 创建 profile —— 同时注册 gateway-<name> s6 槽位
docker exec hermes hermes profile create coder
# 启停/重启 —— 底层分发给 s6-svc
docker exec hermes hermes -p coder gateway start
docker exec hermes hermes -p coder gateway stop
docker exec hermes hermes -p coder gateway restart
# 状态 —— 容器内会显示 `Manager: s6 (container supervisor)`
docker exec hermes hermes -p coder gateway status若第二个 profile 也要暴露 OpenAI 兼容 API server,请在该 profile 自己的 .env 中设置不同的 API_SERVER_PORT,然后重启该 profile 的 gateway;不要把端口放进容器级 environment:,否则所有 profile 都会争抢同一个端口。更底层的监管细节见后文的 Per-profile gateway 监管。
环境变量转发
API 密钥从容器内的 /opt/data/.env 读取。你也可以直接传递环境变量:
docker run -it --rm \
-v ~/.hermes:/opt/data \
-e ANTHROPIC_API_KEY="sk-ant-..." \
-e OPENAI_API_KEY="sk-..." \
nousresearch/hermes-agent直接传入的 -e 标志会覆盖 .env 中的值。这对于不希望将密钥写入磁盘的 CI/CD 或密钥管理器集成非常有用。
寻找 Docker 作为终端后端的说明?
本页介绍在 Docker 内运行 Hermes 本身。如果你希望 Hermes 在 Docker 沙箱容器内执行 agent 的 terminal / execute_code 调用(每个 Hermes 进程对应一个持久容器),那是另一个配置块——terminal.backend: docker 加上 terminal.docker_image、terminal.docker_volumes、terminal.docker_forward_env、terminal.docker_run_as_host_user 和 terminal.docker_extra_args。完整配置请参见 配置 → Docker 后端。
Docker Compose 示例
对于同时运行 gateway 和 dashboard 的持久化部署,使用 docker-compose.yaml 更为方便:
services:
hermes:
image: nousresearch/hermes-agent:latest
container_name: hermes
restart: unless-stopped
command: gateway run
ports:
- "8642:8642" # gateway API
- "9119:9119" # dashboard(仅在 HERMES_DASHBOARD=1 时生效)
volumes:
- ~/.hermes:/opt/data
environment:
- HERMES_DASHBOARD=1
# 取消注释以直接转发特定环境变量而非使用 .env 文件:
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
# - OPENAI_API_KEY=${OPENAI_API_KEY}
# - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
deploy:
resources:
limits:
memory: 4G
cpus: "2.0"使用 docker compose up -d 启动,使用 docker compose logs -f 查看日志。Dashboard 的 stdout/stderr 会直接出现在这里;gateway 主日志则写入每个 profile 的 s6 日志文件,见下方的 Per-profile gateway 监管。
资源限制
Hermes 容器需要适量资源。推荐最低配置:
| | 资源 | 最低 | 推荐
| 内存 | 1 GB | 2–4 GB
| CPU | 1 核 | 2 核
| 磁盘(数据卷) | 500 MB | 2+ GB(随会话/技能增长)
浏览器自动化(Playwright/Chromium)是最耗内存的功能。如果不需要浏览器工具,1 GB 即可。启用浏览器工具时,请至少分配 2 GB。
在 Docker 中设置限制:
docker run -d \
--name hermes \
--restart unless-stopped \
--memory=4g --cpus=2 \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway runDockerfile 说明
官方镜像基于 Debian 13.4,包含:
- 按提交的 uv.lock 同步的 Python 3.14 环境,然后无依赖地安装 Hermes 源码。
- 选定的 extras:all、messaging、otlp、anthropic、bedrock、azure-identity 和 matrix,不是 --all-extras。
- 从摘要固定的 Node 镜像提供的 Node.js 26 和 npm。
- PM 固定版本的 uv、完整 Chromium、FFmpeg 和 ripgrep,位于 /opt/hermes/tools。
- 系统 Git、OpenSSH、Docker CLI 和 Chromium 所需共享库。
- 预构建的 TUI/dashboard 和 Photon sidecar 依赖,以及 s6-overlay。
Chromium 由 PM 准备,不使用 npx playwright install。
实际可执行路径记录在 /etc/hermes/agent-browser-executable-path。
PLAYWRIGHT_BROWSERS_PATH 指向 /opt/hermes/tools,不在数据卷内。
所有镜像(包括不带 -desktop 后缀的标签)都携带完整 Chromium,而不是 Playwright
更轻量的 headless shell:同一个固定且校验过的浏览器同时服务无头浏览和有界面的
Bot Screen 会话。代价是镜像体积:完整版比早期镜像附带的 headless shell 更大。
可选后端 SDK(Edge TTS、Firecrawl、Exa、平台适配器、插件依赖)在首次使用时安装到
/opt/data/installs 下的 PM 依赖代,容器重建和镜像更新后仍然保留;镜像自带的
/opt/hermes/.venv 永不修改。每次启动时,容器在服务启动前按新镜像的锁文件重新解析
已记录的选择;若失败(例如离线),则使用镜像自带环境启动,并保留已记录的 extras,
留待下次启动或安装时重建。设置 security.allow_lazy_installs: false 可拒绝按需安装。
旧 lazy-packages overlay 不再使用。
构建来源记录在 /etc/hermes/image-provenance.json,构建戳记位于 /opt/hermes/install-stamp.json。
没有戳记的本地构建报告未知版本,不猜测提交。hermes update 不修改镜像所有的代码,应用更新需替换镜像。
入口点是 docker/entrypoint-dispatch.sh。正常 Docker/Podman 中它占有 PID 1,转交给 s6 的 /init。
若平台已有 PID-1 init,则直接运行 stage2 和主包装器;命令仍可运行,但没有 s6 监管的 dashboard 或各 profile gateway。
PID-1 路径先准备数据卷和配置,重建各 profile 的 gateway 服务槽,然后运行主命令。
不要绕过这些入口步骤,否则会失去权限处理和服务监管。
主程序及受监管服务以 hermes 用户运行,避免在 /opt/data 中留下 root 所有的文件。
Per-profile gateway 监管
在容器内,每个通过 hermes profile create <name> 创建的 profile 都会自动在 /run/service/gateway-<name>/ 注册一个受 s6 监管的 gateway 服务。你在宿主机上运行的生命周期命令在此同样适用:
hermes profile create coder # 注册 gateway-coder s6 槽
hermes -p coder gateway start # s6-svc -u → 受监管的 gateway
hermes -p coder gateway stop # s6-svc -d → 服务停止
hermes -p coder gateway restart # s6-svc -t → 向 supervisor 发送 SIGTERM
hermes profile delete coder # 拆除 s6 槽相比 pre-s6 镜像的监管优势:
- Gateway 崩溃后由 s6-supervise 在约 1 秒退避后自动重启。
- Dashboard 崩溃后自动重启(设置 HERMES_DASHBOARD=1 以启动)。
- docker restart 保留运行中的 gateway:cont-init 协调器读取 $HERMES_HOME/profiles/<name>/gateway_state.json,若上次记录状态为 running 则恢复该槽。已停止的 gateway 保持停止状态。
- 各 profile 的 gateway 日志持久化于 $HERMES_HOME/logs/gateways/<profile>/current(由 s6-log 轮转),协调器的操作记录在每次启动时追加到 $HERMES_HOME/logs/container-boot.log。
在容器内执行 hermes status 会显示 Manager: s6 (container supervisor)。使用 /command/s6-svstat /run/service/gateway-<name> 查看原始 supervisor 状态(注意 /command/ 仅在监管树进程的 PATH 中;从 docker exec 调用时请传入绝对路径)。
升级
拉取最新镜像并重建容器。你的数据目录不受影响。
docker pull nousresearch/hermes-agent:latest
docker rm -f hermes
docker run -d \
--name hermes \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run或使用 Docker Compose:
docker compose pull
docker compose up -d技能与凭据文件
当使用 Docker 作为执行环境时(不是上述方法,而是 agent 在 Docker 沙箱内运行命令——参见 配置 → Docker 后端),Hermes 为所有工具调用复用单个长期运行的容器,并自动将技能目录(~/.hermes/skills/)和技能声明的所有凭据文件以只读卷的形式绑定挂载到该容器中。技能脚本、模板和引用在沙箱内无需手动配置即可使用,由于容器在 Hermes 进程的整个生命周期内持续存在,你安装的任何依赖或写入的文件都会在下次工具调用时保留。
SSH 和 Modal 后端也会进行相同的同步——技能和凭据文件在每次命令执行前通过 rsync 或 Modal mount API 上传。
在容器中安装更多工具
官方镜像预装了一套精选工具(参见 Dockerfile 说明),但并非 agent 可能需要的每个工具都已预装。以下是五种推荐方式,按工作量和持久性递增排列。
npm 或 Python 工具——使用 npx 或 uvx
对于发布到 npm 或 PyPI 的任何工具,指示 Hermes 通过 npx(npm)或 uvx(Python)运行,并将该命令记入其持久记忆。如果工具需要配置文件或凭据,指示其将这些文件放在 /opt/data 下(如 /opt/data/<tool>/config.yaml)。
依赖按需获取并在容器生命周期内缓存。写入 /opt/data 的配置在容器重启后仍然存在,因为它位于绑定挂载的宿主机目录上。包缓存本身在 docker rm 后会重建,但 npx 和 uvx 会在下次运行工具时透明地重新获取。
其他工具(apt 包、二进制文件)——安装并记住
对于 npm 或 PyPI 之外的工具——apt 包、预构建二进制文件、镜像中未包含的语言运行时——指示 Hermes 如何安装(如 apt-get update && apt-get install -y <package>),并告知它记住该安装命令。工具在容器剩余生命周期内持续可用,Hermes 在容器重启后下次需要该工具时会重新运行安装命令。
这种方式适合安装快速且偶尔使用的工具。对于频繁使用的工具,建议采用下一种方式。
持久安装——构建派生镜像
当工具必须在每次容器启动时立即可用且无需重新安装延迟时,构建一个继承自 nousresearch/hermes-agent 并在层中安装该工具的新镜像:
FROM nousresearch/hermes-agent:latest
USER root
RUN apt-get update \
&& apt-get install -y --no-install-recommends <your-package> \
&& rm -rf /var/lib/apt/lists/*
USER hermes构建并替换官方镜像使用:
docker build -t my-hermes:latest .
docker run -d \
--name hermes \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
-p 8642:8642 \
my-hermes:latest gateway run入口点脚本和 /opt/data 语义原样继承,本页其余内容仍然适用。拉取更新的上游 nousresearch/hermes-agent 时记得重新构建镜像。
复杂工具或多服务栈——运行 sidecar 容器
对于自带服务(数据库、Web 服务器、队列、无头浏览器集群)或过于庞大而不适合放在 Hermes 容器内的工具,将其作为独立容器运行在共享 Docker 网络上。Hermes 通过容器名称访问 sidecar,与访问本地推理服务器的方式相同(参见 连接本地推理服务器)。
services:
hermes:
image: nousresearch/hermes-agent:latest
container_name: hermes
restart: unless-stopped
command: gateway run
ports:
- "8642:8642"
volumes:
- ~/.hermes:/opt/data
networks:
- hermes-net
my-tool:
image: example/my-tool:latest
container_name: my-tool
restart: unless-stopped
networks:
- hermes-net
networks:
hermes-net:
driver: bridge在 Hermes 容器内,sidecar 可通过 http://my-tool:<port> 访问(或其提供的任何协议)。这种模式使每个服务的生命周期、资源限制和升级节奏保持独立,避免因单个工具的依赖而使 Hermes 镜像臃肿。
广泛有用的工具——提交 issue 或 pull request
如果某个工具可能对大多数 Hermes Agent 用户有用,考虑将其贡献到上游,而不是在私有派生镜像中维护。在 hermes-agent 仓库提交 issue 或 pull request,描述该工具及其使用场景。被纳入官方镜像的工具惠及所有用户,并避免了维护下游 fork 的开销。
连接本地推理服务器(vLLM、Ollama 等)
在 Docker 中运行 Hermes 且推理服务器(vLLM、Ollama、text-generation-inference 等)也在宿主机或另一个容器中运行时,网络配置需要额外注意。
Docker Compose(推荐)
将两个服务放在同一 Docker 网络上。这是最可靠的方式:
services:
vllm:
image: vllm/vllm-openai:latest
container_name: vllm
command: >
--model Qwen/Qwen2.5-7B-Instruct
--served-model-name my-model
--host 0.0.0.0
--port 8000
ports:
- "8000:8000"
networks:
- hermes-net
deploy:
resources:
reservations:
devices:
- capabilities: [gpu]
hermes:
image: nousresearch/hermes-agent:latest
container_name: hermes
restart: unless-stopped
command: gateway run
ports:
- "8642:8642"
volumes:
- ~/.hermes:/opt/data
networks:
- hermes-net
networks:
hermes-net:
driver: bridge然后在 ~/.hermes/config.yaml 中,使用容器名称作为主机名:
model:
provider: custom
model: my-model
base_url: http://vllm:8000/v1
api_key: "none"关键点
- 使用容器名称(vllm)作为主机名——而非 localhost 或 127.0.0.1,它们指向 Hermes 容器本身。
- model 值必须与传给 vLLM 的 --served-model-name 一致。
- 将 api_key 设为任意非空字符串(vLLM 要求该请求头,但默认不验证其值)。
- base_url 末尾不要加斜杠。
独立 Docker run(无 Compose)
如果推理服务器直接在宿主机上运行(不在 Docker 中),在 macOS/Windows 上使用 host.docker.internal,在 Linux 上使用 --network host:
macOS / Windows:
docker run -d \
--name hermes \
-v ~/.hermes:/opt/data \
-p 8642:8642 \
nousresearch/hermes-agent gateway run# config.yaml
model:
provider: custom
model: my-model
base_url: http://host.docker.internal:8000/v1
api_key: "none"Linux(host 网络):
docker run -d \
--name hermes \
--network host \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run# config.yaml
model:
provider: custom
model: my-model
base_url: http://127.0.0.1:8000/v1
api_key: "none"使用 --network host 时,-p 标志会被忽略——所有容器端口直接暴露在宿主机上。
验证连通性
从 Hermes 容器内部确认推理服务器可达:
docker exec hermes curl -s http://vllm:8000/v1/models你应该看到列出已服务模型的 JSON 响应。如果失败,请检查:
- 两个容器是否在同一 Docker 网络上(docker network inspect hermes-net)
- 推理服务器是否监听 0.0.0.0 而非 127.0.0.1
- 端口号是否匹配
Ollama
Ollama 的配置方式相同。如果 Ollama 在宿主机上运行,使用 host.docker.internal:11434(macOS/Windows)或 127.0.0.1:11434(Linux 使用 --network host)。如果 Ollama 在同一 Docker 网络的独立容器中运行:
model:
provider: custom
model: llama3
base_url: http://ollama:11434/v1
api_key: "none"故障排查
容器立即退出
检查日志:docker logs hermes。常见原因:
- .env 文件缺失或无效——先以交互方式运行以完成设置
- 开放端口时存在端口冲突
"Permission denied" 错误
容器的 stage2 hook 通过 s6-setuidgid 在每个受监管的服务内将权限降至非 root 用户 hermes(UID 10000)。如果宿主机的 ~/.hermes/ 由不同 UID 拥有,请设置 HERMES_UID/HERMES_GID 以匹配宿主机用户,或确保数据目录可写:
chmod -R 755 ~/.hermes浏览器工具无法使用
Playwright 需要共享内存。在 Docker run 命令中添加 --shm-size=1g:
docker run -d \
--name hermes \
--shm-size=1g \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run网络问题后 gateway 无法重连
--restart unless-stopped 标志可处理大多数瞬时故障。如果 gateway 卡住,重启容器:
docker restart hermes检查容器健康状态
docker logs --tail 50 hermes # 最近日志
docker run -it --rm nousresearch/hermes-agent:latest version # 验证版本
docker stats hermes # 资源使用情况Local Models
Hermes can run open models entirely on your own machine. It downloads and
manages the inference engine (llama.cpp), picks the right build of each
model for your hardware, and handles memory so you never configure context
sizes, GPU layers, or quantization. You pick a model; Hermes does the rest.
Nothing leaves your computer: no account, no API key, and no network access
after a model is downloaded.
Desktop availability and downloads
Local Models is on by default in the desktop app on macOS, Windows, and Linux
(x64 and arm64), on every release channel. On other platforms, launch the app
with --local to show it. A runtime can already be bundled; its absence
triggers the managed-tool install path, not an arbitrary latest llama.cpp
download.
Use Pause and Resume on engine installs, engine updates, catalog models,
Hugging Face downloads, and quickstart. Paused jobs remain visible when you
leave and reopen Local Models. Pause stops the transfer at a chunk boundary;
unpacking, verification, and server activation are separate phases.
PM downloads every engine component, including CUDA runtime DLLs, from the
URLs and SHA-256 pins in pm/lock.json. Model weights, split GGUF parts,
vision projectors, and draft models use the same downloader. Byte progress
covers the whole download plan, including completed files and resumed ranges.
Quickstart labels engine and model progress as separate stages.
Partial downloads use PM's writable cache/partials area, outside a signed
app package. Range-capable hosts resume missing bytes. Hosts without Range
support restart the current file. Completed files are reused.
Getting started
- Open Settings → Providers → Local Models (or choose Run models
locally during onboarding).
- Click Install runtime. Hermes downloads the official llama.cpp
build for your hardware (a few hundred MB), verifies it, and keeps it
updated.
- Pick a model from the catalog and click Download.
- Click Use. New chats now run on the local model.
That's the whole flow. The server starts and stops with Hermes, restarts
survive app restarts, and switching back to a cloud provider is one click
in the model picker.
From a terminal, hermes model → Local models runs the same setup: it
lists the catalog priced for your machine (★ marks the recommendation),
installs the pinned engine, downloads the model with live progress, starts
the server, and makes the model your default. Models already on disk are
offered too. Ctrl+C pauses a download; choosing the model again resumes it.
How Hermes picks what to download
Every model in the catalog is priced against your machine before you
download anything. Each row shows:
- Memory fit — green (Fits your GPU: runs entirely in GPU memory),
amber (Uses system RAM: works, but slower), or red (Too big for this
machine).
- Context — the window the model starts with and the maximum it can
grow to.
- The download size of the build selected for your hardware.
Models ship in several quality grades (quantizations). Hermes picks the
highest-quality build that runs fully on your GPU; machines with less
memory get a more compact build of the same model with the same
guarantees. Below 4-bit the quality loss is too severe, so Hermes never
offers builds smaller than that — a machine that can't run the 4-bit
build spilled to system RAM simply can't run that model.
Models that don't fit stay visible with the reason, so you always know
what a hardware upgrade would unlock.
Hermes recommends the highest-quality model that runs entirely in GPU or
unified memory at a predicted 20 tokens per second or more. A machine's
maker can set a different default, and Hermes recommends that model
whenever it fits. When no model reaches the floor, Hermes recommends none,
and you can still choose any model that fits.
How memory management works
Local models live or die by memory placement, so Hermes manages it
end-to-end and exposes no knobs:
- Models start at a context window that fully fits your GPU and grow
toward their native maximum as your conversation needs more room. You
may see "Context window grown" in the status feed during long sessions
— that's the window expanding, not an error.
- Every recommended model gets at least a 64K context window. When a
model is larger than your GPU's memory, Hermes deliberately places the
overflow in system RAM in the order that hurts least (expert weights
first, never the attention cache), trading some speed to protect the
context guarantee.
- Memory fit includes the launch configuration, not just the model file:
context state, runtime buffers, the vision projector, and MTP buffers all
count. For multi-token prediction (MTP), Hermes uses smaller batches when
larger batches would spill at the same context window. MTP stays enabled.
The same calculation runs when a grown window is restored after restart.
- Conversation compression follows a growth check. If a larger window
cannot fit, generation is too slow, or the native maximum is reached,
Hermes compresses instead of claiming a window the server did not receive.
- Idle models are unloaded after 15 minutes to free GPU memory; they
reload automatically on the next message.
The status bar
Right-click the status bar and enable System resources to see live GPU
utilization, GPU memory, and RAM while local models run. The context meter
always reflects the window the model is actually running with.
Finding more models
The catalog is a curated starting point, not a boundary. The Find more
models section on the same page searches all of Hugging Face:
- Results show download counts and a per-file fit check sized to your
machine, so you know before downloading whether a build runs fully on
your GPU.
- Anything you download behaves exactly like a catalog model — Hermes
reads the model file itself to pick its context window and memory
placement. The only difference: community models don't carry our
"validated" testing badge.
- Already have a .gguf file on disk? Add model file links it into
your library without copying it (the original stays where it is), and
it's usable immediately.
Using your own llama-server
If a llama-server is already running on your machine, Hermes detects it
and uses it instead of starting its own. Point a custom endpoint at any
OpenAI-compatible server for full manual control — the managed runtime is
a default, not a requirement. You can enter the server root (for example
http://127.0.0.1:8080) or the full /v1 URL: the endpoint test tries
both and saves the variant that actually served /models, so chat
requests go to the same prefix the model list came from. For manual setups (Ollama, MLX, custom
builds, headless CLI machines), see
Run Hermes Locally with Ollama and
Run Local LLMs on Mac.
Configuration
The managed runtime is controlled by the local_runtime section of
config.yaml. The desktop UI writes these values for you; they're
documented for CLI and headless use:
local_runtime:
enabled: false # true = start the managed server with Hermes.
# The desktop "Use" button sets this automatically.
backend: auto # auto | cuda | metal | vulkan | hip | cpu
detect_ports: [8081] # extra ports to probe for a llama-server you run
# yourself (the default probe is :8080 only)Running llama-server yourself on a fixed port works with the same
model.provider: llamacpp selection — either list the port in
local_runtime.detect_ports, or define the endpoint explicitly under
providers: (an explicit entry wins over server detection):
providers:
llamacpp:
base_url: http://127.0.0.1:8081/v1
model: my-modelThe /model → Local picker row and provider: llamacpp resolve to that
server; with no server reachable the error names the local runtime ("the local
model server isn't running") instead of an unknown-provider or missing-API-key
message.
Engine versions come only from PM's lockfile, not a local_runtime.tag
override. Boot uses an installed PM engine without downloading. When a new
pin is available, install it with the desktop update button.
Models live in the machine-shared models/ directory. Engine binaries live
in PM's store; runtimes/llamacpp/ holds mutable presets and server state.
Selecting a local model as your main model uses the standard
model.provider: llamacpp + model.default settings.
Requirements and limits
- Windows: CUDA on supported NVIDIA targets, Vulkan on x64, or CPU.
Linux: CUDA on x64 and arm64 NVIDIA (needs a CUDA 13 driver), Vulkan, or CPU.
macOS: Metal or CPU. HIP/ROCm is an explicit choice on supported x64
targets. Unsupported backend/target pairs fail before any download.
- backend: auto prefers CUDA for NVIDIA and Vulkan for AMD and Intel GPUs,
then falls back to the builds listed above for your platform (CPU when no
GPU is found). Behind a Vulkan or HIP engine, a discrete card is sized from
its own memory (system RAM is spill space) and each launch subtracts what
other programs hold on it; an integrated GPU shares system RAM and is sized
from that. Quickstart installs the engine before choosing the model, so the
pick reflects the card.
- A GPU with 8 GB+ of memory runs the small catalog models comfortably;
16 GB+ runs the 27–35B models at high quality.
- Model completeness is checked against the server's response, not catalog
size estimates. Interrupted transfers retain partials for resume; incomplete
files are not published. Engine archives are SHA-256 verified before use.
- Deleting a model removes every file it staged, including vision
adapters and speculative-decoding companions.
检查点与 /rollback
Hermes Agent 可以在破坏性操作之前自动为你的项目创建快照,并通过单条命令恢复。检查点在 v2 中为按需启用——大多数用户从不使用 /rollback,且影子存储(shadow-store)随时间增长不可忽视,因此默认关闭。
在会话中通过 --checkpoints 启用检查点:
hermes chat --checkpoints或在 ~/.hermes/config.yaml 中全局启用:
checkpoints:
enabled: true此安全机制由内部**检查点管理器(Checkpoint Manager)**驱动,它在 ~/.hermes/checkpoints/store/ 下维护一个共享的影子 git 仓库——你真实项目的 .git 永远不会被触碰。Agent 操作的所有项目共享同一个存储,因此 git 的内容寻址对象数据库可以跨项目、跨轮次去重。
触发检查点的条件
检查点在以下操作之前自动创建:
- 文件工具 — write_file 和 patch
- 破坏性终端命令 — rm、rmdir、cp、install、mv、sed -i、truncate、dd、shred、输出重定向(>),以及 git reset/clean/checkout
Agent 每个目录每轮最多创建一个检查点,因此长时间运行的会话不会产生大量快照。
快速参考
会话内斜杠命令:
| | 命令 | 说明
| /rollback | 列出所有检查点及变更统计
| /rollback <N> | 恢复到检查点 N(同时撤销最后一轮对话)
| /rollback diff <N> | 预览检查点 N 与当前状态的差异
| /rollback <N> <file> | 从检查点 N 恢复单个文件
在会话外检查和管理存储的 CLI 命令:
| | 命令 | 说明
| hermes checkpoints | 显示总大小、项目数量及各项目明细
| hermes checkpoints status | 与裸 checkpoints 相同
| hermes checkpoints list | status 的别名
| hermes checkpoints prune | 强制执行清理:删除孤立/过期条目、GC、强制大小上限
| hermes checkpoints clear | 清除整个检查点库(会先询问确认)
| hermes checkpoints clear-legacy | 仅删除 v1 迁移留下的 legacy-* 归档
检查点的工作原理
概要流程:
- Hermes 检测到工具即将修改工作树中的文件。
- 每轮对话(每个目录)执行一次:
- 为该文件解析合理的项目根目录。
- 初始化或复用位于 ~/.hermes/checkpoints/store/ 的单一共享影子存储。
- 写入每个项目的索引,构建树对象,并提交到每个项目的引用(refs/hermes/<project-hash>)。
- 这些每项目引用构成可通过 /rollback 检查和恢复的检查点历史。
配置
在 ~/.hermes/config.yaml 中配置:
checkpoints:
enabled: false # 主开关(默认:false — 按需启用)
max_snapshots: 20 # 每个项目的最大检查点数(通过引用重写 + gc 强制执行)
max_total_size_mb: 500 # 存储总大小硬上限;超出时丢弃最旧的提交
max_file_size_mb: 10 # 跳过大于此值的单个文件
# 自动维护(默认开启):启动时扫描 ~/.hermes/checkpoints/,
# 删除 last_touch 超过 retention_days 的条目。通过 .last_prune
# 标记控制,最多每 min_interval_hours 运行一次。此扫描不会删除
# “孤立”条目(工作目录未找到)——启动时工作目录缺失含义模糊
# (项目被删除,还是外部卷/网络共享/VPN 尚未挂载),因此孤立项
# 清理只能通过下方的 `hermes checkpoints prune` 命令显式触发,
# 并会要求确认。
auto_prune: true
retention_days: 7
min_interval_hours: 24完全禁用:
checkpoints:
enabled: false
auto_prune: false当 enabled: false 时,检查点管理器为空操作,不会尝试任何 git 操作。当 auto_prune: false 时,存储持续增长,直到你手动运行 hermes checkpoints prune。
列出检查点
在 CLI 会话中:
/rollbackHermes 返回带有变更统计的格式化列表:
📸 Checkpoints for /path/to/project:
1. 4270a8c 2026-03-16 04:36 before patch (1 file, +1/-0)
2. eaf4c1f 2026-03-16 04:35 before write_file
3. b3f9d2e 2026-03-16 04:34 before terminal: sed -i s/old/new/ config.py (1 file, +1/-1)
/rollback <N> restore to checkpoint N
/rollback diff <N> preview changes since checkpoint N
/rollback <N> <file> restore a single file from checkpoint N从 Shell 检查存储
hermes checkpoints示例输出:
Checkpoint base: /home/you/.hermes/checkpoints
Total size: 142.3 MB
store/ 138.1 MB
legacy-* 4.2 MB
Projects: 12
WORKDIR COMMITS LAST TOUCH STATE
/home/you/code/hermes-agent 20 2h ago live
/home/you/code/experiments/rl-runner 8 1d ago live
/home/you/code/old-prototype 3 9d ago orphan
...
Legacy archives (1):
legacy-20260506-050616 4.2 MB
Clear with: hermes checkpoints clear-legacy强制执行完整清理(忽略 24h 幂等性标记):
hermes checkpoints prune --retention-days 3 --max-size-mb 200使用 /rollback diff 预览变更
在执行恢复之前,预览自某个检查点以来的变更:
/rollback diff 1此命令显示 git diff 统计摘要,随后是完整差异内容。
使用 /rollback 恢复
/rollback 1Hermes 在后台执行:
- 验证目标提交存在于影子存储中。
- 对当前状态创建回滚前快照,以便之后可以"撤销撤销"。
- 恢复工作目录中被跟踪的文件。
- 撤销最后一轮对话,使 Agent 的上下文与恢复后的文件系统状态一致。
单文件恢复
从检查点恢复单个文件,不影响目录中的其他内容:
/rollback 1 src/broken_file.py安全与性能保障
- Git 可用性 — 若 PATH 中找不到 git,检查点功能将透明地禁用。
- 目录范围 — Hermes 跳过过于宽泛的目录(根目录 /、家目录 $HOME)。
- 仓库大小 — 超过 50,000 个文件的目录将被跳过。
- 单文件大小上限 — 大于 max_file_size_mb(默认 10 MB)的文件不纳入快照,防止意外将数据集、模型权重或生成的媒体文件纳入存储。
- 存储总大小上限 — 当存储超过 max_total_size_mb(默认 500 MB)时,按轮询方式丢弃每个项目最旧的提交,直到低于上限。
- 真实剪枝 — max_snapshots 通过重写每项目引用并随后运行 git gc --prune=now 来强制执行,避免松散对象积累。
- 无变更快照 — 若自上次快照以来没有变更,则跳过本次检查点。
- 非致命错误 — 检查点管理器内部的所有错误均以 debug 级别记录;工具继续正常运行。
检查点的存储位置
~/.hermes/checkpoints/
├── store/ # 单一共享裸 git 仓库
│ ├── HEAD, objects/ # git 内部结构(跨项目共享)
│ ├── refs/hermes/<hash> # 每项目分支尖端
│ ├── indexes/<hash> # 每项目 git 索引
│ ├── projects/<hash>.json # workdir + created_at + last_touch
│ └── info/exclude
├── .last_prune # 自动剪枝幂等性标记
└── legacy-<ts>/ # 归档的 v2 之前每项目影子仓库每个 <hash> 由工作目录的绝对路径派生。通常无需手动操作这些文件——使用 hermes checkpoints status / prune / clear 即可。
从 v1 迁移
在 v2 重写之前,每个工作目录在 ~/.hermes/checkpoints/<hash>/ 下拥有独立的完整影子 git 仓库。该布局无法跨项目去重对象,且剪枝器有已知的空操作问题——存储会无限增长。
首次运行 v2 时,所有 v2 之前的影子仓库将被移入 ~/.hermes/checkpoints/legacy-<timestamp>/,使新的单存储布局从干净状态开始。旧的 /rollback 历史仍可通过 git 手动检查 legacy 归档访问;确认不再需要后,运行:
hermes checkpoints clear-legacy以回收空间。Legacy 归档也会在 retention_days 到期后由 auto_prune 清理。
最佳实践
- 仅在需要时启用检查点 — 使用 hermes chat --checkpoints 或在配置文件中设置 enabled: true。
- 恢复前使用 /rollback diff 预览 — 查看将发生的变更,选择正确的检查点。
- 使用 /rollback 而非 git reset 来撤销 Agent 驱动的变更。
- 定期检查 hermes checkpoints status(如果你经常使用检查点)——显示哪些项目处于活跃状态以及存储占用情况。
- 结合 Git worktree 使用以获得最高安全性 — 将每个 Hermes 会话保持在独立的 worktree/分支中,以检查点作为额外保障层。
关于在同一仓库中并行运行多个 Agent,请参阅 Git worktrees 指南。
Import from Other Agents
hermes import-agent imports your existing Claude Code or OpenAI Codex CLI setup into Hermes with one command. It follows the same preview-first pattern as hermes claw migrate: you always see a per-item plan before anything is written, and --dry-run never touches disk.
hermes import-agent # auto-detect ~/.claude or ~/.codex
hermes import-agent claude-code # import from ~/.claude
hermes import-agent codex # import from ~/.codex
hermes import-agent claude-code --dry-run # preview only
hermes import-agent codex --source /path/to/.codex # custom location
hermes import-agent claude-code --overwrite --yes # replace conflicts, skip promptsWhat gets imported
Claude Code (~/.claude)
| | Claude Code | Hermes
| CLAUDE.md (global instructions) | Memory entries in ~/.hermes/memories/MEMORY.md
| settings.json → permissions.allow (Bash(...) rules) | command_allowlist in config.yaml
| settings.json → permissions.deny (Bash(...) rules) | approvals.deny in config.yaml
| mcpServers (from ~/.claude.json and settings.json) | mcp_servers in config.yaml
| skills/<name>/ (dirs with SKILL.md) | ~/.hermes/skills/claude-code-imports/<name>/
| commands/*.md (slash commands) | Skipped with a note — convert them into skills
Claude's Bash(npm run test:*) prefix rules become npm run test* globs. Non-Bash permission rules (Read(...), WebFetch, ...) gate Claude-specific tools and are reported as unmapped rather than imported.
Codex CLI (~/.codex)
| | Codex CLI | Hermes
| AGENTS.md (global instructions) | Memory entries in ~/.hermes/memories/MEMORY.md
| config.toml → [mcp_servers.*] | mcp_servers in config.yaml
| memories/*.md | Memory entries in ~/.hermes/memories/MEMORY.md
| skills/<name>/ (dirs with SKILL.md) | ~/.hermes/skills/codex-imports/<name>/
What is never imported
API keys and credentials. Credential files (~/.claude/.credentials.json, ~/.codex/auth.json) are never read, and MCP server environment variables or headers with secret-looking names (*_TOKEN, *_API_KEY, Authorization, ...) are stripped and listed in the report so you can re-add them deliberately. Run hermes setup to configure providers, or add secrets to ~/.hermes/.env.
Behavior notes
- Preview first, always. The command prints the full plan before applying; in non-interactive sessions it stops at the preview unless you pass --yes.
- Merges, not replaces. Memory entries are deduplicated against your existing MEMORY.md; allowlist/denylist patterns merge with what's already in config.yaml.
- Conflicts are skipped by default. An MCP server or skill that already exists in Hermes is reported as a conflict; pass --overwrite to replace it.
- Malformed files don't abort the run. A broken settings.json or config.toml becomes a per-item error in the report while everything else still imports.
- Coming from OpenClaw instead? Use hermes claw migrate.
Keeping imports in sync
Every successful import registers its source (and the digest of everything it read) in ~/.hermes/import-sync.json. When the other agent's setup changes later — new skills, edited CLAUDE.md/AGENTS.md, added MCP servers — pull the changes in with:
hermes import-agent --sync # re-import every changed source
hermes import-agent --sync --dry-run # preview what a sync would doSync is prompt-free and cheap: sources whose files are unchanged are skipped by digest comparison, so it is safe to run on a schedule (e.g. a daily cron job). Rules:
- Memory and config merges stay deduplicating — a sync never duplicates entries or patterns you already have.
- Skills previously imported by import-agent are refreshed in place when the source copy changes.
- Skills you created or modified under the import category yourself are never clobbered — they keep normal conflict semantics (use --overwrite on a manual run to force).
- Credential files never trigger a sync — token refreshes in ~/.claude/.credentials.json or ~/.codex/auth.json are ignored by the digest, and secrets are still stripped from anything imported.
This mirrors ChatGPT Work's Settings > Import automatic updates, adapted to an explicit, inspectable command instead of a background service.
The manifest is per profile. A profile created with hermes profile create <name> --clone --sync-imports carries it over, so hermes -p <name> import-agent --sync keeps pulling from the same external trees (see Profiles).
Which File Does What?
"I told my agent something and it forgot." "Which file is my agent's brain?" "I edited SOUL.md — why doesn't it know my name?" These questions all come down to the same thing: Hermes Agent is shaped by several markdown files, and each one has a different job. This page maps them all in one place. For depth on any of them, follow the links to Persistent Memory, Personality & SOUL.md, and Context Files.
The Master Table
| | File | What it holds | Who writes it | When the agent sees it | Where it lives
| SOUL.md | The agent's primary identity — personality, tone, communication style, what to avoid stylistically | You. Hermes seeds a starter file automatically if one doesn't exist; existing files are never overwritten | Slot #1 of the system prompt, at session start | ~/.hermes/SOUL.md (or $HERMES_HOME/SOUL.md with a custom home) — never the working directory
| USER.md | User profile — your name, role, preferences, communication style, expectations | The agent, via the memory tool (you can gate saves with write_approval, or edit entries via hermes journey edit) | Injected into the system prompt as a frozen snapshot at session start | ~/.hermes/memories/
| MEMORY.md | Agent's personal notes — environment facts, project conventions, tool quirks, things learned | The agent, via the memory tool (same gating and editing options as USER.md) | Injected into the system prompt as a frozen snapshot at session start | ~/.hermes/memories/
| AGENTS.md | Project instructions, conventions, architecture — commands, ports, paths, repo-specific workflows | You (or whoever authors the project) | Loaded into the system prompt at startup from your working directory; nested copies are discovered progressively as the agent navigates subdirectories | Project working directory + subdirectories
| .hermes.md / HERMES.md | Project instructions, like AGENTS.md but Hermes-specific and highest priority | You | Loaded into the system prompt at startup (first match wins over AGENTS.md) | Your project — discovery walks up to the git root
One project context file per session
Only one project context type is loaded per session, first match wins: .hermes.md → AGENTS.md → CLAUDE.md → .cursorrules. SOUL.md is always loaded independently as the agent identity — it is not part of that priority chain. See Context Files for the full list, including CLAUDE.md and .cursorrules compatibility.
A useful shorthand:
- SOUL.md is who the agent is — if it should follow you everywhere, it belongs here.
- USER.md is who you are — the agent maintains it for you.
- MEMORY.md is what the agent has learned — it maintains this itself too.
- AGENTS.md (or .hermes.md) is what the project needs — if it belongs to a project, it belongs here.
"Why did it forget what I just said?"
Memory (MEMORY.md and USER.md) is injected into the system prompt as a frozen snapshot captured once at session start — when the agent saves something mid-session, the change is persisted to disk immediately but won't appear in the system prompt until the next session starts. This is intentional: it preserves the LLM's prefix cache for performance, and tool responses always show the live state, so nothing is lost — start a new session and the updated memory is there. Full details in How Memory Appears in the System Prompt.
Common Mix-Ups
"I put facts about myself in SOUL.md, but USER.md stayed empty"
SOUL.md and USER.md are separate systems that never feed each other. SOUL.md is a personality file you edit directly — it shapes tone and identity, and its content is injected verbatim as slot #1 of the prompt. USER.md is part of persistent memory and is written by the agent through the memory tool. If you want facts about yourself in USER.md, tell the agent ("remember that I prefer concise answers") and it saves them — editing SOUL.md won't populate memory, and memory entries won't change the persona. Use SOUL.md for durable voice and personality guidance; leave preferences and profile facts to memory. See What should go in SOUL.md? and Two Targets Explained.
"I told it my name mid-session and it acted like it never heard it"
If the agent saved your name to memory, the save worked — check with the memory tool's responses or hermes journey list. What you're seeing is the frozen-snapshot rule above: the system prompt doesn't refresh mid-session, so the injected memory block still shows the session-start state. The agent can still use what you told it within the current conversation (it's in the context), and the saved entry will be in the system prompt from the next session onward. The same applies to edits you make to SOUL.md or AGENTS.md while a session is running: context is assembled at session start, so restart the session to pick up changes.
Quick decision guide
- Want to change how the agent talks? Edit ~/.hermes/SOUL.md — Personality & SOUL.md.
- Want the agent to remember a fact? Just tell it — it saves to memory itself. Persistent Memory.
- Want to set project rules? Put an AGENTS.md (or .hermes.md) in the project — Context Files.
- Need a temporary personality change? Use /personality — it's a session-level overlay, no file edits needed.
Related Docs
- Persistent Memory — MEMORY.md, USER.md, the memory tool, capacity limits, write_approval
- Personality & SOUL.md — SOUL.md content guidance, /personality presets, the prompt stack
- Context Files — AGENTS.md, .hermes.md, progressive discovery, security scanning
消息网关
通过 Telegram、Discord、Slack、WhatsApp、Signal、SMS、Email、Home Assistant、Mattermost、Matrix、DingTalk、Feishu/Lark、WeCom、Weixin、BlueBubbles(iMessage)、QQ、Yuanbao、Microsoft Teams、LINE、ntfy 或浏览器与 Hermes 对话。网关是一个单一后台进程,连接所有已配置的平台,管理会话,运行 cron 任务,并传递语音消息。
完整的语音功能集——包括 CLI 麦克风模式、消息中的语音回复以及 Discord 语音频道对话——请参阅 Voice Mode 和 Use Voice Mode with Hermes。
平台对比
| | 平台 | 语音 | 图片 | 文件 | 线程 | 表情反应 | 输入提示 | 流式输出
| Telegram | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅
| Discord | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅
| Slack | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅
| Google Chat | — | ✅ | ✅ | ✅ | — | ✅ | —
| WhatsApp | — | ✅ | ✅ | — | — | ✅ | ✅
| Signal | — | ✅ | ✅ | — | — | ✅ | ✅
| SMS | — | — | — | — | — | — | —
| Email | — | ✅ | ✅ | ✅ | — | — | —
| Home Assistant(插件) | — | — | — | — | — | — | —
| Mattermost | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅
| Matrix | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅
| DingTalk | — | ✅ | ✅ | — | ✅ | — | ✅
| Feishu/Lark | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅
| WeCom | ✅ | ✅ | ✅ | — | — | — | —
| WeCom Callback | — | — | — | — | — | — | —
| Weixin | ✅ | ✅ | ✅ | — | — | ✅ | ✅
| BlueBubbles | — | ✅ | ✅ | — | ✅ | ✅ | —
| QQ | ✅ | ✅ | ✅ | — | — | ✅ | —
| Yuanbao | ✅ | ✅ | ✅ | — | — | ✅ | ✅
| Microsoft Teams | — | ✅ | — | ✅ | — | ✅ | —
| LINE | — | ✅ | ✅ | — | — | ✅ | —
| ntfy | — | — | — | — | — | — | —
语音 = TTS 音频回复和/或语音消息转录。图片 = 发送/接收图片。文件 = 发送/接收文件附件。线程 = 线程式对话。表情反应 = 对消息添加 emoji 反应。输入提示 = 处理时显示正在输入状态。流式输出 = 通过编辑消息实现渐进式更新。
架构
每个平台适配器接收消息,通过每个聊天的会话存储进行路由,并将其分发给 AIAgent 处理。网关还运行 cron 调度器,每 60 秒触发一次以执行到期任务。
有意静默令牌
在群聊、hooks 和自动化流程中,Hermes 支持显式的静默令牌。若 agent 的最终回复恰好只是一个受支持的令牌,网关会抑制外发投递,不向聊天发送任何内容。
支持的令牌:
- [SILENT]
- SILENT
- NO_REPLY
- NO REPLY
- [静默] / 静默 与 [沉默] / 沉默 —— 模型把哨兵翻译成中文而非原样输出时产生的形式
空白与大小写会被规范化,但整条最终回复必须只是该令牌。像"没有变化时请用 [SILENT]"这样的句子会正常投递。
静默只是投递层面的决定:Hermes 仍会把这条静默回复保存在会话记录中,因此对话依旧正常交替。失败的回合仍会以错误形式呈现;不会因为文本形似静默令牌而隐藏失败。
快速配置
配置消息平台最简单的方式是使用交互式向导:
hermes gateway setup # 交互式配置所有消息平台该向导引导你通过方向键选择配置各平台,显示哪些平台已配置,并在完成后提示启动/重启网关。
网关命令
hermes gateway # 在前台运行
hermes gateway setup # 交互式配置消息平台
hermes gateway install # 安装为用户服务(Linux)/ launchd 服务(macOS)
sudo hermes gateway install --system # 仅 Linux:安装开机启动的系统服务
hermes gateway start # 启动默认服务
hermes gateway stop # 停止默认服务
hermes gateway status # 检查默认服务状态
hermes gateway status --system # 仅 Linux:显式检查系统服务聊天命令(在消息平台内使用)
| | 命令 | 说明
| /new 或 /reset | 开始新对话
| /model [provider:model] | 显示或切换模型(支持 provider:model 语法)
| /personality [name] | 设置人格
| /retry | 重试上一条消息
| /undo | 删除上一轮对话
| /status | 显示会话信息
| /whoami | 显示你在当前范围内的斜杠命令权限(管理员 / 普通用户 / 无限制)
| /stop | 停止正在运行的 agent
| /approve | 批准待执行的危险命令
| /deny | 拒绝待执行的危险命令
| /sethome | 将此聊天设为主频道
| /compress | 手动压缩对话上下文
| /title [name] | 设置或显示会话标题
| /resume [name] | 恢复之前命名的会话
| /usage | 显示本会话的 token 用量
| /insights [days] | 显示用量洞察与分析
| /reasoning [level|show|hide] | 更改推理强度或切换推理显示
| /voice [on|off|tts|join|leave|status] | 控制消息语音回复和 Discord 语音频道行为
| /rollback [number] | 列出或恢复文件系统检查点
| /bg <prompt> | 在独立后台会话中运行 prompt(提示词)
| /btw <question> | 在不打断当前对话的情况下,就当前对话提出顺带问题
| /reload-mcp | 从配置重新加载 MCP 服务器
| /update | 将 Hermes Agent 更新至最新版本
| /help | 显示可用命令
| /<skill-name> | 调用任意已安装的技能
会话管理
会话持久化
会话在消息之间持续保留,直到重置。Agent 会记住你的对话上下文。
会话连续性
Gateway 不会因空闲时间或每日时间边界而重置对话。需要新对话时使用 /new
或 /reset;上下文压缩仍会自动运行。旧的 session_reset 配置、重置策略覆盖和
重置计时环境变量均被忽略。缓存中的 agent 可以释放资源,但不会替换持久化对话。
重启恢复的新鲜度限制仅约束自动继续执行,不会清除用户发送消息时加载的历史。
安全
默认情况下,网关拒绝所有不在白名单中或未通过私信配对的用户。 这是具有终端访问权限的机器人的安全默认设置。
# 限制为特定用户(推荐):
TELEGRAM_ALLOWED_USERS=123456789,987654321
DISCORD_ALLOWED_USERS=123456789012345678
SIGNAL_ALLOWED_USERS=+155****4567,+155****6543
SMS_ALLOWED_USERS=+155****4567,+155****6543
EMAIL_ALLOWED_USERS=trusted@example.com,colleague@work.com
MATTERMOST_ALLOWED_USERS=3uo8dkh1p7g1mfk49ear5fzs5c
MATRIX_ALLOWED_USERS=@alice:matrix.org
DINGTALK_ALLOWED_USERS=user-id-1
FEISHU_ALLOWED_USERS=ou_xxxxxxxx,ou_yyyyyyyy
WECOM_ALLOWED_USERS=user-id-1,user-id-2
WECOM_CALLBACK_ALLOWED_USERS=user-id-1,user-id-2
TEAMS_ALLOWED_USERS=aad-object-id-1,aad-object-id-2
# 或允许
GATEWAY_ALLOWED_USERS=123456789,987654321
# 或显式允许所有用户(不推荐用于具有终端访问权限的机器人):
GATEWAY_ALLOW_ALL_USERS=true私信配对(白名单的替代方案)
无需手动配置用户 ID,未知用户私信机器人时会收到一次性配对码:
# 用户看到:"Pairing code: XKGH5N7P"
# 你通过以下命令批准:
hermes pairing approve telegram XKGH5N7P
# 其他配对命令:
hermes pairing list # 查看待审核和已批准的用户
hermes pairing revoke telegram 123456789 # 撤销访问权限配对码 1 小时后过期,有频率限制,并使用密码学随机数生成。
管理员与普通用户
白名单解决的是"此人能否访问机器人"的问题。管理员 / 普通用户的划分解决的是"既然已经进来了,他们被允许做什么"的问题。
每个允许的用户在每个范围(私信 vs 群组/频道)内属于以下两个层级之一:
- 管理员 — 完全访问权限。可运行所有已注册的斜杠命令(内置 + 插件)并使用所有受限功能。
- 普通用户 — 受限访问权限。可正常与 agent 聊天,但只能运行你明确启用的斜杠命令。始终允许的最低权限为 /help 和 /whoami。
层级按平台和范围分别配置。私信管理员身份不意味着群组/频道管理员身份——每个范围有各自的管理员列表。
当前层级控制的内容: 斜杠命令。该划分贯穿实时命令注册表,因此无需逐功能配置即可覆盖内置命令和插件注册的命令。普通聊天不受影响——非管理员仍可与 agent 对话。
未来可能受控的内容: 更多功能面(工具访问、模型切换、高消耗操作)将随着我们的添加挂载到同一管理员 / 普通用户区分上。现在配置好划分,意味着未来的限制可以干净落地,无需重新规划谁是管理员。
配置
gateway:
platforms:
discord:
extra:
allow_from: ["111", "222", "333"]
allow_admin_from: ["111"] # 管理员 → 所有斜杠命令
user_allowed_commands: [status, model] # 非管理员可运行的命令
# 可选:单独配置群组/频道范围
group_allow_admin_from: ["111"]
group_user_allowed_commands: [status]向后兼容: 如果某个范围未设置 allow_admin_from,则该范围的层级划分被禁用,所有允许的用户拥有完全访问权限。现有安装无需任何更改即可继续工作——需要区分时再选择启用。
查看你的权限
在任意平台使用 /whoami 查看当前范围、你的层级(管理员 / 普通用户 / 无限制)以及你可以运行的斜杠命令。平台特定示例请参阅 Telegram 和 Discord 页面。
中断 Agent
在 agent 工作时发送任意消息即可中断它。关键行为:
- 正在执行的终端命令立即终止(SIGTERM,1 秒后 SIGKILL)
- 工具调用被取消 — 仅当前正在执行的工具调用会运行,其余跳过
- 多条消息合并 — 中断期间发送的消息合并为一个 prompt
- /stop 命令 — 中断而不排队后续消息
队列 vs 中断 vs 引导(繁忙输入模式)
默认情况下,向繁忙的 agent 发送消息会中断它。另有两种模式可用:
- queue — 后续消息等待,在当前任务完成后作为下一轮运行。
- steer — 后续消息通过 /steer 注入当前运行,在下一次工具调用后到达 agent。不中断,不开新轮次。如果 agent 尚未开始,则回退为 queue 行为。
display:
busy_input_mode: steer # 或 queue,或 interrupt(默认)
busy_ack_enabled: true # 设为 false 可完全抑制 ⚡/⏳/⏩ 聊天回复第一次在任意平台向繁忙的 agent 发送消息时,Hermes 会在繁忙确认中附加一行提示,说明该配置项("💡 First-time tip — …")。该提示每次安装只触发一次——由 onboarding.seen.busy_input_prompt 下的标志锁定。删除该键可再次看到提示。
如果你觉得繁忙确认消息过多——尤其是使用语音输入或快速连续发送消息时——可设置 display.busy_ack_enabled: false。你的输入仍会正常排队/引导/中断,只是聊天回复被静默。
工具进度通知
在 ~/.hermes/config.yaml 中控制显示多少工具活动信息:
display:
tool_progress: all # off | new | all | verbose
tool_progress_command: false # 设为 true 可在消息平台中启用 /verbose启用后,机器人在工作时发送状态消息:
💻 `ls -la`...
🔍 web_search...
📄 web_extract...
🐍 execute_code...后台会话
在独立的后台会话中运行 prompt,让 agent 独立处理,同时保持主聊天响应:
/bg Check all servers in the cluster and report any that are downHermes 立即确认:
🔄 Background task started: "Check all servers in the cluster..."
Task ID: bg_143022_a1b2c3工作原理
每个 /bg prompt 会生成一个独立的 agent 实例异步运行:
- 隔离会话 — 后台 agent 拥有自己的会话和对话历史。它不了解你当前的聊天上下文,只接收你提供的 prompt。
- 相同配置 — 继承当前网关配置中的模型、提供商、工具集、推理设置和提供商路由。
- 非阻塞 — 你的主聊天保持完全交互。在后台任务运行期间,你可以发送消息、运行其他命令或启动更多后台任务。
- 结果传递 — 任务完成后,结果发送回发出命令的同一聊天或频道,前缀为"✅ Background task complete"。如果失败,你会看到"❌ Background task failed"及错误信息。
后台进程通知
当运行后台会话的 agent 使用 terminal(background=true) 启动长时间运行的进程(服务器、构建等)时,网关可以向你的聊天推送状态更新。通过 ~/.hermes/config.yaml 中的 display.background_process_notifications 控制:
display:
background_process_notifications: concise # concise | all | result | error | off| | 模式 | 你收到的内容
| concise | 完成时的单行状态消息;失败时附加简短的输出尾部(默认)
| all | 运行输出更新以及最终原始输出消息
| result | 仅最终原始输出完成消息(无论退出码)
| error | 仅在退出码非零时的最终原始输出消息
| off | 不接收任何进程监控消息
也可通过环境变量设置:
HERMES_BACKGROUND_NOTIFICATIONS=result使用场景
- 服务器监控 — "/bg Check the health of all services and alert me if anything is down"
- 长时间构建 — "/bg Build and deploy the staging environment",同时继续聊天
- 研究任务 — "/bg Research competitor pricing and summarize in a table"
- 文件操作 — "/bg Organize the photos in ~/Downloads by date into folders"
提示
消息平台上的后台任务是即发即忘的——你无需等待或主动查询。任务完成后,结果会自动出现在同一聊天中。
服务管理
Linux(systemd)
hermes gateway install # 安装为用户服务
hermes gateway start # 启动服务
hermes gateway stop # 停止服务
hermes gateway status # 检查状态
journalctl --user -u hermes-gateway -f # 查看日志
# 启用 lingering(注销后保持运行)
sudo loginctl enable-linger $USER
# 或安装开机启动的系统服务,仍以你的用户身份运行
sudo hermes gateway install --system
sudo hermes gateway start --system
sudo hermes gateway status --system
journalctl -u hermes-gateway -f笔记本和开发机使用用户服务。VPS 或无头主机(需要开机自动启动而不依赖 systemd linger)使用系统服务。
除非你确实有此需要,否则避免同时安装用户和系统网关单元。Hermes 检测到两者同时存在时会发出警告,因为 start/stop/status 行为会变得不明确。
多个安装
如果你在同一台机器上运行多个 Hermes 安装(使用不同的 HERMES_HOME 目录),每个安装都有自己的 systemd 服务名称。默认的 ~/.hermes 使用 hermes-gateway;其他安装使用 hermes-gateway-<hash>。hermes gateway 命令会自动针对当前 HERMES_HOME 对应的正确服务。
macOS(launchd)
hermes gateway install # 安装为 launchd agent
hermes gateway start # 启动服务
hermes gateway stop # 停止服务
hermes gateway status # 检查状态
tail -f ~/.hermes/logs/gateway.log # 查看日志生成的 plist 文件位于 ~/Library/LaunchAgents/ai.hermes.gateway.plist。它包含三个环境变量:
- PATH — 安装时你的完整 shell PATH,并在前面添加了 venv bin/ 和 node_modules/.bin。这确保用户安装的工具(Node.js、ffmpeg 等)可供网关子进程(如 WhatsApp 桥接)使用。
- VIRTUAL_ENV — 指向 Python 虚拟环境,使工具能正确解析包。
- HERMES_HOME — 将网关限定到你的 Hermes 安装。
安装后 PATH 变更
launchd plist 是静态的——如果你在配置网关后安装了新工具(例如通过 nvm 安装新版 Node.js,或通过 Homebrew 安装 ffmpeg),请重新运行 hermes gateway install 以捕获更新后的 PATH。网关会检测到过时的 plist 并自动重新加载。
多个安装
与 Linux systemd 服务类似,每个 HERMES_HOME 目录都有自己的 launchd 标签。默认的 ~/.hermes 使用 ai.hermes.gateway;其他安装使用 ai.hermes.gateway-<suffix>。
平台专属工具集
每个平台有自己的工具集:
| | 平台 | 工具集 | 功能
| CLI | hermes-cli | 完全访问
| Telegram | hermes-telegram | 完整工具,包括终端
| Discord | hermes-discord | 完整工具,包括终端
| WhatsApp | hermes-whatsapp | 完整工具,包括终端
| Slack | hermes-slack | 完整工具,包括终端
| Google Chat | hermes-google_chat | 完整工具,包括终端
| Signal | hermes-signal | 完整工具,包括终端
| SMS | hermes-sms | 完整工具,包括终端
| Email | hermes-email | 完整工具,包括终端
| Home Assistant(插件) | hermes-homeassistant | 完整工具 + HA 设备控制(ha_list_entities、ha_get_state、ha_call_service、ha_list_services),由插件目录中的 homeassistant 插件提供
| Mattermost | hermes-mattermost | 完整工具,包括终端
| Matrix | hermes-matrix | 完整工具,包括终端
| DingTalk | hermes-dingtalk | 完整工具,包括终端
| Feishu/Lark | hermes-feishu | 完整工具,包括终端
| WeCom | hermes-wecom | 完整工具,包括终端
| WeCom Callback | hermes-wecom-callback | 完整工具,包括终端
| Weixin | hermes-weixin | 完整工具,包括终端
| BlueBubbles | hermes-bluebubbles | 完整工具,包括终端
| QQBot | hermes-qqbot | 完整工具,包括终端
| Yuanbao | hermes-yuanbao | 完整工具,包括终端
| Microsoft Teams | hermes-teams | 完整工具,包括终端
| API Server | hermes-api-server | 完整工具(去除 clarify、send_message、text_to_speech——程序化访问没有交互用户)
| Webhooks | hermes-webhook | 完整工具,包括终端
运营多平台网关
网关通常同时运行多个适配器(Telegram + Discord + Slack 等)。以下章节涵盖跨所有平台的日常运维操作。
/platform 命令
网关运行后,可从任意已连接的 CLI 会话或聊天使用 /platform 斜杠命令检查和控制单个适配器,无需重启整个网关:
/platform list # 显示所有适配器及其状态
/platform pause <name> # 停止向某个适配器分发新消息
/platform resume <name> # 重新启用已暂停的适配器/platform list 显示每个适配器是 running(运行中)、paused(手动暂停)还是 paused-by-breaker(见下文)。暂停会保持适配器加载状态及其后台循环——传入消息被丢弃,但连接本身保持开启,因此恢复是即时的。
另请参阅更广泛的状态汇总命令 /platforms。
自动熔断器
每个适配器都包裹在熔断器中。反复出现的可重试失败(网络抖动、限流回复、上游 5xx 响应、websocket 断开)会导致熔断器触发——适配器被自动暂停,当配置了主频道时向另一个存活平台的主频道发送运营通知,并输出结构化日志行。
熔断器不会自动恢复——它保持断开状态,直到你手动运行 /platform resume <name>。这是有意为之:如果某个平台持续故障,你不希望网关不断重试重连。
适配器暂停时的排查步骤
当适配器暂停时,检查:
- 网关日志(~/.hermes/logs/gateway.log 或 systemd / launchd 单元日志)。搜索平台名称以及 circuit breaker、paused 或 disabled。触发事件包含失败次数和最后一个错误。
- /platform list 输出——显示当前状态和最后原因。
- 提供商状态页面(Telegram bot API 状态、Discord 状态等)。熔断器触发是因为平台不健康;在平台恢复之前不要尝试恢复。
上游恢复正常后,/platform resume <name> 清除熔断器并重新激活适配器。
重启通知
当网关重启(或在有进行中会话时关闭)时,它可以向每个平台的主频道发送一条"agent 已恢复"/"agent 被中断"的一次性消息。这由 gateway-config.yaml 中每个平台的 gateway_restart_notification 标志控制,默认为 true:
gateway:
platforms:
telegram:
home_chat_id: "123456789"
gateway_restart_notification: false # 为此平台关闭
discord:
home_chat_id: "987654321"
# gateway_restart_notification 未设置 → 默认为 true在嘈杂或低优先级的平台上禁用,同时在主要聊天上保持启用。无论有多少会话正在进行,每次重启只发送一次通知。
正在输入指示器
当 agent 正在处理消息时,网关会在支持的平台上显示实时的输入状态——Telegram/Discord/Signal 上的"正在输入……"气泡,或 Slack 上的"is thinking…"助手状态。这由 gateway-config.yaml 中每个平台的 typing_indicator 标志控制,默认为 true:
gateway:
platforms:
slack:
typing_indicator: false # 在 Slack 上不显示"is thinking…"
telegram:
# typing_indicator 未设置 → 默认为 true在任何不需要该指示器的平台上设置 typing_indicator: false。部分用户觉得 Slack 的"is thinking…"状态比较嘈杂(由于它使用 Slack 的 Assistant API,显示期间还会短暂禁用输入框)。禁用它只会抑制该指示器——消息投递及其他一切均不受影响。该标志是通用的,因此同一个键对每个平台都有效。
网关重启后的会话恢复
当网关在工具调用或生成进行中时关闭,受影响的会话被标记为 restart_interrupted。下次启动时,网关为每个会话安排自动恢复——用户在聊天中收到简短提示("Send any message after restart and I'll try to resume where you left off."),当他们回复时,会话从最后提交的轮次继续。
此行为默认开启,并在网关启动时记录日志:
Scheduled auto-resume for N restart-interrupted session(s)无需配置。如果你不想要提示消息,在该平台上设置 gateway_restart_notification: false。
进度气泡清理(可选启用)
工具进度消息、"仍在处理中……"心跳以及状态回调气泡可在最终响应落地后自动删除。通过 display.platforms.<platform>.cleanup_progress 按平台启用:
display:
platforms:
telegram:
cleanup_progress: true
discord:
cleanup_progress: true默认为 false。仅实现了 delete_message 的适配器平台支持此设置(目前为 Telegram 和 Discord)。运行失败时跳过清理,气泡保留作为调试线索。
后续步骤
- Telegram 配置
- Discord 配置
- Slack 配置
- Google Chat 配置
- WhatsApp 配置
- Signal 配置
- SMS 配置(Twilio)
- Email 配置
- Home Assistant 集成(插件目录)
- Mattermost 配置
- Matrix 配置
- DingTalk 配置
- Feishu/Lark 配置
- WeCom 配置
- WeCom Callback 配置
- Weixin 配置(微信)
- BlueBubbles 配置(iMessage)
- QQBot 配置
- Yuanbao 配置
- Microsoft Teams 配置
- Teams 会议流水线
- Open WebUI + API Server
- Webhooks
Windows(原生)指南
Hermes 可在 Windows 10 和 Windows 11 上原生运行——无需 WSL、Cygwin 或 Docker。本页是深度指南:原生支持哪些功能、哪些仅限 WSL、安装程序实际做了什么,以及你可能需要调整的 Windows 专属配置项。
如果你只是想安装,首页 或安装页面上的一行命令就够了。遇到意外情况时再回来查阅本页。
想用 WSL?
如果你更倾向于真正的 POSIX 环境(用于 dashboard 内嵌终端、fork 语义、Linux 风格文件监视器等),请参阅 Windows(WSL2)指南。两者可以干净共存:原生数据存放在 %LOCALAPPDATA%\hermes,WSL 数据存放在 ~/.hermes。
快速安装
打开 PowerShell(或 Windows Terminal)并运行:
iex (irm https://hermes-agent.nousresearch.com/install.ps1)无需管理员权限。安装程序会写入 %LOCALAPPDATA%\hermes\,并将 hermes 添加到你的用户 PATH——安装完成后打开新终端即可使用。
安装程序选项:
& ([scriptblock]::Create((irm https://hermes-agent.nousresearch.com/install.ps1))) -NonInteractive -Branch main| | 参数 | 用途
| -Branch NAME | 选择源码分支,默认 main。
| -Commit SHA | 在分支检出后固定到指定 commit。
| -HermesHome PATH | 选择数据目录。
| -InstallDir PATH | 选择源码目录。
| -NonInteractive | 跳过需要输入的 setup/gateway 阶段。
| -IncludeDesktop | 构建桌面应用并创建快捷方式。
| -ShowResolvedPaths | 只输出解析后的路径 JSON,不安装。
| -Manifest / -ProtocolVersion | 查看引导 GUI 使用的阶段协议。
| -Stage NAME -Json | 执行单个阶段并输出结果帧。
当前脚本不接受 -NoVenv 或 -Tag;-SkipSetup 仍被接受,作为 -NonInteractive 的已弃用别名,以便旧的安装包装脚本继续兼容。
MSIX / App Installer 和 Microsoft Store
自包含 MSIX 要求 Windows 11 22H2 或更新版本。
Windows 10 源码安装支持不代表 MSIX 支持 Windows 10。
打开 .appinstaller 文件,Windows 会安装签名包并记录更新源。
软件包包含 Python、Node 和基础依赖,首次启动无需克隆或编译源码。
执行别名提供 hermes、hermes-agent 和 hermes-acp。
用 Get-Command hermes -All 检查是否被其他安装覆盖。
在 Windows 的应用执行别名设置中管理这些入口。
侧载版通过桌面 Update 控件交给 App Installer 更新。
Hermes 先下载本地描述文件,再停止自己的后端、退出并等待包替换。
它不依赖 ms-appinstaller: URL 协议。商店版本由 Microsoft Store 更新。
Hermes-Setup.exe 是另一种引导安装程序,会下载并配置源码安装。
不要把它与自包含 MSIX 混为一谈。
源码安装程序实际做了什么
- 使用现有 Git;缺少时下载经过验证的 Git for Windows 工具包。
- 克隆源码分支,并应用可选的 commit pin。
- 引导 uv,再委托 PM 准备 Python 3.14、必要工具和名为 all 的 Python extra。
- 在数据目录的 bin 下生成启动器,并加入用户 PATH。
- 准备配置;非交互模式跳过输入阶段。
- 按需构建桌面应用,并写入完成标记。
PM 通过 pm/lock.json 管理工具版本,不使用旧的 winget/分层 pip 回退。
启动器运行工具存储中的 Python,并在导入依赖前选择完整环境。
可选依赖由 PM 管理,不再调用 install.ps1 -Ensure。
在 Windows 上跳过繁琐的提供商配置
在 Windows 上,逐个配置工具 API key(Firecrawl、FAL、Browser Use、OpenAI TTS)是获得可用 agent 摩擦最大的部分。Nous Portal 订阅通过一次 OAuth 登录即可覆盖模型以及所有这些工具。安装程序完成后,运行 hermes setup --portal 完成配置。
功能矩阵
Windows 支持取决于功能和架构。部分可选 SDK 不支持所有 Windows 目标。
| | 功能 | 原生 Windows | WSL2
| CLI(hermes chat、hermes setup、hermes gateway 等) | ✓ | ✓
| 交互式 TUI(hermes --tui) | ✓ | ✓
| 消息 gateway(Telegram、Discord、Slack、WhatsApp,15+ 平台) | ✓ | ✓
| Cron 调度器 | ✓ | ✓
| 浏览器工具(通过 Node 驱动 Chromium) | ✓ | ✓
| MCP 服务器(stdio 和 HTTP) | ✓ | ✓
| 本地 Ollama / LM Studio / llama-server | ✓ | ✓(通过 WSL 网络)
| Web dashboard(会话、任务、指标、配置) | ✓ | ✓
| Dashboard /chat 内嵌终端面板 | pywinpty/ConPTY | POSIX PTY
| 登录时自动启动 | ✓(schtasks) | ✓(systemd)
Dashboard 已有 Windows ConPTY 实现,依赖 pywinpty。SDK 缺失或损坏时终端仍可能不可用。原生 Windows ARM64 不包含 Mem0/Google Chat SDK、Faster-Whisper 或 openWakeWord。Sherpa 支持原生 Windows ARM64,且是该平台自动选择的唤醒词引擎。
Hermes 在 Windows 上如何运行 shell 命令
Hermes 的终端工具通过 Git Bash 运行命令,与 Claude Code 采用相同策略。这在不重写每个工具的情况下绕过了 POSIX 与 Windows 的差异。
pm.shell() 先读取 PM facts 中的 Git/Bash,再检查 PATH。
当前脚本不再设置 HERMES_GIT_BASH_PATH。MinGit 不能替代带 Bash 的 Git for Windows。
WindowsApps 软件包中的可执行文件可能无法由包外 Python 启动,并返回 WinError 5。
请使用包自己的入口,或为源码环境使用常规工具安装,不要关闭系统安全控制。
Windows 上的 UTF-8 控制台
Python 在 Windows 上的默认 stdio 使用控制台的活动代码页(通常是 cp1252 或 cp437)。Hermes 的横幅、斜杠命令列表、工具输出、Rich 面板和技能描述均包含 Unicode 字符。若不加干预,任何此类内容都会导致 UnicodeEncodeError: 'charmap' codec can't encode character… 崩溃。
修复逻辑位于 hermes_cli/stdio.py::configure_windows_stdio(),在每个入口点(cli.py::main、hermes_cli/main.py::main、gateway/run.py::main)的早期调用。它会:
- 通过 kernel32.SetConsoleCP / SetConsoleOutputCP 将控制台代码页切换为 CP_UTF8(65001)。
- 使用 errors='replace' 将 sys.stdout / sys.stderr / sys.stdin 重新配置为 UTF-8。
- 通过 setdefault 设置 PYTHONIOENCODING=utf-8 和 PYTHONUTF8=1(用户显式设置的值优先),使子 Python 进程继承 UTF-8。
- 如果 EDITOR 和 VISUAL 均未设置,则设置 EDITOR=notepad(详见下方编辑器章节)。
此函数是幂等的,在非 Windows 系统上为空操作。
禁用方式: 在环境中设置 HERMES_DISABLE_WINDOWS_UTF8=1 可回退到旧版 cp1252 stdio 路径。用于排查编码 bug;正常使用中不建议设置。
编辑器(Ctrl-X Ctrl-E、/edit)
在 PR #21561 之前,在 Windows 上按 Ctrl-X Ctrl-E 或输入 /edit 会静默无响应。prompt_toolkit 有一个硬编码的 POSIX 绝对路径回退列表(/usr/bin/nano、/usr/bin/pico、/usr/bin/vi 等),在 Windows 上永远无法解析——即使安装了完整的 Git for Windows 也不行。
Hermes 的 Windows stdio 垫片现在将 EDITOR=notepad 设为默认值。Notepad 随每个 Windows 安装附带,可作为阻塞式编辑器使用——subprocess.call(["notepad", file]) 会阻塞直到窗口关闭。
用户覆盖仍然优先(在 setdefault 之前检查):
| | 编辑器 | PowerShell 命令
| VS Code | $env:EDITOR = "code --wait"
| Notepad++ | $env:EDITOR = "'C:\Program Files\Notepad++\notepad++.exe' -multiInst -nosession"
| Neovim | $env:EDITOR = "nvim"
| Helix | $env:EDITOR = "hx"
VS Code 的 --wait 标志至关重要——没有它,编辑器会立即返回,Hermes 收到的是空缓冲区。
在 PowerShell profile 中永久设置:
# In $PROFILE
$env:EDITOR = "code --wait"或在系统设置的用户环境变量中设置,使每个新 shell 都能获取。
CLI 中用 Ctrl+Enter 换行
Windows Terminal 将 Ctrl+Enter 作为独立按键序列传递。Hermes 将其绑定为"插入换行",使你可以在 CLI 中编写多行 prompt(提示词)而无需回退到 Esc-然后-Enter。适用于 Windows Terminal、VS Code 集成终端以及任何支持 VT 转义序列的现代 Windows 控制台宿主。
在旧版 cmd.exe 控制台上,Ctrl+Enter 会折叠为普通 Enter——请改用 Esc Enter,或升级到 Windows Terminal(免费,Windows 11 默认已安装)。
在 Windows 登录时运行 gateway
Windows 上的 hermes gateway install 使用计划任务,并以 Startup 文件夹作为回退——无需管理员权限。
安装
hermes gateway install底层发生的事情:
- schtasks /Create /SC ONLOGON /RL LIMITED /TN HermesGateway — 注册一个在你登录时以标准(非提升)权限运行的任务。无 UAC 提示。
- 如果 schtasks 被组策略阻止,则回退到在 %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup 中写入 start /min cmd.exe /d /c <wrapper> 快捷方式。效果相同,稍显粗糙。
- 通过 pythonw.exe 以分离方式生成 gateway——而非 python.exe。pythonw.exe 没有附加控制台,可免疫来自同一进程组中兄弟进程的 CTRL_C_EVENT 广播(这是一个真实问题,曾导致在同一进程组中 Ctrl+C 任何进程时 gateway 被杀死)。
生成时使用的标志:DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW | CREATE_BREAKAWAY_FROM_JOB。
管理
hermes gateway status # 合并视图:schtasks + Startup 文件夹 + 运行中的 PID
hermes gateway start # 立即启动计划任务
hermes gateway stop # 等效于优雅的 SIGTERM(通过 psutil 调用 TerminateProcess)
hermes gateway restart
hermes gateway uninstall # 移除 schtasks 条目、Startup 快捷方式、pid 文件hermes gateway status 是幂等的——调用一千次也不会意外杀死 gateway。(PR #21561 之前它会静默地这样做,原因是 os.kill(pid, 0) 在 C 层与 CTRL_C_EVENT 发生碰撞——如果你想了解来龙去脉,请参阅下方"进程管理内部机制"。)
为什么不用 Windows 服务?
服务需要管理员权限安装,并将 gateway 的生命周期绑定到机器启动,而非用户登录。典型的 Hermes 用户希望:登录 → gateway 可用,注销 → gateway 消失。计划任务无需提权即可实现这一点。如果你确实需要服务,可以手动使用 nssm 或 sc create——但你可能并不需要。
数据布局
| | 路径 | 内容
| %LOCALAPPDATA%\hermes\hermes-agent\ | 源码安装的 checkout;纯 MSIX 安装没有此目录。
| %LOCALAPPDATA%\hermes\tools\ | 可写工具存储;MSIX 基础工具保留在包内。
| %LOCALAPPDATA%\hermes\installs\ | 每个安装的环境选择、事务日志和 Python 代际。
| %LOCALAPPDATA%\hermes\bin\ | 源码安装启动器;MSIX 使用执行别名。
| %LOCALAPPDATA%\hermes\ | 用户配置、密钥、会话、插件、技能和日志。
这些是默认路径,HERMES_HOME 可以更改数据位置。
不要删除整个 %LOCALAPPDATA%\hermes 来修复应用,否则会丢失共享数据。
浏览器工具
内置浏览器后端使用 PM 管理的 agent-browser 和 Chromium。
Browser Use 则通过 hermes tools 配置自己的 CLI。
ARM64 Windows 上的 Chromium/agent-browser 可以使用 x64 模拟,这与原生 Python 不同。
详见 浏览器自动化。
在 Windows 上运行 Hermes — 实用说明
安装后的 PATH
安装程序通过 [Environment]::SetEnvironmentVariable 将 %LOCALAPPDATA%\hermes\bin 添加到你的用户 PATH。已打开的终端不会获取此更新——安装完成后请打开新的 PowerShell 窗口(或 Windows Terminal 标签页)。关闭并重新打开,不要手动执行 $env:PATH += …,除非你清楚自己在做什么。
验证:
Get-Command hermes # 应输出 C:\Users\<you>\AppData\Local\hermes\bin\hermes.exe
hermes --version环境变量
Hermes 同时支持 $env:X(进程作用域)和用户环境变量(永久,在系统属性 → 环境变量中设置)。将 API key 放在所选 HERMES_HOME 的 .env 中(默认 %LOCALAPPDATA%\hermes\.env)——与 Linux 相同:
OPENROUTER_API_KEY=sk-or-...
TELEGRAM_BOT_TOKEN=...不要将密钥放在用户环境变量中,除非你明确希望系统上的每个 Windows 进程都能看到它们(通常不是你想要的)。
Windows 专属环境变量
这些变量仅影响原生 Windows 安装:
| | 变量 | 效果
| HERMES_DISABLE_WINDOWS_UTF8 | 设为 1 可禁用 UTF-8 stdio 垫片,回退到区域设置代码页。用于排查编码 bug。
| EDITOR / VISUAL | 用于 /edit 和 Ctrl-X Ctrl-E 的编辑器。如果两者均未设置,Hermes 默认使用 notepad。
卸载
在 PowerShell 中执行:
hermes uninstall源码安装可先用 hermes uninstall --dry-run 查看范围。--full 同时删除数据,--data 只删除数据。MSIX/Store 应通过 Windows 设置的“已安装的应用”移除,CLI 不删除包所有的代码。
删除用户数据
删除前先停止使用所选 HERMES_HOME 的全部进程,并备份数据。
通过 hermes uninstall --dry-run 检查范围,再选择数据删除模式。
不要为了修复一个应用或 profile 而递归删除默认数据根目录。
自定义 HERMES_HOME 可以位于其他位置,移除应用包也不会删除这些数据。
hermes uninstall CLI 子命令还能处理 schtasks 条目以不同任务名注册的情况(旧版安装)——它通过安装路径而非硬编码任务名来搜索。
进程管理内部机制
这是背景资料——除非你在调试"它在自杀"的奇怪现象,否则可以跳过。
在 Linux 和 macOS 上,POSIX 惯用法 os.kill(pid, 0) 是一个无操作的权限检查:"这个 PID 是否存活且我能向它发信号?"在 Windows 上,Python 的 os.kill 将 sig=0 映射到 CTRL_C_EVENT——两者在整数值 0 上发生碰撞——并通过 GenerateConsoleCtrlEvent(0, pid) 将 Ctrl+C 广播到包含目标 PID 的整个控制台进程组。这是 bpo-14484,自 2012 年起一直未修复,因为修改它会破坏依赖当前行为的脚本。
后果:任何通过 os.kill(pid, 0) 检查"此 PID 是否存活"的代码路径,在 Windows 上都会静默地杀死目标进程。Hermes 已将所有此类位置(11 个文件中的 14 处)迁移到 gateway.status._pid_exists(),该函数使用 psutil.pid_exists()(在 Windows 上底层使用 OpenProcess + GetExitCodeProcess——无信号)。如果你在编写插件或补丁,请直接使用 psutil.pid_exists() 或 gateway.status._pid_exists()——永远不要用 os.kill(pid, 0)。
scripts/check-windows-footguns.py 在 CI 中强制执行此规则:任何新的 os.kill(pid, 0) 调用都会导致 Windows footguns (blocking) 检查失败,除非该行带有 # windows-footgun: ok — <reason> 标记。
常见问题
安装后立即出现 hermes: command not found。
打开新的 PowerShell 窗口。安装程序已将 %LOCALAPPDATA%\hermes\bin 添加到用户 PATH,但现有 shell 需要重启才能获取更新。在此期间可以运行 & "$env:LOCALAPPDATA\hermes\bin\hermes.exe"。
运行工具时出现 WinError 193: %1 is not a valid Win32 application。
你触发了绕过 .cmd 垫片的 shebang 脚本调用。Hermes 通过 shutil.which(cmd, path=local_bin) 解析命令,使 PATHEXT 能识别 .CMD——如果你通过硬编码路径调用工具,请切换到 .cmd 变体(例如使用 npx.cmd 而非 npx)。
[scriptblock]::Create(...) 失败,提示 The assignment expression is not valid。
你下载的 install.ps1 携带了 UTF-8 BOM。irm | iex 形式会自动剥离 BOM;[scriptblock]::Create((irm ...)) 不会。请改用简单的 irm | iex 形式,或手动下载脚本并通过 [IO.File]::WriteAllText($path, $text, (New-Object Text.UTF8Encoding $false)) 保存为不带 BOM 的纯 UTF-8。
重启后 gateway 无法持续运行。
运行 hermes gateway status——它会合并 schtasks 条目、Startup 文件夹快捷方式(如有)和运行中的 PID。如果 schtasks 已注册但未运行,组策略可能阻止了 ONLOGON 触发器。运行 schtasks /Query /TN HermesGateway /V /FO LIST 查看任务失败原因,或通过卸载后使用 HERMES_GATEWAY_FORCE_STARTUP=1 重新安装来回退到 Startup 文件夹路径。
设置 $env:EDITOR 后 /edit 仍然无响应。
你只在当前进程中设置了它;请关闭并重新打开 shell,或在系统属性 → 环境变量中以用户作用域设置。在新 PowerShell 窗口中用 echo $env:EDITOR 验证。
浏览器工具启动了,但工具调用超时。
运行 hermes doctor 和 hermes pm doctor,并通过 hermes tools 检查所选浏览器后端。不要向签名包写入另一个 Playwright 版本。
agent-browser 报奇怪的 Node 版本错误。
运行 hermes pm doctor 并检查当前 Hermes 入口。PM 提供固定的 Node 版本,不要为了修复 Hermes 而删除其他程序使用的系统 Node。
CLI 中中文/日文/阿拉伯文字符显示为 ?。
UTF-8 stdio 垫片未激活。检查 HERMES_DISABLE_WINDOWS_UTF8 是否未设置(Get-ChildItem env:HERMES_DISABLE_WINDOWS_UTF8)。如果该变量为空但仍然看到 ?,控制台宿主(非常旧的 cmd.exe)可能完全不支持 UTF-8——请切换到 Windows Terminal。
Gateway 无法发送 Telegram 图片——"BadRequest: payload contains invalid characters"。
这与 Windows 无关,但有时首先在 Windows 上暴露。通常意味着 JSON 请求体中的文件路径包含未转义的反斜杠。Telegram 应该收到 Hermes 规范化后的路径,而非原始 Windows 路径——如果你在自定义插件中看到此问题,请确保传递的是 Hermes 提供的路径,而非来自用户输入的 str(Path(...))。
git pull 后出现"在我另一台机器上能用"的编码怪象。
如果你在 Windows 上使用非 UTF-8 编辑器(旧版 Windows 的 Notepad、某些中文输入法)编辑了 Hermes 配置或技能文件,该文件可能带 BOM 保存。Hermes 在大多数配置读取中能容忍 utf-8-sig,但折叠 YAML 标量(description: >)内部的 BOM 会静默破坏 YAML 解析。请将文件重新保存为不带 BOM 的纯 UTF-8。
下一步
- 安装 — 完整安装页面,包括 Linux/macOS/WSL2。
- Windows(WSL2)指南 — 如果你需要 POSIX 语义或 dashboard 终端面板。
- CLI 参考 — 所有 hermes 子命令。
- FAQ — 常见的非 Windows 专属问题。
- 消息 Gateway — 在 Windows 上运行 Telegram/Discord/Slack。
Windows (WSL2) 指南
Hermes Agent 现已同时支持原生 Windows 和 WSL2。本页介绍 WSL2 路径;如需原生 PowerShell 安装方式,请参阅专属的 Windows(原生)指南。
何时选择 WSL2 而非原生:
- 你想使用 dashboard 内嵌终端(/chat 标签页)—— 该面板需要 POSIX PTY(伪终端),仅 WSL2 支持。
- 你在进行大量 POSIX 相关的开发工作,希望 Hermes 会话与开发工具共享同一文件系统和路径。
- 你已有 WSL2 环境,不想维护第二套安装。
何时原生更合适(甚至更好):
- 交互式聊天、gateway(Telegram/Discord 等)、cron 调度器、浏览器工具、MCP 服务器以及大多数 Hermes 功能均可在 Windows 上原生运行。
- 你不想在每次引用文件或打开 URL 时都考虑跨越 WSL↔Windows 边界的问题。
在 WSL2 中,实际上有两台"计算机"同时运行:你的 Windows 宿主机,以及由 WSL 管理的 Linux 虚拟机。大多数困惑都源于不清楚自己当前处于哪一侧。
本指南涵盖这种分离中专门影响 Hermes 的部分:安装 WSL2、在 Windows 与 Linux 之间传输文件、双向网络配置,以及实际遇到的常见问题。
简体中文
最小安装路径的中文说明维护在本页 —— 通过右上角的语言菜单切换,选择简体中文即可查看。
为什么选择 WSL2(而非原生 Windows)
原生 Windows 安装直接运行在 Windows 上:使用 Windows 终端(PowerShell、Windows Terminal 等)、Windows 文件系统路径(C:\Users\…)和 Windows 进程。Hermes 使用 Git Bash 执行 shell 命令,这也是 Claude Code 等 agent 目前处理 Windows 的方式 —— 无需完整重写即可绕过 POSIX 与 Windows 的差异。
WSL2 在轻量级虚拟机中运行真实的 Linux 内核,因此其中的 Hermes 与在 Ubuntu 上运行几乎完全相同。当你需要真正的 POSIX 环境时,这非常有价值:fork、/tmp、UNIX socket、信号语义、PTY 支持的终端、bash/zsh 等 shell,以及 rg、git、ffmpeg 等在 Linux 上行为一致的工具。
WSL2 的实际影响:
- Hermes CLI、gateway、会话、内存、技能和工具运行时均位于 Linux 虚拟机内部。
- Windows 程序(浏览器、原生应用、带登录 profile 的 Chrome)位于虚拟机外部。
- 每次需要两者通信时 —— 共享文件、打开 URL、控制 Chrome、访问本地模型服务器、将 Hermes gateway 暴露给手机 —— 都需要跨越一道边界。这些边界正是本指南要讲的内容。
安装 WSL2
在管理员 PowerShell 或 Windows Terminal 中执行:
wsl --install在全新的 Windows 10 22H2+ 或 Windows 11 上,此命令会安装 WSL2 内核、虚拟机平台功能以及默认的 Ubuntu 发行版。按提示重启。重启后 Ubuntu 会打开并要求设置 Linux 用户名和密码 —— 这是一个全新的 Linux 用户,与你的 Windows 账户无关。
验证你确实在使用 WSL2(而非旧版 WSL1):
wsl --list --verbose应显示 VERSION 2。如果某个发行版显示 VERSION 1,请转换:
wsl --set-version Ubuntu 2
wsl --set-default-version 2Hermes 在 WSL1 上无法可靠运行 —— WSL1 会动态转译 Linux 系统调用,某些行为(procfs、信号、网络)与真实 Linux 存在偏差。
发行版选择
我们以 Ubuntu(LTS)为测试基准。Debian 同样可用。Arch 和 NixOS 也有人在用,但一键安装脚本假设使用基于 Debian 的 apt 系统 —— 如需其他路径,请参阅 Nix 安装指南。
启用 systemd(推荐)
Hermes gateway(以及任何你希望持续运行的服务)在 systemd 下更易管理。在现代 WSL 上,在发行版内执行一次即可启用:
sudo tee /etc/wsl.conf >/dev/null <<'EOF'
[boot]
systemd=true
[interop]
enabled=true
appendWindowsPath=true
[automount]
options = "metadata,umask=22,fmask=11"
EOF然后在 PowerShell 中执行:
wsl --shutdown重新打开 WSL 终端。ps -p 1 -o comm= 应输出 systemd。
上面的 metadata 挂载选项很重要 —— 没有它,/mnt/c/... 上的文件无法存储真实的 Linux 权限位,这会导致在 Windows 路径下对脚本执行 chmod +x 等操作失效。
在 WSL 内安装 Hermes
打开 WSL2 shell 后执行:
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
source ~/.bashrc
hermes安装程序将 WSL2 视为普通 Linux —— 无需任何 WSL 专属配置。完整目录结构请参阅安装说明。
文件系统:跨越 Windows ↔ WSL2 边界
这是最容易让人踩坑的部分。存在两套文件系统,文件放在哪里至关重要 —— 影响性能、正确性以及哪些工具能访问到它。
两个方向
| | 方向 | 内部路径 | 使用的路径
| Windows 磁盘,从 WSL 访问 | C:\Users\you\Documents | /mnt/c/Users/you/Documents
| WSL 磁盘,从 Windows 访问 | /home/you/code | \\wsl$\Ubuntu\home\you\code(较新版本为 \\wsl.localhost\Ubuntu\...)
两者都是真实存在的,都可以使用,但它们不是同一个文件系统 —— 底层通过 9P 网络协议桥接。这带来了真实的性能和语义差异。
Hermes 和项目应放在哪里
经验法则:将所有 Linux 相关内容保留在 Linux 文件系统内。
- 你的 Hermes 安装目录(~/.hermes/)—— Linux 侧。安装程序已自动处理。
- 你在 WSL 中开发的 git 仓库 —— Linux 侧(~/code/...、~/projects/...)。
- 你的模型、数据集、venv —— Linux 侧。
遵循此规则的好处:
- I/O 速度快。 对 /mnt/c/... 的操作需经过 9P,比原生 ext4 慢 10–100 倍。在 ~/code 下感觉瞬间完成的 git status(10k 文件仓库),在 /mnt/c 下可能需要 15 秒以上。
- 权限正确。 Linux 权限位在 /mnt/c 上只是尽力模拟。ssh 因"权限不当"拒绝密钥,或 chmod +x 静默失败,都是常见问题。
- 文件监听可靠。 跨 9P 的 inotify 不稳定 —— 文件监听器(开发服务器、测试运行器)在 /mnt/c 上经常漏报变更。
- 无大小写敏感问题。 Windows 路径默认不区分大小写;Linux 区分大小写。同时包含 Readme.md 和 README.md 的项目在两侧行为不同。
只有当你确实需要文件存在于 Windows 侧时,才将其放在 /mnt/c 下 —— 例如需要从 Windows GUI 应用打开,或 Windows Chrome 的 DevTools MCP 需要当前目录是 Windows 可访问的路径。
在两侧之间传输文件
从 Windows → 传入 WSL: 最简单的方式是打开资源管理器,在地址栏输入 \\wsl.localhost\Ubuntu,然后拖放到 \home\<you>\...。或者在 PowerShell 中:
wsl cp /mnt/c/Users/you/Downloads/file.pdf ~/incoming/从 WSL → 传入 Windows: 复制到 /mnt/c/Users/<you>/...,Windows 资源管理器会立即看到:
cp ~/reports/output.pdf /mnt/c/Users/you/Desktop/在 Windows 应用中打开 WSL 文件(GUI 编辑器、浏览器等):使用 explorer.exe 或 wslview:
sudo apt install wslu # 安装一次 —— 提供 wslview、wslpath、wslopen 等工具
wslview ~/reports/output.pdf # 用 Windows 默认程序打开
explorer.exe . # 在 Windows 资源管理器中打开当前 WSL 目录在两个世界之间转换路径:
wslpath -w ~/code/project # → \\wsl.localhost\Ubuntu\home\you\code\project
wslpath -u 'C:\Users\you' # → /mnt/c/Users/you行尾符、BOM 与 git
如果你在 Windows 侧用 Windows 编辑器编辑文件,可能会产生 CRLF 行尾符。当 Linux 侧的 bash 或 Python 读取这些文件时,shell 脚本会报错 bad interpreter: /bin/bash^M,带 BOM 的 .env 文件也可能导致 Python 失败。
解决方法是在 WSL 内(而非 Windows 上)配置合理的 git 设置:
git config --global core.autocrlf input
git config --global core.eol lf对于已有 CRLF 的文件:
sudo apt install dos2unix
dos2unix path/to/script.sh"在 WSL 内 clone 还是在 /mnt/c 上 clone?"
在 WSL 内 clone。始终如此,除非有特殊原因。典型的 Hermes 工作流(hermes chat、调用 rg/ripgrep 搜索仓库的工具、文件监听器、后台 gateway)在 ~/code/myrepo 下会比在 /mnt/c/Users/you/myrepo 下快得多,也更可靠。
一个例外:启动 Windows 二进制文件的 MCP bridge。 如果你通过 cmd.exe 使用 chrome-devtools-mcp(参见 MCP 指南:WSL → Windows Chrome),当 Hermes 的当前工作目录是 ~ 时,Windows 可能会报 UNC 警告。此时请从 /mnt/c/ 下的某个目录启动 Hermes,以便 Windows 进程拥有一个带盘符的工作目录。
网络:WSL ↔ Windows
WSL2 在轻量级虚拟机中运行,拥有独立的网络栈。这意味着 WSL 内的 localhost 与 Windows 上的 localhost 并不相同 —— 从网络角度看,它们是两台独立的主机。对于每个服务,你需要确定流量方向,并选择正确的桥接方式。
以下两种情况最为常见。
情况一 —— WSL 中的 Hermes 访问 Windows 上的服务
最常见的场景:你在 Windows 上运行 Ollama、LM Studio 或 llama-server,而 WSL 内的 Hermes 需要访问它。
此场景的权威说明在 providers 指南中:WSL2 本地模型网络配置 →
简要说明:
- Windows 11 22H2+: 启用镜像网络模式(在 %USERPROFILE%\.wslconfig 中设置 networkingMode=mirrored,然后执行 wsl --shutdown)。之后 localhost 在两侧均可互通。
- Windows 10 或旧版本: 使用 Windows 宿主机 IP(WSL 虚拟网络的默认网关),并确保 Windows 上的服务绑定到 0.0.0.0 而非仅 127.0.0.1。通常还需要在 Windows 防火墙中为该端口添加规则。
完整表格(Ollama / LM Studio / vLLM / SGLang 绑定地址、防火墙规则一行命令、动态 IP 辅助工具、Hyper-V 防火墙解决方案)请点击上方链接 —— 此处不再重复。
情况二 —— Windows(或局域网)上的设备访问 WSL 中的 Hermes
这是反向情况,其他地方较少记录,但以下场景需要用到:
- 从 Windows 浏览器使用 Hermes Web Dashboard。
- 从 Windows 侧工具使用 OpenAI 兼容 API 服务器(当 API_SERVER_ENABLED=true 时由 hermes gateway 暴露)。参见 API Server 功能页。
- 测试消息 gateway(Telegram、Discord 等),平台会向本地 webhook URL 发送请求 —— 通常建议使用 cloudflared/ngrok 而非原始端口转发。
子情况 2a:从 Windows 宿主机本身访问
在启用了镜像模式的 Windows 11 22H2+ 上,无需任何额外操作。WSL 中绑定到 0.0.0.0:8080(甚至 127.0.0.1:8080)的进程,可直接从 Windows 浏览器通过 http://localhost:8080 访问。WSL 会自动将绑定发布回宿主机。
在 NAT 模式(Windows 10 / 旧版 Windows 11)下,WSL2 默认的"localhost 转发"通常会将 Linux 侧的 127.0.0.1 绑定转发到 Windows 的 localhost,因此以 --host 127.0.0.1 启动的 Hermes 服务通常可从 Windows 通过 http://localhost:PORT 访问。如果无法访问:
- 在 WSL 内显式绑定到 0.0.0.0。
- 用 ip -4 addr show eth0 | grep inet 获取 WSL 虚拟机的 IP,然后从 Windows 直接访问该 IP。
子情况 2b:从局域网中的其他设备访问(手机、平板、另一台 PC)
这才是真正麻烦的地方。流量路径为 局域网设备 → Windows 宿主机 → WSL 虚拟机,你需要分别配置两段:
-
在 WSL 内绑定所有网络接口。 监听 127.0.0.1 的进程永远无法从虚拟机外部访问。请使用 0.0.0.0。
-
配置 Windows → WSL 虚拟机的端口转发。 镜像模式下自动完成。NAT 模式下需要在管理员 PowerShell 中手动配置,每个端口单独设置:
# 获取 WSL 虚拟机当前 IP(NAT 模式下每次重启 WSL 都会变化)
$wslIp = (wsl hostname -I).Trim().Split(' ')[0]
# 将 Windows 端口 8080 转发到 WSL:8080
netsh interface portproxy add v4tov4 `
listenaddress=0.0.0.0 listenport=8080 `
connectaddress=$wslIp connectport=8080
# 在 Windows 防火墙中放行该端口
New-NetFirewallRule -DisplayName "Hermes WSL 8080" `
-Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow之后可用以下命令删除:netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=8080。
-
让局域网设备访问 http://<windows-lan-ip>:8080。
由于 NAT 模式下 WSL 虚拟机 IP 在每次重启后都会变化,一次性配置的规则在下次 wsl --shutdown 后即失效。如需持久化,要么启用镜像模式,要么将端口代理步骤写入 Windows 登录时自动运行的脚本。
对于来自云端消息服务商的 webhook(Telegram setWebhook、Slack 事件等),不建议折腾端口转发 —— 请使用 cloudflared 隧道。参见 webhook 指南。
在 Windows 上长期运行 Hermes 服务
Hermes 的 Tool Gateway 和 API 服务器都是长期运行的进程。在 WSL2 中,有以下几种方式保持它们持续运行。
在 WSL 内使用 systemd(推荐)
如果你按照上面的安装步骤启用了 systemd,hermes gateway 和 API 服务器的使用方式与任何 Linux 机器上完全相同。使用 gateway 设置向导:
hermes gateway setup它会提示是否安装 systemd 用户单元,以便在 WSL 启动时自动拉起 gateway。
让 WSL 在 Windows 登录时自动启动
WSL 虚拟机只在有进程使用时保持运行。若要在没有终端窗口的情况下保持 gateway 可访问,可通过任务计划程序在 Windows 登录时启动一个 WSL 进程:
- 触发器: 用户登录时(你的账户)。
- 操作: 启动程序
- 程序:C:\Windows\System32\wsl.exe
- 参数:-d Ubuntu --exec /bin/sh -c "sleep infinity"
这样可以保持虚拟机存活,使 systemd 管理的 gateway 持续运行。在 Windows 11 上,较新的 wsl --install --no-launch + 自动启动流程也可以实现;sleep infinity 方案是兼容性最好的版本。
GPU 直通(本地模型)
WSL2 自 WSL 内核 5.10.43+ 起原生支持 NVIDIA GPU —— 在 Windows 上安装标准 NVIDIA 驱动(不要在 WSL 内安装 Linux NVIDIA 驱动),WSL 内的 nvidia-smi 即可识别 GPU。之后,CUDA 工具链、torch、vllm、sglang 和 llama-server 均可正常使用真实 GPU。
AMD ROCm 和 Intel Arc 在 WSL2 内的支持仍在发展中,不在 Hermes 的测试范围内 —— 使用当前驱动可能可以工作,但我们暂无推荐方案。
如果你运行的是原生 Windows 本地模型服务器(Windows 版 Ollama、LM Studio),它已通过 Windows 驱动使用 GPU,则完全不需要 WSL GPU 直通 —— 只需按照上面的情况一,从 WSL 通过网络访问即可。
常见问题
连接 Windows 上的 Ollama / LM Studio 时报"Connection refused"。
参见 WSL2 网络配置。九成情况是服务绑定在 127.0.0.1 上,需要改为 0.0.0.0(Ollama:OLLAMA_HOST=0.0.0.0),或者缺少防火墙规则。
git status / hermes chat 在仓库中极慢。
你很可能在 /mnt/c/... 下工作。将仓库移到 ~/code/...(Linux 侧),速度会有数量级的提升。
脚本报错 bad interpreter: /bin/bash^M。
Windows 编辑器产生的 CRLF 行尾符。执行 dos2unix script.sh,并在 WSL git 配置中设置 core.autocrlf input。
通过 MCP 启动 Windows 二进制文件时出现"UNC paths are not supported"警告。
Hermes 的工作目录在 Linux 文件系统内,Windows cmd.exe 无法识别。在该会话中从 /mnt/c/... 下启动 Hermes,或使用一个在调用 Windows 可执行文件前先 cd 到 Windows 可访问路径的包装脚本。
休眠/睡眠后时钟漂移。
宿主机从睡眠恢复后,WSL2 的时钟可能滞后数分钟,导致所有基于证书的操作失败(OAuth、HTTPS API)。按需修复:
sudo hwclock -s或安装 ntpdate 并在登录时运行。
启用镜像模式后或连接 VPN 时 DNS 停止工作。
镜像模式会将宿主机网络设置代理到 WSL —— 如果 Windows DNS 有问题(VPN 分流隧道、企业解析器),WSL 会继承这些问题。解决方法:手动覆盖 resolv.conf(在 /etc/wsl.conf 中设置 generateResolvConf=false,然后手动编写 /etc/resolv.conf,填入 1.1.1.1 或你的 VPN DNS)。
运行安装程序后找不到 hermes 命令。
安装程序通过 ~/.bashrc 将 ~/.local/bin 添加到 shell 的 PATH 中。需要执行 source ~/.bashrc(或打开新终端)才能在当前会话中生效。
Windows Defender 对 WSL 文件扫描很慢。
Defender 通过 9P 桥接扫描从 Windows 访问的文件,这会放大 /mnt/c 风格跨边界访问的延迟。如果你只在 WSL 内部访问 WSL 文件,则不受影响。如果你频繁使用 Windows 工具访问 \\wsl$\...,可考虑将 WSL 发行版路径排除在实时扫描之外。
磁盘空间不足。
WSL2 将虚拟机磁盘存储为 %LOCALAPPDATA%\Packages\... 下的稀疏 VHDX 文件。它会自动增长,但删除文件后不会自动收缩。回收空间的方法:执行 wsl --shutdown,然后在管理员 PowerShell 中运行 Optimize-VHD -Path <path-to-ext4.vhdx> -Mode Full(需要 Hyper-V 工具),或使用 WSL 文档中记录的更简单的 diskpart 方式。
下一步
- 安装说明 —— 实际安装步骤(Linux/WSL2 使用同一安装程序)。
- 集成 → Providers → WSL2 网络配置 —— 本地模型服务器网络配置的权威深度说明。
- MCP 指南 → WSL → Windows Chrome —— 从 WSL 中的 Hermes 控制你已登录的 Windows Chrome。
- Tool Gateway 和 Web Dashboard —— 你最常需要从 WSL 暴露到网络其他部分的长期运行服务。