# [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 smoke:session/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 复制进 mnote;mnote 只做同源薄代理 | | 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.IO;mnote 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 F:Leptos 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。 - [ ] 写 smoke:tool 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-`。 - [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-` 调用 `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-`。 - [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-`。 - [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]+$"` 覆盖本轮设计稿,无尾随空白。