Files
mnote/design/07-ai/done/7-40-page-ai-context-envelope-and-run-receipt-v1.md
T

457 lines
23 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.
# 7-40 Page AI MNote skill library and context tool contract v1
> 创建时间:2026-05-29
>
> 状态:`PROCESS`
>
> OwnerPage AI skill/tool capability surface + MNote host-side context provider
>
> 上位依据:
> - `design/07-ai/done/7-39-page-ai-agent-selector-context-authorization-settings-v1.md`
> - `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md`
> - `design/07-ai/process/7-38-page-ai-sidebar-runtime-owner-split-v1.md`
> - `design/07-ai/done/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md`
> - `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`
>
> 参考代码:
> - `reference-code/PilotDeck/src/context/prompt/PromptAssembler.ts`
> - `reference-code/PilotDeck/src/context/extension/ExtensionResolver.ts`
> - `reference-code/PilotDeck/src/tool/builtin/readSkill.ts`
> - `reference-code/PilotDeck/src/context/input/InputProcessor.ts`
>
> 关键口径:Hermes 和 Reasonix 本身已经是完整 agent,拥有自己的记忆、会话隔离、agent loop、工具调度和运行时状态。MNote 不建设第三套 agent runtime,不把 MNote 上下文、工具说明或页面正文强行注入每条用户 prompt。MNote 要提供一个专用 skill library 和结构化 context tools,让 agent 在需要时自行选择读取。
## 1. 背景与问题
`7-39` 已经完成第一轮 Page AI UI 收口:
- `sidebar-page-ai-runtime.js` 已有 `PAGE_AI_AGENT_REGISTRY`
- `PAGE_AI_CONTEXT_REF_REGISTRY` 已包含 `current_page / selection / active_editor / file / folder / changed_files`
- 发送 `/api/hermes/client/runs` 时 payload 已包含 `agentId``contextRefs``allowedRoots``editorTarget``runTargetSnapshot`
- SQLite directory grants 和 user preferences 已作为授权与偏好控制面。
但当前暴露出一个更关键的系统问题:
1. 简单聊天请求,例如“收到请回复收到”,仍可能触发大量 `mnote_doc_fetch` 等工具调用。
2. 当前实现倾向把 MNote page context、tool guidance、runTargetSnapshot 等内容拼成大段 instructions 注入上游 prompt。
3. 这种方式会诱导 agent 把所有请求都理解成 MNote 文档任务,也会增加 token、工具误调用和失败噪音。
4. UI 上 contextRefs 与授权区域已经可勾选,但后台语义仍混杂:勾选范围被当成“必须塞给模型的上下文”,而不是“允许 agent 按需读取的能力边界”。
因此本稿替代原先以 `AgentRunEnvelope` 为中心的方案。`AgentRunEnvelope` 仍保留为 audit / permission / receipt 的宿主侧结构,但不再是默认 prompt 注入中心。
## 2. PilotDeck 对照结论
### 2.1 可借鉴模型
PilotDeck 的 `PromptAssembler` 采用分层上下文:
- `ExtensionResolver.listSkills()` 只返回 skill 名称、描述和 namespace。
- `formatSkills()` 只生成 `<available-skills>` 摘要,并明确提示 `Use the read_skill tool to load the full content of any skill listed below.`
- skill 正文不默认进入每轮 prompt。
- slash command 由 `InputProcessor` 识别后作为用户意图进入 agent loop,不把所有 command body 默认注入。
- MCP instructions 也是单独 `<mcp-instructions>` 块,不与用户原文混写。
这些模型适合 MNote
- Page AI 只暴露“MNote 有哪些能力”。
- 详细技能说明通过 `mnote_skill.read` 或 agent 原生 skill 读取。
- 当前页、选区、文件夹、changed files 通过 context tools 按需读取。
- 用户 prompt 保持干净,避免把普通聊天污染成文档任务。
### 2.2 只作参考实现
PilotDeck 的完整 RouterRuntime、Model Canonical Protocol、AgentLoop、MemoryResolver 和 CompactionEngine 不直接移植。MNote 当前已有 Hermes / Reasonix ACP runtime,不应复制第三套 agent runtime。
可参考但不直接搬运:
- `ExtensionResolver` 的只读 contribution snapshot。
- `read_skill` 懒加载模式。
- tool result budget / tool_result_reference 思路。
- permission runtime 的统一决策链。
### 2.3 不适合 MNote 的内容
- 不做 PilotDeck 式全局模型 router。
- 不做 PilotDeck 式后台 always-on agent runtime。
- 不做独立于 SQLite control-plane 的另一套权限真相。
- 不用定时轮询来刷新 Page AI、文件树或页面树。
- 不把 PilotDeck 的项目 memory dataDir 作为 MNote 默认记忆存储;MNote 若后续做记忆,应按 `workspace_id + user_id` 落 SQLite control-plane 或明确的本地数据目录。
## 3. 第一结论
本阶段目标从“把 envelope 注入得更干净”改为:
1. 建立 MNote 专用 skill library 合同。
2. 建立 MNote context tools 合同。
3. Page AI 发送时保留用户原文,不把 MNote context/tool guidance 强行拼到 prompt_blocks。
4. `contextRefs` 只作为用户授权的上下文范围,agent 需要时自行调用工具读取。
5. `AgentRunEnvelope` 只作为宿主侧 audit / permission / tool input snapshot,不默认进入 prompt。
6. Chat-only agent 不挂载 MNote context toolsHermes / Reasonix 是否暴露 MNote tools 由用户勾选的 `contextRefs` 与授权边界决定,普通消息是否调用工具由 agent 自己判断。
7. UI 继续按 7-39 的 Cline 式模型收敛:输入区只保留 `+ / Agent / 上下文 / 停止 / 发送`,不显示 `write · /path` 黑色授权提示。
## 4. 产品设计:Page AI 输入区
### 4.1 默认输入区
Page AI 输入区默认只显示:
```text
[+] [Agent] [上下文] [停止] [发送]
```
要求:
- `Agent` 是按钮 + popover,不在输入区平铺 Hermes / Reasonix / Chat-only chip。
- `上下文` 是按钮 + popover,不在输入区平铺 6 个 context chip。
- 授权区域只在上下文 popover 中展示,不再在输入区显示 `write · /mnt/Data1T/mnote` 黑色提示。
- 输入区不显示 profile、model、gateway、runtime、tool trace 等技术噪音。
### 4.2 Agent popover
Agent popover 显示:
```text
选择 Agent
(x) Reasonix
适合工作区任务和文件编辑
( ) Hermes
适合 MNote 内置工具和页面操作
( ) Chat-only
只聊天,不申请文件读写能力
```
选择 agent 后:
- payload 显式携带 `agentId`
- `chat_only` 强制不挂载 MNote context tools。
- Hermes / Reasonix 可挂载 skill/tool capability,但是否读取由 agent 决定。
### 4.3 Context popover
Context popover 显示:
```text
发送给 AI 的上下文权限
[x] 当前页
允许 agent 按需读取当前 Markdown 页面
[ ] 选区
当前没有选区
[x] 打开资源
允许 agent 按需读取当前打开资源
[ ] 文件
允许 agent 按需读取当前文件
[ ] 文件夹
允许 agent 在授权根内按需读取文件夹
[ ] 最近修改
允许 agent 读取上次 run 的 changed files 摘要
授权区域
write · /mnt/Data1T/mnote
[恢复默认] [完成]
```
语义:
- 勾选项表达“允许 agent 读取的上下文范围”,不是“强制把内容发给模型”。
- `current_page` 勾选后,只代表工具可读取当前页;页面正文不默认塞进 prompt。
- `selection` 勾选且有选区时,agent 可通过 tool 读取选区文本;选区文本不默认塞进 prompt,除非后续用户明确选择“直接附加选区文本”模式。
- `folder` 必须由服务端 SQLite grants 重算 allowed roots。
## 5. MNote Skill Library 合同
### 5.1 Skill registry
新增 MNote 专用 skill registry,第一阶段可以用静态文件或 Rust 静态 registry,后续可落 SQLite / filesystem。
推荐初始技能:
| skill id | 简述 | 默认 agent |
| --- | --- | --- |
| `mnote-current-page` | 如何读取当前页、判断 dirty/conflict、刷新页面 | Hermes / Reasonix |
| `mnote-selection` | 如何读取和使用选区 | Hermes / Reasonix |
| `mnote-local-file` | 如何解析 MNote target / allowed roots,并让 agent 使用自身文件能力读写本地 Markdown | Reasonix |
| `mnote-attachments` | 如何处理 Markdown 标准链接、图片、PDF/Office 附件 | Hermes / Reasonix |
| `mnote-workspace-search` | 如何在授权 workspace 内搜索文件和引用 | Reasonix |
| `mnote-agent-receipt` | agent 写入后如何报告 changed files 与刷新需求 | Hermes / Reasonix |
| `mnote-chat-only` | 只聊天时不得调用 MNote 文件/页面工具 | Chat-only |
Skill 元数据:
```json
{
"id": "mnote-local-file",
"title": "MNote local file editing",
"description": "Resolve MNote targets and allowed roots before using the agent runtime's native local file tools.",
"agentIds": ["reasonix", "hermes"],
"readOnly": false,
"requiresContextRefs": ["current_page", "file", "folder"],
"toolNames": ["mnote.context.snapshot", "mnote.context.resolve_target"],
"path": "skills/mnote-local-file/SKILL.md"
}
```
### 5.2 Prompt 中只出现摘要
Page AI 上游 prompt 中最多允许出现类似摘要:
```xml
<available-skills>
Use mnote_skill.read to load the full content of any skill listed below.
- mnote-current-page — Read the current MNote Markdown page when the task needs page content.
- mnote-local-file — Resolve MNote targets and then use native file tools inside allowed roots.
- mnote-attachments — Resolve and open Markdown attachment links.
</available-skills>
```
禁止:
- 把每个 skill 的完整正文默认塞入 prompt。
- 把当前页全文默认塞入 prompt。
- 把 runTargetSnapshot 的正文、pageXml、contextBlocks 默认塞入 prompt。
- 把工具长说明拼进用户消息。
### 5.3 Skill 读取工具
新增或适配工具:
```json
{
"name": "mnote_skill.read",
"description": "Read a MNote skill by id when the task requires this MNote capability.",
"input_schema": {
"type": "object",
"properties": {
"skillId": { "type": "string" }
},
"required": ["skillId"]
}
}
```
返回:
```json
{
"skillId": "mnote-local-file",
"content": "...SKILL.md...",
"tools": ["mnote_context.resolve_target", "mnote_file.read", "mnote_file.patch"],
"constraints": {
"allowedRootsRequired": true,
"mustReadBackAfterWrite": true
}
}
```
## 6. MNote Context Tool 合同
### 6.1 工具列表
第一阶段建议提供最小工具集:
| tool | 职责 |
| --- | --- |
| `mnote_context.snapshot` | 返回本次 run 可用上下文摘要,不含正文全文 |
| `mnote_context.read_current_page` | 在 `current_page` 被授权时读取当前页 Markdown |
| `mnote_context.read_selection` | 在 `selection` 被授权且存在时读取选区 |
| `mnote_context.resolve_target` | 返回当前 rootUri、relativePath、documentId、file version |
| `mnote_file.read` | 在 allowed roots 内读取文件 |
| `mnote_file.patch` | 在 allowed roots 内 patch 文件,写入后回读 |
| `mnote_receipt.changed_files` | agent 完成后提交 changed files 或读取 audit 结果 |
### 6.2 权限原则
- 前端 `allowedRoots` 只作为 UI 展示与用户选择,不作为服务端真相。
- 服务端必须按当前 SQLite user session 重算 grants。
- 每次 tool 调用都按 `user_id + workspace_id + contextRefs + allowedRoots` 判定。
- `chat_only` 不注册文件读写工具。
- 未勾选 `current_page` 时,`mnote_context.read_current_page` 返回 blocked。
- 未勾选 `folder` 时,`mnote_file.read` 只能读 primary target 或当前文件。
### 6.3 上下文快照
`mnote_context.snapshot` 返回示例:
```json
{
"schema": "mnote.context_snapshot.v1",
"runId": "page-ai-run-abc",
"agentId": "reasonix",
"workspace": {
"sourceKind": "local_folder",
"rootUri": "file:///mnt/Data1T/mnote"
},
"contextRefs": ["current_page", "active_editor"],
"primaryTarget": {
"documentId": "local-md:README.md",
"relativePath": "README.md",
"fileVersion": "mtime:size:hash"
},
"availableReads": {
"currentPage": true,
"selection": false,
"folder": false,
"changedFiles": false
}
}
```
注意:snapshot 是工具返回,不是默认 prompt 注入。
## 7. AgentRunEnvelope 与 Receipt 的新位置
`AgentRunEnvelope` 不废弃,但职责调整:
- 宿主侧构建,用于 audit、权限判定、工具输入和 receipt 关联。
- 可以被 `mnote_context.snapshot` 精简返回。
- 不默认进入 ACP `prompt_blocks`
- 不作为普通聊天请求的 system instructions。
`AgentRunReceipt` 仍保留:
- run 完成后记录 changed files、touchesCurrentFile、shouldRefreshEditor、conflict。
- clean buffer 下 agent 写入当前 `.md` 后触发事件驱动刷新。
- dirty buffer 下进入 `external-change-conflict`
- 不通过轮询等待结果;使用 ACP events、watcher、WS/SSE 或现有浏览器事件。
## 8. 后端数据流
```text
Page AI composer
-> 用户选择 Agent / contextRefs
-> sendPageAiMessage()
-> payload: agentId + contextRefs + editorTarget + runTargetSnapshot metadata
-> 服务端认证 + SQLite grants 重算
-> 构建 host-side AgentRunEnvelope
-> 判断是否挂载 MNote skill/tool capability
- chat_only: 不挂载
- simple chat: 不挂载
- Hermes/Reasonix + 文档任务: 挂载 skill summary + context tools
-> ACP run prompt_blocks 只包含用户原文
-> agent 按需调用 mnote_skill.read / mnote_context.* / mnote_file.*
-> run completed
-> AgentRunReceipt / agentAudit.changedFiles
-> 事件驱动刷新当前页、文件树、页面树或显示 conflict
```
## 9. “是否挂载能力”的判定
第一阶段采用保守规则:
暴露 MNote skill/tool capability
- agentId 是 `hermes``reasonix`
- 用户在 context popover 中勾选了 `current_page / selection / active_editor / file / folder / changed_files` 这类 MNote 上下文能力。
- 用户 prompt 原文只用于 agent 自己推理,不由 MNote 侧按关键词分类成“普通聊天 / 文档任务”。
不挂载:
- agentId 是 `chat_only`
- 用户没有勾选任何 MNote contextRef。
- 当前没有有效 workspace / rootUri / session。
能力暴露不等于工具调用。MNote 只提供边界、授权和 skill/tool 描述;是否读取当前页、文件或附件由 agent 根据任务自行选择。
## 10. 与当前半成品实现的差异
当前半成品中已有一些方向需要修正:
- `acp_stream_events` 已改为 prompt_blocks 只放用户原文,这是正确方向。
- `should_skip_mnote_tool_context` 这类短句特判不应保留;应升级成只看 agentId / contextRefs / 授权边界的 capability attach policy。
- `build_run_upstream_body(...).instructions` 仍可作为 legacy Hermes route 兼容,但不应进入 ACP prompt。
- `toolGuidance` 不应变成长文本默认 instructions;应拆入 skill 正文,按需读取。
- `AgentRunEnvelope` 应保留为 audit/tool input,不作为默认 prompt 文本。
## 11. 验收 Checklist
### Batch A:冻结现状与 PilotDeck 对照
- [x] 记录当前“收到请回复收到”触发工具调用的真实浏览器证据。证据:用户截图/反馈记录了旧 UI 下短消息出现大量工具调用;本轮用 `task502-page-ai-agent-selector-context-smoke` 固化回归断言:短消息无 tool card,payload 不默认上传正文。
- [x] 记录当前 ACP payload / prompt_blocks 是否包含 MNote instructions。证据:`acp_stream_events` 现只构造用户原文 `ContentBlock::Text`,不再调用 `build_run_upstream_body(...).instructions` 塞入 ACP prompt。
- [x] 对照 PilotDeck `PromptAssembler.formatSkills()`,确认 MNote 只注入 skill 摘要。证据:参考 `reference-code/PilotDeck/src/context/prompt/PromptAssembler.ts`MNote 新增 skill registry 摘要与 `mnote.skill.read` 懒加载。
- [x] 对照 PilotDeck `InputProcessor`,确认 slash/command 不强行注入所有 command body。证据:参考 `reference-code/PilotDeck/src/context/input/InputProcessor.ts`,本轮未把 MNote skill 正文塞入普通用户 prompt。
### Batch BSkill registry 设计与最小实现
- [x] 新增 MNote skill metadata registry。证据:`rust/crates/mnote-web/src/hermes_tools/skill.rs`
- [x] 新增 `mnote_skill.read` 或等价工具。证据:`mnote.skill.read` manifest + route dispatch。
- [x] 添加 `mnote-current-page``mnote-local-file``mnote-chat-only` 三个最小 skill 正文。证据:`skills/mnote-*/SKILL.md`
- [x] 单测覆盖 skill id lookup、未知 skill、agentId 过滤。证据:`cargo test --manifest-path rust/Cargo.toml -p mnote-web skill_lookup -- --nocapture`
### Batch CContext tools 合同
- [x] 新增或收口 `mnote_context.snapshot`。证据:`mnote.context.snapshot` manifest + `context_tools::context_snapshot`
- [x] 新增或收口 `mnote_context.read_current_page`。证据:`mnote.context.read_current_page` manifest + current_page contextRef guard。
- [x] 新增或收口 `mnote_context.resolve_target`。证据:`mnote.context.resolve_target` manifest + route dispatch。
- [x] 工具返回结构化 JSON,不把正文混入 prompt。证据:context snapshot / resolve target 只返回 JSON;读取正文必须显式调用 read_current_page。
- [x] Rust 定点测试覆盖 contextRefs 权限。证据:`cargo test --manifest-path rust/Cargo.toml -p mnote-web context_ref_enabled -- --nocapture`
### Batch DCapability attach policy
- [x] 实现 `PageAiCapabilityPolicy` 或等价函数。证据:`page_ai_capability_policy`
- [x] `chat_only` 永不挂载 MNote 文件/页面工具。证据:policy 单测 + Reasonix wrapper chatLoop 使用空 ToolRegistry。
- [x] MNote 不按“收到/你好/总结当前页”等 prompt 文本强行分类能力;普通消息是否调用工具由 agent 自己判断。证据:`page_ai_capability_policy_exposes_selected_context_without_prompt_classifying``capability_policy_does_not_text_classify_ack_prompt`
- [x] 只在 Hermes / Reasonix 且用户勾选 MNote contextRefs 时暴露 skill/tool capability;没有 contextRefs 或 Chat-only 时不挂载。证据:`page_ai_capability_policy_does_not_attach_without_context_refs``page_ai_capability_policy_never_attaches_for_chat_only`
- [x] 单测覆盖 selected contextRefs、无 contextRefs、Chat-only、ack prompt 不文本分类四类输入。证据:`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_capability_policy -- --nocapture`
### Batch EACP prompt 收口
- [x] ACP `prompt_blocks` 只包含用户原文和用户显式附件,不包含 MNote tool guidance。证据:`acp_stream_events` 只传 `input.to_string()`
- [x] skill 摘要如果需要进入 system/context,只作为短 `<available-skills>`。证据:Reasonix wrapper mnoteLoop system prompt 仅保留短摘要,正文走 `mnote_skill_read`
- [x] `AgentRunEnvelope` 不进入 prompt_blocks。证据:ACP path 不再使用 upstream instructions。
- [x] `build_run_upstream_body` 的 legacy instructions 不再被 ACP path 直接使用。证据:`acp_stream_events` 不调用 `build_run_upstream_body`
### Batch FUI 收口
- [x] Agent selector 改为 `+` 旁边的 Agent 按钮 + popover。证据:`task502-page-ai-agent-selector-context-smoke` 通过。
- [x] Context refs 改为 `+` 旁边的上下文按钮 + popover。证据:`task502-page-ai-agent-selector-context-smoke` 通过。
- [x] 输入区不显示 `write · /path` 黑色授权提示。证据:截图 `tmp/task502-page-ai-agent-selector-context-smoke/01-agent-selector-context.png`
- [x] 默认聊天面不显示 profile/model/gateway/runtime/tool trace。证据:`task502-page-ai-agent-selector-context-smoke` 断言通过。
- [x] 浏览器截图验证 popover、选中态、输入区清爽度。证据:`tmp/task502-page-ai-agent-selector-context-smoke/01-agent-selector-context.png`
### Batch GTool card 降噪
- [x] 普通聊天不显示 MNote tool card。证据:`task502-page-ai-agent-selector-context-smoke` 对“收到请回复收到”断言无 tool card。
- [x] Page AI run payload 不默认上传 `pageText/pageXml/contextBlocks/selectedText/evidence` 正文;当前页/选区正文必须由 agent 通过 context tools 按需读取。证据:`pageAiPageContextForRefs()` 默认裁剪正文,`task502-page-ai-agent-selector-context-smoke` 断言通过。
- [x] tool 调用失败时卡片默认折叠,显示简短错误和展开入口。证据:tool message 使用 `<details class="wolai-page-ai-tool-details">`,默认不加 `open`
- [x] debug/raw trace 进入高级/调试区,不污染默认聊天。证据:trace/auditId 只在 tool `<details>` 内显示;默认聊天 smoke 截图无 runtime/raw trace。
- [x] “收到请回复收到”真实浏览器测试只显示用户消息与 assistant 回复。证据:`task502-page-ai-agent-selector-context-smoke` 通过并生成截图。
### Batch HReceipt 与事件驱动刷新
- [x] 保留 `AgentRunReceipt` / `agentAudit.changedFiles`。证据:`local_agent_audit_event` 写入 `agentRunReceipt``cargo test --manifest-path rust/Cargo.toml -p mnote-web local_agent_audit_event_carries_agent_run_receipt -- --nocapture`
- [x] clean buffer agent 写入后事件驱动刷新当前页。证据:前端优先消费 `agentRunReceipt.refresh.touchesCurrentFile` 并派发 `mnote:page-ai-tool-write-completed``document-session-runtime.js` 负责 clean refresh / dirty conflict。
- [x] dirty buffer agent 写入后进入 conflict。证据:`document-session-runtime.js``refreshDocumentSessionsFromExternalWrite` 对 dirty / saving / recent input 标记 `external-change-conflict`,事件来源改为 receipt。
- [x] 不引入定时轮询。证据:本轮只使用 ACP SSE `run.completed`、浏览器 CustomEvent、现有 watcher/WS/SSE 事件,没有新增 `setInterval` 或轮询。
- [x] changed files 与文件树/页面树刷新走 watcher、ACP event、WS/SSE 或现有事件。证据:`agentRunReceipt.changedFiles` 转为 `tree:local-folder-watch-batch`,由 `sidebar-tree-live-apply-runtime.js` 事件驱动刷新 affected parents。
### Batch I:验证矩阵与归档
- [x] Rust targeted testsskill registry、capability policy、context tool auth。证据:`page_ai_capability_policy``skill_lookup``context_ref_enabled` 定点测试通过。
- [x] JS `node --check`Page AI runtime 与 smoke。证据:`node --check scripts/reasonix-acp-wrapper.mjs rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js rust/crates/mnote-web/browser/sidebar-tree-runtime.js scripts/task502-page-ai-agent-selector-context-smoke.js`
- [x] 浏览器 smokeAgent popover、Context popover、纯聊天无工具调用、payload 不默认上传正文、receipt 事件驱动刷新。证据:`node scripts/task502-page-ai-agent-selector-context-smoke.js`
- [x] 浏览器截图:输入区不显示授权黑 chip。证据:`tmp/task502-page-ai-agent-selector-context-smoke/01-agent-selector-context.png`
- [x] `git diff --check`
- [x] `codegraph sync .`,必要时 `codegraph index . --force`
- [x] checklist 全部完成后移动到 `design/07-ai/done/`
## 12. 归档条件
满足以下条件后,本稿可移动到 `design/07-ai/done/`
- Page AI 已采用 MNote skill library + context tools 模型。
- ACP 用户 prompt 保持干净,不再默认注入 MNote instructions。
- 纯聊天请求不会触发 MNote doc/page/file 工具。
- 文档任务仍能通过 skill/tool 按需读取当前页或文件。
- Agent / Context UI 已收敛为输入区按钮 + popover。
- 真实浏览器截图与 smoke 证明默认聊天面没有冗余技术噪音。