405 lines
23 KiB
Markdown
405 lines
23 KiB
Markdown
# 7-8 [process] 页面 AI Hermes Runtime BFF 下一阶段设计 v1
|
||
|
||
> 更新时间:2026-05-15
|
||
>
|
||
> 当前状态:`PROCESS`。本文承接 `7-3`、`7-4`、`7-5`、`7-6`、`7-7` 已完成的 Hermes 页面内客户端主线,写入对 `hermes-web-ui-0.5.18` Chat Runtime 架构的吸收结论。
|
||
>
|
||
> 上位依据:
|
||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
|
||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18`
|
||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-7-page-ai-mini-hermes-control-surface-v1.md`
|
||
|
||
---
|
||
|
||
## 1. 结论
|
||
|
||
`hermes-web-ui-0.5.18` 对 mnote 有用,但不能原样照搬。
|
||
|
||
可吸收的是:
|
||
|
||
- Web 端应采用 `Browser -> BFF -> Hermes gateway`,不是浏览器直接 ACP。
|
||
- BFF 应承担 runtime 管理:active run registry、resume、queue、abort、event normalization、gateway manager、错误规整。
|
||
- 前端只展示 message、tool event、状态和输入,不持有 AI 编排真相。
|
||
|
||
不可吸收的是:
|
||
|
||
- 不把 mnote 的页面 AI 聊天真相改为 Convex、SQLite 或 mnote 自建 chat DB。
|
||
- 不把 Hermes 降级成 `/v1/responses store=false` 的纯模型执行器。
|
||
- 不让 mnote-web 根据自有数据库长期组装完整 `conversation_history`。
|
||
|
||
当前冻结方向:
|
||
|
||
> **mnote 学 `hermes-web-ui` 的 BFF/runtime 架构思想,但会话真相继续归 Hermes;mnote 只保存业务事实、audit、artifact、edge、page/body/title/options 结果。**
|
||
|
||
---
|
||
|
||
## 2. 当前实现定位
|
||
|
||
当前页面 AI 接入是:
|
||
|
||
```text
|
||
Leptos 页面 AI 面板
|
||
-> mnote-web /api/hermes/client/*
|
||
-> Hermes gateway /v1/runs
|
||
-> Hermes gateway /v1/runs/{run_id}/events
|
||
```
|
||
|
||
同时:
|
||
|
||
- `profiles / skills / memory` 等设置能力由 `mnote-web` 本地 BFF 通过 Hermes CLI 或 `~/.hermes` 文件读写补齐。
|
||
- `mnote.*` 工具由 mnote Rust runtime 承接,Hermes 只通过 skill/plugin/tool 调用。
|
||
- 页面 AI 不再走旧 `/api/ai-agent/run`。
|
||
|
||
这已经满足最小闭环,但仍只是“最小 runtime 接入”,缺少 `hermes-web-ui` 已经具备的完整 Chat Runtime 能力。
|
||
|
||
---
|
||
|
||
## 3. ACP、gateway、responses 的取舍
|
||
|
||
### 3.1 ACP
|
||
|
||
ACP 适合:
|
||
|
||
- VSCode 插件
|
||
- Tauri / Electron 桌面端
|
||
- 本地 IDE host
|
||
- 需要由宿主启动 `hermes acp` 并通过 stdio JSON-RPC 深度交互的场景
|
||
|
||
ACP 不适合作为 mnote 页面 AI 第一主链:
|
||
|
||
- 浏览器不能直接 `spawn hermes acp`。
|
||
- 若 Web 使用 ACP,仍需 mnote-web 作为 ACP bridge 管理 stdio 子进程。
|
||
- 这会引入长期进程、session、backpressure、并发、恢复和崩溃清理复杂度。
|
||
- 当前 Hermes gateway 已提供 Web 更自然的 `/v1/*` 与 stream 能力。
|
||
|
||
结论:
|
||
|
||
> **ACP 后续只作为桌面端或高级 host bridge 候选,不作为页面 AI Web 主链。**
|
||
|
||
### 3.2 `/v1/runs`
|
||
|
||
`/v1/runs` 适合当前 mnote 主线:
|
||
|
||
- Hermes 持有 run / session / event 语义。
|
||
- mnote 页面 AI 只是 Hermes client。
|
||
- mnote 不需要长期组装 `conversation_history`。
|
||
- 与“AI 会话归 Hermes,mnote 能力归 Rust kernel”一致。
|
||
|
||
风险:
|
||
|
||
- mnote-web 需要补 runtime 状态管理,否则刷新、断线、队列、abort 体验弱。
|
||
- Hermes gateway 的非 `/v1/*` 管理面不足,需要 BFF 补 profiles/skills/memory/gateway manager。
|
||
|
||
结论:
|
||
|
||
> **短期继续以 `/v1/runs` 为默认页面 AI 主链,并在 mnote-web 上补 Runtime BFF。**
|
||
|
||
### 3.3 `/v1/responses`
|
||
|
||
`hermes-web-ui` 的产品化 Chat Runtime 使用 `/v1/responses stream=true`,典型形态是:
|
||
|
||
```text
|
||
Browser Vue UI
|
||
-> Socket.IO /chat-run
|
||
-> hermes-web-ui server
|
||
-> /v1/responses stream=true
|
||
-> server 从本地 DB 组装 conversation_history
|
||
-> server 把 response.* 事件转成 run/message/tool 事件
|
||
-> server 写入 SQLite session/messages
|
||
```
|
||
|
||
它适合“自建 Chat Runtime”:
|
||
|
||
- 后端自有 session/messages 数据库。
|
||
- 后端自行压缩上下文。
|
||
- 后端自行映射 tool event。
|
||
- `store=false` 时 Hermes 不再是会话真相。
|
||
|
||
这与 mnote 当前页面 AI 方向有冲突。除非后续明确改变设计,把“AI 会话真相”从 Hermes 迁到 mnote,否则不能把 `/v1/responses store=false + mnote conversation_history` 写成默认主链。
|
||
|
||
允许使用 `/v1/responses` 的条件:
|
||
|
||
- 只作为特定 runtime 实验或 fallback。
|
||
- 必须证明 Hermes session 仍能作为用户可见会话真相,或者另开设计稿明确改变会话真相。
|
||
- 不得把 mnote page aggregate / Convex 页面数据变成聊天消息数据库。
|
||
|
||
结论:
|
||
|
||
> **`/v1/responses` 先作为参考实现和可评估候选,不作为当前页面 AI 默认主链。**
|
||
|
||
---
|
||
|
||
## 4. 目标架构
|
||
|
||
下一阶段目标不是改成 ACP,也不是直接改成 `/v1/responses`,而是在当前 gateway 主线上补一个 mnote-web Runtime BFF:
|
||
|
||
```text
|
||
Leptos 页面 AI 面板
|
||
-> mnote-web Hermes Runtime BFF
|
||
- active run registry
|
||
- session/run resume
|
||
- queue
|
||
- abort
|
||
- event normalization
|
||
- gateway/profile/skill/memory BFF
|
||
- trace/audit 串联
|
||
-> Hermes gateway /v1/runs
|
||
-> Hermes session storage
|
||
-> mnote skill/plugin/tool
|
||
-> Rust runtime / kernel
|
||
```
|
||
|
||
真相边界:
|
||
|
||
- Hermes:AI session、message、run、tool event、usage、model、profile。
|
||
- mnote:页面、树、正文、artifact、edge、projection、audit、权限、idempotency。
|
||
- mnote-web Runtime BFF:运行时连接和事件编排,不保存长期聊天真相。
|
||
|
||
---
|
||
|
||
## 5. Hermes Web UI 参考索引
|
||
|
||
参考根目录:
|
||
|
||
```text
|
||
/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18
|
||
```
|
||
|
||
只参考下表,不整站搬运:
|
||
|
||
| 参考主题 | 文件 | 搜索点 | mnote 中吸收什么 | mnote 中不采用什么 |
|
||
| --- | --- | --- | --- | --- |
|
||
| Socket runtime 主链 | `packages/server/src/services/hermes/chat-run-socket.ts` | `ChatRunSocket`、`onConnection`、`resumeSession`、`handleRun`、`dequeueNextQueuedRun`、`handleAbort` | active run registry、resume、queue、abort、前端刷新不丢运行态 | 不照搬 Socket.IO;mnote 可用 SSE / WebSocket / HTTP 状态轮询 |
|
||
| `/v1/responses` 事件映射 | `packages/server/src/services/hermes/chat-run-socket.ts` | `/v1/responses`、`applyResponseStreamEvent`、`response.output_text.delta`、`response.output_item.done`、`response.completed` | 学习如何把上游事件标准化为 `message.delta`、`tool.started`、`tool.completed`、`run.completed` | 不把 `/v1/responses store=false` 写成默认主链 |
|
||
| 后端 session store | `packages/server/src/db/hermes/session-store.ts` | `HermesSessionRow`、`HermesMessageRow`、`createSession`、`addMessage`、`getSessionDetail` | 理解 Hermes Web UI 如何建完整 chat DB,以及字段形态 | 不在 mnote 建第二套 chat DB;不把 Convex 当 AI 会话真相 |
|
||
| 前端 chat store | `packages/client/src/stores/hermes/chat.ts` | `resumeServerWorkingRun`、`queueLengths`、`abortState`、`case 'message.delta'`、`case 'tool.started'`、`case 'run.completed'` | 前端状态机、队列提示、abort UI、tool event 归并 | 不复用 Vue/Pinia;不把前端 state 当真相 |
|
||
| 前端 API | `packages/client/src/api/hermes/chat.ts` | `startRunViaSocket`、`resumeSession`、`registerSessionHandlers`、`RunEvent` | 客户端事件合同与 runtime event 类型 | 不让浏览器绕过 mnote-web 直连 Hermes |
|
||
| VSCode ACP tool UI | `design/05-editor-mainline/reference-code/hermes-vscode-main/src/sessionManager.ts`、`src/protocol.ts`、`src/chatPanel.ts`、`src/webview/main.ts` | `tool_call`、`tool_call_update`、`parseToolCall`、`parseToolCallUpdate`、`case 'toolCall'`、`data-tool-id` | 学习按 `toolCallId` 追加工具行、再用后续 update 原地更新状态的 UI 模型 | 不采用 ACP 作为 Web 主链;只参考 tool event UI 状态机 |
|
||
| gateway 管理 | `packages/server/src/services/hermes/gateway-manager.ts`、`packages/server/src/services/gateway-bootstrap.ts` | `getUpstream`、`start`、`stop`、`health` | profile 对应 gateway 的发现、启动、健康检查、错误提示 | 不让页面 AI 抽屉变成完整 gateway 管理台 |
|
||
| proxy handler | `packages/server/src/routes/hermes/proxy-handler.ts` | `resolveUpstream`、`SSE_EVENTS_PATH`、`streamSSE`、`/v1/runs` | 路由改写、SSE 拦截、upstream 错误规整 | 不把全量 proxy catch-all 暴露给浏览器 |
|
||
| skills 来源 | `packages/server/src/controllers/hermes/skills.ts` | `.bundled_manifest`、`.hub/lock.json`、`.usage.json`、`source`、`modified` | skill 来源与生成/安装/本地分类 | 不复制完整 skill 管理台 |
|
||
|
||
快速定位命令:
|
||
|
||
```bash
|
||
cd /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18
|
||
rg -n "ChatRunSocket|resumeSession|handleRun|dequeueNextQueuedRun|handleAbort|/v1/responses|applyResponseStreamEvent|response\\.output_text\\.delta|response\\.output_item\\.done|response\\.completed|HermesSessionRow|startRunViaSocket|gateway-manager" packages/server/src packages/client/src
|
||
```
|
||
|
||
---
|
||
|
||
## 6. RuntimeEvent 合同
|
||
|
||
mnote-web BFF 对前端暴露的事件应稳定为 `mnote.hermes_runtime_event.v1`。
|
||
|
||
最小事件集:
|
||
|
||
```json
|
||
{ "event": "run.started", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "traceId": "trace_1" }
|
||
```
|
||
|
||
```json
|
||
{ "event": "message.delta", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "delta": "当前页", "traceId": "trace_1" }
|
||
```
|
||
|
||
```json
|
||
{ "event": "tool.started", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "toolCallId": "call_1", "toolName": "mnote.page.get", "traceId": "trace_1" }
|
||
```
|
||
|
||
```json
|
||
{ "event": "tool.completed", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "toolCallId": "call_1", "toolName": "mnote.page.get", "summary": "读取当前页面", "auditId": "audit_1", "traceId": "trace_1" }
|
||
```
|
||
|
||
```json
|
||
{ "event": "run.completed", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "output": "总结文本", "usage": { "inputTokens": 0, "outputTokens": 0 }, "traceId": "trace_1" }
|
||
```
|
||
|
||
```json
|
||
{ "event": "run.failed", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "code": "hermes_client_upstream_error", "message": "Hermes upstream 返回错误", "traceId": "trace_1" }
|
||
```
|
||
|
||
```json
|
||
{ "event": "abort.started", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "traceId": "trace_1" }
|
||
```
|
||
|
||
```json
|
||
{ "event": "abort.completed", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "synced": true, "traceId": "trace_1" }
|
||
```
|
||
|
||
如果上游是 `/v1/runs/{run_id}/events`,BFF 只做字段规整;如果后续试验 `/v1/responses`,BFF 负责把 `response.*` 映射为上述 RuntimeEvent。前端不直接依赖上游原始事件格式。
|
||
|
||
---
|
||
|
||
## 7. 下一阶段 checklist
|
||
|
||
### A. Runtime 状态注册表
|
||
|
||
- [x] 在 `mnote-web` 建立内存级 `sessionId -> active run state` registry。
|
||
- [x] 记录 `sessionId`、`runId`、`profile`、`documentId`、`traceId`、`status`、`startedAt`、`lastEventAt`。
|
||
- [x] registry 只保存运行态和最近事件摘要,不保存完整聊天历史。
|
||
- [x] 服务重启后允许 registry 丢失,但页面必须能从 Hermes session detail 恢复已完成消息。
|
||
|
||
验收标准:
|
||
|
||
- [x] 页面刷新时,若 run 仍在 registry 中,前端能看到 `running/tool_calling` 状态。
|
||
- [x] 关闭页面再打开,不会把 mnote local storage 当作聊天真相。
|
||
|
||
### B. Resume
|
||
|
||
- [x] 增加 `POST /api/hermes/client/sessions/{session_id}/resume` 或等价同源接口。
|
||
- [x] 返回 Hermes session detail 中的 messages,并附带 registry 中当前运行态。
|
||
- [x] 若 session 不在 registry 中,只返回 Hermes 已持久化状态。
|
||
|
||
验收标准:
|
||
|
||
- [x] run 过程中刷新页面,UI 能恢复 run 状态或明确显示“正在同步 Hermes 状态”。
|
||
- [x] run 完成后刷新页面,消息来自 Hermes session detail / export,不来自 mnote 自建聊天表。
|
||
|
||
验证证据:
|
||
|
||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 9 个 Hermes client 单测,覆盖 registry 不保存 `messages`、tool event 摘要和 `/resume` 返回 runtime。
|
||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-smoke.js`:通过。13000 是本轮临时启动的新 `mnote-web`,用于避免打断当前 3000;验证后已关闭。
|
||
- 当前 3000 运行的是本轮修改前的旧 `mnote-web` 二进制,`POST /api/hermes/client/sessions/{id}/resume` 返回 404;需要重启 3000 后再用同一 smoke 复验主入口。
|
||
|
||
### C. Queue
|
||
|
||
- [x] 同一 session 已有 active run 时,后续输入进入后端 queue。
|
||
- [x] queue item 只保存待发送 input、profile、context snapshot 摘要和 trace,不保存长期聊天真相。
|
||
- [x] 当前 run 结束后自动触发下一条。
|
||
- [x] 前端显示 queue length 和可取消项。
|
||
|
||
验收标准:
|
||
|
||
- [x] 连续发送两条消息时,第二条显示 queued,而不是并发打到同一 Hermes session。
|
||
- [x] 取消 queued item 后,不触发 Hermes run。
|
||
|
||
验证证据:
|
||
|
||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 12 个 Hermes client 单测,新增覆盖 active run 时第二条入队、queue item 不返回完整 `messages/pageContext`、后端取消 queued item、终态后用 mock Hermes upstream 自动启动下一条 queued run。
|
||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-queue-smoke.js`:通过。验证连续发送两条消息时第二条走 `queued` 响应,UI 显示队列长度,并能从 Runtime 面板取消 queued item。
|
||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-smoke.js`:通过。确认 resume / tool / 完成态基础链路未被 queue 改动破坏。
|
||
- 13000 是本轮临时启动的新 `mnote-web`,验证后已关闭;当前 3000 仍需重启后复验。
|
||
|
||
### D. Abort
|
||
|
||
- [x] abort 由 BFF 统一执行,调用 Hermes `/v1/runs/{run_id}/stop` 或中断当前 upstream request。
|
||
- [x] abort 前后发出 `abort.started`、`abort.completed`。
|
||
- [x] abort 后清理 registry 状态,并处理队列后续项。
|
||
|
||
验收标准:
|
||
|
||
- [x] 点击停止后,UI 不再继续追加该 run 的 assistant delta。
|
||
- [x] 若 abort 后有队列项,后端按顺序继续处理或明确要求用户确认。
|
||
|
||
验证证据:
|
||
|
||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 13 个 Hermes client 单测,新增覆盖 abort 响应返回 `abort.started/abort.completed`,并在 abort 后继续处理 queued run。
|
||
- 临时 `mnote-web` 以 `MNOTE_WEB_HERMES_UPSTREAM_URL=http://127.0.0.1:3999` 启动后,执行 `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-runtime-controls-smoke.js`:通过。验证停止 run 后 UI 状态进入 `aborted`,并且后续 `message.delta` 不再追加到聊天区。
|
||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-queue-smoke.js` 与 `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-smoke.js`:通过。确认 queue / resume 基础链路未被 abort 改动破坏。
|
||
- 13000 是本轮临时启动的新 `mnote-web`,验证后已关闭;当前 3000 仍需重启后复验。
|
||
|
||
### E. Event normalization
|
||
|
||
- [x] mnote-web 将 Hermes 上游事件规整为 `mnote.hermes_runtime_event.v1`。
|
||
- [x] 前端只消费标准事件,不直接分支处理多套 Hermes 原始事件。
|
||
- [x] tool event 展示独立于 assistant 文本。
|
||
- [x] 当前 UI 已能在聊天区输出 `工具` 消息并显示工具名,例如 `mnote.page.get`;下一阶段必须升级为按 `toolCallId` 合并的 tool event card。
|
||
- [x] `tool.started` 创建或更新同一个工具卡,显示工具名、运行中状态、参数摘要。
|
||
- [x] `tool.completed` 原地更新同一个工具卡,显示完成状态、结果摘要、audit/trace id。
|
||
- [x] `tool.failed` 原地更新同一个工具卡,显示失败状态、错误码和可复制详情。
|
||
- [x] Runtime 页的最近 tool call 摘要继续保留,但不能替代聊天区内联工具卡。
|
||
|
||
验收标准:
|
||
|
||
- [x] `message.delta`、`tool.started`、`tool.completed`、`run.completed`、`run.failed` 均有 smoke 覆盖。
|
||
- [x] `tool.started -> tool.completed` 不产生两条互不关联的纯文本工具消息;同一 `toolCallId` 在 UI 中表现为同一张工具卡状态变化。
|
||
- [x] 工具卡至少展示 `toolName`、`toolCallId`、状态、参数摘要、结果摘要、`traceId/auditId`。
|
||
- [x] tool result 不混入正文编辑器,不写入 page body,除非 Hermes 明确调用 `mnote.page.save` 等写工具。
|
||
|
||
验证证据:
|
||
|
||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-smoke.js`:通过。smoke 发送同一 `toolCallId=call_smoke_page_get` 的 `tool.started -> tool.completed`,断言聊天区只有 1 张 completed 工具卡;同时发送 `toolCallId=call_smoke_page_save` 的 `tool.started -> tool.failed`,断言 failed 工具卡显示 `permission_denied`。
|
||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-runtime-controls-smoke.js` 与 `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-queue-smoke.js`:通过。确认工具卡改动未破坏 abort / queue。
|
||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 14 个 Hermes client 单测,新增覆盖 `assistant_message`、`tool.failed` 等上游事件被规整为 `mnote.hermes_runtime_event.v1`。
|
||
- mnote-web `/client/events/{run_id}` 输出 `event: <canonical>` 与 `schemaVersion: "mnote.hermes_runtime_event.v1"`;前端消费路径只处理 `message.delta`、`run.completed`、`run.failed`、`run.aborted` 与 `tool.*` 标准事件名。
|
||
|
||
### F. Gateway manager
|
||
|
||
- [x] BFF 能检查当前 Hermes gateway health。
|
||
- [x] BFF 能识别 profile 对应 gateway / model 状态。
|
||
- [x] 缺 model/default 或 API key 时,错误提示指向 Hermes 设置,而不是只显示 502。
|
||
|
||
验收标准:
|
||
|
||
- [x] `hermes_client_upstream_error` 场景能显示具体修复建议,例如“当前 profile 缺 model.default”。
|
||
- [x] profile/skill/settings API 不再错误代理到 Hermes gateway 非 `/v1/*` 路径。
|
||
|
||
验证证据:
|
||
|
||
- `mnote-web` 新增 `GET /api/hermes/client/gateway/health?profile=<name>`,只做只读探测,不启动/停止 gateway;返回 `gateway.configured/upstream/status/httpStatus`、`profile.modelDefault/provider/apiKeyConfigured` 与 `suggestions`。
|
||
- `profile_detail_payload()` 与 fallback profiles 会从 Hermes profile `config.yaml` 读取 `model.default`、`model.provider`、provider `api_key/key_env` 摘要,但不返回 API key 原文。
|
||
- `upstream_error()` 对 `hermes_client_upstream_error` 增加 Hermes 设置修复建议;当上游错误正文包含 `model.default`、`API key`、`profile/provider` 等线索时,错误 message 会指向 Hermes 设置。
|
||
- Runtime 面板增加 gateway/profile 状态区;页面请求失败时前端显示后端 `message`,不再只显示错误 code。
|
||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 17 个 Hermes client 单测,新增覆盖 gateway 未配置、profile 缺 model/API key、`hermes_client_upstream_error` 错误建议。
|
||
- 临时 `mnote-web` 以 `MNOTE_WEB_HERMES_UPSTREAM_URL=http://127.0.0.1:3999` 启动到 `127.0.0.1:13000` 后,`task-hermes-page-ai-smoke.js`、`task-hermes-page-ai-runtime-controls-smoke.js`、`task-hermes-page-ai-queue-smoke.js` 均通过;验证后已关闭 13000。
|
||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-agent-skill-smoke.js`:通过。确认新增 gateway health 请求不影响 agent/profile 选择、SOUL.md 保存、skill toggle 与 run profile 透传。
|
||
|
||
### G. `/v1/responses` 评估,不默认切换
|
||
|
||
- [x] 增加只读 spike 文档或测试,验证当前 Hermes gateway 的 `/v1/responses stream=true` 事件格式。
|
||
- [x] 明确 `/v1/responses` 是否能在不迁移会话真相的前提下复用 Hermes session。
|
||
- [x] 若不能保持 Hermes session 真相,则不得进入默认页面 AI 主链。
|
||
|
||
验收标准:
|
||
|
||
- [x] 有明确结论:继续 `/v1/runs`,或另开设计稿说明为什么改变会话真相。
|
||
- [x] 任何 `/v1/responses` 实验都不得把 mnote Convex / page aggregate 变成聊天消息真相。
|
||
|
||
只读 spike 结论:
|
||
|
||
- 参考 `hermes-web-ui-0.5.18/packages/server/src/services/hermes/chat-run-socket.ts`,其 `/v1/responses` 主链在发送前从后端 DB 组装 `conversation_history`,随后设置 `body.stream = true` 与 `body.store = false`,再请求 `${upstream}/v1/responses`。
|
||
- 同一文件的 `applyResponseStreamEvent()` 表明 responses 事件格式主要是 `response.created`、`response.output_text.delta`、`response.output_item.added`、`response.output_item.done`、`response.completed`、`response.failed`,由上层 BFF 映射为 `run.started`、`message.delta`、`tool.started`、`tool.completed`、`run.completed`、`run.failed`。
|
||
- `response.output_item.done` 中 `function_call` 会被映射为 `tool.started`;`function_call_output` 会被映射为 `tool.completed`。这对 mnote 的 tool card 状态机有参考价值,但不改变主链。
|
||
- 本机只读探测 `http://127.0.0.1:3999/health`、`/v1/models`、`OPTIONS /v1/responses` 时,当前没有 gateway 监听,无法取得 live `/v1/responses stream=true` 事件样本;因此本轮不把 live event capture 写成验收依据。
|
||
- 基于参考实现,`/v1/responses store=false + conversation_history` 的顺滑体验依赖上层 BFF/SQLite 自建 chat session 真相。除非后续确认 Hermes gateway 支持把 `/v1/responses` 直接绑定到 Hermes session storage,否则 mnote 不应把它切成默认主链。
|
||
|
||
当前决策:
|
||
|
||
> **继续 `/v1/runs` 作为页面 AI 默认主链;`/v1/responses` 只作为事件标准化与 tool card 的参考实现。若未来要切换,必须另开设计稿证明 Hermes session 仍是 AI 会话真相,或明确批准迁移会话真相。**
|
||
|
||
---
|
||
|
||
## 8. 非目标
|
||
|
||
- 不在本阶段引入 ACP 主链。
|
||
- 不在本阶段把页面 AI 主链切到 `/v1/responses store=false`。
|
||
- 不在 mnote 建 Hermes Web UI 那样的 SQLite chat DB。
|
||
- 不把 Convex 作为 AI 会话真相。
|
||
- 不把页面 AI 抽屉扩成完整 Hermes 管理台。
|
||
- 不让 mnote plugin 直接写 Convex;写入仍回到 Rust runtime / kernel。
|
||
|
||
---
|
||
|
||
## 9. 最终验收口径
|
||
|
||
本稿完成时,应能同时成立:
|
||
|
||
- [x] 页面 AI 仍是 Hermes 页面内客户端。
|
||
- [x] Hermes 仍是 AI session/message/tool event/usage/model/profile 真相。
|
||
- [x] mnote-web 有 Runtime BFF 能力,支持 run resume、queue、abort 和事件标准化。
|
||
- [x] 页面刷新或断线不导致 active run UI 状态丢失;完成后的消息从 Hermes 恢复。
|
||
- [x] mnote 不保存完整聊天历史,只保存 mnote 业务写入结果和 audit。
|
||
- [x] `/v1/responses` 是否进入主链已有单独评估结论,且没有绕过 Hermes session 真相。
|
||
|
||
一句话:
|
||
|
||
> **补齐 Web Chat Runtime 的工程能力,但不改变“AI 会话归 Hermes,mnote 事实归 Rust kernel”的长期边界。**
|