feat: 灵犀 Studio Web UI 定制版
Build / build (push) Has been cancelled
NPM Lockfile Check / npm ci --ignore-scripts (push) Has been cancelled
Playwright / e2e (push) Has been cancelled

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yi
2026-06-05 11:29:11 +08:00
commit 7d10320a82
643 changed files with 164406 additions and 0 deletions
+472
View File
@@ -0,0 +1,472 @@
# CLI/Bridge Chat Sessions 实现文档
> 状态:本文档描述当前 `main` 中 Web UI 聊天会话的 API Server / Bridge(beta) 双路径实现。
## 概述
当前实现把原来的聊天通道统一到 Socket.IO namespace `/chat-run`。前端仍使用同一套 `ChatPanel + MessageList + ChatInput`,通过会话的 `source` 字段区分运行方式:
| source | 运行路径 | 说明 |
|--------|----------|------|
| `api_server` | Web UI Server → Hermes Gateway `/v1/responses` | 默认聊天路径 |
| `cli` | Web UI Server → Python agent bridge → `AIAgent` | Bridge(beta),在 Web UI 服务端子进程里直接运行 Hermes Agent |
Bridge 会话不是一个独立 UI 面板,而是普通会话的一种来源。用户通过“新建聊天”下拉菜单选择 `API``Bridge (beta)`
Bridge 模式支持:
- 流式文本输出
- reasoning/thinking 增量
- tool started/completed 事件
- 工具审批请求与响应
- abort 中断
- per-session 队列
- profile 隔离
- 从 DB resume 会话
- 与 API Server 路径共用上下文压缩逻辑
当前不再支持旧文档里的独立 `/cli-chat-run` namespace、`CliChatPanel.vue``cli-chat.ts` 和独立 `command` / `steer` socket 事件。CLI/Bridge 会话中的 slash command 现在通过统一 `/chat-run``run` payload 进入后端解析;当前支持 `/usage``/status``/abort``/queue``/clear``/title``/compress``/steer``/destroy`
---
## 整体架构
```text
ChatPanel.vue
├─ MessageList.vue
└─ ChatInput.vue
│ Socket.IO /chat-run
ChatRunSocket (Node.js)
├─ source=api_server → Hermes Gateway /v1/responses
└─ source=cli → AgentBridgeClient
│ TCP/Unix socket, newline JSON
hermes_bridge.py
│ in-process import
AIAgent (hermes-agent)
```
### 分流规则
`ChatRunSocket.resolveRunSource()` 决定本轮运行走哪个后端:
1. `run` payload 中 `source === 'cli'` 时走 bridge。
2. `source === 'api_server'` 时走 gateway。
3. 未显式传 `source` 时,如果 DB 中已有 session 的 `source``cli`,继续走 bridge。
4. 其他情况默认走 `api_server`
---
## 主要文件
### 前端
| 文件 | 说明 |
|------|------|
| `packages/client/src/components/hermes/chat/ChatPanel.vue` | 统一聊天面板;新建菜单包含 `API``Bridge (beta)`;渲染审批条 |
| `packages/client/src/components/hermes/chat/MessageList.vue` | 统一消息列表;展示文本、reasoning、tool 消息等 |
| `packages/client/src/components/hermes/chat/ChatInput.vue` | 统一输入框;发送、停止、附件上传入口 |
| `packages/client/src/api/hermes/chat.ts` | `/chat-run` Socket.IO 客户端;注册 session 事件处理器;发送 run/abort/approval |
| `packages/client/src/stores/hermes/chat.ts` | 会话状态、发送流程、resume、队列、审批、消息映射 |
### 后端
| 文件 | 说明 |
|------|------|
| `packages/server/src/services/hermes/run-chat/index.ts` | `/chat-run` Socket.IO 入口;按 `source` 分流 API Server 与 Bridge 运行 |
| `packages/server/src/services/hermes/run-chat/handle-api-run.ts` | API Server 路径;调用 Hermes Gateway `/v1/responses` 并消费流式响应 |
| `packages/server/src/services/hermes/run-chat/handle-bridge-run.ts` | Bridge 路径;调用 Agent Bridge 并写入本地会话库 |
| `packages/server/src/services/hermes/run-chat/session-command.ts` | CLI/Bridge slash command 解析与处理 |
| `packages/server/src/services/hermes/agent-bridge/client.ts` | Node 端 bridge 客户端;通过 socket 请求 Python bridge |
| `packages/server/src/services/hermes/agent-bridge/manager.ts` | Python bridge 子进程生命周期管理 |
| `packages/server/src/services/hermes/agent-bridge/hermes_bridge.py` | Python bridge 服务;创建并复用 `AIAgent` 实例 |
| `packages/server/src/services/hermes/agent-bridge/index.ts` | bridge 模块导出 |
| `packages/server/src/index.ts` | 启动 `AgentBridgeManager``ChatRunSocket` |
| `packages/server/src/services/shutdown.ts` | 关闭时停止 chat socket 和 bridge 子进程 |
| `packages/server/src/controllers/hermes/sessions.ts` | 会话列表和详情读取,包含 `source` 信息 |
| `packages/server/src/controllers/hermes/profiles.ts` | profile 管理接口;按 URL/body 中的 profile 做权限校验 |
### 已移除的旧文件
| 文件 | 状态 |
|------|------|
| `packages/client/src/api/hermes/cli-chat.ts` | 已删除 |
| `packages/client/src/components/hermes/chat/CliChatPanel.vue` | 已删除 |
| `packages/server/src/services/hermes/cli-chat-run-socket.ts` | 已删除 |
---
## 前端流程
### 新建会话
`ChatPanel.vue` 中的新建按钮使用下拉菜单:
- `API`:调用 `chatStore.newChat()`,创建默认 `api_server` 会话。
- `Bridge (beta)`:调用 `chatStore.newCliSession()`,创建 `source: 'cli'` 会话。
Bridge 会话 ID 使用类似 `YYYYMMDD_HHMMSS_xxxxxx` 的格式,便于与 Hermes CLI 风格的 session ID 对齐。
### 发送消息
1. `ChatInput.vue` 触发 store 的发送逻辑。
2. `chat.ts` 根据 active session 组装输入内容,附件会被转为 `ContentBlock[]`
3. 调用 `startRunViaSocket()`
4. 前端向 `/chat-run` emit
```ts
socket.emit('run', {
session_id,
input,
instructions,
model,
queue_id,
source, // api_server 或 cli
})
```
5. 前端注册本 session 的事件 handler,通过 `session_id` 隔离多会话并发事件。
### Resume
切换会话、页面恢复可见、或刷新后,前端通过:
```ts
socket.emit('resume', { session_id })
```
服务端返回:
```ts
{
session_id,
messages,
isWorking,
isAborting,
events,
inputTokens,
outputTokens,
queueLength,
}
```
如果服务端发现该 session 仍在运行,前端会重新注册 handler,并允许继续 abort。
### 审批
Bridge 工具需要人工确认时,服务端会发 `approval.requested`,前端 store 记录为 `activePendingApproval``ChatPanel.vue` 在输入框上方显示审批条。
前端响应审批:
```ts
socket.emit('approval.respond', {
session_id,
approval_id,
choice, // once | session | always | deny
})
```
---
## `/chat-run` Socket.IO 协议
### 客户端 → 服务端
| 事件 | 数据 | 说明 |
|------|------|------|
| `run` | `{ session_id, input, model?, instructions?, queue_id?, source? }` | 启动一轮运行;`source` 决定 API Server 或 Bridge |
| `resume` | `{ session_id }` | 加入 session room 并恢复状态 |
| `abort` | `{ session_id }` | 中断当前运行 |
| `cancel_queued_run` | `{ session_id, queue_id }` | 取消等待队列中的一条 run |
| `approval.respond` | `{ session_id, approval_id, choice }` | 响应 Bridge 工具审批 |
客户端不再发送独立 `command``steer` Socket.IO 事件;slash command 作为普通 `run.input` 进入 `/chat-run`,由服务端在 `source=cli` 时解析。
### 服务端 → 客户端
| 事件 | 说明 |
|------|------|
| `resumed` | 返回 DB 消息、运行状态、队列长度和最近事件 |
| `run.started` | 运行开始 |
| `run.queued` | 当前 session 已有运行,新请求进入队列 |
| `message.delta` | 文本增量 |
| `reasoning.delta` | reasoning 增量 |
| `thinking.delta` | thinking 增量 |
| `reasoning.available` | reasoning 内容可用 |
| `tool.started` | 工具调用开始 |
| `tool.completed` | 工具调用结束 |
| `approval.requested` | Bridge 工具请求人工审批 |
| `approval.resolved` | 审批完成或超时 |
| `compression.started` | 上下文压缩开始 |
| `compression.completed` | 上下文压缩结束 |
| `usage.updated` | token 用量更新 |
| `abort.started` | 中断开始 |
| `abort.completed` | 中断结束 |
| `session.command` | slash command 的执行结果或错误反馈 |
| `run.completed` | 运行完成 |
| `run.failed` | 运行失败 |
### 认证
`/chat-run` 使用 Socket.IO auth token
```ts
io(`${baseUrl}/chat-run`, {
auth: { token },
query: { profile },
})
```
服务端会与 Web UI token 比对。
---
## ChatRunSocket 后端行为
### API Server 路径
`source=api_server` 时:
1. 写入用户消息到 Web UI 本地 session DB。
2. 通过 `buildCompressedHistory()` 构建上下文。
3. 请求当前 profile 的 Hermes Gateway
```text
POST <upstream>/v1/responses
```
4. 读取 SSE frame,映射为统一的 `/chat-run` 事件。
5. 完成后写入 assistant/tool 消息,更新 usage。
### Bridge 路径
`source=cli` 时:
1. 写入用户消息到 Web UI 本地 session DB。
2. 复用同一套 `buildCompressedHistory()` 构建压缩上下文。
3. 调用:
```ts
this.bridge.chat(session_id, input, history, instructions, profile)
```
4. 轮询 `AgentBridgeClient.streamOutput(run_id)`
5. 将 Python bridge 的 delta 和 events 映射成统一事件。
6. 将 assistant 文本、reasoning、tool 调用结果 flush 回 DB。
### 队列
同一个 `session_id` 同时只能有一个 active run。新的 `run` 到达时:
- 如果当前 session 正在运行,则放入 `state.queue`
- 发送 `run.queued` 更新队列长度。
- 当前 run 结束或 abort 完成后,自动执行下一条 queued run。
---
## Python Agent Bridge
### 通信协议
Node 和 Python bridge 之间使用本地 socket 的单行 JSON 协议:
```json
{ "action": "chat", "session_id": "xxx", "message": "hello" }
```
响应也是单行 JSON
```json
{ "ok": true, "run_id": "xxx", "session_id": "xxx", "status": "running" }
```
### Endpoint
默认 endpoint 按平台选择:
| 平台 | 默认 endpoint |
|------|---------------|
| Windows | `tcp://127.0.0.1:18765` |
| macOS/Linux | `ipc:///tmp/hermes-agent-bridge.sock` |
Windows 使用 TCP 是因为部分 Python/Windows 环境没有 Unix domain socket 支持。
### 当前实际使用的 action
| Action | 说明 |
|--------|------|
| `chat` | 启动一轮 `AIAgent.run_conversation()` |
| `get_output` | 通过 `cursor``event_cursor` 获取增量文本与事件 |
| `interrupt` | 调用 agent 中断当前运行 |
| `approval_respond` | 响应工具审批 |
| `destroy_all` | 维护动作;仅用于明确的全量清理/进程关闭场景,普通 profile 切换不会调用 |
bridge 代码里还保留了一些调试/维护 action,例如 `ping``get_result``get_history``destroy``list``shutdown``steer`。当前 `/chat-run` 前端路径不会直接暴露这些 action;需要的能力由 Node `/chat-run` 层封装,例如 `/steer` slash command 会调用 `steer` action。
旧的 `command` action 已移除,Python bridge 不再直接解析 `/new``/undo``/retry``/branch` 等旧斜杠命令;当前 CLI/Bridge slash command 支持范围以 Node `/chat-run``session-command.ts` 为准。
### 会话和 profile
`AgentPool` 维护 `session_id -> AgentSession`
- 每个 session 持有独立 `AIAgent` 实例。
- session 按请求中的 profile 创建和复用;前端切换 Hermes Profile 只改变后续请求使用的 profile,不会影响其他 bridge 内存 session。
- `HERMES_HOME` 会在创建 agent 时临时切到 profile home。
- `SessionDB` 按 profile 的 `state.db` 路径缓存。
- 空闲 session 会被 bridge GC,默认 30 分钟无运行后销毁内存态。
### 工具和审批事件
bridge 从 `AIAgent` 回调中收集事件:
- `stream.delta`
- `reasoning.delta`
- `thinking.delta`
- `tool.started`
- `tool.completed`
- `tool.progress`
- `approval.requested`
- `approval.resolved`
- `turn.boundary`
- `status`
`ChatRunSocket` 会把这些事件转换为前端统一事件,并负责 DB 落盘。
审批默认等待 60 秒,超时自动 `deny`
---
## AgentBridgeClient
`AgentBridgeClient` 是 Node 端本地 socket 客户端。
行为:
- 支持 `ipc://``tcp://` endpoint。
- 每次请求新建 socket,发送一行 JSON,读取一行 JSON。
- 请求通过内部 lock 串行化。
- 默认请求响应超时为 `120000ms`
- `streamOutput()` 每 100ms 轮询一次 `get_output`
示例:
```ts
const started = await bridge.chat(sessionId, input, history, instructions, profile)
for await (const chunk of bridge.streamOutput(started.run_id)) {
// chunk.delta
// chunk.events
// chunk.done
}
```
注意:目前 socket connect 阶段没有独立 connect timeout,主要依赖系统连接错误和请求响应 timeout。
---
## AgentBridgeManager
`AgentBridgeManager` 负责启动和停止 Python bridge。
启动流程:
1. 定位 `hermes_bridge.py`
2. 发现 `hermes-agent` 根目录。
3. 选择 Python 解释器。
4. 以子进程启动:
```text
python hermes_bridge.py --endpoint <endpoint> --agent-root <root> --hermes-home <home>
```
5. 监听 stdout,等待:
```json
{ "event": "ready", "endpoint": "..." }
```
6. 默认 ready 超时为 `120000ms`
Python 选择优先级:
1. `HERMES_AGENT_BRIDGE_PYTHON`
2. `agentRoot/venv``agentRoot/.venv`
3. installed `hermes` 命令 shebang
4. `uv run --project <agentRoot> python`
5. 系统 `python3` / `python`
关闭时先发 `SIGTERM`1.5 秒后仍未退出则 `SIGKILL`
---
## 启动与关闭
### 启动
`bootstrap()` 中会先尝试启动 bridge
```ts
agentBridgeManager = await startAgentBridgeManager()
```
bridge 启动失败不会阻止 Web UI 启动,但 Bridge(beta) 会话后续运行会失败。
随后创建统一的 chat socket
```ts
chatRunServer = new ChatRunSocket(groupChatServer.getIO())
chatRunServer.init()
```
### 关闭
服务关闭时会清理:
- `/chat-run` Socket.IO 状态
- Python agent bridge 子进程
- 其他 WebSocket/Socket.IO 服务
---
## 环境变量
| 变量 | 说明 |
|------|------|
| `HERMES_AGENT_BRIDGE_ENDPOINT` | Bridge endpointWindows 默认 `tcp://127.0.0.1:18765`macOS/Linux 默认 `ipc:///tmp/hermes-agent-bridge.sock` |
| `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT` | profile worker endpoint transport;设为 `tcp` 使用 loopback TCP,设为 `ipc`/`unix` 使用 Unix domain socket;默认 Windows 使用 TCPmacOS/Linux 使用 IPC |
| `HERMES_AGENT_BRIDGE_WORKER_PORT_BASE` | TCP worker endpoint 起始端口,默认 `18780`;仅在 worker transport 为 TCP 时生效 |
| `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT` | Version Preview 的 bridge broker endpoint transport;设为 `tcp` 可让预览环境在 macOS/Linux 上也使用 loopback TCP;未设置时会跟随 `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp` |
| `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_ENDPOINT` | 直接覆盖 Version Preview 的 bridge broker endpoint;用于需要完全自定义预览 bridge 地址的部署 |
| `HERMES_AGENT_BRIDGE_TIMEOUT_MS` | Node 等待 bridge 请求响应的超时,默认 `120000` ms |
| `HERMES_AGENT_BRIDGE_CONNECT_RETRY_MS` | Node 连接 bridge socket 失败时的短重试窗口,默认 `5000` ms |
| `HERMES_AGENT_BRIDGE_STARTUP_TIMEOUT_MS` | Node 等待 Python bridge ready 的超时,默认 `120000` ms |
| `HERMES_AGENT_BRIDGE_AUTO_RESTART` | bridge broker 意外退出后是否自动重启;设为 `0`/`false`/`no`/`off` 可关闭,默认开启 |
| `HERMES_AGENT_BRIDGE_RESTART_DELAY_MS` | bridge broker 自动重启基础延迟,默认 `1000` ms,连续失败时最多退避到 `30000` ms |
| `HERMES_AGENT_BRIDGE_PYTHON` | 指定 Python 解释器路径 |
| `HERMES_AGENT_ROOT` | hermes-agent 安装目录 |
| `HERMES_AGENT_BRIDGE_UV` | 指定 uv 可执行文件路径 |
| `HERMES_AGENT_BRIDGE_PLATFORM` | bridge 传给 Hermes Agent 的平台标识,默认 `cli` |
| `HERMES_BRIDGE_PROVIDER` | 覆盖 bridge 使用的 provider |
| `HERMES_BRIDGE_MAX_TURNS` | 覆盖 bridge 最大轮数 |
| `UV` | uv 可执行文件路径 fallback |
正常使用不需要配置这些变量。Bridge 支持多个用户/多个 profile 的运行并存;Web UI 的 Hermes Profile 切换不会重启 bridge 或销毁其他正在运行的任务。`HERMES_AGENT_BRIDGE_ENDPOINT` 控制 Node 与 Python bridge broker 的连接地址;`HERMES_AGENT_BRIDGE_WORKER_TRANSPORT` 控制 broker 与每个 profile worker 的连接方式。macOS/Linux 默认仍使用 IPC;如果 Electron、沙盒或安全软件环境下 IPC 不稳定,可以设置 `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp` 切到 loopback TCP。Version Preview 默认继续使用独立的 broker endpoint,也会为 TCP worker 使用独立端口段,避免和正式实例共享端口池;如需让 Preview 的 broker 在 macOS/Linux 上也走 TCP,可设置 `HERMES_WEB_UI_PREVIEW_AGENT_BRIDGE_TRANSPORT=tcp`,未设置时会跟随 `HERMES_AGENT_BRIDGE_WORKER_TRANSPORT=tcp`。Windows 下如果默认 TCP 端口被旧 bridge/broker/worker 占用,新 bridge 会先按端口杀掉旧进程树,再用同一个 endpoint 重建。
Windows 首次启动慢时可以临时放大:
```powershell
$env:HERMES_AGENT_BRIDGE_STARTUP_TIMEOUT_MS = "300000"
$env:HERMES_AGENT_BRIDGE_TIMEOUT_MS = "300000"
```
---
## 当前限制
- Bridge(beta) 仍依赖 Python bridge 成功启动;启动失败时 Web UI 可用,但 bridge 会话不可用。
- bridge socket connect 阶段还没有单独 connect timeout。
- 旧 CLI 独立面板和独立 `/cli-chat-run` namespace 已移除。
- 旧 bridge `command/steer` socket 控制层已移除;CLI/Bridge slash command 现在通过统一 `/chat-run``run.input` 解析并以 `session.command` 反馈。
+104
View File
@@ -0,0 +1,104 @@
# Docker Compose Guide
This repository ships an environment-variable driven Docker Compose setup.
## Quick Start
### Pull pre-built image (Recommended)
```bash
WEBUI_IMAGE=ekkoye8888/hermes-web-ui docker compose up -d
docker compose logs -f hermes-webui
```
Open: `http://localhost:6060`
### Build from source
```bash
docker compose up -d --build
docker compose logs -f hermes-webui
```
## Services
This compose file runs a single service:
- `hermes-webui` — Web UI dashboard with integrated Hermes Agent runtime (pre-built image or built from source)
The Web UI container is built on the `nousresearch/hermes-agent` base image and uses the Hermes CLI / agent bridge runtime for chat execution. It does not start or manage a separate Hermes gateway process.
## Environment Variables
All key runtime settings are configured from compose variables.
| Variable | Default | Description |
|---|---|---|
| `PORT` | `6060` | Web UI listen port |
| `BIND_HOST` | `0.0.0.0` | Optional Web UI bind host. Defaults to IPv4 for stable WSL/Windows access. Set `::` explicitly if you want IPv6 listening. |
| `HERMES_BIN` | `/opt/hermes/.venv/bin/hermes` | Path to Hermes CLI binary |
| `HERMES_AGENT_IMAGE` | `nousresearch/hermes-agent:latest` | Hermes Agent base image (used only during build) |
| `WEBUI_IMAGE` | `hermes-web-ui-local:latest` | Web UI image (set to `ekkoye8888/hermes-web-ui` to use pre-built) |
| `HERMES_DATA_DIR` | `./hermes_data` | Hermes runtime data directory |
Override variables directly from shell:
```bash
PORT=16060 docker compose up -d
```
Or create a `.env` file in the project root:
```
WEBUI_IMAGE=ekkoye8888/hermes-web-ui
PORT=6060
```
## Data Persistence
| Path | Description |
|---|---|
| `${HERMES_DATA_DIR}` (`./hermes_data`) | Hermes runtime data (sessions, config, profiles) |
| `${HERMES_DATA_DIR}/hermes-web-ui` | Web UI data (auth token, etc.) |
- Hermes data persists in `./hermes_data`, mapped to `/home/agent/.hermes` in the container.
- Web UI data persists in `./hermes_data/hermes-web-ui/`, mapped to `/home/agent/.hermes-web-ui` in the container.
- The auth token is auto-generated on first run and printed to container logs.
- Deleting the token file and restarting will generate a new one.
## Port Mapping
| Port | Description |
|---|---|
| `${PORT}` (6060) | Web UI dashboard |
No Hermes gateway ports are exposed by this compose setup.
## Code Runtime Behavior
- Hermes CLI binary comes from `HERMES_BIN` env (`packages/server/src/services/hermes-cli.ts`).
- If `HERMES_BIN` is not provided, code falls back to `hermes` in `PATH`.
- Profile-specific chat runs are handled through the Hermes agent bridge. The selected/requested profile is authorized per account and passed with runtime requests; switching the frontend Hermes Profile does not restart the bridge or clear other running tasks.
- Docker is a managed gateway runtime: Web UI checks profile gateways on startup, but it does not run a periodic gateway recovery loop.
## Common Operations
Recreate:
```bash
docker compose up -d --force-recreate
```
View auth token:
```bash
docker compose logs hermes-webui | grep token
# or
cat ./hermes_data/hermes-web-ui/.token
```
Stop:
```bash
docker compose down
```
+40
View File
@@ -0,0 +1,40 @@
# Harness Overview
This harness turns recurring project knowledge into files and checks that an
agent can discover without chat history.
## Goals
- Make repository context legible through short maps and deeper docs.
- Keep architecture constraints close to the code they protect.
- Give agents a deterministic validation path before opening or updating a PR.
- Prefer mechanical checks over reminder text when a rule can be verified.
## Entry Points
- `AGENTS.md` is the root map for coding agents.
- `ARCHITECTURE.md` documents package boundaries and state ownership.
- `DEVELOPMENT.md` remains the contributor rules and command reference.
- `docs/harness/validation.md` maps change types to checks.
- `docs/harness/worktree-runbook.md` explains isolated worktree development.
- `docs/harness/pr-review.md` provides a PR self-review checklist.
- `scripts/harness-check.mjs` enforces baseline repository invariants.
## Operating Model
1. Read the root map and the specific doc for the task.
2. Make the smallest scoped change.
3. Add or update focused tests when behavior changes.
4. Run `npm run harness:check` and the relevant validation commands.
5. If a failure pattern repeats, improve this harness with docs, tests, scripts,
or CI instead of relying on a longer prompt.
## What Belongs In The Harness
- Facts that future agents must know to work safely.
- Checklists that prevent repeated PR review comments.
- Scripts that fail fast on repository-wide invariants.
- Runbooks for local, CI, release, and desktop packaging flows.
Do not put long implementation notes in `AGENTS.md`. Add them under `docs/` and
link to them from the map.
+41
View File
@@ -0,0 +1,41 @@
# PR Self-Review
Use this checklist before pushing or updating a pull request.
## Scope
- The PR title states the behavior being changed.
- The diff is limited to the requested task and required harness updates.
- Unrelated formatting or refactors are not bundled into the change.
- User-facing text has locale coverage.
## Architecture
- Client code uses shared API helpers and existing UI patterns.
- Server routes stay thin and delegate reusable behavior to controllers/services.
- Web UI state uses `config.appHome` or documented helpers.
- Hermes Agent state and Web UI state remain separate.
- Subprocess calls use argument arrays instead of shell string construction.
## Tests And Validation
- A focused test was added or updated for behavior changes.
- Browser-visible flows have e2e coverage when the risk justifies it.
- `npm run harness:check` passes.
- The PR body lists validation commands that actually ran.
- Known limitations or follow-ups are called out.
## Release And CI
- Workflow changes were checked with `npm run harness:check`.
- Desktop release artifacts remain platform-specific.
- `fail_on_unmatched_files: true` is preserved when each matrix target has its
own expected artifact list.
- Package manifest changes have matching lockfile changes when dependencies
change.
## Before Merge
- CI is green or failures are explained as unrelated.
- The branch is mergeable.
- The PR does not depend on hidden local state, credentials, or uncommitted files.
+68
View File
@@ -0,0 +1,68 @@
# Validation Guide
Run the smallest relevant checks while iterating. Escalate to the broad checks
when touching shared behavior, release automation, auth, persistence, or chat.
## Always Run For PRs
```bash
npm run harness:check
```
For broad or shared changes, also run:
```bash
npm run test:coverage
npm run test:e2e
npm run build
```
## Change-Type Matrix
| Change | Minimum local validation |
| --- | --- |
| Docs only | `npm run harness:check` |
| Client component/store/API | focused `npm run test -- <pattern>`, then `npm run build` |
| User-visible browser flow | focused Vitest plus `npm run test:e2e` |
| Server controller/service/db | focused `npm run test -- tests/server/<file>` |
| Auth, profile, or credential behavior | focused server tests plus relevant e2e auth tests |
| Chat, Socket.IO, group chat | focused server tests plus relevant e2e chat tests |
| Desktop packaging | `npm run harness:check`, `npm run build`, and a platform-specific desktop build when practical |
| GitHub workflow | `npm run harness:check` and `actionlint` when available |
| Package manifests | `npm ci --ignore-scripts` and lockfile workflow expectations |
## CI Mapping
- Build workflow: installs dependencies, runs coverage, and builds production
assets on pushes and pull requests.
- Playwright workflow: runs browser e2e tests.
- NPM lockfile workflow: verifies `package-lock.json` is synchronized.
- Desktop release and manual desktop build workflows build and upload
platform-specific desktop artifacts.
- Docker workflow: builds and publishes release images.
## Release Workflow Guardrail
Desktop release jobs must upload only the artifacts that their matrix target can
produce. Keep artifact globs in matrix data and keep `fail_on_unmatched_files:
true` so missing expected files still fail.
Expected desktop release outputs:
| Target | Required release globs |
| --- | --- |
| macOS | `*.dmg`, `*.dmg.blockmap`, `*.zip`, `*.zip.blockmap`, `latest*.yml` |
| Windows | `*.exe`, `*.exe.blockmap`, `latest*.yml` |
| Linux x64 | `*.AppImage`, `*.deb`, `latest*.yml` |
| Linux arm64 | `*.AppImage`, `latest*.yml` |
## Failure Handling
When a command fails:
1. Read the first actionable error, not just the final stack trace.
2. Check whether the failure indicates missing context, missing test coverage,
or a missing mechanical rule.
3. Fix the product bug when there is one.
4. Update docs or `scripts/harness-check.mjs` when the same class of mistake
should be prevented next time.
+64
View File
@@ -0,0 +1,64 @@
# Worktree Runbook
Use a separate git worktree for agent changes so local user work remains
untouched.
## Create A Worktree
```bash
git fetch origin --prune
git worktree add -b codex/<short-topic> ../worktrees/hermes-web-ui-<short-topic> origin/main
cd ../worktrees/hermes-web-ui-<short-topic>
```
If the repository uses a fork remote, push to the remote requested by the task.
Do not rewrite or reset unrelated branches.
## Install
```bash
npm ci --ignore-scripts
npm rebuild node-pty
```
Desktop package dependencies are separate:
```bash
npm ci --prefix packages/desktop --no-audit --no-fund
```
## Isolated Runtime
Use per-worktree state and ports to avoid colliding with a running local app:
```bash
export PORT=18648
export HERMES_WEB_UI_HOME="$PWD/.tmp/hermes-web-ui"
export HERMES_WEBUI_STATE_DIR="$HERMES_WEB_UI_HOME"
export UPLOAD_DIR="$PWD/.tmp/uploads"
npm run dev
```
Do not point `HERMES_WEB_UI_HOME` at a user's real `~/.hermes-web-ui` when a task
only needs local verification.
## Browser Checks
For browser-visible changes:
```bash
npm run test:e2e
```
Prefer existing Playwright fixtures and mocked backend services. Add real-service
requirements only when the behavior cannot be represented with mocks.
## Cleanup
After a PR is pushed and no more local work is needed:
```bash
git worktree remove ../worktrees/hermes-web-ui-<short-topic>
```
Only remove the worktree you created.
+3449
View File
File diff suppressed because it is too large Load Diff