diff --git a/design/07-ai/process/7-61-yuxi-reference-middleware-extensions-dashboard-v1.md b/design/07-ai/process/7-61-yuxi-reference-middleware-extensions-dashboard-v1.md new file mode 100644 index 00000000..3d3537a7 --- /dev/null +++ b/design/07-ai/process/7-61-yuxi-reference-middleware-extensions-dashboard-v1.md @@ -0,0 +1,493 @@ +# 7-61 [process] Yuxi reference: middleware tool composition, extensions entry, knowledge settings UI, and local dashboard v1 + +> 创建时间:2026-06-13 +> +> 当前状态:`PROCESS` +> +> Owner:07-ai / 03-rust-web / Hermes tools / Page AI settings / Knowledge RAG UI / Dashboard +> +> 上位设计:`design/07-ai/done/7-57-yuxi-reference-ai-run-config-knowledge-facade-v1.md` +> +> 本稿是 7-57 的补充,覆盖 7-57 未涉及的 Yuxi 参考领域。 +> +> 参考代码: +> - `reference-code/Yuxi/backend/package/yuxi/agents/middlewares/`(中间件分层组合) +> - `reference-code/Yuxi/backend/package/yuxi/agents/base.py`(Agent 基类 + checkpointer) +> - `reference-code/Yuxi/web/src/views/ExtensionsView.vue`(扩展统一入口) +> - `reference-code/Yuxi/web/src/views/DataBaseInfoView.vue`(知识库详情页) +> - `reference-code/Yuxi/web/src/views/DashboardView.vue`(Dashboard 可观测性) +> - `reference-code/Yuxi/web/src/composables/useAgentStreamHandler.js`(流式事件状态机) +> - `reference-code/Yuxi/backend/package/yuxi/agents/backends/sandbox/paths.py`(虚拟路径安全) +> +> 上位依据: +> - `ARCHITECTURE.md` +> - `CURRENT_ARCHITECTURE.md` +> - `design/07-ai/done/7-50-lightrag-knowledge-rag-provider-v1.md` +> - `design/07-ai/done/7-57-yuxi-reference-ai-run-config-knowledge-facade-v1.md` +> - `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` +> - `design/03-rust-web/reference/3-23-sidebar-local-folder-resource-runtime-followup-v1.md` +> - `design/10-review/done/16-mnote-web-runtime-module-maintainability-checklist-v1.md` + +## 1. 第一结论 + +Yuxi 有六个工程模式值得 MNote 借鉴,但不适合整体搬移: + +1. **中间件分层工具组合**:Hermes tool manifest 当前是扁平 Vec,每次新增能力(knowledge base、skills、MCP、filesystem、subagent、summary)都在同一个 manifest 函数中线性追加。Yuxi 的 middleware 模式把每种能力作为独立层,按需组合。 +2. **扩展管理统一入口**:当前 MNote 的 Skills/MCP/Knowledge Base/Tools 入口分散在 settings、Page AI settings、侧边栏等不同位置。Yuxi 的 `ExtensionsView` 用 tab 统一管理所有扩展。 +3. **知识库详情设置页**:当前 LightRAG settings 较薄,只有 source directory 和 basic status。Yuxi 的 `DataBaseInfoView` 提供了文件管理、检索配置、图谱配置、评估等完整详情页。 +4. **流式事件状态机规范化**:当前 Hermes SSE/WS 事件处理分散在多个 consumer(sidebar runtime、document adapter、Page AI sidebar)。Yuxi 的 `handleStreamChunk` 是单一状态机,覆盖 init→loading→stream_event→finished/interrupted/error 全生命周期。 +5. **本地 Dashboard**:当前 MNote 没有本地可观测性面板。Yuxi 的 Dashboard 提供了 agent run 调用量、token 消耗、知识库检索次数、工具调用频率等可视化。 +6. **虚拟路径安全**:当前 `WorkspacePath` 校验较松散。Yuxi 的 sandbox paths 有 `validate_thread_id` + `relative_to` 防穿越、虚拟路径前缀隔离、按 thread/user 分层。 + +这些都属于 MNote 现有主线的 enhancer,不改变底层(Hermes ACP / Reasonix / LightRAG / Rust SSR),不引入新存储或新 agent 框架。 + +## 2. 非目标 + +- 不引入 LangGraph / DeepAgents 作为 agent runtime。 +- 不复制 Yuxi 的 Milvus / Neo4j / Postgres 存储体系。 +- 不把 Vue/Ant Design 前端模式迁入 Rust SSR / browser runtime。 +- 不改变 LightRAG 作为唯一知识库 provider 的定位。 +- 不新增定时轮询刷新。 +- 不改变 local-first 普通 Markdown 编辑主路径。 +- 不改变 7-57 已完成的 run journal / agent descriptor / knowledge facade 成果。 + +## 3. 六个借鉴领域详析 + +### 3.1 中间件分层工具组合 + +**Yuxi 模式**: + +```python +class ChatbotAgent(BaseAgent): + async def get_graph(self, context=None, **kwargs): + middlewares = [ + create_agent_filesystem_middleware(...), # 文件系统 + save_attachments_to_fs, # 附件落地 + KnowledgeBaseMiddleware(), # 知识库检索 + SkillsMiddleware(), # Skills 注入 + create_subagent_task_middleware(context), # 子智能体 + summary_middleware, # 上下文压缩 + TodoListMiddleware(...), # 任务列表 + PatchToolCallsMiddleware(), # 工具调用修复 + ModelRetryMiddleware(max_retries=2), # 模型重试 + ] + return create_agent(model=..., tools=..., middleware=middlewares, ...) +``` + +每个中间件是独立类/函数,按声明顺序构成 agent 的能力栈。新增能力只需新增一个 middleware,不改动 agent 核心流程。 + +**MNote 当前状态**: + +`manifest.rs` 中 Hermes tool manifest 是扁平 `Vec`,约 30+ 个工具在一个函数中线性追加。新增 knowledge_rag、skills、context、block、onlyoffice 等工具时,每次都在同一个 manifest 函数末尾追加,没有按能力域分组。 + +```rust +pub fn manifest() -> Value { + let tools = annotate_tools_with_capabilities(vec![ + skill_read_tool(), + context_snapshot_tool(), + // ... 30+ tools + available_tool("mnote.artifact.create_summary", ...), + ]); + // ... +} +``` + +**借鉴方向**: + +不是引入 Python 式 class middleware,而是把 Rust manifest 按能力域拆成独立 registry 模块,每个模块自声明工具和能力: + +```text +hermes_tools/ + manifest.rs → 顶层装配,组合各 registry + registry/ + context_registry.rs → context.* 工具组 + doc_registry.rs → mnote.doc.* 工具组 + block_registry.rs → mnote.block.* 工具组 + page_registry.rs → mnote.page.* 工具组 + knowledge_rag_registry.rs → mnote.knowledge_rag.* 工具组 + skill_registry.rs → mnote.skill.* 工具组 + mindmap_registry.rs → mindmap.* 工具组 + onlyoffice_registry.rs → onlyoffice.* 工具组 + artifact_registry.rs → mnote.artifact.* 工具组 +``` + +每个 registry 暴露 `pub fn tools() -> Vec` 和 `pub fn capabilities() -> Vec<&'static str>`。manifest 只负责装配和 capability annotation。Agent descriptor 可以按 capability 选择性包含工具组。 + +### 3.2 扩展管理统一入口 + +**Yuxi 模式**: + +`ExtensionsView.vue` 用一个 tab 组件统一管理四种扩展: + +``` +智能体扩展 + ├── 知识库(管理员可见) + ├── 工具(管理员可见) + ├── MCP(管理员可见) + └── Skills(所有用户可见) +``` + +每种扩展是独立子组件,通过 tab 切换懒加载。URL query 参数持久化当前 tab,刷新后不丢失。 + +**MNote 当前状态**: + +- Skills 入口:Page AI settings → Skills 子面板 +- Knowledge base 入口:settings → 资料库 settings +- MCP:Hermes MCP 配置在 Hermes workspace settings +- Tools:无独立管理入口,Hermes tool manifest 是编译期静态 + +这四个入口分散在不同 settings surface,用户需要记住去哪找。 + +**借鉴方向**: + +在 settings 页面增加"扩展"tab(或在现有 settings IA 中统一),按权限分组: + +```text +设置 + ├── 通用 + ├── 外观 + ├── 编辑器 + ├── 扩展 ← 新增 + │ ├── 知识库(LightRAG 资料库管理) + │ ├── Skills(已安装 + 可用) + │ ├── MCP 服务器 + │ └── Agent 工具状态(只读能力清单) + ├── AI(Hermes / Reasonix 配置) + └── 关于 +``` + +第一阶段不要求完整 CRUD,先做只读能力清单 + 已安装 Skills/MCP 状态展示 + 知识库详情入口。URL hash 持久化当前 tab。 + +### 3.3 知识库详情设置页 + +**Yuxi 模式**: + +`DataBaseInfoView.vue` 是知识库详情页,子 tab 包括: + +- **文件管理**:文件夹树、上传、解析状态跟踪(pending/processing/done/error)、批量删除、文件预览 +- **检索测试**:输入 query → 展示检索结果 + 相似度分数 + 来源文件 +- **检索配置**:向量/BM25/混合检索模式选择、TopK、相似度阈值、BM25 权重 +- **图谱配置**:实体抽取 schema、关系类型约束 +- **评估**:检索质量评估、问答对测试 +- **设置**:名称、描述、嵌入模型、分块策略、共享配置 + +**MNote 当前状态**: + +LightRAG settings 较薄,只有: +- source directory 配置 +- ingest 触发按钮 +- 基本状态灯(indexed / pending) +- OCR 开关 + +**借鉴方向**: + +在不改变 LightRAG provider 的前提下,增强 knowledge base settings UI: + +1. **文件管理面板**:展示已 ingest 文件列表、解析状态(parsing/chunking/indexing/done/error)、文件类型图标、最后更新时间 +2. **检索测试面板**:输入 query → 直接调 `/api/knowledge-rag/search` → 展示 top-K 结果 + citation preview + 来源文件 + 相关度 +3. **检索参数配置**:search mode(local/global/hybrid/naive/mix)、top_k、相似度阈值(expose LightRAG `QueryParam` 可配置项) +4. **Ingest 状态跟踪**:LightRAG 后台完成状态通过现有 status bridge/backoff 同步到 UI,展示每个文件的 stage 和 error + +不新增: +- 分块策略 UI(由 LightRAG 管) +- 图谱 schema 编辑(由 LightRAG 管) +- 嵌入模型切换(由 LightRAG 管) + +### 3.4 流式事件状态机规范化 + +**Yuxi 模式**: + +`useAgentStreamHandler.js` 的 `handleStreamChunk` 是一个规范的状态机: + +``` +┌─────────────────────────────────────────────┐ +│ stream start │ +└─────────────────┬───────────────────────────┘ + │ + ┌────────────▼────────────┐ + │ init │ ← 建立 request_id 绑定 + │ → replyLoadingVisible │ + └────────────┬────────────┘ + │ + ┌────────────▼────────────┐ + │ loading │ ← message_delta / tool_call_delta + │ → push msgChunks │ + └────────────┬────────────┘ + │ + ┌────────────▼────────────┐ + │ stream_event │ ← tool-started / tool-finished + │ → 关联 tool_call_id │ agent_state (todos/uploads) + └────────────┬────────────┘ + │ + ┌────────────▼────────────┐ + │ finished / interrupted │ ← terminal + │ / error / approval │ + └──────────────────────────┘ +``` + +关键设计点: +- 每种 status 有明确的过渡规则 +- `stream_event` 中的 `tool-finished` 通过 `tool_call_id` 关联到对应 AI 消息 +- `agent_state` 事件同步 todos/uploads 等运行时状态 +- 中断(approval/interrupted)有独立的恢复路径 + +**MNote 当前状态**: + +Hermes SSE/WS 事件处理分散在多个 consumer: +- `sidebar-page-ai-runtime.js` 处理 Page AI 面板的流式回复 +- `document-editor-adapter-runtime.js` 处理编辑器内的 agent 交互 +- 每个 consumer 有自己的事件解析和状态管理 + +**借鉴方向**: + +不是引入 Vue composable,而是在 browser runtime 中抽出一个共享的 `AgentStreamEventRouter`: + +```javascript +// rust/crates/mnote-web/browser/agent-stream-event-router.js + +const EVENT_STATES = { + INIT: 'init', + LOADING: 'loading', + STREAM_EVENT: 'stream_event', + FINISHED: 'finished', + INTERRUPTED: 'interrupted', + ERROR: 'error', + APPROVAL_REQUIRED: 'approval_required', +}; + +function createAgentStreamRouter({ onDelta, onToolCall, onToolResult, onAgentState, onTerminal }) { + return function routeEvent(event) { + switch (event.status) { + case EVENT_STATES.INIT: + // 建立 request_id ↔ message 绑定 + break; + case EVENT_STATES.LOADING: + // 判断 message_delta / tool_call_delta → onDelta / onToolCall + break; + case EVENT_STATES.STREAM_EVENT: + // tool-started / tool-finished → onToolResult + // agent_state → onAgentState + break; + case EVENT_STATES.FINISHED: + case EVENT_STATES.INTERRUPTED: + case EVENT_STATES.ERROR: + onTerminal(event); + break; + case EVENT_STATES.APPROVAL_REQUIRED: + // 中断等待用户确认 + break; + } + }; +} +``` + +Page AI sidebar runtime 和 document editor adapter 都消费同一个 router,各自提供自己的 `onDelta`/`onToolCall`/`onTerminal` 回调。 + +### 3.5 本地 Dashboard + +**Yuxi 模式**: + +Dashboard 包含: +- **调用统计**:按时间线的 agent run 次数、成功/失败率 +- **用户活跃度**:DAU/MAU、会话时长 +- **AI 智能体分析**:各 agent 调用量排行 +- **工具调用监控**:各 tool 被调用次数、耗时 +- **知识库使用情况**:检索次数、文件数、chunk 数 + +**MNote 当前状态**:无 Dashboard。 + +**借鉴方向**: + +为 local-first 场景做最小 Dashboard: + +1. **Agent Run 统计**(基于 7-57 run journal):最近 N 天 run 次数、平均耗时、成功/失败/中断分布 +2. **Token 消耗**(基于 run journal 中的 token usage):按 agent、按日期维度 +3. **知识库状态**(基于 LightRAG status bridge):已索引文件数、总 chunk 数、最近 ingest 时间 +4. **Skills/MCP 健康**:已安装 skill 数、MCP 连接状态 + +数据源优先级: +- Agent run 统计:SQLite control-plane `agent_runs` 表(7-57 已建) +- Token 消耗:从 run journal 中提取 +- 知识库状态:LightRAG `/status` API + MNote status bridge +- Skills/MCP:Hermes workspace 配置 + Rust manifest + +不新增: +- 多用户统计(local-first 不需要) +- 实时推送更新(10 分钟 TTL + 手动刷新即可) +- 独立的时序数据库 + +### 3.6 虚拟路径安全加固 + +**Yuxi 模式**: + +沙盒路径的关键安全设计: + +```python +# 1. ID 安全校验 +_SAFE_ID_RE = re.compile(r"^[A-Za-z0-9_-]+$") + +def validate_thread_id(thread_id: str) -> str: + if not _SAFE_ID_RE.match(value): + raise ValueError("thread_id contains invalid characters") + +# 2. 虚拟路径前缀隔离 +def get_virtual_path_prefix() -> str: + return "/" + VIRTUAL_PATH_PREFIX.strip("/") # /sandbox-v1 + +# 3. 路径穿越防护 +def resolve_virtual_path(thread_id, virtual_path, *, uid): + # 必须从虚拟前缀开始 + # 用 relative_to 校验不穿越 base_dir + +# 4. 按 thread/user 分层 +# workspace/ → 用户级共享 +# uploads/ → 线程级 +# outputs/ → 线程级 +``` + +**MNote 当前状态**: + +`WorkspacePath` 主要通过 `allowed_roots` 做目录白名单校验,但没有: +- 线程级隔离(同一用户的多个 agent run 共享同一文件系统视图) +- 虚拟路径前缀(agent 直接看到宿主机真实路径) +- 正则 ID 校验 + +**借鉴方向**: + +不是引入 Yuxi 的 Python sandbox,而是在 Rust `WorkspacePath` / `AiAccessScope` 中加固: + +1. **Agent 可见路径使用虚拟前缀**:agent 通过 `mnote://workspace/`、`mnote://uploads/` 等虚拟路径引用文件,MNote 在 tool 边界做映射 +2. **Run 级临时目录隔离**:每个 agent run 有独立 `mnote://runs/{run_id}/outputs/`,不污染 workspace +3. **路径穿越防护**:所有 agent 发来的路径必须在 allowed roots 内,且经过 `canonicalize` + `starts_with` 双重校验 +4. **Attachments 目录**:agent 上传/生成的附件落 `mnote://attachments/{run_id}/`,与 workspace 文件明确分开 + +第一阶段只加固现有 `AiAccessScope` + `allowed_roots`,不改变 agent 现有文件操作语义。 + +## 4. 执行优先级 + +| 批次 | 内容 | 优先级 | 预计影响面 | +|------|------|--------|-----------| +| **Batch A** | Hermes tool registry 模块化拆分 | 中 | manifest.rs 重构,不改变 API | +| **Batch B** | 扩展管理 settings 入口 + 能力清单 | 高 | settings UI,不改变后端 | +| **Batch C** | LightRAG 知识库详情页(文件管理 + 检索测试 + 检索参数) | 高 | knowledge-rag settings UI + 少量 API | +| **Batch D** | AgentStreamEventRouter 抽共享 | 中 | browser runtime 重构,不改变 API | +| **Batch E** | 本地 Dashboard 最小版 | 低 | 新页面 + SQLite 查询 | +| **Batch F** | WorkspacePath 虚拟路径安全加固 | 低 | AiAccessScope 增强,agent tool 适配 | + +## 5. Checklist + +### Batch A:Hermes tool registry 模块化拆分 + +- [ ] A1. 在 `hermes_tools/` 下创建 `registry/` 子目录 +- [ ] A2. 把 `manifest.rs` 中工具按能力域拆到独立 registry 模块 + - [ ] A2a. `context_registry.rs`:`context_snapshot`、`context_read_current_page`、`context_resolve_target` + - [ ] A2b. `doc_registry.rs`:`doc_fetch`、`doc_find`、`doc_markdown_edit`、`doc_apply_block_ops`、`doc_plan_update` + - [ ] A2c. `block_registry.rs`:`block_fetch`、`block_replace`、`block_insert_after`、`block_delete`、`block_move_after` + - [ ] A2d. `page_registry.rs`:`page_get`、`page_save`、`page_update_title`、`page_update_options` + - [ ] A2e. `knowledge_rag_registry.rs`:`knowledge_rag_status`、`knowledge_rag_query`、`knowledge_rag_section_context`、`knowledge_rag_open_reference` + - [ ] A2f. `skill_registry.rs`:`skill_read`、`skill_*` + - [ ] A2g. `mindmap_registry.rs`:`mindmap_fetch`、`mindmap_apply_ops`、`mindmap_create_from_outline` + - [ ] A2h. `onlyoffice_registry.rs`:所有 `onlyoffice_*` 工具 + - [ ] A2i. `artifact_registry.rs`:`artifact_*` 工具 +- [ ] A3. 每个 registry 暴露 `pub fn tools() -> Vec` + `pub fn capability_names() -> &[&str]` +- [ ] A4. `manifest.rs` 改为装配层:收集各 registry 的 tools + 统一 capability annotation +- [ ] A5. `cargo test -p mnote-web hermes_tool_manifest --lib` 通过 +- [ ] A6. Agent descriptor smoke 验证 manifest 返回的工具列表与拆分前一致(`scripts/task557-page-ai-agent-descriptor-smoke.js`) + +### Batch B:扩展管理 settings 入口 + +- [ ] B1. 在 settings 页面增加"扩展"tab,路由 `/settings?tab=extensions` +- [ ] B2. 扩展 tab 包含子面板:知识库、Skills、MCP、Agent 工具 +- [ ] B3. 知识库子面板:展示 LightRAG 当前状态 + 已索引文件数 + "管理"按钮 → 跳转知识库详情 +- [ ] B4. Skills 子面板:展示已安装 skill 列表(名称、版本、状态、来源) +- [ ] B5. MCP 子面板:展示已配置 MCP 服务器列表(名称、URL、连接状态) +- [ ] B6. Agent 工具子面板:从 `/api/page-ai/agent-descriptors` 读取能力清单,只读展示 +- [ ] B7. URL query 参数持久化当前子 tab(`&ext=knowledge|skills|mcp|tools`) +- [ ] B8. 登录态浏览器验证:settings → 扩展 tab,切换子面板,刷新后 tab 不丢失 + +### Batch C:LightRAG 知识库详情页 + +- [ ] C1. 新增知识库详情页路由 `/knowledge-base` +- [ ] C2. 文件管理面板 + - [ ] C2a. 展示已 ingest 文件列表(文件名、类型图标、大小、状态灯、最后更新时间) + - [ ] C2b. 状态灯映射:pending/parsing/chunking/indexing/done/error,每 30s 有界刷新(来自 status bridge) + - [ ] C2c. 文件删除按钮(调 `mnote.knowledge_rag.delete` 或 LightRAG API) + - [ ] C2d. 手动 re-index 按钮(单文件) +- [ ] C3. 检索测试面板 + - [ ] C3a. 输入框 + 搜索按钮 → 调 `/api/knowledge-rag/search` + - [ ] C3b. 结果列表:citation preview + 来源文件 + 相关度分数 + - [ ] C3c. 点击结果 → `open_reference` 打开对应资源 +- [ ] C4. 检索参数配置面板 + - [ ] C4a. search mode 下拉:local/global/hybrid/naive/mix + - [ ] C4b. top_k 数字输入(1-100) + - [ ] C4c. 相似度阈值滑块(0.0-1.0) + - [ ] C4d. 保存到 `user_ui_preferences`(SQLite control-plane) +- [ ] C5. Ingest 源目录管理:展示当前 source roots + 添加/移除(只改 MNote source registry,不动 LightRAG 配置) +- [ ] C6. 登录态浏览器验证:知识库详情页三个面板切换、检索测试真实返回结果、参数保存后刷新不丢失 + +### Batch D:AgentStreamEventRouter 抽共享 + +- [ ] D1. 在 `rust/crates/mnote-web/browser/` 创建 `agent-stream-event-router.js` +- [ ] D2. 定义标准事件类型 enum:init/loading/stream_event/finished/interrupted/error/approval_required +- [ ] D3. 实现 `createAgentStreamRouter({ onDelta, onToolCall, onToolResult, onAgentState, onTerminal })` 工厂函数 +- [ ] D4. `sidebar-page-ai-runtime.js` 改为消费 AgentStreamEventRouter +- [ ] D5. `document-editor-adapter-runtime.js` 改为消费 AgentStreamEventRouter(如涉及编辑器内 agent 交互) +- [ ] D6. 浏览器 smoke 回归:Page AI 对话、tool call 展示、中断恢复、finished 状态均正常 +- [ ] D7. `scripts/task*-page-ai-*.js` 相关 smoke 通过 + +### Batch E:本地 Dashboard 最小版 + +- [ ] E1. 新增 `/dashboard` 路由 + 页面壳 +- [ ] E2. Agent Run 统计卡片 + - [ ] E2a. 后端:`GET /api/dashboard/agent-run-stats?days=7` → 返回 run 总数、成功/失败/中断分布、按 agent 分组 + - [ ] E2b. 前端:卡片 + 简易柱状图(按天分布) +- [ ] E3. Token 消耗卡片 + - [ ] E3a. 后端:从 `agent_runs` 表提取 token usage 汇总(总 token、按 agent 分组) + - [ ] E3b. 前端:数值卡片(本周消耗 / 总计) +- [ ] E4. 知识库状态卡片 + - [ ] E4a. 后端:代理 LightRAG `/status` + MNote status bridge,返回文件数、chunk 数、最近 ingest 时间 + - [ ] E4b. 前端:数值卡片 + 状态灯 +- [ ] E5. Skills/MCP 健康卡片 + - [ ] E5a. 后端:从 Hermes workspace 配置 + Rust manifest 汇总 + - [ ] E5b. 前端:列表 + 状态灯 +- [ ] E6. 所有卡片使用 10 分钟 TTL + 手动刷新按钮,不做实时推送 +- [ ] E7. 登录态浏览器验证:Dashboard 页面展示各卡片数据 + +### Batch F:WorkspacePath 虚拟路径安全加固 + +- [ ] F1. 在 `AiAccessScope` 中增加 `run_id` 字段 +- [ ] F2. 实现 `AgentVirtualPath` 类型:`mnote://workspace/`、`mnote://outputs/{run_id}/`、`mnote://uploads/{run_id}/` +- [ ] F3. 在 Hermes tool 边界做虚拟路径 ↔ 真实路径映射(`resolve_agent_path` / `to_agent_virtual_path`) +- [ ] F4. 路径穿越防护:`canonicalize` + `starts_with(allowed_root)` 双重校验 +- [ ] F5. Run 级临时目录:每个 agent run 开始时创建 `mnote://outputs/{run_id}/`,run 结束时清理 +- [ ] F6. Agent tool smoke 验证:agent 只能访问 allowed roots 内文件,路径穿越被拒绝 + +## 6. 与现有设计的关系 + +| 现有设计 | 本稿关系 | +|----------|---------| +| 7-57 Batch A (run journal) | 本稿 Batch E (Dashboard) 消费 run journal 做统计 | +| 7-57 Batch B (agent descriptor) | 本稿 Batch B 的工具清单面板消费 descriptor | +| 7-50 (LightRAG provider) | 本稿 Batch C 是其 UI 增强 | +| 7-18 (agent file editing) | 本稿 Batch F 加固其 AiAccessScope | +| 3-23 (sidebar local folder runtime) | 本稿 Batch B/C 在 settings 中增加入口 | +| 7-38 (Page AI sidebar owner split) | 本稿 Batch D 是 sidebar runtime 的事件路由重构 | + +## 7. 风险 + +- **Batch A 重构 manifest**:如果工具名或 capability annotation 在拆分中漂移,会导致 agent descriptor 不一致。必须用 smoke 对比拆分前后的 manifest JSON。 +- **Batch C 检索参数**:LightRAG `QueryParam` 的字段可能随版本变化。MNote 透传用户配置时应做字段白名单,未知字段忽略。 +- **Batch D 事件路由重构**:如果 browser runtime 中的事件格式在不同 consumer 间有细微差异(例如 Page AI 用 `message_id`,editor 用 `msg_id`),统一 router 时需要兼容映射。 +- **Batch E Dashboard**:SQLite `agent_runs` 表可能膨胀。后续需要 TTL 清理策略,当前阶段保留最近 30 天。 +- **Batch F 虚拟路径**:agent 端(Hermes / Reasonix)需要能处理 `mnote://` 前缀,或 MNote 在 tool 边界双向映射。第一阶段先只映射,不要求 agent 原生理解虚拟路径。 + +## 8. 验证基线 + +- `cargo test -p mnote-web --lib` 全绿 +- `scripts/task557-page-ai-agent-descriptor-smoke.js` 通过 +- `scripts/task529-knowledge-rag-citation-resource-tab-smoke.js` 通过 +- `scripts/task530-knowledge-rag-page-ai-final-answer-smoke.js` 通过 +- 新增 smoke:`scripts/task561-extensions-settings-smoke.js`(Batch B+C) +- 新增 smoke:`scripts/task561-dashboard-smoke.js`(Batch E) +- 登录态浏览器验证:settings 扩展 tab、知识库详情页、Dashboard 页面均可正常访问 +