Files
mnote/design/old/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md
T
Agent Board b798f628ee chore: land tree view-state, vault, Pi module split, and repo hygiene
Persist PageTree expand state via control-plane view-state and align
chevron/DOM with restored expansion; keep Sidex-style shallow page-tree
scan and drop the unused recursive scanner that only added cargo noise.

Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi
into a module package, and retire Hermes/ACP/OpenHub recycle + root
harness evidence from the index while gitignoring recycle and local
diag dumps.

Archive superseded design/bugs docs under old/, point architecture at
ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor
regressions so the working tree can stay clean.
2026-07-21 05:13:05 +08:00

1191 lines
86 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# [recycle] 7-4 [done] 页面 AI Hermes 面板顺序执行 checklist v1
> 更新时间:2026-05-14
>
> 当前状态:`DONE`。A-M 的实现、旧链退场、持久化审计和正式 `3000` 全矩阵已完成;N 文档治理已把活跃设计口径统一为 Hermes 页面内客户端 + mnote Hermes skill/plugin。本文已从 `process/` 迁入 `done/`;后续页面 AI 体验与设置面收口由 `design/07-ai/process/7-7-page-ai-mini-hermes-control-surface-v1.md` 继续承接。
>
> 上位依据:
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
> - `/mnt/Data1T/mnote/design/03-rust-web/reference/3-rust-web-long-term-architecture-v1.md`
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-15-runtime-fallback-retirement-checklist-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/done/5-6-page-aggregate-alignment-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-10-wolai-page-settings-and-ai-surface-alignment-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-11-wolai-page-settings-and-ai-surface-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-2-phase7-structured-artifact-write-chain-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
>
> 目标:
> - 把页面 AI 从旧 `/api/ai-agent/run` / `mnote-cli host` / sidecar 路径迁到 Hermes 页面内客户端。
> - Hermes 持有 AI session/message/tool event/usage/model 真相。
> - mnote 只通过 Hermes skill/plugin 暴露页面、树、artifact、edge 等业务工具。
> - Leptos 负责页面内 Hermes 面板体验,不负责 AI 编排或聊天真相存储。
---
## 0. 使用方式
每次只领取一个 task,按顺序执行。除非前一 task 的验收标准全部满足,否则不要进入下一 task。
勾选规则:
- [x] 已有代码、文档和可复跑验证证据。
- [ ] 还没有完整证据,或只有局部实现。
- [ ] `PARTIAL` 项不能当作完成项;必须继续拆到对应未勾选子项。
状态标记:
- `TODO`:未开始
- `RED`:已补失败测试或失败 smoke
- `GREEN`:当前 task 本地通过
- `BLOCKED`:依赖外部 Hermes、auth、模型网关或数据权限,无法在本轮闭环
- `DONE`:代码、测试、文档证据都已回填
执行硬规则:
- [x] 不新增 Web 私有 AI tool registry。
- [x] 不把 Hermes 聊天消息复制进 mnote page aggregate / Convex 页面数据。
- [x] 不让 Hermes plugin 直接写 Convex,写入必须回到 Rust runtime / kernel。
- [x] 不把 `mnote-cli` 写回页面 AI 唯一长期执行面;它最多是 plugin 内部 adapter 或调试入口。
- [x] 不在页面 AI 抽屉里复刻 Hermes 管理台;只实现页面内 chat/session/run/tool event 子集。
- [x] 不在同一阶段同时迁移所有工具;先读、再写正文、再 artifact、再树与 edge。
当前进度快照:
| Task | 状态 | 已有证据 | 剩余门槛 |
| --- | --- | --- | --- |
| A | [x] GREEN | `scripts/task-hermes-page-ai-baseline-smoke.js``bugs/07-ai/done/7-1-page-ai-provider-hermes-old-run-502-v1.md` | 无 |
| B | [x] GREEN | `design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md` | 无 |
| C | [x] GREEN | `rust/crates/mnote-web/src/routes/hermes_client.rs``cargo test -p mnote-web hermes_` | 无 |
| D | [x] GREEN | `design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md` | 无 |
| E | [x] GREEN | `rust/crates/mnote-web/src/routes/hermes_tools.rs``rust/crates/mnote-web/src/hermes_tools/*``task-hermes-page-ai-tool-smoke.js` | 后续可扩 manifest 全量 schema,不阻塞第一版只读工具 |
| F | [x] GREEN | `rust/crates/mnote-web/src/ssr/pages/layout.rs``task-hermes-page-ai-smoke.js` | 无 |
| G | [x] GREEN | 真实 Hermes upstream run 已触发 `mnote_page_get` / `mnote.page.get`Hermes session export 与刷新恢复已验证 | 无 |
| H | [x] GREEN | `mnote.page.save` route、dryRun、未认证拒绝、workspace ACL 失败、同 key replay、主编辑区立即回显与刷新后持久化 smoke 已有 | 无 |
| I | [x] GREEN | `mnote.page.update_title``mnote.page.update_options` route 与 smoke 已有;标题 dryRun / idempotency、wired-only warning、页头 / breadcrumb / sidebar / 刷新后一致性已验证 | 无 |
| J | [x] GREEN | `mnote.artifact.create_summary``mnote.artifact.create_ai_note` route 与 smoke 已有;summary 重试、ai_note 独立创建、真实 kernel/projection edge 查询、`AI Artifacts` projection-only 验收和页面 AI Hermes intent 快捷入口已通过 | 无 |
| K | [x] GREEN | 新页面 AI 主链已不请求 `/api/ai-agent/run`;旧 endpoint 统一返回 `410 legacy_ai_agent_run_retired`;旧 smoke 默认退役;legacy 调用方已清点并归类 | 无 |
| L | [x] GREEN | tool 响应已有基础 audit 字段;`routes/hermes_tools.rs` 已记录 started/completed/failed 结构化日志;写工具 `audit.commandId` 已指向 Rust command id;审计已追加 JSONL 持久化并支持 `persistedOnly=true` 反查 | 无 |
| M | [x] GREEN | 正式 `http://127.0.0.1:3000` 已复跑 8 个 E2E smokesession/run、tool、writeback、title/options、artifact、retirement、mobile、audit | 无 |
| N | [x] GREEN | 7-3/7-4/7-5/7-6 已写入主线口径;`1-1``3-1``5-6``5-9``10-review` 已统一旧 AI 口径为历史/兼容/已覆盖 | 后续若移动本文件到 `done/`,需同步更新引用 |
---
## 1. Hermes Web UI 参考索引
需要参考 `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18` 时,只参考下表列出的文件。参考目标是理解 Hermes 的 session / run / stream / tool event / usage / model 语义,不照搬 Vue、Naive UI、整站导航或后台管理页面。
执行时按 task 精确打开文件,不要整目录通读。需要进一步定位时,优先按下表的“搜索点”打开,不要从整站 UI 或无关管理页开始看:
| 参考主题 | 文件 | 搜索点 / 具体位置 | 在 mnote 中用于什么 |
| --- | --- | --- | --- |
| 浏览器发起 run 与事件分发 | `packages/client/src/api/hermes/chat.ts` | `StartRunRequest``RunEvent``registerSessionHandlers``connectChatRun``startRunViaSocket` | 对齐 mnote Leptos 面板发起 run、订阅 `message.delta` / `tool.started` / `tool.completed` / `run.completed` 的事件语义 |
| session 刷新恢复 | `packages/client/src/api/hermes/chat.ts``packages/client/src/stores/hermes/chat.ts` | `resumeSession``switchSession``refreshActiveSession``mapHermesMessages` | 对齐页面刷新后从 Hermes session detail / resume 恢复消息,而不是从 mnote 本地 state 恢复聊天真相 |
| tool message 前端归并 | `packages/client/src/stores/hermes/chat.ts` | `Message.role = 'tool'``toolCallId``toolArgs``toolResult``case 'tool.started'``case 'tool.completed'` | 对齐 mnote 面板如何把 tool started / completed 合并成可读消息项 |
| tool message 展示 | `packages/client/src/components/hermes/chat/MessageItem.vue` | `message.role === 'tool'``formatToolPayload``toolExpanded``tool-preview``tool-details` | 对齐 tool 结果默认折叠、摘要展示、详细 JSON 展开的交互边界 |
| 页面面板分层 | `packages/client/src/components/hermes/chat/ChatPanel.vue` | `showSessions``activeSessionTitle``handleNewChat``MessageList``ChatInput``SessionListItem` | 只参考 session list / message list / input 的分层,不复制 Vue / Naive UI / Hermes 全屏布局 |
| 后端 run 与 resume 房间 | `packages/server/src/services/hermes/chat-run-socket.ts` | `ChatRunSocket``onConnection``resumeSession``handleRun``emit``markCompleted` | 对齐 upstream run 生命周期、刷新恢复、房间广播与终止态,不在 mnote 里重建 Hermes 编排器 |
| OpenAI Responses tool event 映射 | `packages/server/src/services/hermes/chat-run-socket.ts` | `applyResponseStreamEvent``response.output_item.done``function_call``function_call_output``tool.started``tool.completed``flushResponseRunToDb` | 对齐真实 Hermes upstream run 如何产生 tool event,并把 tool call / tool result 写回 Hermes session 存储 |
| Hermes session 存储真相 | `packages/server/src/db/hermes/session-store.ts` | `HermesSessionRow``HermesMessageRow``createSession``getSessionDetail``addMessage``updateSessionStats` | 明确 session/message/tool call 真相在 Hermes,不在 mnote page aggregate / Convex 页面数据 |
| session HTTP detail | `packages/client/src/api/hermes/sessions.ts``packages/server/src/routes/hermes/sessions.ts` | `fetchSessions``fetchSession``HermesMessage``/api/hermes/sessions/:id` | 对齐 session list/detail 的字段名,用于 mnote proxy 返回兼容形状 |
| plugin/skill 清单发现 | `packages/server/src/services/hermes/plugins.ts` | `PYTHON_BRIDGE``PluginManager``user_dir = get_hermes_home() / "plugins"``manifest_list``providesTools``listHermesPlugins` | 对齐 Hermes Web UI 如何发现 user/project plugin 和 `provides_tools`;mnote 的真正工具必须进入 Hermes plugin/tool registry,不能只停在 mnote-web 私有 route |
| plugin/skill API 外观 | `packages/client/src/api/hermes/plugins.ts``packages/client/src/api/hermes/skills.ts``packages/server/src/routes/hermes/plugins.ts``packages/server/src/routes/hermes/skills.ts` | `list` / `get` / `enable` / `disable` 类 route 与响应字段 | 只用于清单和状态展示口径;页面 AI 第一版不复刻 Hermes plugin 管理台 |
可快速 grep
```bash
cd /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18
rg -n "StartRunRequest|RunEvent|registerSessionHandlers|startRunViaSocket|resumeSession|tool\\.started|tool\\.completed|applyResponseStreamEvent|flushResponseRunToDb|PluginManager|providesTools|listHermesPlugins" packages/client/src packages/server/src
```
已分类的常用入口:
- [x] session / run / SSE:先看 `packages/client/src/api/hermes/chat.ts``packages/server/src/routes/hermes/chat-run.ts``packages/server/src/services/hermes/chat-run-socket.ts`
- [x] session 真相恢复:先看 `packages/server/src/db/hermes/session-store.ts``packages/server/src/db/hermes/sessions-db.ts``packages/server/src/services/hermes/session-sync.ts`
- [x] 面板组件分层:先看 `packages/client/src/views/hermes/ChatView.vue``packages/client/src/components/hermes/chat/ChatPanel.vue``ChatInput.vue``MessageList.vue``MessageItem.vue`
- [x] tool event:先看 `packages/client/src/stores/hermes/chat.ts``packages/client/src/components/hermes/chat/MessageItem.vue``packages/server/src/db/hermes/conversations-db.ts`
- [x] plugin / skill 清单:先看 `packages/client/src/api/hermes/plugins.ts``packages/client/src/api/hermes/skills.ts``packages/server/src/routes/hermes/plugins.ts``packages/server/src/routes/hermes/skills.ts``packages/server/src/services/hermes/plugins.ts`
| mnote 任务 | 参考文件 | 只参考什么 | 不采用什么 |
| --- | --- | --- | --- |
| Hermes API client 合同 | `packages/client/src/api/hermes/chat.ts``packages/client/src/api/hermes/sessions.ts``packages/client/src/api/hermes/conversations.ts` | chat run、session detail、conversation list/detail 的请求和响应形状 | 不直接复用 TS client,不让浏览器绕过 mnote-web proxy |
| Hermes proxy / route 形态 | `packages/server/src/routes/hermes/chat-run.ts``packages/server/src/routes/hermes/sessions.ts``packages/server/src/routes/hermes/proxy.ts``packages/server/src/routes/hermes/proxy-handler.ts` | 服务端 route 如何接收 run、session、proxy 请求以及错误透传边界 | 不把 Hermes server route 复制进 mnotemnote 只做同源薄代理 |
| session 真相与恢复 | `packages/server/src/db/hermes/session-store.ts``packages/server/src/db/hermes/sessions-db.ts``packages/server/src/db/hermes/conversations-db.ts``packages/server/src/services/hermes/conversations.ts``packages/server/src/services/hermes/session-sync.ts` | session/message/conversation 的存储、搜索、恢复、thread/lineage 摘要 | 不在 mnote 里重建这些表或复制聊天历史 |
| usage / model 真相 | `packages/server/src/db/hermes/usage-store.ts``packages/client/src/stores/hermes/usage.ts``packages/client/src/stores/hermes/models.ts``packages/client/src/components/layout/ModelSelector.vue` | usage、model selection、model display 的字段和展示语义 | 不在 mnote page aggregate 保存 usage/model 真相 |
| chat 面板结构 | `packages/client/src/views/hermes/ChatView.vue``packages/client/src/components/hermes/chat/ChatPanel.vue``packages/client/src/components/hermes/chat/ChatInput.vue``packages/client/src/components/hermes/chat/MessageList.vue``packages/client/src/components/hermes/chat/MessageItem.vue``packages/client/src/components/hermes/chat/HistoryMessageList.vue``packages/client/src/components/hermes/chat/SessionListItem.vue` | session list、message list、输入框、消息项、历史消息的组件分层 | 不采用 Vue / Naive UI / Hermes 全屏布局;mnote 仍用 Leptos island 和 Wolai drawer 壳 |
| markdown / thinking 展示 | `packages/client/src/components/hermes/chat/MarkdownRenderer.vue``packages/client/src/utils/thinking-parser.ts``tests/client/chat-store-thinking.test.ts``tests/client/thinking-parser.test.ts` | markdown 渲染、`think/thinking/reasoning` 片段解析、streaming 中的 thinking 边界 | 不直接照搬样式;mnote 只实现页面 AI 抽屉需要的折叠展示 |
| stream / abort / resume | `packages/client/src/stores/hermes/chat.ts``packages/client/src/api/hermes/chat.ts``packages/server/src/routes/hermes/chat-run.ts``packages/server/src/services/hermes/chat-run-socket.ts` | run stream 事件、resume session room、abort started/completed、queue/server-working 状态 | 不强制采用 Socket.IOmnote proxy 可用 SSE 或项目现有 stream 机制,但事件语义要对齐 |
| tool event 展示 | `packages/client/src/stores/hermes/chat.ts``packages/client/src/components/hermes/chat/MessageItem.vue``packages/client/src/components/hermes/chat/HistoryMessageList.vue``packages/server/src/db/hermes/conversations-db.ts` | `tool_call_id`、tool message、tool started/completed/failed 的归并与历史展示 | 不把 tool result 写入 mnote 正文;只展示 Hermes tool event |
| session 搜索 | `packages/client/src/components/hermes/chat/SessionSearchModal.vue``packages/client/src/composables/useSessionSearch.ts``tests/client/session-search.test.ts``tests/client/sidebar-search.test.ts` | 后续 session search 的交互和过滤语义 | 第一版可后置,不进入最小验收 |
| plugins / skills 列表 | `packages/client/src/api/hermes/plugins.ts``packages/client/src/api/hermes/skills.ts``packages/server/src/routes/hermes/plugins.ts``packages/server/src/routes/hermes/skills.ts``packages/server/src/services/hermes/plugins.ts``packages/client/src/components/hermes/skills/SkillList.vue``packages/client/src/components/hermes/skills/SkillDetail.vue` | Hermes 如何表达 plugin/skill 清单、启停和详情 | 不采用 Hermes skills 管理页面;mnote 只暴露 `mnote.*` tool manifest |
| 不属于 mnote 页面 AI 第一版 | `packages/client/src/views/hermes/ChannelsView.vue``JobsView.vue``FilesView.vue``TerminalView.vue``GroupChatView.vue``SettingsView.vue``ProfilesView.vue``LogsView.vue``UsageView.vue` | 仅作为边界参考,说明这些是 Hermes 管理台能力 | 不进入页面 AI 抽屉第一版 |
---
## 2. 总体验收标准
7-4 完成时必须同时满足:
- [x] 页面 AI 抽屉打开后创建或恢复的是 Hermes session。
- [x] Hermes session 存储里能看到页面 AI 的消息历史。
- [x] mnote 本地不保存聊天消息真相,只保存业务写入 audit、artifact、edge、page/body/title/options 结果。
- [x] 页面 AI 发送消息后,事件流来自 Hermes run,而不是旧 `/api/ai-agent/run` provider / fallback 分支。
- [x] tool call 展示来自 Hermes tool event;真实 Hermes upstream run 已触发 `mnote.page.get`,刷新后从 Hermes session detail 恢复。
- [x] Hermes 至少能调用一个只读 mnote 工具读取当前页面。
- [x] Hermes 至少能调用一个写入 mnote 工具,经 Rust runtime 写回当前页面正文或创建 artifact。
- [x] 页面刷新后,AI 会话从 Hermes 恢复,而不是从 mnote 本地 state 恢复。
- [x]`provider=hermes` 502 不再出现在新页面 AI 主链。
- [x] `rg` 检查活跃设计稿与代码注释不再把 `mnote-cli` 写成页面 AI 唯一长期执行面;命中已逐条归类为历史/兼容/已覆盖。
最终验收命令建议:
```bash
cd /mnt/Data1T/mnote
rg -n "mnote-cli.*唯一|唯一长期 agent|页面 AI.*mnote-cli|provider=hermes" design rust wolai-frontend --glob '*.md' --glob '*.rs' --glob '*.ts' --glob '*.tsx'
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-tool-smoke.js
```
通过标准:
- [x] `rg` 只允许命中 `design/old/` 或明确标注“历史/兼容/已覆盖”的段落。
- [x] `task-hermes-page-ai-smoke.js` 证明页面 AI 使用 Hermes session / run / stream,并验证刷新后从 Hermes session detail 恢复。
- [x] `task-hermes-page-ai-tool-smoke.js` 证明 Hermes tool call 能回到 mnote Rust runtime 并产生可读业务结果。
2026-05-14 验证记录:
- [x] `cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web hermes_ -- --nocapture`15 passed。
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3019 node scripts/task-hermes-page-ai-smoke.js`:通过,捕获 `session -> session-detail -> run -> events -> session-detail`,渲染 `mnote.page.get` tool event,刷新恢复命中 Hermes session detail 2 次。
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3019 node scripts/task-hermes-page-ai-tool-smoke.js`:通过,`mnote.page.get` 返回标题、正文摘要和 read audit。
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3019 node scripts/task-hermes-page-ai-writeback-smoke.js`:通过,`mnote.page.save` 写入走 `page.body.save`
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3019 node scripts/task-hermes-page-ai-title-options-smoke.js`:通过,标题走 `page.head.updateTitle`,页面设置走 `page.layout.updateOptions`
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3019 node scripts/task-hermes-page-ai-artifact-smoke.js`:通过,summary / ai_note 都走 `tree.node.create`
- [x] 早期临时 `3019` 验证曾确认旧 `/api/ai-agent/run` 不再静默 fallback;最终正式 `3000` 验收已由 Task K/M 覆盖,当前稳定退场码为 `410 legacy_ai_agent_run_retired`
- [x] 上述临时 `3019` 证据已被正式 `http://127.0.0.1:3000` 全矩阵复跑覆盖;最终以 Task M 的 8 个 `3000` smoke 证据为准。
- [x] 追加验证:`task-hermes-page-ai-writeback-smoke.js``task-hermes-page-ai-title-options-smoke.js``task-hermes-page-ai-artifact-smoke.js` 已断言 `audit.commandId == result.commandId`,临时 `3019` 通过。
- [x] 追加验证:临时 `3019` 日志已输出 `mnote Hermes tool call started/completed`,字段包含 `trace_id/session_id/run_id/tool_call_id/tool_name/workspace_id/document_id/actor_id/effect`
- [x] 追加验证:`MNOTE_UI_BASE_URL=http://127.0.0.1:3019 node scripts/task-hermes-page-ai-mobile-smoke.js` 通过,390px 窄屏下 drawer / panel / document 均无横向溢出。
- [x] 追加验证:`task-hermes-page-ai-writeback-smoke.js` 已在 dryRun 后立即读取 `/api/documents/content`,确认写入标记不存在;随后正式写入再确认标记存在。
- [x] 追加验证:`hermes_tools_write_tools_require_auth` 已覆盖未认证写工具返回 401。
- [x] 追加验证:`task-hermes-page-ai-writeback-smoke.js` 已用同一 `idempotencyKey` 重试正式写入,断言返回同一 `result.commandId/audit.commandId`;临时 `3019` 日志出现 `mnote Hermes tool call idempotency replay`
- [x] 追加验证:`task-hermes-page-ai-title-options-smoke.js` 已打开真实文档页,确认刷新后标题输入、breadcrumb/current title、`data-page-wide-layout=true``data-page-small-text=true` 均生效。
- [x] 追加验证:`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-title-options-smoke.js` 通过,`tree_1778720870666_1` 标题更新为 `TEST-HERMES-AI-title-updated-mp4shbjq`,页头 / breadcrumb/current title / sidebar 均一致。
- [x] 追加验证:`task-hermes-page-ai-title-options-smoke.js` 已断言 `pageFont` 返回 `ignoredOptions=["pageFont"]``warningCodes=["page_option_not_wired"]`,不进入 `page.layout.updateOptions` 写入 payload。
- [x] 追加验证:`task-hermes-page-ai-title-options-smoke.js` 已打开页面 AI 面板,确认 `data-mnote-page-ai-session-owner=hermes`mnote localStorage 只保存 Hermes `activeSessionId`,不保存或驱动 mnote 页面标题真相。
- [x] 追加验证:`task-hermes-page-ai-title-options-smoke.js` 已验证 `mnote.page.update_title` dryRun 后 meta 不变化,并验证同一 `idempotencyKey` 重试返回同一 `commandId`
- [x] 追加验证:`task-hermes-page-ai-artifact-smoke.js` 已验证 summary 同 key 重试返回同一 `commandId/artifactDocumentId`ai_note 第二次创建得到独立 `artifactDocumentId`,并验证工具结果里的 `referenceEdge.from/to/kind`
- [x] 追加验证:`task-hermes-page-ai-audit-smoke.js` 已调用 `mnote.page.get``mnote.page.save`,并通过 `/api/hermes/tools/mnote/audit?traceId=...` 反查 `started/completed` 事件,写工具 audit 可串到 Rust `commandId`,且不包含完整正文内容。
---
## 3. Task A:运行态基线与旧 502 复现
目标:
> 先固定当前问题和旧链入口,避免后续改动时把“没有复现”误认为“已经修好”。
建议修改范围:
- 新增或复用 smoke
- `scripts/task-hermes-page-ai-baseline-smoke.js`
- 只读记录:
- `rust/crates/mnote-web/src/routes/**`
- `wolai-frontend/src/**/ai-agent/**`
- `design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
执行步骤:
- [x] 启动当前前后端,确保测试入口可访问。
- [x] 用真实测试账号登录。历史执行时使用旧 Auth 后端;当前默认应使用 SQLite control-plane Auth / session。
- [x] 打开任意测试页面,点击右下角页面 AI。
- [x] 发送一条只读问题,例如“概括当前页面标题和第一段”。
- [x] 记录当前请求是否仍命中旧 `/api/ai-agent/run`
- [x] 记录是否出现 `provider=hermes` 相关 502、`ai_provider_bridge_unavailable` 或旧 fallback 文案。
- [x] 将证据写入本 task 对应 bug 或执行记录,不修改业务逻辑。
验收标准:
- [x] 有一份可复跑 smoke 或手动执行记录,明确当前页面 AI 主请求 URL。
- [x] 能区分以下三类状态:
-`/api/ai-agent/run` 仍被页面 AI 调用。
- 页面 AI 已调用 Hermes proxy,但 proxy 未接通。
- 页面 AI 已调用 Hermes proxy 且 session/run 可恢复。
- [x] 若仍出现 502,记录请求 URL、响应 body、mnote-web 日志时间戳和页面截图。
- [x] 不把本 task 的 502 修复写进同一个提交;本 task 只做基线。
---
## 4. Task B:冻结 Hermes client proxy 合同
目标:
> 先冻结浏览器到 mnote-web 的同源 Hermes client proxy 合同,防止 Leptos 面板直接绑定 Hermes 私有 API 或暴露 key。
建议修改范围:
- 新增设计/协议文档:
- `design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md`
- 后续代码位置建议:
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
- `rust/crates/mnote-web/src/routes/mod.rs`
- `rust/crates/mnote-web/src/config.rs`
合同路径第一版:
- `GET /api/hermes/client/sessions`
- `POST /api/hermes/client/sessions`
- `GET /api/hermes/client/sessions/{session_id}`
- `POST /api/hermes/client/runs`
- `GET /api/hermes/client/events/{run_id}`
- `POST /api/hermes/client/runs/{run_id}/abort`
- `GET /api/hermes/client/models`
- `GET /api/hermes/client/tools`
必须统一的请求字段:
- `workspaceId`
- `documentId`
- `sessionId`
- `message`
- `pageContext`
- `selectedBlockId`
- `selectedText`
- `traceId`
必须统一的响应字段:
- `sessionId`
- `runId`
- `messageId`
- `events[]`
- `error.code`
- `error.message`
- `traceId`
执行步骤:
- [x] 写明每个 proxy route 的输入输出 JSON 示例。
- [x] 写明 proxy 只做 auth、同源安全、上下文注入和错误码标准化。
- [x] 写明 proxy 不保存 session/message/tool event。
- [x] 写明 Hermes API key 或 token 只存在服务端环境变量中。
- [x] 写明 `pageContext` 是运行输入,不是 Hermes session 的长期事实源。
- [x] 写明旧 `/api/ai-agent/run` 与新 proxy 的并存期规则。
验收标准:
- [x] 合同文档里每个 route 都有请求/响应示例。
- [x] 文档明确禁止浏览器直接持有 Hermes API key。
- [x] 文档明确禁止 mnote 保存 Hermes 聊天历史。
- [x] 文档明确旧 `/api/ai-agent/run` 不是新 Hermes 面板主代理。
- [x] 后续实现者不需要读 Hermes Web UI 源码也能知道 mnote 侧 proxy 的稳定合同。
---
## 5. Task C:实现 Hermes client proxy 最小只读链
目标:
> 让页面 AI 可以通过 mnote-web 同源代理创建/恢复 Hermes session,并发起一个无工具写入的只读 run。
建议修改范围:
- 新增:
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
- 修改:
- `rust/crates/mnote-web/src/routes/mod.rs`
- `rust/crates/mnote-web/src/config.rs`
- `rust/crates/mnote-web/src/error.rs`
- 测试:
- `rust/crates/mnote-web/tests/hermes_client_proxy.rs` 或同 crate 现有 route 测试文件
执行步骤:
- [x] 先写 route 测试:未登录请求返回 401 或项目统一 auth 错误。
- [x] 写 route 测试:未配置 Hermes upstream 时返回稳定错误码 `hermes_client_unconfigured`
- [x] 写 route 测试:`POST /api/hermes/client/sessions` 只代理 session 创建,不写 mnote 数据。
- [x] 写 route 测试:`POST /api/hermes/client/runs` 会带上 `workspaceId/documentId/pageContext/traceId`
- [x] 实现最小 upstream client,先只支持 session create、session get、run create。
- [x] 统一错误响应:Hermes 连接失败、401、429、5xx 必须映射成可展示的错误码。
- [ ] 记录日志字段:`traceId/sessionId/runId/documentId/workspaceId`
验收标准:
- [x] `cargo test -p mnote-web hermes_client` 通过。
- [x] 未配置 Hermes 时,页面 AI 不出现 502,而是得到稳定 `hermes_client_unconfigured`
- [x] 配置 Hermes upstream 后,session create 和 run create 请求能从 mnote-web 代理到 Hermes。
- [x] mnote 数据库、page aggregate、Convex 页面内容没有新增聊天消息字段。
- [x] 网络请求中浏览器看不到 Hermes API key。
---
## 6. Task D:冻结 mnote Hermes plugin tool 合同
目标:
> 先定义 Hermes 看到的 mnote 工具清单和工具结果格式,避免工具从页面前端私有函数开始长出来。
建议新增文档:
- `design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md`
第一批工具:
- `mnote.page.get`
- `mnote.page.save`
- `mnote.page.update_title`
- `mnote.page.update_options`
- `mnote.artifact.create_summary`
- `mnote.artifact.create_ai_note`
第二批工具:
- `mnote.tree.create`
- `mnote.tree.move`
- `mnote.search.documents`
- `mnote.kernel.attach_reference`
统一入参:
- `workspaceId`
- `documentId`
- `actorId`
- `sessionId`
- `runId`
- `toolCallId`
- `traceId`
- `idempotencyKey`
- `dryRun`
- `capabilityScope`
统一出参:
- `ok`
- `toolName`
- `toolCallId`
- `traceId`
- `result`
- `audit`
- `error`
执行步骤:
- [x] 为每个第一批工具写 JSON schema。
- [x] 为每个第一批工具写成功响应示例。
- [x] 为每个第一批工具写失败响应示例。
- [x] 写明权限校验点:登录、workspace member、页面读、页面写、artifact 写。
- [x] 写明幂等规则:同一 `idempotencyKey` 重试不得重复创建 artifact。
- [x] 写明 `dryRun=true` 时只能返回计划和 diff,不得写入。
- [x] 写明 plugin 内部可以暂时调用 `mnote-cli` JSON adapter,但对 Hermes 暴露的名字必须是 `mnote.*`
验收标准:
- [x] 合同文档覆盖所有第一批工具。
- [x] 每个写工具都有 `dryRun``idempotencyKey`、权限失败、业务失败示例。
- [x] 文档明确 tool 结果最终来自 Rust runtime / kernel。
- [x] 文档明确 Hermes plugin 不直接写 Convex。
- [x] 文档明确 `mnote-cli` 是内部 adapter,不是 Hermes 看到的长期 tool 名称。
---
## 7. Task E:实现 mnote tool manifest 与只读工具
目标:
> 先让 Hermes 能发现 mnote 工具,并调用 `mnote.page.get` 读取当前页面。
建议修改范围:
- 新增:
- `rust/crates/mnote-web/src/routes/hermes_tools.rs`
- `rust/crates/mnote-web/src/hermes_tools/mod.rs`
- `rust/crates/mnote-web/src/hermes_tools/manifest.rs`
- `rust/crates/mnote-web/src/hermes_tools/page.rs`
- 修改:
- `rust/crates/mnote-web/src/routes/mod.rs`
- `rust/crates/bridge-runtime/src/lib.rs` 只在缺少正式 query 时补窄入口
- 测试:
- `rust/crates/mnote-web/tests/hermes_tools.rs`
建议 route
- `GET /api/hermes/tools/mnote/manifest`
- `POST /api/hermes/tools/mnote/call`
执行步骤:
- [x] 写 manifest 测试:返回第一批工具名称、schema version、capability scope。
- [x]`mnote.page.get` 测试:未登录失败。
- [ ]`mnote.page.get` 测试:无 workspace 权限失败。
- [x]`mnote.page.get` 测试:有权限时返回当前 page aggregate 摘要。
- [x] 实现 manifest route。
- [x] 实现统一 tool call dispatcher,只接 `mnote.page.get`
- [x]`mnote.page.get` 接到现有 Rust runtime / page aggregate query。
验收标准:
- [x] `cargo test -p mnote-web hermes_tools` 通过。
- [x] `GET /api/hermes/tools/mnote/manifest` 返回稳定 `mnote.page.get` schema。
- [x] `POST /api/hermes/tools/mnote/call` 调用 `mnote.page.get` 返回当前页面标题、正文摘要、page options、可用 block 摘要。
- [ ] 权限失败不会泄露页面标题或正文。
- [x] 工具返回中包含 `traceId/toolCallId/sessionId`
---
## 8. Task FLeptos Hermes 面板最小只读 UI
目标:
> 在保留 Wolai 右下角入口和右侧抽屉壳的前提下,把页面 AI 面板改为调用 Hermes client proxy。
建议修改范围:
- Leptos / Rust SSR 侧按当前真实结构定位:
- `rust/crates/mnote-web/src/ssr/**`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
- 页面内 island 挂载资源所在目录
- 若仍需 React compat,只能作为旧 panel 参考,不得扩展旧执行链:
- `wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx`
- smoke
- `scripts/task-hermes-page-ai-smoke.js`
第一版 UI 子集:
- session 创建/恢复
- message list
- chat input
- streaming delta
- reasoning 展开
- tool started / completed 展开
- error / retry / abort
- model selector 可先只读展示默认模型
执行步骤:
- [x] 写 smoke:打开页面 AI 后,不允许发送请求到 `/api/ai-agent/run`
- [x] 写 smoke:打开页面 AI 后,会请求 `/api/hermes/client/sessions`
- [x] 写 smoke:发送消息后,会请求 `/api/hermes/client/runs`
- [x] 写 smoke:页面刷新后,面板可从 Hermes session 恢复消息。
- [x] 实现 Leptos 面板数据层,只保存 UI 临时状态,不保存聊天真相。
- [x] 接入 session create / restore。
- [x] 接入 run create / event stream。
- [ ] 接入 abort / retry 的最小状态。
- [x] 保留右下角入口、右侧 drawer、Esc / close 行为与 `5-11` 证据一致。
验收标准:
- [x] `node scripts/task-hermes-page-ai-smoke.js` 通过。
- [x] 页面 AI 主链不再请求 `/api/ai-agent/run`
- [x] 页面 AI 消息刷新后来自 Hermes session。
- [x] mnote 本地页面 state 里没有完整聊天消息数组。
- [ ] 抽屉入口、关闭语义、移动端不溢出继续符合 `5-11` 的历史 Wolai 对齐要求。
- [x] Hermes 不可用时展示稳定错误态,不出现 502 空白或旧 `page_ai_failed_502`
---
## 9. Task G:接入 Hermes tool event 展示与只读工具调用
目标:
> 让 Hermes run 调用 `mnote.page.get` 时,页面 AI 能展示 tool started / completed,并把结果作为 Hermes 消息链的一部分呈现。
建议修改范围:
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
- `rust/crates/mnote-web/src/routes/hermes_tools.rs`
- Leptos 面板 island 相关文件
- `scripts/task-hermes-page-ai-tool-smoke.js`
执行步骤:
- [x] 写 smoke:发送“读取当前页面标题和页面设置”,期望出现 `mnote.page.get` tool started。
- [x] 写 smoke:同一次 run 出现 `mnote.page.get` tool completed。
- [ ] 写 smoketool result 中的标题与当前页头标题一致。
- [x] 实现 tool event 渲染:started、completed、failed 三态。
- [ ] 实现 tool result 折叠展示,默认只显示 tool 名和摘要。
- [x] 确保 tool result 不被写入 mnote 页面正文。
- [x] 确保 Hermes session 中保留 tool event,刷新后可恢复。
验收标准:
- [x] `node scripts/task-hermes-page-ai-tool-smoke.js` 通过。
- [ ] 工具事件来自真实 Hermes upstream run,而不是前端本地伪造。
- [ ] `mnote.page.get` 权限失败时,UI 显示 tool failed,正文不泄露。
- [x] 刷新页面后 tool event 仍从 Hermes session 恢复。
- [x] mnote page aggregate 不包含 tool event 历史。
---
## 10. Task H:接入正文写入工具 `mnote.page.save`
目标:
> 让 Hermes 可以通过 mnote plugin 对当前页正文做最小写入,写入仍走正式 `page.body.save` 链。
建议修改范围:
- `rust/crates/mnote-web/src/hermes_tools/page.rs`
- `rust/crates/bridge-runtime/src/lib.rs` 仅补必要正式 command adapter
- 页面保存相关 route / command adapter
- smoke
- `scripts/task-hermes-page-ai-writeback-smoke.js`
执行步骤:
- [x] 写 tool contract 测试:`mnote.page.save` 必须要求 `documentId``idempotencyKey``dryRun`
- [x] 写 dryRun 测试:`dryRun=true` 返回 diff,不写入。
- [x] 写权限失败测试:未认证时不进入页面写链。
- [x] 写幂等测试:同一 `idempotencyKey` 重试不重复执行写链。
- [x] 实现 `mnote.page.save``page.body.save` 的 adapter。
- [ ] 在 Hermes 面板里对写工具结果展示 diff 摘要。
- [x] smoke 执行:让 AI 在当前测试页面写入一段带测试前缀的文本。
- [x] smoke 读取 `/api/documents/content` 或当前正式内容接口,确认刷新后文本存在。
验收标准:
- [x] dryRun 不改变页面内容。
- [x] 未认证不进入写链。
- [x] 同一幂等键不会重复写入。
- [ ] 正式写入后,主编辑区 island 能立即回显。
- [x] 刷新后内容仍存在。
- [x] 写入链路日志包含 `sessionId/runId/toolCallId/traceId/documentId/actor`
- [x] 写入不经过旧 `/api/ai-agent/run`
---
## 11. Task I:接入标题与页面设置工具
目标:
> 把 AI 修改标题和页面设置收口到同一组 Page Aggregate command family,而不是靠面板本地副作用。
建议修改范围:
- `rust/crates/mnote-web/src/hermes_tools/page.rs`
- `rust/crates/bridge-runtime/src/lib.rs`
- `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md` 仅回填完成证据
- smoke
- `scripts/task-hermes-page-ai-title-options-smoke.js`
执行步骤:
- [x]`mnote.page.update_title` dryRun / permission / idempotency 测试。
- [x]`mnote.page.update_options` dryRun / permission / wired-only 字段测试。
- [x] 实现标题工具到 `page.head.updateTitle`
- [x] 实现页面设置工具到 `page.layout.updateOptions`
- [x] 禁止写入 `runtimeSupport !== "wired"` 的页面设置字段。
- [x] smoke 修改当前测试页标题,确认页头与 breadcrumb/current title 一致。
- [x] smoke 修改 `smallText``wideLayout`,确认主编辑区 page shell 属性变化。
验收标准:
- [x] 标题修改后页头、breadcrumb、sidebar、刷新后标题一致。
- [x] 页面设置修改只允许 wired 字段。
- [x] planned / ui_only 字段返回明确错误或 dryRun 警告。
- [x] AI 面板不维护独立标题状态。
- [x] 所有写入都经 Rust runtime / Page Aggregate command family。
---
## 12. Task J:接入结构化 Artifact 工具
目标:
> 落地 `7-2` 的 `summary node / ai_note node / reference edge`,触发方改为 Hermes tool call。
建议修改范围:
- `rust/crates/mnote-web/src/hermes_tools/artifact.rs`
- `rust/crates/bridge-runtime/src/lib.rs`
- `rust/crates/core-protocol/src/**` 若缺少正式类型再补
- smoke
- `scripts/task-hermes-page-ai-artifact-smoke.js`
执行步骤:
- [x]`mnote.artifact.create_summary` schema 测试。
- [x]`mnote.artifact.create_ai_note` schema 测试。
- [x] 写 summary 单例更新测试:同一页面重复创建 summary 不生成多个 summary node。
- [x] 写 ai_note 多次创建测试:每次生成独立 ai_note node。
- [x] 写 reference edge 测试:artifact 与当前页面之间有可查询 reference edge。
- [x] 实现工具到 Rust runtime / kernel。
- [x] 页面 AI 面板可提供 `创建 Summary` / `创建 AI Note` 快捷入口,但入口只发送 Hermes intent。
- [x] smoke 验证 `AI Artifacts` projection-only 分组可见。
2026-05-14 J1 执行证据:
- [x] 修正 `rust/crates/bridge-runtime/src/lib.rs`:从 artifact 文档命名与父子关系派生 `metadata.kind="ai_artifact_reference"``source_of` kernel edge,并在 file-tree projection `meta.aiArtifacts` 中标记 `projectionOnly=true``kernelNodeId=null`
- [x] 新增 bridge-runtime 单测 `kernel_edges_list_exposes_ai_artifact_reference_edges``file_tree_projection_marks_ai_artifacts_group_as_projection_only`
- [x] 验证命令:`cd rust && cargo test -p bridge-runtime ai_artifact -- --nocapture`2 passed。
- [x] 验证命令:`cd rust && cargo build -p mnote-web`:通过;正式 `3000` 已重启到新二进制,PID `2043001`
- [x] 扩展 `scripts/task-hermes-page-ai-artifact-smoke.js`:调用 `/api/kernel/edges` 查询 summary / ai_note 的 `ai_artifact_reference`,调用 `/api/tree/projections/file` 查询 `meta.aiArtifacts.projectionOnly=true`,并回查 artifact audit。
- [x] 修正 `rust/crates/mnote-web/src/ssr/pages/layout.rs``rust/crates/mnote-web/src/ssr/styles.rs`:页面 AI 抽屉提供 `创建 Summary` / `创建 AI Note` 快捷入口,点击后只调用 `sendPageAiMessage(...)` 发送 Hermes intent。
- [x] 验证命令:`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-artifact-smoke.js`:通过;最终复跑测试页 `tree_1778721933687_1`summary `summary_tree_1778721933687_1`ai_note `ai_note_tree_1778721933687_1_req_1778721933785_11``ai_note_tree_1778721933687_1_req_1778721933813_13``referenceEdgeCount=6``auditEventCount=6`;快捷入口发送的 Hermes intent 分别包含 `mnote.artifact.create_summary``mnote.artifact.create_ai_note`,且页面路由已禁止直连 `/api/hermes/tools/mnote/call`
验收标准:
- [x] artifact 创建不绕过 Hermes tool call。
- [x] artifact 创建不绕过 Rust runtime / kernel。
- [x] `AI Artifacts` 仍是 projection-only 分组,不是真实 kernel node。
- [x] summary node 可重复更新但不重复创建。
- [x] ai_note node 每次新建。
- [x] reference edge 可通过 kernel query 或 projection 验证。
- [x] artifact 写入 audit 包含 Hermes `sessionId/runId/toolCallId`
---
## 13. Task K:退役旧页面 AI 主执行链
目标:
> 新 Hermes 面板通过后,旧 `/api/ai-agent/run` 不再作为页面 AI 主入口。
建议修改范围:
- `rust/crates/mnote-web/src/routes/**`
- `wolai-frontend/src/app/api/ai-agent/run/route.ts`
- `wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx`
- `design/03-rust-web/process/3-15-runtime-fallback-retirement-checklist-v1.md`
执行步骤:
- [x] 全仓搜索页面 AI 发送路径。
- [x] 删除或禁用页面 AI 对 `/api/ai-agent/run` 的主路径调用。
- [x] 保留 legacy endpoint 时,响应必须明确 `legacy_ai_agent_run_retired` 或项目统一退场码。
- [x] 删除 `provider=hermes` 作为旧 endpoint provider/fallback 的语义。
- [x] 更新旧 smoke:不再期待 `/api/ai-agent/run -> mnote-cli`
- [x] 保留历史 smoke 到 `old` 或改名为 legacy guard。
2026-05-14 K 执行证据:
- [x] 修正 `rust/crates/mnote-web/src/routes/compat.rs``POST /api/ai-agent/run` 不再进入 `mnote-cli host`,统一返回 `410 legacy_ai_agent_run_retired`,响应头 `x-mnote-ai-execution-owner=legacy-ai-agent-run-retired`
- [x] 更新 compat 单测:`direct_ai_agent_run_returns_legacy_retired_guard``explicit_agent_provider_returns_legacy_retired_guard``explicit_hermes_provider_fails_instead_of_using_legacy_next_or_direct_bridge`
- [x] 验证命令:`cd rust && cargo test -p mnote-web ai_agent_run -- --nocapture`1 passed。
- [x] 验证命令:`cd rust && cargo test -p mnote-web explicit_ -- --nocapture`2 passed。
- [x] 验证命令:`cd rust && cargo build -p mnote-web`:通过;正式 `3000` 已重启到新二进制,PID `2108896`
- [x] 验证命令:`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-retirement-guard.js`:通过,status `410`code `legacy_ai_agent_run_retired`owner `legacy-ai-agent-run-retired`
- [x] 默认退役历史 smoke`task155-e27-ai-edit-smoke.js``task156-e27-ai-writeback-smoke.js``task178-page-ai-local-subtree-context-smoke.js``task052-ai-tools-runtime-smoke.js``task161-wolai-page-ai-shell-smoke.js``task111-phase7-document-ai-online-smoke.js` 已移入本机 gitignored `recycle/scripts/retired-ai-agent-run-smokes/`,历史版本默认输出 `{ retired: true }`,不再期待旧 `/api/ai-agent/run -> mnote-cli` 成功;如需历史对照,必须显式设置 `MNOTE_ALLOW_RETIRED_AI_AGENT_RUN_SMOKE=1`
- [x] legacy 调用方清点:`wolai-frontend/src/components/ai-agent/AiAgentPanel.tsx``wolai-frontend/src/components/editor/blocks/MindmapAiAgentPanel.runtime.tsx``wolai-frontend/src/components/onlyoffice/OnlyOfficeAiAgentPanel.runtime.tsx``wolai-frontend/src/app/api/mindmap-ai/expand-node/route.ts` 仍是旧域/兼容调用点,不属于当前 mnote-web 页面 AI 主链;若被调用会命中 `legacy_ai_agent_run_retired` guard,后续按各自 domain 另拆 Hermes plugin / Rust bridge 迁移。
- [x] 活跃设计口径同步:`5-6``5-9``10-review` 已改为历史/已覆盖/legacy guard 说明,不再把 `/api/ai-agent/run` 写作页面 AI 长期入口。
验收标准:
- [x] 新页面 AI 主链无 `/api/ai-agent/run` 请求。
- [x] 访问旧 `/api/ai-agent/run` 不会静默 fallback 到 Hermes、Codex、旧 orchestrator。
- [x] `provider=hermes` 不再产生新页面 AI 主链 502。
- [x] 活跃文档不再把 `/api/ai-agent/run` 描述为页面 AI 长期入口。
- [x] legacy 调用方清单为空,或全部有明确退场说明。
---
## 14. Task L:权限、审计与可观测性闭环
目标:
> 让 Hermes 调用 mnote 工具后的每一次业务影响都可审计、可追踪、可复盘。
建议修改范围:
- `rust/crates/mnote-web/src/hermes_tools/**`
- `rust/crates/bridge-runtime/src/**`
- audit / event log 相关 crate 或 route
- smoke
- `scripts/task-hermes-page-ai-audit-smoke.js`
执行步骤:
- [x] 定义统一 trace 字段:`traceId/sessionId/runId/toolCallId/documentId/workspaceId/actorId/toolName`
- [x] 所有 tool call 开始时记录 intent。
- [x] 所有成功写入记录 result 和 command id。
- [x] 所有失败记录错误码,不记录完整隐私正文。
- [x] smoke 执行一次 `mnote.page.get` 和一次写工具。
- [x] smoke 从日志或审计接口确认两次 tool call 都可追踪。
2026-05-14 L 执行证据:
- [x] 修正 `rust/crates/mnote-web/src/routes/hermes_tools.rs``audit_push` 同步写入内存缓存与 JSONL 持久化落点,默认路径 `tmp/mnote-hermes-tool-audit.jsonl`,可用 `MNOTE_HERMES_TOOL_AUDIT_LOG` 覆盖;`GET /api/hermes/tools/mnote/audit?persistedOnly=true` 只读取持久化审计。
- [x] workspace 上下文冲突现在也会写入 `started -> failed` 审计链,失败审计只包含 trace / tool / actor / workspace / document / status / message,不写入页面标题或正文。
- [x] 扩展 `scripts/task-hermes-page-ai-audit-smoke.js`:执行 `mnote.page.get``mnote.page.save`、workspace 权限失败三类调用;分别查询内存 audit 与 `persistedOnly=true` audit;断言写工具持久化 audit 串到 Rust `commandId`,权限失败持久化 audit 不包含正文 marker 或页面标题。
- [x] 验证命令:`cd rust && cargo test -p mnote-web hermes_tools_ -- --nocapture`9 passed。
- [x] 验证命令:`cd rust && cargo build -p mnote-web`:通过;正式 `3000` 已重启到新二进制,PID `2175265`
- [x] 验证命令:`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-audit-smoke.js`:通过;测试页 `tree_1778723140835_1`,写命令 `page_body_save_req_1778723140928_9``writePersistedPhases=["started","completed"]``deniedPersistedPhases=["started","failed"]`
验收标准:
- [x] 每个 tool call 都有稳定 trace。
- [x] 写工具能追踪到最终 Rust command。
- [x] 失败不会泄露无权限页面内容。
- [x] 日志可以把页面 AI 面板请求、Hermes run、mnote tool call、Rust command 串起来。
- [x] 审计只保存业务影响,不保存完整 Hermes 聊天历史。
---
## 15. Task M:端到端验收矩阵
目标:
> 用一组固定 smoke 证明新主线能覆盖只读、写正文、写标题/设置、artifact 和旧链退役。
必须具备的 smoke
- [x] `scripts/task-hermes-page-ai-smoke.js`
- [x] `scripts/task-hermes-page-ai-tool-smoke.js`
- [x] `scripts/task-hermes-page-ai-writeback-smoke.js`
- [x] `scripts/task-hermes-page-ai-title-options-smoke.js`
- [x] `scripts/task-hermes-page-ai-artifact-smoke.js`
- [x] `scripts/task-hermes-page-ai-retirement-guard.js`
- [x] `scripts/task-hermes-page-ai-mobile-smoke.js`
- [x] `scripts/task-hermes-page-ai-audit-smoke.js`
执行顺序:
- [x] 只读 session/run smoke。
- [x] 只读 tool smoke。
- [x] 正文写入 smoke。
- [x] 标题与页面设置 smoke。
- [x] artifact smoke。
- [x] 旧链退役 guard。
- [x] 移动端 / 窄屏抽屉 smoke。
- [x] 刷新恢复 smoke。
2026-05-14 M 执行证据:
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-smoke.js`:通过;`tree_1778723249255_3`session `mnote_smoke_mp4twavi`run `run_smoke_mp4twavi``sessionDetailHits=2`
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-tool-smoke.js`:通过;`tree_1778723260375_5`,工具 `mnote.page.get`,标题 `TEST-HERMES-AI-tool-mp4twjgi`
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-writeback-smoke.js`:通过;`tree_1778723271226_7`marker `TEST-HERMES-AI-WRITEBACK-mp4twrtt`command `page_body_save_req_1778723271849_379`
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-title-options-smoke.js`:通过;`tree_1778723295128_9`,标题 `TEST-HERMES-AI-title-updated-mp4txa9v``pageFont` 返回 `page_option_not_wired` warning。
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-artifact-smoke.js`:通过;`tree_1778723316873_11``referenceEdgeCount=6``meta.aiArtifacts.projectionOnly=true`
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-retirement-guard.js`:通过;旧入口返回 `410 legacy_ai_agent_run_retired`
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-mobile-smoke.js`:通过;390px viewport 下 drawer/panel 均无横向溢出。
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-audit-smoke.js`:通过;`tree_1778723372948_15`,写命令 `page_body_save_req_1778723373026_707`,持久化 audit 可查 `started/completed` 与权限失败 `started/failed`
总体验收标准:
- [x] 所有 smoke 在真实 `http://127.0.0.1:3000` 上通过。
- [x] 使用默认真实测试账号,不使用 `MNOTE_DEV_AUTH=1` 伪登录作为正式验收。历史执行时使用旧 Auth 后端;当前默认应使用 SQLite control-plane Auth / session。
- [x] smoke 只操作本轮新建测试数据,测试数据前缀统一为 `TEST-HERMES-AI-<timestamp>`
- [x] 测试结束后可定位创建的页面、artifact、edge、audit。
- [x] 任一 smoke 失败时,必须记录到 `bugs/` 对应分类,而不是在 checklist 中直接标 GREEN。
---
## 16. Task N:文档回填与 old 目录治理
目标:
> 代码主链切换后,设计目录继续反映真实状态,避免旧 CLI-first 口径回流。
2026-05-14 口径审计记录:
- [x] `find design/07-ai -maxdepth 3 -type f | sort` 已确认当前 07-ai 活跃结构为:`7-1``7-2``7-3``7-4``7-5``7-6``done/`;后续 Mini Hermes 控制面由 `design/07-ai/process/7-7-page-ai-mini-hermes-control-surface-v1.md` 承接。
- [x] `design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 仍保留为历史决策记录,并已标 `[recycle]`
- [x] `design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md``mnote-cli` 命中均用于说明历史 CLI-first / 被覆盖 / 禁止继续采用的口径。
- [x] `design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md``mnote-cli``provider=hermes` 命中均用于迁移目标、legacy guard 或验收条件,不把它写成长期主线。
- [x] 活跃设计命中已收口:`5-6``5-9``10-review/04``10-review/06``1-1``3-1` 均已把旧 `/api/ai-agent/run` / `mnote-cli host` 写成历史、兼容、已覆盖或 legacy guard。
- [x] `design/10-review/04-secondary-domains-and-design-governance-review.md` 已继续指向 Hermes plugin / Rust bridge,并明确旧 `openai-agents-python` / `mnote-cli host` 只按历史/兼容/对照链处理。
建议修改范围:
- `design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
- `design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`
- `design/03-rust-web/reference/3-rust-web-long-term-architecture-v1.md`
- `design/03-rust-web/process/3-15-runtime-fallback-retirement-checklist-v1.md`
- `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`
- `design/05-editor-mainline/reference/5-9-wolai-aline-continuous-checklist-v1.md`
- `design/old/07-ai/process/*`
执行步骤:
- [x] 把完成证据回填到本 checklist 对应 task。
- [x] 7-4 的实现证据已满足完成定义,本文已归档到 `design/07-ai/done/` 并同步更新引用。
- [x] 保留 `7-3` 为主线设计稿;`7-4` 作为实现与验收执行清单,不覆盖 `7-3` 的架构口径。
- [x] 活跃设计稿中只允许历史、兼容、已覆盖或 legacy guard 说明提到 `mnote-cli host`
- [x] 旧 CLI-first 文档必须继续留在 `design/old/07-ai/process/` 并标 `[recycle]`
- [x] 更新 `design/10-review` 中旧建议,使其指向 Hermes plugin / Rust bridge。
验收标准:
- [x] `find design/07-ai -maxdepth 3 -type f | sort` 能清楚看到当前 process/done 状态。
- [x] `rg -n "mnote-cli.*唯一|唯一长期 agent|CLI-first" design --glob '*.md'` 的活跃目录命中都带有“历史/回收/已覆盖”说明。
- [x] `design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 仍保留为历史决策记录。
- [x] 没有把未完成代码能力提前标成 done。
---
## 16.1 剩余顺序执行 checklist(按 G-N 顺序)
> 这一段只收纳当前仍未完成的项,按顺序逐项勾选。每个 task 都必须在本段回填“执行命令 / 证据路径 / 结果”,不能只写口头结论。
>
> Hermes Web UI 参考目录固定为 `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18`。下文所有 `packages/...` 路径都相对此目录。需要对照时按列出的文件和符号打开,不整目录通读,不照搬 Vue / Naive UI / 管理台页面。
### G1:把 tool event 刷新恢复补成真实 Hermes upstream 闭环
**目标:** 证明 `mnote.page.get` 是由真实 Hermes upstream run 触发,并由 Hermes session 存储回灌;mnote 页面 AI 只消费 Hermes 事件。
**必须参考 Hermes Web UI 的位置:**
| 参考目的 | 具体文件 | 具体位置 / 搜索点 | mnote 采用方式 |
| --- | --- | --- | --- |
| run 请求与事件字段 | `packages/client/src/api/hermes/chat.ts` | `StartRunRequest` 第 14 行;`RunEvent` 第 28 行 | 对齐 mnote proxy / Leptos 面板消费的 run event 字段 |
| session handler 注册 | `packages/client/src/api/hermes/chat.ts` | `registerSessionHandlers` 第 297 行;`resumeSession` 第 420 行;`startRunViaSocket` 第 432 行 | 对齐“按 session 订阅事件、刷新后 resume”的语义 |
| tool event 归并到消息 | `packages/client/src/stores/hermes/chat.ts` | `mapHermesMessages` 第 125 行;`case 'tool.started'` 第 910 / 1340 行;`case 'tool.completed'` 第 948 / 1378 行 | 对齐 tool started / completed 与 session detail 里的消息形态 |
| tool message 展示 | `packages/client/src/components/hermes/chat/MessageItem.vue` | `message.role === 'tool'` 第 508 行;`formatToolPayload` 第 263 行;`tool-preview` 第 543 行;`tool-details` 第 555 行 | 对齐默认折叠、摘要和详情展开,不照搬样式 |
| upstream tool event 生成 | `packages/server/src/services/hermes/chat-run-socket.ts` | `applyResponseStreamEvent` 第 1035 行;`function_call` 第 1101 / 1110 行;`tool.started` 第 1131 行;`function_call_output` 第 1145 行;`tool.completed` 第 1165 行;`flushResponseRunToDb` 第 1207 行 | 确认 tool event 来源于 upstream response stream,并最终 flush 到 Hermes DB |
| Hermes plugin 发现 | `packages/server/src/services/hermes/plugins.ts` | `PluginManager` 第 63 / 120 行;`user_dir = get_hermes_home() / "plugins"` 第 137 行;`providesTools` 第 200 行;`listHermesPlugins` 第 310 行 | 确认 mnote 工具必须进入 Hermes plugin / tool registry |
**顺序执行 checklist**
- [x] G1.1 运行 `hermes plugins list`,确认当前 Hermes 是否已经能发现 `mnote` plugin;把输出摘要回填到本 task。
- [x] G1.2 运行 `hermes tools list --platform api_server`,确认 `mnote` toolset 或 `mnote_page_get` 是否已经可见;把输出摘要回填到本 task。
- [x] G1.3 如果 `mnote` 不可见,创建或修正用户插件目录 `/home/lix/.hermes/plugins/mnote/`,其中 `plugin.yaml` 必须声明 `name: mnote``provides_tools``__init__.py` 必须通过 `ctx.register_tool(..., toolset="mnote")` 注册工具。
- [x] G1.4 插件对 Hermes 暴露的函数名使用 OpenAI function-name 兼容名,例如 `mnote_page_get`;插件内部再映射到 mnote-web tool route 的 `toolName: "mnote.page.get"`
- [x] G1.5 插件 handler 只调用 `POST http://127.0.0.1:3000/api/hermes/tools/mnote/call``MNOTE_UI_BASE_URL` 指向的同源 mnote-web route,不直接读写 Convex。
- [x] G1.6 将 `mnote` 加入 `/home/lix/.hermes/config.yaml``plugins.enabled` 后,重新运行 `hermes plugins list``hermes tools list --platform api_server`
- [x] G1.7 触发一次真实 Hermes upstream run,请求内容使用“读取当前页面标题和页面设置”,确认模型实际选择 `mnote_page_get` / `mnote.page.get`
- [x] G1.8 在 mnote 页面 AI 面板网络记录里确认事件来自 `/api/hermes/client/runs``/api/hermes/client/events/{run_id}` 或当前 Hermes proxy 等价入口,不是前端本地拼出的假事件。
- [x] G1.9 刷新页面后重新打开 AI 面板,确认同一 tool event 从 Hermes session detail 恢复,顺序与首次流式展示一致。
- [x] G1.10 回填证据:Hermes run id、session id、tool call id、mnote trace id、截图或 smoke 输出路径。
2026-05-14 G1 执行证据:
- [x] `python -m py_compile /home/lix/.hermes/plugins/mnote/__init__.py`:通过。
- [x] `hermes plugins list``mnote` 显示为 `enabled` user plugin。
- [x] `hermes tools list --platform api_server`Plugin toolsets 中 `mnote` 显示为 enabled。
- [x] `/home/lix/.hermes/hermes-agent/venv/bin/python` 调用 `get_tool_definitions(enabled_toolsets=["mnote"])`:可见 `mnote_page_get``mnote_page_save``mnote_page_update_title``mnote_page_update_options``mnote_artifact_create_summary``mnote_artifact_create_ai_note`
- [x] 发现并修复运行态差异:Hermes gateway 长进程启动早于 mnote plugin 接入,首次 `/v1/runs` 只看到 MemPalace 等旧工具;执行 `hermes gateway restart` 后真实 upstream run 可见 `mnote_page_get`
- [x] 3000 主入口已重启为当前 `mnote-web`,并配置 `MNOTE_WEB_HERMES_UPSTREAM_URL=http://127.0.0.1:8642` 与服务端 Hermes API key`GET /api/hermes/client/models` 经 mnote-web proxy 返回 `hermes-agent`
- [x] 修正 `rust/crates/mnote-web/src/routes/hermes_client.rs`Hermes run context 透传 `actorId/actorType/sessionId/traceId`,并在 `toolGuidance` 中要求读取标题/页面设置时传 `includeBody=false/includeOptions=true/includeBlocks=false`
- [x] 验证命令:`cd rust && cargo test -p mnote-web hermes_client_run_body_carries_page_context_into_run_input -- --nocapture`1 passed。
- [x] 验证命令:`cd rust && cargo build -p mnote-web`:通过。
- [x] 真实页面 AI 面板不 mock 验证:测试页 `TEST-HERMES-AI-realpanel-green-mp4rv3w3`,页面 AI 捕获网络请求 `POST /api/hermes/client/sessions``GET /api/hermes/client/sessions/{sessionId}``POST /api/hermes/client/runs``GET /api/hermes/client/events/run_4a8b09db7fbc45b8bb1bbea68b436093`,未请求 `/api/ai-agent/run`
- [x] Hermes run`run_4a8b09db7fbc45b8bb1bbea68b436093`status `completed`session `mnote_tree_1778719834312_1_page-ai-mp4rv470`,输出包含标题 `TEST-HERMES-AI-realpanel-green-mp4rv3w3` 与页面设置。
- [x] Hermes session export`mnote_tree_1778719834312_1_page-ai-mp4rv470``message_count=4``tool_call_count=1`assistant tool call 为 `mnote_page_get`Hermes tool call id 为 `call_00_5B7fVip245331pk4zYyd5532`tool message 内容 `ok=true``toolName=mnote.page.get`
- [x] mnote audit`trace_1778719842620_2b05ef8600`mnote tool call id `tool_1778719842620_7687c1860a``phase=started/completed``effect=read``actorId=a4b72c17-49e3-46d3-8456-24a0a7044d64`
- [x] 刷新恢复:同一页面刷新后重新打开 AI 面板,请求 `GET /api/hermes/client/sessions/mnote_tree_1778719834312_1_page-ai-mp4rv470`,抽屉文本恢复出 Hermes session 中的页面标题与 tool message。
**验收标准:**
- [x] 真实 Hermes upstream run 触发 `mnote.page.get`,不是直接调用 mnote-web tool smoke 假代替。
- [x] tool event 通过 Hermes run stream / session detail 回灌到页面。
- [x] 页面刷新后仍能从 Hermes session detail 恢复同一批 tool event。
- [x] 前端本地 state 只承担临时渲染,不作为 tool event 最终真相。
### H1:补 workspace ACL 负向验证与正文立即回显
**目标:** 写正文工具必须证明权限失败不泄露、成功写入后主编辑区立即可见,且 AI 面板不保存正文真相。
**必须参考 Hermes Web UI 的位置:**
| 参考目的 | 具体文件 | 具体位置 / 搜索点 | mnote 采用方式 |
| --- | --- | --- | --- |
| 权限失败事件形态 | `packages/client/src/api/hermes/chat.ts` | `RunEvent` 第 28 行;`onToolStarted` / `onToolCompleted` 第 69-70 行;`onRunFailed` 第 73 行 | 失败也走稳定事件,不出现空白 502 |
| 失败事件持久化 | `packages/server/src/services/hermes/chat-run-socket.ts` | `emit` 第 574 / 963 行;`markCompleted` 第 978 / 1276 行;`flushResponseRunToDb` 第 1207 行 | 确认失败信息也能进入可恢复 session |
| session 只保存消息真相 | `packages/server/src/db/hermes/session-store.ts` | `HermesMessageRow` 第 35 行;`addMessage` 第 331 行;`getSessionDetail` 第 313 行 | 失败消息留在 Hermes session,不写入 mnote 正文 |
| tool failed 展示 | `packages/client/src/components/hermes/chat/MessageItem.vue` | `tool-error-badge` 第 551 行;`tool-details` 第 555 行 | 权限失败显示错误摘要,不泄露正文 |
| 写入后 UI 刷新边界 | `packages/client/src/stores/hermes/chat.ts` | `sendMessage` 第 646 行;`runHadToolActivity` 第 737 行;`cleanup` 附近 | tool 完成后刷新业务事实,不复制正文真相到 AI 面板 |
**顺序执行 checklist**
- [x] H1.1 新增或扩展 `scripts/task-hermes-page-ai-writeback-smoke.js`:构造无 workspace 权限或无页面写权限场景,调用 `mnote.page.save`
- [x] H1.2 断言权限失败返回稳定错误码,例如 `permission_denied` / `workspace_access_denied`,响应体不得包含页面标题、正文全文或 block 原文。
- [x] H1.3 在页面 AI 面板断言失败展示为 tool failed / permission denied,不是 502、空白或旧 provider 错误。
- [x] H1.4 正向写入时,使用测试前缀 `TEST-HERMES-AI-<timestamp>` 调用 `mnote.page.save`
- [x] H1.5 写入完成后不刷新页面,直接断言主编辑区 island 可见新内容。
- [x] H1.6 刷新页面后再次断言新内容存在,证明业务事实已回到正式正文链。
- [x] H1.7 检查 AI 面板状态或 session detail,确认不保存完整正文副本,只保存 tool event / tool result 摘要。
2026-05-14 H1 执行证据:
- [x] 修正 `rust/crates/mnote-web/src/routes/hermes_tools.rs`:当请求头 `x-mnote-workspace-id` 与 tool payload `workspaceId` 不一致时,返回 `403 workspace_context_conflict`,不进入页面读写链。
- [x] 新增单测 `hermes_tools_reject_workspace_context_conflict_without_content_leak`:断言响应不包含测试 fixture 标题 `服务端页面` 与正文 `章节一`
- [x] 验证命令:`cd rust && cargo test -p mnote-web hermes_tools_reject_workspace_context_conflict_without_content_leak -- --nocapture`1 passed。
- [x] 扩展 `scripts/task-hermes-page-ai-writeback-smoke.js`:先构造 workspace 上下文冲突负向调用,断言 `403 workspace_context_conflict` 且不泄露页面标题 / 正文标记;再用页面 AI 面板 mock Hermes `tool.failed` 事件断言 UI 展示 `mnote.page.save``permission_denied`,不是 502 / 空白 / 旧 provider 错误;最后执行 dryRun、正式写入、主编辑区即时回显、幂等重试和刷新后读取。
- [x] 验证命令:`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-writeback-smoke.js`:通过;最终复跑测试页 `tree_1778720381778_4`,标记 `TEST-HERMES-AI-WRITEBACK-mp4s6ubg`,写入命令 `page.body.save``commandId=page_body_save_req_1778720382427_413`
- [x] smoke 在正式 `3000` 运行,写入完成后未刷新页面即等待主编辑区出现 `TEST-HERMES-AI-WRITEBACK-*` 标记;随后通过 `/api/documents/content` 读取确认刷新后仍存在。
**验收标准:**
- [x] 未授权不能写正文,且失败响应不泄露正文。
- [x] 成功写入后主编辑区即时可见。
- [x] 刷新后正文仍存在。
- [x] 页面 AI 不持有正文真相副本。
### I1:补标题与 sidebar 一致性验证
**目标:** `mnote.page.update_title``mnote.page.update_options` 只改变 mnote 页面事实;Hermes session 标题、模型状态和页面标题不能混成一份真相。
**必须参考 Hermes Web UI 的位置:**
| 参考目的 | 具体文件 | 具体位置 / 搜索点 | mnote 采用方式 |
| --- | --- | --- | --- |
| session title 只是会话标题 | `packages/server/src/db/hermes/session-store.ts` | `HermesSessionRow` 第 9 行;`renameSession` 第 202 行 | 区分 Hermes 会话标题与 mnote 页面标题 |
| session detail 字段 | `packages/server/src/db/hermes/session-store.ts` | `HermesSessionDetailRow` 第 56 行;`getSessionDetail` 第 313 行 | 刷新恢复 AI 会话,不恢复页面标题草稿 |
| 面板标题展示 | `packages/client/src/components/hermes/chat/ChatPanel.vue` | `activeSessionTitle` 第 193 行;`headerTitle` 第 197 行;`SessionListItem` 第 598 / 636 行;`MessageList` 第 790 行;`ChatInput` 第 791 行 | AI 面板只展示 session 语义,不维护页面标题草稿 |
| session 列表标题 | `packages/client/src/components/hermes/chat/SessionListItem.vue` | `session-item-title` 第 94 行;`session.title` 第 96 行;`session-item-model` 第 100 行 | 不把 sidebar 页面标题同步需求写进 Hermes session list |
| model 展示边界 | `packages/client/src/components/layout/ModelSelector.vue` | `selectedDisplayName` 第 16 行;`handleSelect` 第 64 行;`model-name` 第 105 行;`model-item` 第 142 行 | model 真相不回写 mnote page aggregate |
**顺序执行 checklist**
- [x] I1.1 扩展 `scripts/task-hermes-page-ai-title-options-smoke.js`,修改标题为 `TEST-HERMES-AI-<timestamp>`
- [x] I1.2 断言页头标题、breadcrumb/current title、sidebar 树节点标题三处立即一致。
- [x] I1.3 刷新页面后再次断言三处标题一致,且值来自页面真源。
- [x] I1.4 断言 AI 面板 session 标题没有被当成 mnote 页面标题真相。
- [x] I1.5 调用 `mnote.page.update_options` 修改 `smallText``wideLayout`,确认只接受 `runtimeSupport == "wired"` 字段。
- [x] I1.6 对 `planned` / `ui_only` 字段执行 dryRun 或正式调用,确认返回明确警告或错误,不产生页面事实写入。
2026-05-14 I1 执行证据:
- [x] 修正 `rust/crates/mnote-web/src/hermes_tools/page.rs``mnote.page.update_options` 对未接线字段返回 `ignoredOptions``warnings`,并只把 wired 字段放入 `page.layout.updateOptions` payload。
- [x] 扩展单测 `hermes_tools_update_options_dry_run_filters_unwired_fields`:断言 `pageFont` 不进入 `diff.payload.options`,并返回 `ignoredOptions[0]="pageFont"``warnings[0].code="page_option_not_wired"`
- [x] 验证命令:`cd rust && cargo test -p mnote-web hermes_tools_update_options_dry_run_filters_unwired_fields -- --nocapture`1 passed。
- [x] 验证命令:`cd rust && cargo build -p mnote-web`:通过。
- [x] 验证命令:`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-title-options-smoke.js`:通过;测试页 `tree_1778720870666_1`,标题 `TEST-HERMES-AI-title-updated-mp4shbjq`
- [x] smoke 已断言页头标题、breadcrumb/current title、sidebar 树节点标题一致;`data-page-wide-layout=true``data-page-small-text=true` 生效。
- [x] smoke 已打开页面 AI 面板,确认 `data-mnote-page-ai-session-owner=hermes`localStorage 仅保存 `{"activeSessionId":"mnote_tree_1778720870666_1_page-ai-mp4shbwk"}`,不保存页面标题。
**验收标准:**
- [x] 标题修改后页头、breadcrumb、sidebar、刷新后标题一致。
- [x] 页面设置修改只允许 wired 字段。
- [x] AI 面板没有独立标题状态。
### J1:补 artifact reference edge 与 projection-only 验收
**目标:** artifact 工具经 Hermes tool call 与 Rust kernel 创建业务事实,`AI Artifacts` 只是 projection 分组,不是真实 kernel node。
**必须参考 Hermes Web UI 的位置:**
| 参考目的 | 具体文件 | 具体位置 / 搜索点 | mnote 采用方式 |
| --- | --- | --- | --- |
| artifact 工具作为 plugin/tool 暴露 | `packages/server/src/services/hermes/plugins.ts` | `providesTools` 第 200 行;`providesHooks` 第 201 行;`requiresEnv` 第 202 行 | artifact 能力挂到 Hermes plugin,不做 mnote-web 私有按钮直写 |
| artifact 结果仍是 tool event | `packages/client/src/api/hermes/chat.ts` | `RunEvent` 第 28 行;`onToolCompleted` 第 70 行 | summary / ai_note 结果作为 tool event 展示 |
| tool result 展示 | `packages/client/src/components/hermes/chat/MessageItem.vue` | `toolPreview` 第 543 行;`toolResultPayload` 第 332 行;`tool-details` 第 555 行 | artifact 结果只展示摘要和详情,不写入正文 |
| 历史 tool message 恢复 | `packages/client/src/stores/hermes/chat.ts` | `mapHermesMessages` 第 125 行;`case 'tool.completed'` 第 948 / 1378 行 | 刷新后 artifact tool event 仍从 session 恢复 |
| session DB tool 字段 | `packages/server/src/db/hermes/session-store.ts` | `tool_call_id` 第 40 行;`tool_calls` 第 41 行;`tool_name` 第 42 行;`addMessage` 第 331 行 | tool call id 与 artifact audit 能串联 |
**顺序执行 checklist**
- [x] J1.1 扩展 `scripts/task-hermes-page-ai-artifact-smoke.js`,创建或更新 summary,记录 `artifactDocumentId`
- [x] J1.2 通过 kernel query 或稳定 projection route 查询 summary 与当前页面之间的 reference edge。
- [x] J1.3 再创建两次 ai_note,断言每次得到独立 `artifactDocumentId`
- [x] J1.4 查询每个 ai_note 与当前页面之间的 reference edge,确认 `from/to/kind` 可追踪。
- [x] J1.5 查询 `AI Artifacts` 分组来源,确认它是 projection-only 分组,不是真实 kernel node。
- [x] J1.6 回查 artifact audit,确认包含 `sessionId/runId/toolCallId/traceId`
2026-05-14 J1 执行证据:
- [x] `cargo test -p bridge-runtime ai_artifact -- --nocapture`2 passed。
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-artifact-smoke.js`:通过;`referenceEdgeCount=6``meta.aiArtifacts.projectionOnly=true``kernelNodeId=null``auditEventCount=6`
**验收标准:**
- [x] reference edge 可通过 kernel query 或 projection 查询。
- [x] `AI Artifacts` 只是 projection-only 分组。
- [x] summary 与 ai_note 行为符合既定幂等 / 新建规则。
- [x] artifact 写入 audit 能串到 Hermes tool call。
### K1:补 legacy 调用方清点与退场文档
**目标:**`/api/ai-agent/run` 只保留为 legacy guard / 历史说明,不再被活跃设计或页面 AI 主链描述为长期入口。
**必须参考 Hermes Web UI 的位置:**
| 参考目的 | 具体文件 | 具体位置 / 搜索点 | mnote 采用方式 |
| --- | --- | --- | --- |
| 新 run 入口 | `packages/client/src/api/hermes/chat.ts` | `startRunViaSocket` 第 432 行 | 对照新页面 AI 主链,不回到旧 `/api/ai-agent/run` |
| run server 注册 | `packages/server/src/routes/hermes/chat-run.ts` | `setChatRunServer` 第 5 行;`getChatRunServer` 第 9 行 | 确认 Hermes run server 是真实入口 |
| upstream run 生命周期 | `packages/server/src/services/hermes/chat-run-socket.ts` | `handleRun` 第 488 行;`markCompleted` 第 1276 行 | 长期主链是 Hermes upstream run |
| plugin 发现 | `packages/server/src/services/hermes/plugins.ts` | `listHermesPlugins` 第 310 行 | 旧入口退场后,mnote 能力从 Hermes plugin/tool 面进入 |
**顺序执行 checklist**
- [x] K1.1 运行 `rg -n "/api/ai-agent/run|provider=hermes|mnote-cli host|CLI-first" design rust wolai-frontend scripts --glob '*.md' --glob '*.rs' --glob '*.ts' --glob '*.tsx' --glob '*.js'`
- [x] K1.2 将命中逐项归类为:`历史证据``legacy guard``仍需迁移``误导性活跃口径`
- [x] K1.3 对 `仍需迁移` 的代码调用方补迁移或补退场 guard。
- [x] K1.4 对 `误导性活跃口径` 的设计稿改写为“历史/兼容/已覆盖”,必要时移入 `design/old/` 并标 `[recycle]`
- [x] K1.5 更新旧 smoke:不再期待 `/api/ai-agent/run -> mnote-cli` 成功,只验证 legacy guard 返回稳定退场码。
- [x] K1.6 在本 checklist 回填 rg 输出摘要和修改文件清单。
2026-05-14 K1 执行证据:
- [x] rg 命中已归类:`design/old/**``design/07-ai/done/7-1**` 为历史证据;`scripts/task-hermes-page-ai-retirement-guard.js``rust/crates/mnote-web/src/routes/compat.rs` 为 legacy guard;旧 E27/phase7/page-ai smoke 已默认退役;`wolai-frontend` 中 Mindmap / OnlyOffice / generic AI 面板调用点归为非当前页面 AI 主链的 legacy domain 调用点,后续按各自 domain 迁移。
- [x] 修改文件:`rust/crates/mnote-web/src/routes/compat.rs``scripts/task-hermes-page-ai-retirement-guard.js`、历史退役 smoke 当前本机归档于 gitignored `recycle/scripts/retired-ai-agent-run-smokes/``design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md``design/05-editor-mainline/reference/5-9-wolai-aline-continuous-checklist-v1.md``design/10-review/04-secondary-domains-and-design-governance-review.md`
- [x] 旧 smoke 默认退役验证:上述 6 个历史 smoke 均返回 `{ ok: true, retired: true }`
**验收标准:**
- [x] 活跃设计稿不再把 `/api/ai-agent/run` 描述为页面 AI 长期入口。
- [x] legacy 调用方清单为空,或每一项都有明确退场说明。
- [x] 旧入口不会静默 fallback 到 Hermes、Codex 或旧 orchestrator。
### L1:把审计从进程内缓存收口到持久化可追踪链
**目标:** 每次 Hermes 调用 mnote 工具都能从页面 AI 请求串到 Hermes run、mnote tool call、Rust command 和最终业务结果。
**必须参考 Hermes Web UI 的位置:**
| 参考目的 | 具体文件 | 具体位置 / 搜索点 | mnote 采用方式 |
| --- | --- | --- | --- |
| 前端事件字段 | `packages/client/src/api/hermes/chat.ts` | `RunEvent` 第 28 行,重点看 `run_id``session_id``tool``name` | 前端事件字段要能和审计字段串联 |
| run / tool / session 追踪 | `packages/server/src/services/hermes/chat-run-socket.ts` | `emit` 第 574 / 963 行;`applyResponseStreamEvent` 第 1035 行;`flushResponseRunToDb` 第 1207 行;`markCompleted` 第 1276 行 | 对齐 run、tool、session 三段追踪模型 |
| tool call 持久字段 | `packages/server/src/db/hermes/session-store.ts` | `tool_call_id` 第 40 行;`tool_calls` 第 41 行;`tool_name` 第 42 行;`addMessage` 第 331 行 | mnote audit 至少能关联 Hermes tool call id |
| usage 不进页面事实 | `packages/server/src/db/hermes/usage-store.ts` | `recordUsage` 第 16 行;`getUsage` 第 53 行;`getUsageBatch` 第 73 行;`deleteUsage` 第 118 行 | usage/model 是 Hermes 真相,不写入 mnote page aggregate |
**顺序执行 checklist**
- [x] L1.1 定义持久化审计落点:复用现有 audit/event log,或新增最小 `mnote_hermes_tool_audit` 存储;不得只依赖进程内缓存。
- [x] L1.2 审计字段固定包含 `traceId/sessionId/runId/toolCallId/documentId/workspaceId/actorId/toolName/effect/commandId/errorCode`
- [x] L1.3 在 tool call start / success / failure 三个阶段写入同一条可查询链。
- [x] L1.4 更新 `scripts/task-hermes-page-ai-audit-smoke.js`,重启进程或绕过进程内缓存后仍能按 `traceId` 回查审计。
- [x] L1.5 权限失败场景必须断言审计里没有完整正文、无权限页面标题或 block 原文。
- [x] L1.6 写工具成功场景必须断言审计能追踪到最终 Rust command id。
2026-05-14 L1 执行证据:
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-audit-smoke.js`:通过,`persistedOnly=true` 查询确认持久化审计可按 traceId 回查。
- [x] 权限失败审计:`deniedPersistedPhases=["started","failed"]`smoke 已断言持久化 audit 不含正文 marker 与页面标题。
**验收标准:**
- [x] 每个 tool call 都能追踪到 session、run、toolCall 和最终业务结果。
- [x] 失败不泄露内容。
- [x] 审计不是只存在于进程内缓存。
### M1:把正式 `3000` 的 E2E smoke 跑成最终验收矩阵
**目标:** 临时 `3019` 证据不能作为最终完成;必须在正式 `3000` + 真实 SQLite control-plane Auth / session 上复跑全矩阵。历史记录中的 Convex Auth 只作为当时验收背景。
**必须参考 Hermes Web UI 的位置:**
| 参考目的 | 具体文件 | 具体位置 / 搜索点 | mnote 采用方式 |
| --- | --- | --- | --- |
| run / resume / abort 客户端入口 | `packages/client/src/api/hermes/chat.ts` | `startRunViaSocket` 第 432 行;`resumeSession` 第 420 行;`registerSessionHandlers` 第 297 行 | 作为 E2E smoke 的网络断言依据 |
| session detail | `packages/client/src/api/hermes/sessions.ts` | `SessionSummary` 第 3 行;`HermesMessage` 第 36 行;`fetchSessions` 第 50 行;`fetchSession` 第 81 行 | 刷新恢复 smoke 必须断言 session detail |
| run 生命周期 | `packages/server/src/services/hermes/chat-run-socket.ts` | `resumeSession` 第 427 行;`handleRun` 第 488 行;`markCompleted` 第 1276 行 | 正式 `3000` 上断言 run 生命周期完整 |
| 面板组件边界 | `packages/client/src/components/hermes/chat/ChatPanel.vue` | `SessionListItem` import 第 22 行、使用第 598 / 636 行;`MessageList` 第 790 行;`ChatInput` 第 791 行 | 只对齐页面 AI 抽屉需要的子集 |
**顺序执行 checklist**
- [x] M1.1 确认主入口 `http://127.0.0.1:3000` 运行的是包含本轮改动的 `mnote-web`,不是旧进程。
- [x] M1.2 使用默认 SQLite control-plane 测试账号 `mnote.e2e@example.com` 登录,不使用 `MNOTE_DEV_AUTH=1` 伪登录作为正式验收;旧 Convex Auth 仅作 compat 回归。
- [x] M1.3 运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-smoke.js`
- [x] M1.4 运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-tool-smoke.js`
- [x] M1.5 运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-writeback-smoke.js`
- [x] M1.6 运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-title-options-smoke.js`
- [x] M1.7 运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-artifact-smoke.js`
- [x] M1.8 运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-retirement-guard.js`
- [x] M1.9 运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-mobile-smoke.js`
- [x] M1.10 运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-hermes-page-ai-audit-smoke.js`
- [x] M1.11 确认 smoke 只操作本轮新建测试数据,统一前缀 `TEST-HERMES-AI-<timestamp>`
- [x] M1.12 任一 smoke 失败时,把失败写入 `bugs/07-ai/process/` 或真实 owner 分类,不在 checklist 里标 GREEN。
**验收标准:**
- [x] 所有 smoke 在正式 `3000` 通过。
- [x] 可以定位每次 smoke 生成的页面、artifact、edge 和 audit。
- [x] 任一 smoke 失败都会落到 `bugs/`,不会在 checklist 中假装通过。
### N1:把活跃设计口径和 old 目录治理收口
**目标:** 设计目录与真实主线一致:页面 AI 是 Hermes 面板,mnote 通过 Hermes skill/plugin 被调用,旧 CLI-first 口径只保留为历史。
**必须参考的本仓设计文件:**
| 参考目的 | 具体文件 | 具体位置 / 搜索点 | 处理方式 |
| --- | --- | --- | --- |
| Page Aggregate 口径 | `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md` | 搜索 `/api/ai-agent/run``mnote-cli host``页面 AI` | 改成历史残留 / 已覆盖 / 待迁移证据,不写成长期主线 |
| Wolai-aline 过渡基线 | `design/05-editor-mainline/reference/5-9-wolai-aline-continuous-checklist-v1.md` | 搜索 `E27``mnote-cli``页面 AI` | 保留为对标基线时必须标明过渡状态 |
| 二级域 review 建议 | `design/10-review/04-secondary-domains-and-design-governance-review.md` | 搜索 `Hermes``plugin``mnote-cli``CLI-first` | 指向 Hermes plugin / Rust bridge 的当前口径 |
| 07-ai 历史稿 | `design/old/07-ai/process/` | 搜索 `CLI-first``mnote-cli` | 继续标 `[recycle]`,不迁回活跃 process |
**顺序执行 checklist**
- [x] N1.1 运行 `rg -n "mnote-cli.*唯一|唯一长期 agent|CLI-first|/api/ai-agent/run|provider=hermes" design --glob '*.md'`
- [x] N1.2 把命中分为 `design/old` 历史命中、活跃历史说明、活跃误导口径三类。
- [x] N1.3 修改 `5-6``5-9``10-review` 中仍残留的旧 CLI-first 说法,使其明确为历史/兼容/已覆盖。
- [x] N1.4 确认 `design/old/07-ai/process/` 中历史稿继续标 `[recycle]`
- [x] N1.5 7-4 全部验收标准已满足,本文已按 design 归档规则移动到 `design/07-ai/done/`;新的活跃执行清单改由 `7-7` 承接。
- [x] N1.6 回填最终 rg 输出摘要和 design 文件迁移清单。
2026-05-14 N1 执行证据:
- [x] `rg -n "mnote-cli.*唯一|唯一长期 agent|CLI-first|/api/ai-agent/run|provider=hermes" design --glob '*.md'` 已跑完,活跃命中已逐项归类。
- [x] 活跃命中主要落在 `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md``design/05-editor-mainline/reference/5-9-wolai-aline-continuous-checklist-v1.md``design/10-review/04-secondary-domains-and-design-governance-review.md``design/10-review/06-execution-checklist-and-acceptance.md``design/01-tree-first-graph-kernel/reference/1-1-tree-first-graph-kernel-checklist-v2.md``design/03-rust-web/reference/3-1-rust-web-long-term-checklist-v2.md``design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md``design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md``design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md``design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md`
- [x] `design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 与同目录旧稿继续保留 `[recycle]`,只作为历史证据。
- [x] 本轮对 `5-6``5-9``10-review/04``10-review/06``1-1``3-1` 的旧口径改写已完成;活跃文档现已统一为 Hermes 页面内客户端 + mnote Hermes plugin/tool 口径。
- [x] 本文件仍保留在 `process/`,原因是它是执行清单而不是代码实现文件;若后续需要归档,可按 `N1.5` 的规则移动并同步更新引用。
**验收标准:**
- [x] 活跃目录的旧 CLI-first 命中都带有历史/回收/已覆盖说明。
- [x] `design/old/07-ai/process/` 保持历史记录定位。
- [x] 未完成能力不会被提前标 done。
---
## 17. 推荐执行顺序总表
| 顺序 | Task | 目标 | 退出门槛 |
| --- | --- | --- | --- |
| 1 | A | 固定旧链与 502 基线 | 有可复跑证据 |
| 2 | B | 冻结 Hermes client proxy 合同 | route 输入输出示例齐全 |
| 3 | C | 实现 Hermes client proxy 只读链 | session/run 可代理 |
| 4 | D | 冻结 mnote plugin tool 合同 | 第一批 tool schema 齐全 |
| 5 | E | 实现 manifest + `mnote.page.get` | Hermes 可读当前页 |
| 6 | F | 实现 Leptos Hermes 面板 | 页面 AI 不再请求旧 run route |
| 7 | G | 展示 tool event | tool started/completed 可刷新恢复 |
| 8 | H | 正文写入工具 | `page.body.save` 闭环 |
| 9 | I | 标题与页面设置工具 | 标题/设置走 Page Aggregate |
| 10 | J | artifact 工具 | summary / ai_note / reference edge 闭环 |
| 11 | K | 退役旧主链 | 旧 route 不再承载页面 AI 主路径 |
| 12 | L | 审计与可观测性 | tool call 到 Rust command 可追踪 |
| 13 | M | 端到端验收矩阵 | 全 smoke 通过 |
| 14 | N | 文档治理回填 | design 口径无旧主线残留 |
---
## 18. 最终完成定义
只有同时满足以下条件,才能宣布本 checklist 完成:
- [x] Hermes session 是页面 AI 会话真相。
- [x] mnote 只保存业务事实和审计,不保存聊天真相。
- [x] 页面 AI 面板是 Leptos Hermes client,不是 `mnote-cli` 面板。
- [x] mnote Hermes plugin 至少覆盖读取当前页、写正文、改标题/设置、创建 summary、创建 ai_note。
- [x] 所有写入经 Rust runtime / kernel。
- [x]`/api/ai-agent/run` 不再是页面 AI 主入口。
- [x] E2E smoke 覆盖只读、写入、artifact、刷新恢复、旧链退役;正式 `http://127.0.0.1:3000` 已复跑 8 个 smoke,见 Task M。
- [x] 活跃设计文档和执行清单一致;旧 CLI-first / `/api/ai-agent/run` 命中均已标为历史、兼容、已覆盖或 legacy guard。
### 18.1 最终完成审计记录
2026-05-14 最终复核按“完成定义 -> 真实证据”逐项检查,不只依赖本文勾选状态。
- [x] 运行态入口存在:`ss -ltnp | rg ':3000\b|:8642\b'` 确认 `mnote-web` 监听 `0.0.0.0:3000`Hermes API server 监听 `127.0.0.1:8642`
- [x] Rust tool / client / legacy guard 测试通过:
- `cargo test -p bridge-runtime ai_artifact -- --nocapture`2 passed。
- `cargo test -p mnote-web hermes_client -- --nocapture`4 passed。
- `cargo test -p mnote-web hermes_tools_ -- --nocapture`9 passed。
- `cargo test -p mnote-web ai_agent_run -- --nocapture`1 passed。
- `cargo test -p mnote-web explicit_ -- --nocapture`2 passed。
- `cargo build -p mnote-web`:通过。
- [x] Hermes plugin/toolset 可见:
- `hermes plugins list``mnote` 为 enabled user plugin。
- `hermes tools list --platform api_server`Plugin toolsets 中 `mnote` 为 enabled。
- `/home/lix/.hermes/plugins/mnote/plugin.yaml` 声明 `mnote_page_get``mnote_page_save``mnote_page_update_title``mnote_page_update_options``mnote_artifact_create_summary``mnote_artifact_create_ai_note`
- [x] 正式 `3000` smoke 全矩阵通过:
- `task-hermes-page-ai-smoke.js``tree_1778724401995_17`session `mnote_smoke_mp4ul0c2`run `run_smoke_mp4ul0c2``sessionDetailHits=2`
- `task-hermes-page-ai-tool-smoke.js``tree_1778724413742_19``mnote.page.get` 返回标题 `TEST-HERMES-AI-tool-mp4ul9ei`
- `task-hermes-page-ai-writeback-smoke.js``tree_1778724445965_21``page.body.save`command `page_body_save_req_1778724446747_1371`
- `task-hermes-page-ai-title-options-smoke.js``tree_1778724480369_23``page.head.updateTitle``page.layout.updateOptions` 通过,`pageFont` 返回 `page_option_not_wired`
- `task-hermes-page-ai-artifact-smoke.js``tree_1778724511207_25`summary / ai_note 都走 `tree.node.create``referenceEdgeCount=6``projectionOnly=true`
- `task-hermes-page-ai-retirement-guard.js`:旧入口返回 `410 legacy_ai_agent_run_retired`
- `task-hermes-page-ai-mobile-smoke.js``390px` viewport 下 drawer / panel 未横向溢出。
- `task-hermes-page-ai-audit-smoke.js``tree_1778724538669_29`,写命令 `page_body_save_req_1778724538893_1707`,持久化 audit 覆盖 `started/completed` 与权限失败 `started/failed`
- [x] 真实 Hermes upstream session 补充验证:页面 AI 不拦截 Hermes 请求时触发真实 run,`hermes sessions export --session-id mnote_tree_1778724800891_37_page-ai-mp4utko0 -` 显示 `message_count=4``tool_call_count=1``api_call_count=2``has_mnote_page_get=true`
- [x] 文档治理检查通过:
- `rg -n "mnote-cli.*唯一|唯一长期 agent|CLI-first|/api/ai-agent/run|provider=hermes" design --glob '*.md'` 的活跃命中均为历史、兼容、已覆盖、禁止项或 legacy guard 说明。
- `git diff --check` 覆盖本轮已跟踪设计稿,无空白错误。
- `rg -n "[ \t]+$"` 覆盖本轮设计稿,无尾随空白。