重构基线

This commit is contained in:
Agent Board
2026-06-25 21:08:17 +08:00
parent cc4f975466
commit dabaf03bd7
42 changed files with 7720 additions and 138 deletions
@@ -0,0 +1,555 @@
> 状态补充(2026-06-25):本稿降级为 opencode runtime / iframe fallback 参考;Page AI 产品主线由 `7-68-openhub-weknora-mnote-deep-fusion-v1.md` 接管。
# 7-65 [process] Page AI opencode WebUI embed v1
> 创建时间:2026-06-23
>
> 当前状态:`PROCESS / opencode-chat style iframe MVP 已落地,bridge 与 event receipt 已补第一版`
>
> Owner07-ai / Page AI / opencode WebUI embed
>
> 替代方案:
> - `design/old/07-ai/process/7-62-recycle-page-ai-board-first-full-rewrite-v1.md`
> - `design/old/07-ai/process/7-63-recycle-page-ai-board-first-productization-v1.md`
> - `design/old/07-ai/process/7-64-recycle-codexmobile-embed-page-ai-v1.md`
>
> 上位依据:
> - `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md`
> - `design/07-ai/done/7-38-page-ai-sidebar-runtime-owner-split-v1.md`
> - `design/07-ai/done/7-40-page-ai-context-envelope-and-run-receipt-v1.md`
> - `design/07-ai/done/7-50-lightrag-knowledge-rag-provider-v1.md`
## 1. 核心结论
废弃 MNote Page AI 自研 provider 接入与 Board-first 产品壳,改为 **只接入 opencode 官方 runtime + 官方 WebUI**
```text
MNote Page AI = MNote 宿主壳 + opencode 官方 WebUI + MNote 打开/刷新/上下文集成
opencode = 官方 WebUI + 官方 HTTP server/OpenAPI/SSE + 官方 SDK + opencodego provider
```
MNote 不再直接维护 Reasonix / ZCode / Hermes / Chat-only / Board worker 作为页面 AI provider。它们可以继续存在于历史、调试或外部工作流边界,但不进入 Page AI 主路径。
选择 opencode 的原因:
- `opencode web` 官方提供本地 WebUI,不需要 MNote 自研完整聊天前端。
- `opencode serve` 官方提供 headless HTTP server 和 OpenAPI,适合 MNote 做轻量 adapter。
- `@opencode-ai/sdk` 覆盖 session、message、diff、permission、event,足够承接上下文注入与回写 receipt。
- opencode 原生支持 SSE、权限审批、文件 diff、MCP、ACP、session export/import。
- 本机已有 `opencode` 和 opencodego 订阅链路,provider/runtime/UI 属于同一生态,少一层兼容债。
## 2. 明确废弃
### 2.1 Page AI 主路径不再接入
- Reasonix native session / Reasonix desktop bridge。
- ZCode worker / Board worker selector。
- Hermes profile / Hermes Web control surface。
- Chat-only remote conversation。
- Agent Board run/workflow 作为默认 Page AI 后端。
- CodexMobile iframe 作为默认 Page AI 后端。
这些能力不删除历史代码,不立刻清理工具层,只从 Page AI 新主路径退出。
### 2.2 仍可保留的边界
- `mnote.doc.*``mnote.block.*`、LightRAG facade 等工具可继续作为 opencode 可调用的 MCP/工具能力。
- Agent Board 仍可作为外部 workflow/QA/review 系统,不再作为 Page AI 默认聊天后端。
- CodexMobile 可保留为备选 spike 或体验对照,不作为当前实现目标。
## 3. 新系统边界
### 3.1 MNote 只做四件事
1. **上下文**:当前页、选区、页面标题、真实 `.md` 路径、workspaceId、allowed roots、LightRAG 引用。
2. **授权**:把 MNote local-first 文件权限转换为 opencode permission / external_directory / working directory。
3. **嵌入**:第一版通过 MNote 同源受登录态保护反代嵌入 opencode 官方 WebUI`npm run dev:hot` 默认拉起 `opencode serve --hostname=127.0.0.1 --port 4096`
4. **回执**:监听 opencode event/diff,触发 MNote watcher 刷新,记录 Page AI session binding。
### 3.2 opencode 负责完整 agent runtime
- 聊天 UI。
- 流式事件。
- session 管理与恢复。
- provider/model 调用。
- tool call 展示。
- permission ask/allow/deny。
- 文件读写、patch、diff。
- MCP / ACP / agent 配置。
### 3.3 集成形态:不是新的 Leptos island
当前 MNote 文档编辑器已经是 `leptos_tiptap_island`,但 Page AI 不应该再做一个重前端 island。Page AI 更像 VSCode 里的 Cline
```text
VSCode workbench host + Cline webview/extension
MNote web shell host + opencode WebUI iframe/bridge
```
因此第一版形态是 **mnote-web sidebar host runtime**
- 继续使用 `rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js` 作为宿主入口,但把它瘦身成 opencode host。
- iframe 内尽量保留 opencode 官方 UI,包括消息、tool、permission、diff/session 页面。
- iframe 外只放 MNote 必要宿主控件:当前页 context bar、打开变更、刷新当前页、授权状态、运行状态。
- 不新建 Leptos island,不引入 React/Vue 到 MNote 主壳,不重写 opencode 消息 UI。
如果后续必须深度定制 opencode UI,也优先 fork opencode WebUI 的少量页面,而不是在 MNote 里复刻一套聊天前端。
## 4. 集成拓扑
```text
MNote Rust SSR :3000
└─ Page AI sidebar
├─ MNote host chrome
│ [当前页] [选区] [可写目录] [知识库引用]
│ [打开变更] [刷新当前页] [在主编辑区打开]
└─ iframe http://127.0.0.1:4096/<project>/session
↓ first MVP direct localhost iframe
opencode web :4096
├─ 官方 WebUI
├─ 官方 HTTP server / OpenAPI
├─ SSE /event
├─ session/message/diff/permission APIs
└─ opencodego / configured providers
```
第一版优先 iframe 官方 WebUI,不 fork、不精简、不重写样式。实测 opencode WebUI 使用根路径 `/assets``/session``/global/health` 等资源/API,子路径 `/page-ai/opencode/` iframe 会产生 root path 错位;因此 MVP 采用 MNote 同源根路径反代 `/<base64-project>/session/<sessionId>`,让 iframe 内 `location.pathname` 与 opencode 官方 WebUI 预期保持一致,同时通过 MNote 登录态保护反代入口。`/api/page-ai/opencode/*` 负责 session binding、context 注入、status、diff/receipt adapter。只有 iframe/adapter 实测无法满足产品嵌入时,才考虑 fork WebUI。
### 4.1 MNote host chrome
Page AI 抽屉由 MNote 控制尺寸、开关、上下文和跨应用动作,opencode 只负责 AI 交互主体。
宿主控件最小集:
- 当前页 pill:标题、相对路径、读写状态。
- 选区 pill:有选区才展示,点击可重新注入上下文。
- 变更 pill:来自 opencode diff/event,点击用 MNote 打开对应文件。
- 刷新按钮:调用 `window.__mnoteDocumentPaneRuntime.refreshPrimaryDocument()`
- 打开按钮:调用 `window.__mnoteDocumentPaneRuntime.openResourceInActiveTab()`
这些控件由 MNote 渲染,避免修改 opencode 官方页面结构。
### 4.2 MNote 打开 opencode 变更
opencode WebUI 里的 diff / changed file 默认按 opencode 自己的 UI 打开。MNote 需要额外提供宿主打开能力:
```text
opencode diff/event
→ MNote adapter 归一化 changed files
→ Page AI host chrome 显示 changed file chips
→ 用户点击 chip
→ window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({ path })
```
第一版不强行改 opencode diff 内部点击行为;先在 iframe 外给 MNote-native changed file chips。这样即使 opencode DOM 改版,MNote 打开变更仍可用。
### 4.3 可选 bridge:只做宿主动作,不接管 UI
如果需要让 opencode 官方 UI 内部的文件链接也能用 MNote 打开,可在同源反代 HTML 中注入一个很小的 bridge 脚本:
```text
opencode iframe click(file/diff link)
→ postMessage({ type: 'mnote:open-file', path })
→ parent MNote host 调用 openResourceInActiveTab
```
bridge 只允许三类消息:
- `mnote:open-file`
- `mnote:refresh-file`
- `mnote:session-ready`
不通过 bridge 解析模型事件、不重绘消息、不替换 permission UI。DOM 选择器脆弱时立即退回 host chrome chips。
## 5. 最小实现
### Phase Aruntime spike
- [x] 运行 `opencode --version``opencode web --help``opencode serve --help`;当前版本已升级到 `1.17.9`
- [x] 卸载 oh-my-openagent / oh-my-opencode 默认插件:`opencode.json``plugin` 已为空,`oh-my-openagent.jsonc` 已移除并备份。
- [x] 启动/复用 `opencode serve --hostname=127.0.0.1 --port=4096``/global/health` 返回 healthy。
- [x] 验证 WebUI 可打开、可进入 `/mnt/Data1T/mnote` project session、可真实回复。
- [x] 验证 opencodego/OmniRoute 模型可用:iframe 内真实回复 `OPENCODE_MNOTE_IFRAME_OK_*`
- [x] 验证当前工作目录指向 MNote workspace`/session` 返回 `directory=/mnt/Data1T/mnote`
- [x] 验证编辑一个 `.md` 文件后,`/session/:id/diff` 能返回文件 diff`opencode-smoke-test.md` 返回 `modified` diff。
- [ ] `/event` SSE 只做了接口可达性探索,尚未接入持续事件消费。
### Phase BMNote iframe embed
- [x] Rust 新增 `/page-ai/opencode/{*path}` 反向代理到 `127.0.0.1:4096`,并新增 `/api/page-ai/opencode/status``/api/page-ai/opencode/diff`
- [x] 只允许已登录 MNote session 访问反代/API;未登录请求返回 `401 page_ai_opencode_unauthorized`
- [x] Page AI sidebar 精简为:MNote host chrome + iframe + basic status。
- [x] 尽量不改 opencode WebUI,保留官方页面布局、消息样式、tool/diff/permission UI。
- [x] iframe 容器与 host chrome 由 `sidebar-page-ai-runtime.js` + `page-ai.css` 承载,不新增 Page AI Leptos island。
- [x] WebUI 默认 iframe 使用 MNote 同源 `/<base64-project>/session` 反代;避免局域网浏览器访问自身 `127.0.0.1:4096`,同时保留 opencode 官方 URL 形态。
### Phase B2MNote-native changed files
- [x] 建立 opencode sessionId ↔ MNote host 状态的持久 binding`/api/page-ai/opencode/session` 创建/复用 session,并落 SQLite/control-plane,按用户/session/workspace 区分。
- [x] 通过 `/api/page-ai/opencode/events` 代理 opencode `/event`,收到 session/message/diff/file 事件后触发有界 refresh。
- [x] 通过 `/session/:id/diff` 生成 changed file chips。
- [x] chip 点击走 `window.__mnoteDocumentPaneRuntime.openResourceInActiveTab()`
- [x] 当前页刷新按钮已走 `refreshPrimaryDocument({ reason: 'page-ai-opencode-host' })`changed files 命中当前页时由 event/diff refresh 链路自动触发刷新。
### Phase Ccontext 注入
优先不修改 opencode WebUI,通过官方 API/SDK 注入上下文:
```text
MNote open Page AI
→ create or resume opencode session
→ session.prompt(noReply=true, parts=[MNote context envelope])
→ iframe 打开对应 session
```
上下文 envelope 内容:
- 当前页标题。
- 当前页真实 Markdown 路径。
- selection 文本。
- allowed roots 与读写权限。
- LightRAG 引用摘要。
- 当前任务约束:优先编辑 primaryTarget,禁止越权修改。
当前状态:`done for MVP`。host chrome 已展示当前页、真实 Markdown path(可定位时)、selection、allowed roots/writable 状态;打开 Page AI 时会调用 `/api/page-ai/opencode/session`,用 `noReply=true` 向 opencode session 注入 MNote context envelope,并把 iframe 打到绑定 session URL。2026-06-24 已按 opencode-chat 参考改回官方 WebUI iframe 主路径,MNote-native timeline 仅保留为 debug/receipt 边界。
### Phase Dwriteback / receipt
- [x] 监听 opencode `/event` 或 SDK `event.subscribe()`:当前通过 `/api/page-ai/opencode/events` 代理 `/event`EventSource 收到相关事件后触发有界 refresh。
- [partial] prompt / event 后通过绑定 session 的 `/session/:id/diff` 刷新 changed files;仍需继续核对 opencode 各类 file/diff event payload。
- [x] changed files 命中当前页时调用 `refreshPrimaryDocument({ reason: 'page-ai-opencode-event' })`;手动刷新按钮保留。
- [x] 保存 MNote pageId ↔ opencode sessionId binding:当前已落 SQLite/control-plane,按用户/session/workspace 区分;浏览器刷新后可恢复同一 binding。
- [x] Page AI context bar 显示最近一次 changed files / runtime / error 摘要。
### Phase E:可选 WebUI bridge
- [ ] 只有 host chrome chips 体验不足时,才在反代层注入 `mnote-opencode-bridge.js`
- [ ] bridge 只把 opencode UI 内部文件点击转成 `postMessage`
- [ ] bridge 不解析/修改 opencode 消息流、tool UI、permission UI。
- [ ] selector 失效时不阻塞主流程,回退 host chrome chips。
## 6. 安全与权限
- opencode 只监听 `127.0.0.1`;MVP iframe 直连本机地址,同源反代/API 仍必须受 MNote 登录态保护。
- 生产/长期运行必须设置 `OPENCODE_SERVER_PASSWORD`,或改为完整同源反代 + MNote 反代层隔离。
- opencode working directory 优先指向当前 workspace root。
- MNote allowed roots 映射到 opencode permission
- 当前 workspace root:允许读,写按用户授权。
- 当前页文件:允许读写。
- workspace 外路径:默认 deny,必要时显式 `external_directory`
- 不使用 `--dangerously-skip-permissions` 作为默认路径。
## 7. 验收标准
### 7.1 UI
- Page AI 面板内显示 opencode 官方 WebUI。
- 官方消息流、tool 卡片、permission 交互、diff/session UI 尽量原样保留。
- MNote 只在 iframe 外展示 host chrome,不重做 opencode UI。
- opencode 产生的 changed files 可以用 MNote 主编辑区或资源 tab 打开。
### 7.2 Runtime
- 可创建/恢复 opencode session。
- 可用 opencodego 模型完成真实回复。
- 流式回复浏览器可见。
- 权限审批走 opencode 原生机制。
- 文件修改后 MNote 当前页面能刷新。
### 7.3 代码收敛
- `sidebar-page-ai-runtime.js` 不再承载 Reasonix/ZCode/Hermes/Board provider 状态机。
- 不新增 MNote 自研聊天 message store。
- 不 fork opencode WebUI,除非 spike 证明 iframe 方案不可用。
- 不新增 Page AI Leptos islandPage AI 是 mnote-web sidebar host runtime。
## 8. 与旧方案对比
| 方案 | 优点 | 主要问题 | 当前结论 |
|---|---|---|---|
| Board-first | 可接多 worker/workflow | MNote 仍要维护产品壳和 Board adapter,聊天体验不成熟 | 废弃为主路径 |
| CodexMobile embed | Codex 体验强,贴近现有 Codex 体系 | 需要 fork/精简/修 bug,维护派生产品 | 备胎/对照 |
| opencode WebUI embed | 官方 runtime + 官方 WebUI + 官方 API,维护成本低 | opencode 能力可能不如 Codex 先进,嵌入细节需实测 | 当前主路径 |
## 9. 非目标
- 不重写 opencode WebUI。
- 不把 opencode WebUI 拆成 MNote 原生组件。
- 不同时接入 Reasonix/ZCode/Hermes/CodexMobile 多后端。
- 不把 Agent Board 控制台嵌入 Page AI。
- 不在第一版实现完整 MNote SSO 到 opencode;先由 MNote 反代保护。
## 10. 退出条件
只有出现以下任一情况,才重新启用 CodexMobile 或自研 UI 方案:
- opencode WebUI 无法稳定 iframe/反代嵌入。
- opencode session 无法通过 API 定位并打开指定 session。
- opencode 无法可靠编辑 MNote workspace 文件。
- opencode permission/diff/event 无法满足 MNote 最小安全闭环。
- opencodego/provider 链路在真实使用中明显不稳定且短期不可修。
## 9. 2026-06-24 iframe 主路径验证记录
- [x] `opencode --version``1.17.9`
- [x] `opencode serve --hostname=127.0.0.1 --port 4096 --print-logs`:真实可启动;官方 WebUI URL `http://127.0.0.1:4096/L21udC9EYXRhMVQvbW5vdGU/session` 可显示 `Build anything`
- [x] `npm run dev:hot`:真实拉起 `mnote-web :3000``opencode :4096`
- [x] 浏览器 smoke:登录 `mnote.e2e@example.com` 后打开 Page AIiframe URL 为 `http://127.0.0.1:3000/L21udC9EYXRhMVQvbW5vdGU/session`,显示官方 opencode WebUI;截图 `/tmp/mnote-page-ai-opencode-iframe.png`
- [partial] 发送真实消息:官方 WebUI 可输入并进入 `Thinking/Stop` 运行态;截图 `/tmp/mnote-page-ai-opencode-send.png`。本轮未等待到最终回复,不能声明 provider 回复完成。
- [x] binding 持久化 smoke:浏览器 reload 后仍恢复同一 session `ses_106555512ffe5wDz9eMlP4v2i2`
- [x] 静态检查:`node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
- [x] Rust 检查:`cd rust && cargo check -p mnote-web`
剩余 gap
- [x] 在同源反代层补最小 `postMessage` bridge`open-file / refresh-file / insert-text`,不接管消息流。
- [partial] 更完整核对 opencode `/event` 的 file/diff payload:已递归解析常见 changed/diff/file payload 并由 event 触发有界 refresh;仍需真实编辑文件后补最终证据。
- [ ] 若要局域网访问,继续确认所有 opencode WebUI root-level API 路由都已被 MNote 登录态反代覆盖,避免直接暴露 4096。
## 10. 2026-06-25 bridge / receipt 补充记录
- [x] HTML 反代注入极小 bridge:只处理 `insert-text / open-file / refresh-file / session-ready`,不接管 opencode 消息流、tool UI、permission UI。
- [x] MNote host chrome 新增“插入当前页上下文”按钮,通过 `postMessage` 把当前页标题、路径、选区、allowed roots 插入 opencode 官方输入框。
- [x] 浏览器 smoke`/tmp/mnote-page-ai-opencode-bridge-final.png`,验证 iframe 内 `window.__mnoteOpencodeBridgeInstalled === true`,点击 host 按钮后官方输入框出现 `MNote 当前页上下文标题:主页`
- [x] postMessage smoke:模拟 iframe 发 `open-file / refresh-file`,确认 MNote host 调用 `openResourceInActiveTab()``refreshPrimaryDocument()`;输出见 `tmp/mnote-opencode-postmessage-smoke.cjs` 运行结果。
- [x] event receipt 强化:`pageAiOpencodeNormalizeChangedFiles()` 改为递归收集 `changedFiles / files / diff / changes / edited / created / deleted / data / properties`,事件到达时先更新 chips,再做有界 projection refresh。
- [x] 静态检查:`node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
- [x] Rust 检查:`cd rust && cargo check -p mnote-web`
剩余 gap
- [ ] 让 opencode 真实修改一个测试 Markdown 文件,等待 `/event` + `/session/:id/diff` 产出 changed file chips,再截图验证 chip 点击打开 MNote 主编辑区。
- [ ] 若 opencode 官方 WebUI 后续 CSP 变化,需要把 inline bridge 改成 nonce/hash 或外部小脚本。
## 11. 2026-06-25 opencode 用户隔离与工作目录结论
### 11.1 当前结论
- [x] Page AI 工作目录应简化为“当前用户打开的根文件夹”:MNote 当前只能打开一个大的本地根目录,`rootUri` 对应的真实目录就是 opencode `directory` / project directory。
- [x] 切换根文件夹应视为新的 opencode session 边界:同一用户同一根目录内可按页面恢复 binding;根目录变化时默认新建 session,不把旧 session 带到新根目录。
- [x] MNote 自身 binding 已按 `user_id + workspace_id + mnote_session_id + provider` 隔离;`mnote_session_id` 内包含 workspace/page/directory,因此同一用户跨根目录不会复用同一 binding。
- [partial] opencode 自身默认实例没有 MNote 用户概念;如果所有 MNote 用户共用一个 `opencode serve`opencode 的 session、permission、credential、account、skill、MCP、snapshot、tool-output 会共用同一套本机状态。
### 11.2 opencode 1.17.9 真实机制证据
本机 spike 使用临时环境启动:
```bash
HOME=/tmp/.../home \
XDG_CONFIG_HOME=/tmp/.../config \
XDG_DATA_HOME=/tmp/.../data \
XDG_CACHE_HOME=/tmp/.../cache \
XDG_STATE_HOME=/tmp/.../state \
opencode serve --port 4197 --hostname 127.0.0.1 --print-logs
```
观察结果:
- 配置读取路径变为 `$XDG_CONFIG_HOME/opencode/{config.json,opencode.json,opencode.jsonc}`
- 持久数据写入 `$XDG_DATA_HOME/opencode/opencode.db`
- 日志写入 `$XDG_DATA_HOME/opencode/log/opencode.log`
- 锁写入 `$XDG_STATE_HOME/opencode/locks/*`
- 当前全局实例的默认持久库是 `/home/lix/.local/share/opencode/opencode.db`
- `opencode.db` 内包含 `session``message``part``permission``credential``account``account_state``project``workspace``event` 等表;这些表没有 MNote 用户维度。
- `permission` 只按 `project_id + action + resource` 唯一;共用实例会导致不同 MNote 用户在同一 project/directory 下共享 opencode 权限记忆。
- `session` 表包含 `directory` / `project_id` / `workspace_id` / `metadata`,但不包含 MNote `user_id`;共用实例不能作为安全隔离边界。
- opencode 会从当前用户 HOME/配置路径加载 skill/MCP;未隔离时可看到 `/home/lix/.claude``/home/lix/.agents``/home/lix/.config/opencode/skill` 等重复 skill 警告。
### 11.3 推荐隔离方案
第一版不要试图在单个 opencode server 内实现多用户隔离;改为 **MNote 用户/根目录维度的 opencode runtime profile**
```text
MNote user + workspace/rootUri
-> runtime profile id
-> dedicated XDG_CONFIG_HOME / XDG_DATA_HOME / XDG_CACHE_HOME / XDG_STATE_HOME
-> dedicated opencode serve process on 127.0.0.1:dynamic_port
-> MNote 登录态反代 /page-ai/opencode/...
```
目录建议:
```text
$MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/config/opencode/opencode.json
$MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/data/opencode/opencode.db
$MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/cache/opencode/
$MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/state/opencode/
```
配置策略:
- provider/API key 可由 MNote 管理后写入每个 profile 的最小 `opencode.json`,或在本机单用户 dev 模式从 `/home/lix/.config/opencode/opencode.json` 复制 provider/model 白名单。
- skill/MCP 默认不继承宿主 HOME 的全部内容;只显式安装 MNote 允许的 skill/MCP,例如 `codegraph``mempalace`、后续 MNote MCP。
- opencode 原生 permission 继续保留,但存储在该 profile 的独立 `opencode.db` 中。
- MNote 的 allowed roots 仍作为上下文与反代边界;opencode 的工作目录固定为当前打开根目录。
### 11.4 session 策略
- 同一 `user_id + workspace_id/rootUri + pageAbsolutePath`:优先恢复 MNote control-plane binding 指向的 opencode session。
- `rootUri` 变化:默认新建 session;旧 binding 保留但不跨根目录复用。
- `pageAbsolutePath` 变化:默认新建或按页面 binding 恢复,不把旧页面上下文继续注入到新页面。
- opencode 原生 session 恢复只作为 profile 内部能力;MNote 是否恢复以 control-plane binding 为准。
- 跨浏览器恢复依赖 SQLite control-plane binding + per-user opencode data profile,不依赖 `sessionStorage`
### 11.5 后续实现项
- [ ] 新增 opencode runtime profile manager:按 MNote user/rootUri 分配 XDG 目录、端口、启动/健康检查、生命周期。
- [ ] `dev:hot` 继续可一键启动,但默认只启动 dev 单用户 profile;多用户 profile 由后端按需拉起。
- [ ] 反代不再只读 `MNOTE_OPENCODE_BASE_URL` 全局值,需按当前登录用户和 rootUri 解析到对应 profile base URL。
- [ ] session binding metadata 写入 `runtimeProfileId``rootUri``projectDirectory``opencodeDataDir`,方便审计和恢复。
- [ ] 增加最小测试:两个不同 MNote 用户同一 Markdown 根目录下创建 session,不共享 opencode `opencode.db`、permission、session list。
### 11.6 官方/社区多用户实现调研补充
本轮先核验豆包给出的项目名,再补充搜索到的真实社区线索。结论:**没有发现可直接嵌入 MNote 的官方/社区“单 opencode 进程多 MNote 用户强隔离”实现**;官方当前也把多用户 Web/serve 部署视为待增强能力。
豆包列表验真:
| 项目 | 验真结果 | 对 MNote 的价值 |
|---|---|---|
| `anomalyco/opencode-orchestrator` | 未找到公开仓库;真实相近项目是 `agnusdei1207/opencode-orchestrator` | 后者是 opencode 多 agent 编排插件,不是多用户 runtime 隔离层 |
| `pRizz/opencode-cloud` / `gitea.com/pRizz/opencode-cloud` | 真实存在 | Docker/container 级隔离参考;安全强但本地 MNote 开销偏大 |
| `oc-ext/ocx` | 未找到公开仓库 | 暂不可作为依据 |
| `daytonaio/daytona-opencode-plugin` | 未找到公开仓库 | 暂不可作为依据 |
| `anomalyco/openwork` | 未找到公开仓库 | 暂不可作为依据 |
| `lucentia/opencode-svip-proxy` | 未找到公开仓库 | 暂不可作为依据 |
| `kwickramasekara/opencode-chat` | 真实存在 | VSCode WebView 嵌入参考:启动/复用 `opencode serve`、固定端口保留 localStorage、WebView proxy、剪贴板/键盘 bridge;不是多用户隔离实现 |
额外发现:
| 项目/线索 | 结论 |
|---|---|
| 官方 `anomalyco/opencode` issue `#20067` | open:请求 `opencode web` 支持 multi-user auth 与 per-user provider credentialsissue 描述确认共享 Web 实例会共享身份、session、provider credentials |
| 官方 `anomalyco/opencode` issue `#5784` | closed:请求多租户 `serve` 下 MCP auth/config;说明多租户 MCP/资源隔离是社区真实痛点,但不是已可用的完整 MNote 用户隔离方案 |
| 官方 `SECURITY.md` | 明确 opencode 不提供安全 sandboxserver mode 只支持 `OPENCODE_SERVER_PASSWORD` Basic Auth;需要真隔离时建议 Docker/VM |
| `millerjes37/opencode-multiplexer` | 真实存在,是 opencode fork 的 multi-client server 支持;文档承认“知道 sessionID 即可交互”的 session hijacking 风险,session ownership 仍是 future enhancement;不适合作 MNote 多用户隔离底座 |
| `joeyism/opencode-multiplexer` | 真实存在,是多 session/多项目终端 dashboard,不是 Web 多用户隔离 |
对当前方案的修正:
- 不应引入豆包描述的“单进程多租户 opencode”作为近期主路径;目前没有可靠公开实现,官方也还在 issue 层面。
- 也不应一开始上 Docker/container;它解决安全隔离但会显著增加本地笔记软件的启动、资源和运维成本。
- 近期最稳妥路线是 **单 opencode 进程 + 单 MNote 当前用户/当前根目录 profile**,先满足本机单用户和局域网同一登录用户;等 MNote 真正进入多用户同时在线场景,再升级为按需 profile manager。
- 若要减少端口/生命周期复杂度,可先采用 `opencode-chat` 的轻量做法:固定一个 dev/local 端口、优先复用已存活 server、MNote 反代统一入口;不要提前实现多实例调度。
- 多用户隔离仍必须作为设计约束保留:不能把共享 opencode `opencode.db` 声称为安全隔离,只能标为单用户/dev 模式。
更新后的分阶段建议:
1. **Phase MVP-local**:一个 MNote 登录用户 + 一个当前根目录 + 一个 opencode server;工作目录固定为 `rootUri`MNote control-plane 做跨浏览器 session binding。
2. **Phase shared-device**:为每个 MNote 用户准备独立 XDG profile,但不常驻多进程;登录/打开 Page AI 时按需启动,空闲回收。
3. **Phase SaaS/团队**:再评估 `opencode-cloud`/Docker 或等待官方 multi-user auth/per-user credentials 落地;不要自己 fork 官方 opencode 做单进程多租户。
### 11.7 OpenHub 对照结论
`xcl1989/OpenHub` 是目前找到的最接近“opencode 多用户平台”的社区实现,README 明确主张:一个 `opencode serve (:4096)`,后端按用户 workspace 通过 `?directory=` 路由到不同目录,并在应用 SQLite 中维护 users、sessions、messages、permissions、skills、tools 等业务层权限。
可复用点:
- 单 opencode server + per-user workspace:后端调用 `/session``/session/{id}/prompt_async``/global/event` 时统一带 `directory=<user workspace>`
- 应用层 session ownershipOpenHub 自己用 SQLite 记录 `conversation_sessions` / messages / user_id,不把 opencode 原生 session 列表直接暴露给所有用户。
- 应用层权限面板:模型权限、工具权限、skill 权限都在业务 DB 中维护,再同步/注入到用户 workspace。
- per-user `.opencode` 目录:README 架构图显示每个 workspace 下有独立 `.opencode/skills``.opencode/tools`
- 单进程运维简单:固定 `OPENCODE_BASE_URL=http://127.0.0.1:4096`Basic Auth 保护后端到 opencode 的内部访问。
关键风险:
- OpenHub 不是 opencode 原生多租户;它仍依赖一个全局 opencode server 和全局 opencode 数据库/credential/account 状态。
- 隔离主要靠 `directory` 与 OpenHub 后端不暴露跨用户 session;如果绕过 OpenHub 直接访问 opencode,或知道别人的 session id,仍要依赖外层鉴权/反代拦截。
- provider credentials 是 opencode server 全局配置;OpenHub 的用户模型/工具权限是应用层控制,不等于 opencode 内核 per-user credentials。
- 它自研了聊天前端、消息库、知识/记忆/任务系统;这不符合 MNote 当前“尽量保留 opencode 官方 WebUI,不复刻消息 UI”的边界。
对 MNote 的启发:
- OpenHub 证明“单 opencode serve + `?directory=` 按用户工作区隔离”在产品上可跑,比一开始做多进程 profile manager 更轻。
- MNote MVP 可以采用 OpenHub 的轻量隔离思路:固定一个本机 opencode server,所有请求由 MNote 登录态反代,MNote 后端只允许当前用户的 `rootUri` 作为 `directory`,并用 control-plane binding 限制 session ownership。
- 但必须把这种模式标为 **应用层隔离 / 单机可信 opencode 后端**,不能标为强安全多租户。强隔离仍需后续 XDG profile 或 Docker。
更新后的推荐:
1. **立即采用 OpenHub-lite**:单 `opencode serve`、固定端口、MNote 反代、`directory=rootUri`、control-plane session ownership。
2. **不复刻 OpenHub UI**:仍保留 opencode 官方 WebUI iframeMNote 只做 host chrome、context、open/refresh、changed files。
3. **补安全闸**:所有 `/api/page-ai/opencode/*` 和 iframe 反代必须校验当前登录用户、rootUri、session binding;不允许前端任意传 directory 打开非当前 root。
4. **后续 shared-device 再升级**:当确实有多 MNote 用户同时使用同一机器时,再做 per-user XDG profile manager。
### 11.8 OpenHub 融合可行性评估
用户新判断:OpenHub 的前端、消息库、权限、记忆、文件、任务等功能与 MNote Page AI 长期目标高度重合,应评估是否直接融合,减少 MNote 自研量。
结论:**可融合,但不建议整套 OpenHub 作为 MNote 新后端;推荐抽取 OpenHub Page-AI 子系统,形成 MNote 内的 `OpenHub-lite`。**
可最大化复用的部分:
- **React/AntD 聊天前端**`SmartQueryPage.jsx``ChatInput``AssistantMessage``ToolCall``QuestionForm``HistoryDrawer``DiffViewer``FileManager``GitTimeMachine` 等,可作为 Page AI 的 micro frontend,而不是继续维护当前简陋 host UI。
- **消息库模型**`conversation_sessions``conversation_messages`、图片、turn、opencode message id、归档、retry、last-turn delete 等,适合迁移到 MNote control-plane,替代 `sessionStorage` 和当前临时 binding。
- **opencode 单进程接入模式**:后端按用户 workspace/rootUri 调 `/session``/session/{id}/prompt_async``/global/event` 并带 `directory=`,适合 MNote 当前“一个打开根目录”的简化模型。
- **应用层权限面板**:模型权限、工具权限、skill 权限、usage 统计可以映射到 MNote 用户体系;短期先只做 Page AI 所需的模型/工具/skill 白名单。
- **任务/团队/记忆模块**Smart Entity、Team、Memory、Scheduler 与 MNote 长期 agent 目标相关,但第一阶段只作为后续模块,不应阻塞 Page AI MVP。
不建议直接搬入的部分:
- OpenHub 自带登录、用户管理、admin 页,与 MNote control-plane auth 重叠;应替换成 MNote 登录态。
- OpenHub FastAPI 后端与 MNote Rust SSR/control-plane 双后端并存会增加部署复杂度;长期应把必要 API/DB schema 移植到 Rust,而不是新增常驻 Python 服务。
- OpenHub 自研知识库/记忆/任务会与 MNote 现有 LightRAG、workspace、tree/file resource、control-plane 产生事实源冲突;应按模块逐步映射。
- OpenHub 不是 opencode 内核级强隔离,仍需 MNote 反代和 session ownership 限制。
推荐融合路线:
1. **Phase 1iframe micro frontend spike**
- 直接运行 OpenHub 前端的 Page-AI/Chat 子集,嵌入 MNote sidebar。
- 后端 API 不直接用 OpenHub FastAPI,而是由 MNote 提供兼容 `/api/query/stream``/api/sessions/*``/api/files/*` 的最小 Rust adapter。
- 目标是快速验证 UI/消息体验是否明显优于 opencode 官方 iframe。
2. **Phase 2:消息库迁移**
- 在 MNote control-plane 增加 OpenHub-like `page_ai_sessions` / `page_ai_messages` / `page_ai_message_parts` / `page_ai_turns`
- 将 opencode session id、message id、tool calls、diff、reasoning、attachments、rootUri、pageAbsolutePath 统一持久化。
- 替代当前临时 Page AI binding;支持跨浏览器、跨会话恢复。
3. **Phase 3UI 组件裁剪融合**
- 从 OpenHub 前端抽出 Chat shell、消息列表、工具调用、历史抽屉、Diff/File/GitTimeMachine 组件。
- 去掉 Login/Admin/Knowledge/SmartEntity/Team 等非 Page AI 首屏模块。
- 适配 MNote host chrome、当前页 context、changed file chip、MNote open/refresh。
4. **Phase 4:高级能力选择性引入**
- FileManager 映射 MNote resource/file tree。
- GitTimeMachine 映射 MNote changed files / snapshot / restore 设计。
- Memory/Skill/Tool permission 映射 MNote 用户权限和未来 MNote MCP。
- Smart Entity/Team 作为 Page AI 后续 agent team,不进入当前 MVP。
技术判断:
- 如果目标是“尽快有成熟 Page AI UI”,OpenHub 前端比 opencode 官方 iframe 更适合深度定制,因为它已经是普通 React/AntD 应用,消息、工具、历史、文件、diff 都在前端组件内。
- 如果目标是“最少维护债”,opencode 官方 iframe 仍最省事,但 MNote 与页面/文件/权限/历史的融合会受 iframe 限制。
- 当前更适合改为 **OpenHub UI + MNote Rust adapter + opencode runtime**:UI 和消息体验复用 OpenHub,用户/文件/权限/工作区真相仍归 MNoteagent runtime 仍归 opencode。
新的建议:
-`7-65` 当前 iframe 方案降级为 runtime spike 与 fallback。
- 新增或接续设计 `7-67-openhub-page-ai-fusion-v1`,目标是用 OpenHub Chat 子系统替代当前 Page AI UI。
- 第一阶段只做 Chat/Session/Message/Diff/File open 五件事,不引入 OpenHub 登录/admin/知识库/team。
### 11.9 OpenHub 知识库实现与 WeKnora 对照
用户最新判断:Page AI 要做深度融合;MNote 当前已放弃 LightRAG,知识库方向原计划是 WeKnora。因此需要单独核验 OpenHub 自带知识库是否能替代 WeKnora。
源码核验结论:**OpenHub 自带知识库是轻量 SQLite 文本知识库,不是完整 RAG/知识库底座;适合复用 UI、API 形状和 prompt 注入链路,不建议替代 WeKnora。**
OpenHub 知识库真实实现:
- 数据表只有 `knowledge_bases``knowledge_sources`:字段包括 `scope=enterprise/user``owner_id``title``source_type``content``tags`、统计字段;没有 chunk 表、embedding 表、向量库、图谱或 citation 表。
- 上传解析支持 `.md/.txt/.pdf/.docx/.xlsx/.csv`PDF 走 PyMuPDF 文本抽取,DOCX 走 python-docx 段落抽取,表格转文本行;没有 OCR、版面恢复、图片解析或复杂文档结构保真。
- `chunker.py` 存在 Markdown/文本/表格切块逻辑,但当前知识库主链没有把 chunk 持久化到 DB,检索与注入仍围绕整份 `knowledge_sources.content`
- 检索分两层:DB 层用 `LIKE` 关键字筛出候选;服务层再对候选全文做 CJK/英文 token 的 BM25 + TF-IDF 重排;没有 embedding、semantic search、rerank model、hybrid vector search。
- 注入方式是 prompt stuffing:小型个人知识库全量或近似全量注入,大型个人知识库取 2 条结果,企业知识库最多取 1 条结果,每条截取相关片段,总上下文默认限制约 1200 字符。
- opencode 集成点是在发送用户问题前构造 `<context>...</context>`,并提示模型如果上下文不足就调用 `knowledge_knowledge_search` 工具继续查。
- 前端 `KnowledgeManager.jsx` 和 admin 企业知识库 UI 可直接参考:列表、搜索、上传、添加、编辑、删除、统计、企业只读提示这些产品能力与 MNote 需要高度重合。
与 WeKnora 的关系:
- WeKnora 应继续作为 MNote 长期知识库底座候选:负责文档解析、索引、检索、召回、引用、权限过滤与跨文档问答。
- OpenHub 知识库不应替代 WeKnora;它更像“用户短记忆/轻量知识片段/企业公告文本”的 fallback。
- 最优融合方式是 **OpenHub Knowledge UI + MNote Rust 知识库 adapter + WeKnora provider**:前端交互复用 OpenHub,后端接口形状兼容 OpenHub,但真正的 ingestion/search/citation 由 MNote 调 WeKnora。
- OpenHub 的 `knowledge_sources` schema 可以作为 MNote control-plane 的 source registry 参考,但需要增加 `workspace_id/root_uri/resource_id/source_uri/provider_doc_id/index_status/permission_scope/citation_locator` 等 MNote 字段。
- OpenHub 的 prompt 注入链路可以短期复用为 Page AI context block,但 WeKnora 命中结果必须带 citation/open-reference 映射,不能只塞纯文本。
对 7-67 深度融合设计的影响:
1. Page AI 主 UI 继续选 OpenHub Chat 子系统,而不是官方 opencode iframe。
2. Knowledge 模块第一阶段只迁移 UI 与 API contract,不迁移其 SQLite 文本检索为长期底座。
3. MNote Rust adapter 提供 OpenHub-compatible `/api/knowledge/*`,内部走 WeKnora 或本地 fallback。
4. 保留 OpenHub 轻量知识库作为“未配置 WeKnora 时的 local fallback / 用户手工短知识”,但不能称为默认知识库主线。
5. 新设计稿应明确:`OpenHub UI` 负责交互,`MNote control-plane` 负责用户与权限,`WeKnora` 负责知识库索引与检索,`opencode` 负责 agent 执行。
@@ -0,0 +1,87 @@
# 7-66 [process] Page AI opencode-native UI v1
> 创建时间:2026-06-24
>
> 当前状态:`RECYCLED / 失败方向,仅保留为反例`
>
> Owner07-ai / Page AI / opencode-native UI
## 1. 结论
本稿提出的 MNote-native opencode UI 方向已废弃。用户确认目标应参考 `opencode-chat`**MNote 薄宿主壳 + 官方 opencode WebUI iframe + 最小 host bridge**,而不是在 MNote 内复刻 opencode 消息流、tool UI、permission UI。
因此 `7-66` 只作为反例保留:native message timeline / composer / permission cards 可以临时作为 debug receipt,但不得成为 Page AI 主 UI,也不继续扩写。当前主线回到 `7-65`
```text
MNote Page AI = MNote host chrome + opencode 官方 WebUI iframe + MNote open/refresh/changed files receipt
opencode = 官方聊天 UI、session、tool、permission、diff、model/provider
```
## 2. 参考结论
### 2.1 opencode 官方 VSCode 插件
官方 VSCode 插件 `sst-dev.opencode` 的公开说明更偏 IDE 集成而不是完整 WebUI 复刻:
- 快捷键启动/聚焦 opencode terminal session。
- 新建 opencode terminal session。
- 自动共享当前 selection/tab。
- 支持 `@File#L37-42` 文件引用。
这说明官方 IDE 路线不是“把 WebUI 完整 iframe 到 IDE”,而是让宿主 IDE 负责上下文、入口和文件引用,opencode runtime 负责 agent。
### 2.2 社区 VSCode WebUI 插件
社区方案集中在两类:
- sidebar chat + diff viewer + multi-session tabs;自动启动 `opencode serve`,通过 HTTP/SSE 通信。
- React webview 渲染 session、message parts、tool cards、permission、diff、provider/model。
对 MNote 的启发:不要再塞 iframe;应该使用 opencode API/SSE 重渲染 MNote-native UI,并保留 MNote 打开文件/刷新编辑器。
## 3. 必须覆盖的 opencode 功能
### Phase A:可用会话壳
- [ ] session list / create / resume / current binding。
- [ ] 当前 session 状态:agent、model/provider、token/cost、idle/running/error。
- [ ] composer:发送 prompt、排队/运行状态、abort。
- [ ] context bar:当前页、真实 Markdown 路径、selection、allowed roots。
### Phase B:消息与 parts
- [ ] user / assistant / system / synthetic 区分展示。
- [ ] 隐藏 MNote context envelope 噪声,但保留“上下文已注入”状态。
- [ ] text part 正文渲染。
- [ ] reasoning part 折叠展示。
- [ ] tool part 卡片:tool 名、状态、输入/输出摘要。
- [ ] patch part / file part / shell part / agent/subtask part 以卡片展示。
- [ ] message error、finish、tokens/cost 展示。
### Phase C:权限、问题、diff
- [ ] permission.asked 列表:allow once / always / reject。
- [ ] question request:文本输入回复 / reject。
- [ ] changed files chips:点击用 MNote 打开。
- [ ] diff summary:新增/修改/删除数量;当前页命中后刷新编辑器。
- [ ] abort / revert / unrevert 留出按钮,但 MVP 可先只接 abort。
### Phase D:事件驱动刷新
- [ ] 订阅 `/api/page-ai/opencode/events`
- [ ] 处理 `message.updated``message.part.updated``message.part.delta``session.next.*``permission.v2.asked/replied``session.diff``session.error`
- [ ] 不高频轮询;事件只触发有界 refreshmessages / permissions / diff。
## 4. MNote 特有集成
- changed file click`window.__mnoteDocumentPaneRuntime.openResourceInActiveTab()`
- 当前页被修改:`refreshPrimaryDocument({ reason: 'page-ai-opencode-event' })`
- selection/current page/allowed roots 由现有 Page AI target runtime 计算。
- iframe 反代只保留 `debug fallback`,默认 UI 不使用 iframe。
## 5. 第一版验收
- 打开 Page AI 看到 MNote-native opencode 面板,不出现 iframe 空壳。
- 能创建/恢复 session。
- 能发送真实消息,看到 user message 和 assistant/text/tool/reasoning/patch 卡片之一。
- 能看到 session list、model/provider、changed files、permission 区域。
- 如 opencode/provider 出错,UI 显示真实错误,不伪装成功。
- 浏览器截图自检通过后再汇报。
@@ -0,0 +1,170 @@
> 状态补充(2026-06-25):本稿为 OpenHub 初步融合稿;更完整的 OpenHub + WeKnora + MNote 取舍与实施主线由 `7-68-openhub-weknora-mnote-deep-fusion-v1.md` 接管。
# 7-67 OpenHub Page AI 深度融合设计 v1
状态:process
Owner07-ai / mnote-web / control-plane
日期:2026-06-25
## 1. 背景
`7-65` 的 opencode 官方 WebUI iframe 路线验证了 opencode runtime、同源反代、session binding、文件打开/刷新等接缝,但 UI 深度融合受 iframe 与官方 WebUI 结构限制。用户目标已经调整为:尽量复用成熟社区项目 OpenHub 的前端、消息库、权限、知识库 UI 和 opencode 接入模式,把 MNote Page AI 做成类似 VSCode + Cline 的一体化侧边栏,而不是 MNote 自研一个简陋聊天框。
当前 Page AI 新主路径:
```text
MNote Rust SSR / control-plane / local workspace
-> OpenHub Chat 子系统 UI(裁剪融合)
-> MNote Rust adapter(兼容 OpenHub API 形状)
-> opencode serve runtime
-> WeKnora knowledge provider(长期知识库底座)
```
`7-65` 降级为 opencode runtime spike / fallback;不再把官方 opencode WebUI iframe 作为默认产品 UI。
## 2. 硬边界
- 不恢复 Reasonix / ZCode / Hermes / Board / CodexMobile 为 Page AI 默认后端。
- 不新增 Page AI Leptos islandPage AI 仍属于 `mnote-web` host runtime 与普通前端资源。
- 不直接引入 OpenHub 登录、用户管理、admin 整站后端;用户、权限、workspace 真相归 MNote control-plane。
- 不把 OpenHub 自带 SQLite 文本知识库当作 MNote 长期知识库底座;长期知识库 provider 是 WeKnora。
- 不使用 `--dangerously-skip-permissions` 作为默认路径。
- 不把 opencode 原生 session 列表裸露给前端;所有 session 必须绑定 MNote 用户、rootUri、page path。
- 不通过高频轮询刷新消息或 changed files;优先使用 opencode event stream、MNote watcher、programmatic refresh。
## 3. OpenHub 可复用资产
### 3.1 前端组件
第一阶段优先复用并裁剪:
- `SmartQueryPage.jsx`:聊天 shell、消息流、状态管理、移动端布局参考。
- `ChatInput.jsx`:输入框、附件、快捷操作、发送体验。
- `AssistantMessage.jsx`Markdown、代码块、tool result、reasoning 展示基础。
- `ToolCall.jsx`:工具调用卡片与权限提示参考。
- `HistoryDrawer.jsx`:历史会话抽屉。
- `DiffViewer.jsx`:变更展示,但文件打开必须接 MNote open-resource。
- `FileManager.jsx`:映射 MNote local workspace / file tree。
- `KnowledgeManager.jsx`:知识库 UI 参考,后端改接 MNote/WeKnora adapter。
暂缓融合:
- OpenHub Login/Admin 整页。
- SmartEntity / Team / Scheduler。
- GitTimeMachine 的 restore 写入能力;可先只显示 diff / changed files。
### 3.2 后端模式
可复用其 API contract 和 opencode 调用模式:
- `/api/query/stream`
- `/api/sessions`
- `/api/sessions/{sessionId}/messages`
- `/api/knowledge/*`
- `/api/files/*`
- opencode `/session?directory=<workspace>``/session/{id}/prompt_async?directory=<workspace>``/global/event?directory=<workspace>`
但实现落在 Rust `mnote-web` / control-plane,不新增常驻 FastAPI 后端。
## 4. MNote 目标架构
### 4.1 UI 层
`sidebar-page-ai-runtime.js` 不再维护自研聊天消息渲染主链,而是挂载 OpenHub-derived Page AI micro frontend
- MNote-native host chrome:当前页、selection、workspace、授权状态、runtime 状态、changed file chips。
- OpenHub-derived chat area:消息、tool call、diff、history、input、附件。
- Bridge:只处理 MNote 专属动作:`open-file``refresh-file``insert-context``session-ready``changed-files`
### 4.2 Rust adapter 层
新增或重构 Page AI API
- `page_ai_sessions`:MNote 用户维度的跨浏览器 session binding。
- `page_ai_messages`:用户消息、assistant 消息、tool call、opencode ids、状态。
- `page_ai_context_snapshots`:当前页标题、真实 Markdown 路径、selection、rootUri、allowed roots、知识摘要。
- `page_ai_changed_files`opencode event/diff 得到的 changed files 与 MNote resource mapping。
所有 API 必须读取 MNote 登录态,禁止由前端自由传入 user id 或任意 directory。
### 4.3 opencode runtime 层
短期:单 opencode server + 当前打开 rootUri + MNote 用户级 binding。
中期:按 MNote user/profile 隔离 XDG profile,按需启动、空闲回收。
多用户强隔离不由 OpenHub 原生保证,必须由 MNote 反代与 profile manager 实现。
### 4.4 知识库层
OpenHub 知识库结论:其自带实现是 `knowledge_bases + knowledge_sources + LIKE/BM25/TF-IDF + prompt stuffing`,不是完整 RAG。
MNote 采用:
- UI:复用 OpenHub `KnowledgeManager` 交互。
- API:提供 OpenHub-compatible `/api/knowledge/*`
- Provider:默认走 WeKnora。
- Fallback:未配置 WeKnora 时,可临时用 OpenHub-like SQLite 文本知识源做短知识。
- CitationWeKnora 结果必须保留 source/resource/open-reference 映射,方便点击回 MNote 文件或资源页。
## 5. 实施阶段
### Phase A:源码裁剪 spike
- [ ] 抽取 OpenHub Chat 组件依赖图,确认最小可运行组件集。
- [ ]`mnote-web` 静态资源中引入 OpenHub-derived bundle 或独立构建产物。
- [ ] 去除 OpenHub 登录/admin 路由依赖,改用 MNote 当前登录态。
- [ ] 用静态 fixture 跑出接近 OpenHub 原始体验的 Page AI sidebar。
### Phase BOpenHub-compatible session/message API
- [ ] 增加 MNote control-plane session/message 表。
- [ ] 实现 `/api/page-ai/openhub/sessions``/messages` adapter。
- [ ] 绑定 `mnote_user_id + rootUri + pageAbsolutePath + opencode_session_id`
- [ ] 支持跨浏览器恢复同一 MNote 用户的会话。
### Phase C:真实 opencode streaming
- [ ] adapter 创建/恢复 opencode sessiondirectory 固定为当前打开 rootUri。
- [ ] `/query/stream` 转发到 opencode prompt_async + global event。
- [ ] 保存 user/assistant/tool/diff 消息。
- [ ] 解析 changed files 并驱动 MNote changed file chips。
### Phase DMNote 文件与刷新融合
- [ ] changed file chip 点击走 `openResourceInActiveTab()`
- [ ] 当前页被修改后调用 `refreshPrimaryDocument()` 或 watcher 刷新链路。
- [ ] DiffViewer 中所有 file path 点击都映射到 MNote resource/file open。
- [ ] 文件路径必须限制在当前 rootUri / allowed roots 内。
### Phase EKnowledge / WeKnora adapter
- [ ] 兼容 OpenHub `knowledgeService` 的 list/create/upload/search/stats API。
- [ ] 后端默认调用 WeKnora ingestion/search。
- [ ] 将 WeKnora 命中结果转成 OpenHub UI 可展示的 source/citation。
- [ ] 未配置 WeKnora 时启用 SQLite fallback,并在 UI 明确标注 fallback。
### Phase F:浏览器真实验证
- [ ] `npm run dev:hot` 一键拉起 MNote + opencode runtime + Page AI UI。
- [ ] 登录测试账号后打开真实 Markdown 页面。
- [ ] Page AI 看到 OpenHub-derived UI,而不是旧简陋聊天框或官方 iframe。
- [ ] 发送真实消息,模型能识别当前 rootUri 内文件。
- [ ] 让 opencode 修改测试 MarkdownMNote changed chip 可打开,当前页可刷新。
- [ ] Knowledge UI 可上传/检索,WeKnora provider 有真实命中与引用。
- [ ] 保存截图与 smoke 输出。
## 6. 验收标准
MVP 完成条件:
- Page AI 主要视觉与交互来自 OpenHub Chat 子系统。
- 会话和消息持久化在 MNote control-plane,支持同用户跨浏览器恢复。
- opencode 真实流式回复可用,工作目录固定为当前打开 rootUri。
- changed files 与 MNote open/refresh 打通。
- Knowledge UI 至少能展示 WeKnora-backed 搜索结果;未接 WeKnora 时必须标注 fallback,不得声称知识库主线已完成。
- `npm run dev:hot` 后可用真实浏览器截图证明。
## 7. 当前结论
OpenHub 是目前最适合 MNote Page AI 深度融合的参考实现。它不解决 opencode 内核级多用户隔离,也不提供完整知识库底座,但它提供了 MNote 当前最缺的成熟 Chat/UI/message/session/diff/file/knowledge 管理壳。正确路线不是整站照搬 OpenHub,而是把 OpenHub Page AI 子系统移植为 MNote 原生 Page AI UI,后端由 MNote Rust adapter 接 opencode 与 WeKnora。
@@ -0,0 +1,751 @@
# 7-68 OpenHub + WeKnora + MNote Page AI 嵌入式集成设计 v1
状态:process
Owner07-ai / mnote-web / control-plane / knowledge-provider
日期:2026-06-25
## 0. 结论先行
MNote Page AI 不应再沿“官方 opencode WebUI iframe”或“自研简陋聊天框”继续堆功能。新的主路线是:
```text
MNote 当前文档页 / workspace / auth / resource truth
├─ Page AI UI:嵌入 OpenHub AI 界面(只暴露 AI 面板能力)
├─ OpenHub backend:运行 OpenHub FastAPI + Redis + OpenHub session/skill/MCP/permission
├─ Agent runtimeOpenHub FastAPI 调用 opencode serve / opencodego provider
├─ Knowledge providerWeKnora,通过 OpenHub MCP/CLI/skill 工具调用
└─ MNote boundary:统一登录授权、workspace/rootUri scope、文件打开、citation 回跳
```
核心取舍:
总原则:**各部分尽量保持原有已完成能力,只有发生产品真相冲突时才做最小胶水/嫁接/裁剪**。OpenHub 保持 AI 面板、FastAPI、Redis、session、skill/MCP、opencode clientWeKnora 保持知识库底座、MCP/CLI/APIMNote 保持 workspace/auth/resource tree/document pane/source registry。MNote 不重写 OpenHub/WeKnora 已有能力,只在登录态、workspace scope、文件打开、citation 回跳、禁用无关入口这些冲突点上做最小移植。
- **OpenHub 负责 Page AI 界面与多用户 AI 运行栈**:嵌入 OpenHub AI 面板,运行 FastAPI/Redis,使用 OpenHub 的用户隔离、session、skill、agent/MCP、tool permission、opencode 调用链;FileManager/KnowledgeManager/Admin/Login 等非 AI 入口掐断或转接。
- **WeKnora 负责知识库底座、知识库页面参考实现和 MCP/CLI/API 工具能力**:解析、chunk、混合检索、RAG 引用、Wiki/图谱、知识库管理 API;知识库页面优先复用 WeKnora 的 KB list/detail/upload/status 体验,但不让 WeKnora 接管 MNote 文件真相。
- **MNote 负责宿主真相与冲突胶水**:登录、用户、workspace/rootUri、resource tree、页面打开、allowed roots、session binding、source registry、citation/open-reference;不重写 OpenHub/WeKnora 已有主功能。
- **opencode 负责 agent 执行**:文件编辑、diff、工具审批、模型调用。
不得把 OpenHub 登录入口、OpenHub FileManager、OpenHub KnowledgeManager、WeKnora RBAC/前端变成 MNote 的用户/文件/知识库真相。OpenHub AI session/message/skill/MCP/permission/FastAPI/Redis 可以作为 Page AI 运行真相,但必须受 MNote 派生的用户与 workspace scope 隔离;WeKnora 保持知识库底座真相,MNote 只做展示、授权和回跳映射。
## 1. 背景与当前状态
### 1.1 已知事实
- OpenHub 是一个基于 opencode 的多用户 AI 平台参考实现,前端 React/AntD 组件较完整,包含 Chat、History、ToolCall、Diff、FileManager、KnowledgeManager 等。
- OpenHub 多用户主要是应用层隔离:后端带 `directory=<user workspace>` 调 opencode,自己用 SQLite 管用户、session、message、权限、技能/工具权限,并可能对 workspace 执行 Git snapshot / restore。
- OpenHub 自带知识库是轻量 `knowledge_bases + knowledge_sources + 本地全文 content + SQLite LIKE 候选 + Python BM25/TF-IDF rerank + prompt stuffing`,不是完整 RAG 底座;默认注入上下文约 1200 字符。
- WeKnora 已部署在本机 `/mnt/Data1T/Mnote_data/weknora/WeKnora`,并提供知识库、知识文件、chunk、`/api/v1/knowledge-search`、knowledge-chat、agent-chat、tenant/RBAC、共享空间、CLI/MCP 等能力。
- MNote 当前 active 代码仍大量使用 `knowledge_rag` / `LightRAG` 命名;用户已明确当前放弃 LightRAG,因此需要 provider-neutral 化并切到 WeKnora。
- MNote 当前已有 `/api/page-ai/opencode/*` 反代/绑定雏形和 `ai_external_conversation_bindings`;在本路线下应转为 OpenHub host/proxy/session binding 与 artifact/open-reference index,而不是重写 OpenHub FastAPI 的 opencode client。
### 1.2 本设计覆盖范围
本设计替代 `7-65` 的官方 opencode iframe 产品主线,并收敛 `7-67` 的 OpenHub 初步融合设想。核心不是重写三套系统,而是保留各自已有能力,只对冲突点做最小胶水:
1. 登录/用户/权限冲突。
2. WeKnora 知识库底座、MNote 展示层、OpenHub MCP/skill/tool 调用边界。
3. 页面/文件打开、changed files、citation 回跳边界。
## 2. 三方职责边界
| 能力 | MNote | OpenHub | WeKnora | 取舍 |
|---|---|---|---|---|
| 登录/用户 | MNote SQLite control-plane、`mnote_session` | OpenHub JWT/localStorage 禁用或后端注入;使用 MNote 派生 user/workspace | WeKnora tenant/RBAC/API key | MNote 是唯一登录入口;OpenHub 运行态按 MNote 用户/工作区派生 |
| workspace/rootUri | MNote local workspace、rootUri 授权 | OpenHub user workspace path / session scope / skill scope / MCP scope | WeKnora tenant/KB | rootUri 归 MNoteOpenHub 的 workspace directory 由 MNote 授权 rootUri 派生 |
| Page AI UI | MNote sidebar host / iframe/proxy shell | OpenHub AI 界面 | WeKnora Web UI 不嵌入 | 嵌入 OpenHub AI 面板;隐藏或掐断非 AI 页面入口 |
| session/message | MNote 只做 host binding / ownership index | OpenHub conversation/session tables + Redis/cache | WeKnora chat sessions 暂弃用 | OpenHub session/message 是 Page AI 真相;MNote 不复制消息全文 |
| agent runtime | MNote 启停/健康检查/反代边界 | OpenHub FastAPI + Redis + opencode client | WeKnora MCP/CLI/APIagent-chat 暂弃用 | 运行 OpenHub 后端;由 OpenHub 调 opencodeMNote 不重写 opencode client |
| 知识库 UI | MNote shell / FileTree 灯号 / citation 回跳 | OpenHub KnowledgeManager 掐断或跳转 MNote | 复用 WeKnora KB list/detail/upload/status 页面能力 | 用 WeKnora 知识库页面替换 MNote 简陋页,但本地文件和授权仍归 MNote |
| 检索/RAG | MNote source registry / citation 回跳 / scope 集合定义 | OpenHub AI 通过 MCP/skill/tool 调 WeKnora | WeKnora hybrid/vector/graph/chunk | 知识库是 MNote 授权文件/文件夹集合的索引视图;WeKnora 是唯一索引与查询 provider |
| 文件打开 | MNote document pane/resource tab/FileTree,相当于 FileManager | OpenHub FileManager 掐断或跳转 MNote | WeKnora 不负责页面打开 | OpenHub AI 回复中的 path/citation 点击回 MNote 打开页面 |
| 权限审批 | MNote allowed roots + workspace grants + scope 注入 | OpenHub tool/model/skill/MCP permissions | WeKnora RBAC | MNote 提供授权边界;OpenHub 按原生权限规范执行;WeKnora RBAC 作 provider 防线 |
| Git / snapshot / restore | MNote watcher、buffer、用户显式保存/版本策略 | OpenHub Git snapshot 默认关闭 | 无关 | 第一阶段禁用 Git snapshot/restore,避免污染 local-first workspace |
| Redis/cache/queue | MNote 不存 Page AI 消息真相 | OpenHub Redis/cache/queue 由 OpenHub stack 管理 | WeKnora 如有内部 Redis 由 WeKnora stack 管理 | Redis 归各自服务栈;不作为 MNote 权限/文件/知识库真相 |
## 3. 登录、OpenHub 隔离与 WeKnora 授权融合
### 3.1 核心更正
这里不能简单写成“只使用 MNote 单一用户认证,然后 OpenHub/WeKnora 都完全无用户”。更准确的模型是保留各自用户/权限机制中有价值的部分,并由 MNote 在边界上做最小嫁接:
```text
MNote 登录态 / user_id / workspace grants
├─ OpenHub 派生隔离上下文:openhub_user_key + workspace/runtime/session/skill/MCP scope
└─ WeKnora 派生知识库上下文:已授权 workspace/rootUri -> provider tenant/profile/KB/source registry
```
- **MNote 负责入口认证与授权判断**:当前用户是谁、能访问哪些 workspace/rootUri、能读写哪些 source。
- **OpenHub 需要 per-user / per-workspace 隔离**session、message、skill、MCP、tool permission、opencode directory、changed files 都必须绑定 MNote 用户与 workspace;不能所有 MNote 用户共用一个 OpenHub runtime identity。
- **WeKnora 是唯一知识库底座,并通过 MCP / CLI / API 暴露给 OpenHub/opencode**:它只接收 MNote 已授权 workspace 的文件/文件夹集合 ingest/search/query 或工具调用;知识库可见性本身依赖 MNote 的 source registry 和 allowed rootsWeKnora 用户认证可以保持简单。
- **WeKnora RBAC/API key 是 provider 防线**:不承担 MNote 产品层用户隔离,不把 WeKnora tenant/user 反向暴露成 MNote 登录体系。
### 3.2 冲突
OpenHub 和 WeKnora 的用户模型对 MNote 的影响不同:
- OpenHub 前端会使用 `auth_token` / JWT / localStorage,并在 401 后跳转到 `/login`
- OpenHub 后端还有会话、消息、skill、MCP、tool/model permission、workspace path 和 Git snapshot 等用户相关状态。
- WeKnora 有 tenant RBAC、Owner/Admin/Contributor/Viewer、共享空间与 API Key,但 MNote 的知识库使用场景主要来自“用户已授权 workspace/rootUri”。
- MNote 已有 SQLite control-plane auth、`mnote_session` cookie、测试账号与 local workspace 授权。
如果直接嵌入 OpenHub 或 WeKnora Web UI,会出现三套登录入口、三套用户 id、三套权限判断;但如果把 OpenHub 也降成“无用户共享 runtime”,又会让 session、skill、MCP、工具审批和文件变更串用户。
### 3.3 决策
- MNote 是唯一**前端登录入口**和产品层授权入口。
- OpenHub 不保留自己的 Login 页面、JWT/localStorage 登录跳转,但 MNote boundary 必须为每个 MNote 用户派生 OpenHub runtime identity。
- OpenHub 派生 identity 至少包含:`mnote_user_id``workspace_id``root_uri``openhub_user_key``opencode_session_scope``skill_scope``mcp_scope``tool_permission_scope`
- OpenHub session/message/skill/MCP/tool permission 不得跨 `mnote_user_id + workspace_id/root_uri` 共享。
- WeKnora 使用 MNote 后端服务 API key 或受控 profile 调用;前端不直接持有 WeKnora API key。
- WeKnora KB/source 由 MNote 的 workspace grants、source registry、allowed roots 决定;WeKnora tenant/RBAC 只作为 provider 内部防线。
### 3.4 映射建议
短期本机/local-first
```text
MNote user_id + workspace_id/rootUri
-> OpenHub runtime identity: mnote:{user_id}:{workspace_id}:{root_hash}
-> OpenHub scopes:
session_scope = user_id + workspace_id + rootUri + page_resource_id
skill_scope = user_id + workspace_id + rootUri
mcp_scope = user_id + workspace_id + allowed_roots
tool_permission_scope = user_id + workspace_id + rootUri
-> WeKnora provider profile: mnote-local 服务 API key
-> WeKnora KB: mnote-{workspace_id}-{purpose}
-> MNote source registry 记录 provider KB / knowledge / chunk 映射
```
中期多用户/局域网:
```text
MNote user_id + workspace membership
-> OpenHub runtime identity 按 user/workspace 派生或映射到受控 OpenHub user
-> MNote credential vault 选择 WeKnora service profile/API key
-> WeKnora 默认按 workspace/profile/KB 隔离,不强制每个 MNote 用户对应 WeKnora 用户
-> 所有 OpenHub 可见性仍由 MNote 登录态 + OpenHub scope 校验
-> 所有 WeKnora 结果仍由 MNote source registry / allowed roots 二次过滤
```
不要在第一阶段为每个 MNote 用户强行同步 WeKnora RBAC;这会放大生命周期与权限同步复杂度。相反,第一阶段应优先保证 OpenHub 派生上下文隔离,因为 Page AI 的 session、skill、MCP、工具审批和文件变更都直接依赖 MNote 登录态。
## 4. OpenHub Session / Message 真相
### 4.1 核心更正
这里不应设计“三方 session 融合”,也不应新增一套 MNote `page_ai_messages` 作为 AI 面板消息真相。应保留 OpenHub 已做好的 session/message 能力,只做 MNote ownership binding
```text
MNote Page AI 面板
-> MNote 自有 Page AI 映射 OpenHub session/conversation
-> OpenHub session / conversation 是唯一 AI 面板会话真相
-> MNote control-plane 只保存绑定、索引和打开/权限映射
-> WeKnora 不参与 Page AI 会话真相
```
- **OpenHub session 是 Page AI session 真相**:消息、turn、tool call、skill/MCP 状态、history、retry、visible/hidden 等语义以 OpenHub conversation/session 模型为准。
- **MNote 不复制消息主存储**:MNote 只需要保存 `mnote_user/workspace/rootUri/page_resource_id -> openhub_session_id/opencode_session_id` 的绑定,以及 changed file / citation 回跳所需的轻量索引。
- **WeKnora 不产生会话冲突**:当前 WeKnora 主要作为 MCP/CLI/知识库工具 provider`knowledge-chat` / `agent-chat` 暂时弃用,不纳入 Page AI 主链,因此不设计 `knowledge_session_id`
### 4.2 冲突
真正的冲突不是“三套消息历史融合”,而是:
- OpenHub session/message 本来就是 AI 面板的产品模型,MNote 自建 `page_ai_messages` 会变成第二份聊天真相。
- MNote 仍需要知道某个 OpenHub session 属于哪个 `mnote_user_id + workspace_id/rootUri + page_resource_id`,否则无法做跨浏览器恢复、权限过滤和打开文件回跳。
- WeKnora 的 agent-chat / knowledge-chat 若混入主链,会引入第二套 provider chat session;当前应明确弃用。
### 4.3 决策
MNote control-plane 只新增或复用**绑定/索引层**,不新增消息全文主表。表名可复用现有 `ai_external_conversation_bindings` 并扩展 metadata,不要求一定新建下列物理表:
```text
page_ai_openhub_bindings
id
mnote_user_id
workspace_id
root_uri
page_resource_id
page_absolute_path
openhub_user_key
openhub_session_id
opencode_session_id nullable
status = active | archived | stale
metadata_json
created_at / updated_at / archived_at
page_ai_artifact_index
id
binding_id
openhub_session_id
kind = changed_file | diff | citation | tool_call_ref | attachment_ref
provider = openhub | opencode | weknora
provider_id
payload_json
mnote_resource_id nullable
mnote_open_reference_json nullable
created_at
```
`page_ai_artifact_index` 不是消息真相,只是为了 MNote sidebar/document pane 能打开 changed files、diff、citation、attachment。消息正文、历史列表、tool card 展示、retry/隐藏状态仍从 OpenHub session/conversation 读取。
### 4.4 OpenHub session scope
OpenHub session 必须绑定 MNote 登录态派生的 scope
```text
openhub_session_scope = hash(mnote_user_id, workspace_id, root_uri, page_resource_id)
openhub_user_key = stable_hash(mnote_user_id)
openhub_workspace_key = stable_hash(workspace_id, root_uri)
```
- 同一用户同一页面可恢复最近 active OpenHub session。
- 切换 rootUri 或 workspace 必须新开 OpenHub session;旧 session 标记 stale 或 archived。
- 不同 MNote 用户不得共享同一个 OpenHub session、skill scope、MCP scope 或 tool permission scope。
- MNote boundary 对 OpenHub session 的读写必须先校验 `mnote_session` 与 binding ownership。
### 4.5 WeKnora session policy
第一阶段不使用 WeKnora `knowledge-chat` / `agent-chat` 作为 Page AI 会话层:
- WeKnora 通过 MCP/CLI/API 暴露知识库能力给 OpenHub/opencode 工具链。
- WeKnora 检索结果返回 chunk/referenceMNote 负责 source registry 映射和 citation 回跳。
- 如果未来启用 WeKnora agent-chat,只能作为 OpenHub tool call 的内部 provider call,不能成为 Page AI 历史会话真相。
## 5. Knowledge 融合
### 5.1 OpenHub 知识库定位
OpenHub `KnowledgeManager` 第一阶段不复用;其后端知识库也不应成为主线:
- 数据模型只有 base/source,没有持久 chunk/citation/embedding。
- 检索是 SQLite `LIKE` 候选 + BM25/TF-IDF 重排。
- 注入是 prompt stuffing,总上下文默认约 1200 字符。
适合:作为 OpenHub 源码理解和对照材料。
不适合:MNote 知识库主线、fallback 知识库、KnowledgeManager 页面复用、长期资料库、复杂 PDF/图片/OCR、可点击 citation、跨文档图谱、长期 RAG。
### 5.2 WeKnora 能力定位
WeKnora 应承担 MNote 知识库 provider
- 知识库类型:以 WeKnora `KnowledgeBase.Type` 和 FAQ 配置为准;Wiki/图谱属于 WeKnora Wiki mode / graph 能力,不能未经接口枚举直接当作 KB type 写死。
- 导入:文件、URL、Markdown/手工知识、外部数据源。
- 文档处理:chunk、OCR/VLM/ASR、图谱抽取、问题生成、reparse。
- 检索:优先对接 `POST /api/v1/knowledge-search`,或按 KB 维度对接 hybrid-search;返回分数为融合排序分(如 RRF),不能当原始相似度解释。
- 问答:`POST /api/v1/knowledge-chat/:session_id``POST /api/v1/agent-chat/:session_id` SSE 作为后续可选 provider 能力;第一阶段 Page AI 主链暂弃用,不产生主会话真相。
- 权限:tenant RBAC / shared organization 作内部防线。
MNote 侧不要把 WeKnora RBAC 当唯一隔离边界:WeKnora RBAC 可能受配置开关影响,关闭时 guard 可能记录但放行;MNote 必须始终按 `mnote_session + workspace/rootUri + source registry + allowed roots` 二次过滤。
### 5.3 MNote Knowledge Adapter / Search Replacement
保留 MNote 对外 canonical API
```text
/api/knowledge-rag/status
/api/knowledge-rag/ingest
/api/knowledge-rag/search
/api/knowledge-rag/query
/api/knowledge-rag/section-context
/api/knowledge-rag/open-reference
/api/knowledge-rag/delete-source
/api/knowledge-rag/prune-registry
```
但内部主链从 LightRAG 聚合检索切到 WeKnora。当前 `knowledge_rag.rs``/api/knowledge-rag/search``/api/knowledge-rag/query``/api/knowledge-rag/section-context` 仍围绕 LightRAG `/query/search``/query/data`、sidecar block、reference mapper、rank/dedupe 展开;替换时不能只改 endpoint,需要把 provider 调用、结果模型、locator 映射和排序语义一起替换。
目标 adapter
```rust
trait KnowledgeProvider {
fn status(root_uri, workspace_id) -> ProviderStatus;
fn ensure_kb(scope) -> ProviderKbRef;
fn ingest(source) -> ProviderKnowledgeRef;
fn search(query, scope, filters) -> Vec<ProviderReference>;
fn query(question, scope, filters) -> ProviderQueryResult;
fn section_context(provider_ref, query, scope) -> ProviderSectionContext;
fn open_reference(provider_ref) -> MnoteOpenReference;
fn delete_source(provider_ref) -> DeleteResult;
}
```
新增 `weknora` provider 实现;旧 LightRAG provider 标记 legacy,不再作为默认。第一阶段 `query` 可以由 WeKnora search results + citations 组成 answer envelope,不启用 WeKnora `knowledge-chat` / `agent-chat` 会话。
检索替换原则:
- `/api/knowledge-rag/search`:调用 WeKnora `/api/v1/knowledge-search` 或 KB 级 hybrid-search,返回 MNote `search_results.v1` 兼容结构。
- `/api/knowledge-rag/query`:不再调用 LightRAG `/query/data`;第一阶段用 WeKnora search result 生成带 citations/references 的 query result。
- `/api/knowledge-rag/section-context`:不再读 LightRAG sidecar blocks;改为基于 WeKnora chunk / source registry / 本地文件 locator 构造上下文。
- 排序与阈值:WeKnora score 是 RRF 融合分,不能沿用 LightRAG 相似度阈值和旧 rank 解释。
- 引用映射:WeKnora `knowledge_id` / `chunk_id` / `knowledge_base_id` 只用于回查 registry,不能直接作为 MNote 文件路径。
WeKnora search result 到 MNote registry 的最低字段映射:
| WeKnora 字段 | MNote 派生字段 | 说明 |
|---|---|---|
| `id` | `providerChunkId` | WeKnora chunk id |
| `knowledge_id` | `providerKnowledgeId` | 回查 registry 的主键之一 |
| `knowledge_base_id` | `providerKnowledgeBaseId` | provider KB 映射 |
| `chunk_index` | `chunkIndex` | 可用于同一 knowledge 内排序/定位 |
| `start_at` / `end_at` | `providerOffsets` | 只能作为 provider 内偏移,不能直接当 Markdown 行号 |
| `knowledge_filename` | `providerDisplayName` | 只用于显示,不能当本地路径真相 |
| `knowledge_source` / `knowledge_channel` | `providerSourceMeta` | 用于辅助映射和审计 |
`sourcePath``lineStart``lineEnd``mnoteResourceId``openReference` 必须由 MNote registry / locator 派生;WeKnora 未返回时不能伪造。
### 5.4 知识库定义、页面选择与 WeKnora Tool Bridge
这里不应设计 `OpenHub-compatible /knowledge/*`,也不应把 OpenHub `KnowledgeManager.jsx` 作为第一阶段知识库页。知识库页面建议复用 WeKnora 的 `KnowledgeBaseList.vue` / `KnowledgeBase.vue` / 上传与 processing timeline 组件,作为 MNote 知识库展示层的实现。当前边界是:
```text
MNote Knowledge UI(复用 WeKnora KB list/detail/upload/status 体验)
-> MNote source set / source registry / allowed roots
-> WeKnora ingest / search / status / open-reference
MNote Page AI / OpenHub-style session
-> opencode 按 MCP/skill/tool 规范自行决定 tool call
-> 已注册的 `mnote.weknora.*` MCP/CLI/API tool
-> WeKnora search/query
-> tool result 回 OpenHub session
```
决策:
- **知识库不是新的文件真相**:真相永远是 MNote 授权 rootUri 下的本地文件/文件夹/page resource;知识库是这些 source 的命名集合、索引状态和检索配置。
- **唯一知识库底座是 WeKnora**OpenHub 自带 knowledge tables、KnowledgeManager 页面和 prompt stuffing 知识库第一阶段全部不使用。
- **知识库页面选择 WeKnora**:复用 WeKnora 的 KB list/detail/upload/status/reparse/processing timeline 体验来替换 MNote 简陋知识库页;MNote 外壳负责登录、workspace、source registry、FileTree 灯号和 citation 回跳。
- **OpenHub 不管理知识库页面**OpenHub 只在对话过程中通过 MCP/CLI/API tool 调用 WeKnoratool result 进入 OpenHub session。
- **MNote tool facade 是注册与权限边界**OpenHub/opencode 不能直接持有 WeKnora API key,也不能绕过 MNote allowed roots / source registry;是否调用工具、如何组织 tool call 由 OpenHub/opencode 自己判断。
第一阶段需要的接口不是 OpenHub-compatible knowledge API,而是三类接口:
```text
MNote UI canonical API:
/api/knowledge-rag/status
/api/knowledge-rag/ingest
/api/knowledge-rag/search
/api/knowledge-rag/open-reference
/api/knowledge-rag/delete-source
/api/knowledge-rag/prune-registry
WeKnora-page-in-MNote adapter:
list_kbs / create_kb / update_kb / delete_kb
list_sources / add_source_set / upload_or_link_source / reparse_source / delete_source
get_processing_status / get_citation_open_reference
OpenHub/opencode tool facade:
mnote.weknora.search
mnote.weknora.open_reference
mnote.weknora.list_sources
mnote.weknora.get_source_status
```
WeKnora 页面 adapter 可以复用 WeKnora 前端组件/交互,但数据入口必须先经过 MNote source registry 与 allowed roots`mnote.weknora.*` 可以底层走 WeKnora MCP、CLI 或 HTTP API,但对 OpenHub 暴露的合同必须是 MNote 权限过滤后的 tool contract。
### 5.5 Source Registry provider-neutral 化
旧 LightRAG 字段应迁移为:
```text
mnote_knowledge_sources
id
user_id
workspace_id
root_uri
resource_id nullable
source_uri
source_path
source_hash
provider = weknora | lightrag_legacy | local_fallback
provider_kb_id
provider_knowledge_id
provider_doc_id nullable
provider_status
index_status = queued | parsing | indexed | failed | stale | deleted
title
source_type
tags_json
citation_locator_json
metadata_json
created_at / updated_at / deleted_at
```
关键原则:provider 返回的 `knowledge_filename` / `chunk_id` 不能直接当 MNote 文件真相,必须回查 registry 映射到 `resource_id/source_uri/open_reference`
## 6. 页面打开 / 文件打开 / 引用回跳融合
### 6.1 冲突
- MNote 自己就是 FileManagerresource tree / file tree / document pane / resource tab 是唯一页面/文件打开入口。
- OpenHub 第一阶段只借用多用户 AI 能力和 AI 页面产品参考,不使用 OpenHub FileManager 或完整前端。
- WeKnora 只暴露 MCP/CLI/API 知识工具,返回 knowledge/chunk/reference,不参与页面打开。
因此这里不需要做 OpenHub FileManager、WeKnora Web UI 或 provider path 的打开融合;只需要保证 MNote Page AI 回复中的 changed file、diff、citation、tool result 能映射回 MNote 页面。
### 6.2 决策
所有打开动作只发生在 MNote Page AI / document pane 内:
- changed file chip 点击:`window.__mnoteDocumentPaneRuntime.openResourceInActiveTab()`
- 当前页被修改:`window.__mnoteDocumentPaneRuntime.refreshPrimaryDocument()` 或 watcher 链路。
- WeKnora citation / MCP/CLI tool result 点击:`/api/knowledge-rag/open-reference` 返回 MNote locator,再由 MNote 前端打开。
- OpenHub session 中出现的 path/diff/tool reference 只作为数据来源,渲染和点击由 MNote Page AI 处理。
- 不接 OpenHub FileManager,不使用 OpenHub `/api/files` 作为页面读写入口。
- 不嵌 WeKnora Web UI,不让 WeKnora 决定打开哪个 MNote 页面。
### 6.3 MNote Page AI open reference payload
```json
{
"provider": "weknora",
"providerKbId": "kb-...",
"providerKnowledgeId": "...",
"providerChunkId": "...",
"sourceId": "mnote-source-...",
"rootUri": "file:///...",
"sourcePath": "docs/a.md",
"locator": {
"kind": "markdown-range",
"heading": "...",
"lineStart": 12,
"lineEnd": 20,
"quote": "..."
}
}
```
后端必须二次校验:当前用户、rootUri、workspace、source ownership、allowed read scope。这个 payload 只服务 MNote Page AI 中的点击打开,不是 OpenHub FileManager 或 WeKnora 前端合同。
## 7. OpenHub FastAPI / Redis / opencode Runtime 安排
### 7.1 核心边界
这里不应让 MNote 重写 OpenHub FastAPI 的 opencode client。最小干扰路径是运行 OpenHub AI 后端栈,让 MNote 只做宿主、scope 注入、入口裁剪和回跳桥:
```text
MNote Page AI host
-> MNote 校验登录态、workspace、rootUri、allowed roots
-> 注入/映射 OpenHub user workspace + session/skill/MCP scope
-> OpenHub AI UI
-> OpenHub FastAPI + Redis/session/cache
-> OpenHub opencode client
-> opencode serve /global/event /session/{id}/prompt_async /diff
```
- **OpenHub FastAPI 继续运行**:负责 AI session/message、skill/MCP、tool permission、opencode client、event stream、diff/changed files 等 OpenHub 原生能力。
- **Redis 跟随 OpenHub stack**:如果 OpenHub 用 Redis 做 cache/queue/session 辅助,就由 OpenHub stack 管理;MNote 不把 Redis 当自己的消息、权限、文件或知识库真相。
- **MNote 不重写 planner/client**MNote 不替 OpenHub 判断何时调用 WeKnora,也不重写 opencode prompt/event/diff 流程。
- **MNote 只裁剪不用入口**OpenHub Login、FileManager、KnowledgeManager、Admin 等页面不暴露;必要时在 proxy 层 404、隐藏菜单或跳转到 MNote 对应页面。
### 7.2 FastAPI 的具体作用
OpenHub FastAPI 在本设计中保留以下作用:
- 管理 OpenHub conversation/session/message/history。
- 管理 skill、agent/MCP、tool/model permission。
- 调用 `opencode serve`,包括创建 session、发送 prompt、监听 `/global/event`、读取 diff。
- 向 OpenHub AI 前端提供消息流、tool card、diff、changed files、history 等 API。
- 读取由 MNote 注入的 workspace directory、user scope、allowed roots、WeKnora MCP/CLI/skill 配置。
OpenHub FastAPI 不保留以下作用:
- 不作为 MNote 登录入口。
- 不接管 MNote resource tree / FileTree / document pane。
- 不启用 OpenHub 自带 KnowledgeManager 作为知识库 UI。
- 不启用 OpenHub 自带轻量 knowledge tables 作为 RAG 底座。
- 不执行 Git snapshot / restore / revert,除非未来另开设计并经 MNote 显式确认。
### 7.3 Redis 安排
- OpenHub 需要的 Redis/cache/queue 由 OpenHub deployment 管理,随 OpenHub FastAPI 启停和 health check。
- WeKnora 如果有内部 Redis/队列依赖,由 WeKnora stack 管理,MNote 只检查 WeKnora health。
- MNote control-plane 不依赖 Redis 保存登录授权、workspace grants、source registry、open-reference 或文件真相。
- Redis 中只允许放可重建状态:缓存、队列、临时流状态;不能成为用户权限、文件内容、知识库 source registry 或 Page AI 消息的唯一持久真相。
### 7.4 Page AI context
MNote 注入给 OpenHub / opencode 的 context
```text
- 当前 MNote 用户/匿名显示名,不含敏感 cookie/token
- 当前 workspace/rootUri
- 当前页面标题、真实 Markdown path、resource id
- selection 摘要
- allowed roots / write constraints
- OpenHub session scope / skill scope / MCP scope / tool permission scope
- WeKnora tool scope:允许查询的 kb ids/source ids/citation policy
- MNote 文件打开/刷新 bridge usage
```
不要注入整篇正文;路径与 allowed roots 足够让 opencode 在本地读取文件。正文只在 selection 或用户明确需要时作为有限上下文传入。
## 8. UI 边界:嵌入 OpenHub AI 面板,保留 AI 原能力
### 8.1 Page AI Shell
MNote sidebar host 的职责应尽量薄:承载 OpenHub AI 面板、注入 MNote context、处理 MNote 回跳,不重新设计一套顶部/侧边栏产品结构。
- 顶部:第一阶段可以不要 MNote 自定义顶部;如需状态,只做极简 host 状态条或错误提示,避免覆盖 OpenHub AI 面板原有布局。
- 主体:OpenHub AI 面板,由 OpenHub 前端/后端处理消息、tool card、diff、history、agent/MCP/skill 状态。
- 侧边:优先保留 OpenHub AI 相关侧边能力,包括历史 session、MCP、skill、agent、tool/model permission 等设置。
- MNote context:以 context pills / hidden bootstrap / postMessage / proxy header 方式注入当前 page path、rootUri、selection、allowed roots,不强行改 OpenHub UI 主结构。
- 回跳:changed file、citation、reference 点击时走 MNote bridge 打开页面。
### 8.2 OpenHub 前端裁剪策略
第一阶段不是“大面积禁用 OpenHub 前端”,而是**保留 AI 面板相关能力,只掐断与 MNote 真相冲突的入口**:
保留:
- AI chat 主界面。
- history / session 抽屉。
- MCP 设置与连接状态。
- skill / agent 设置。
- tool/model permission UI。
- tool card、diff、changed files、运行日志等 AI 运行态 UI。
裁剪或转接:
- OpenHub Login:禁用,改由 MNote 登录态注入 OpenHub 派生用户。
- OpenHub workspace selector:禁用或固定为 MNote 授权 rootUri 派生 workspace。
- OpenHub FileManager:不作为文件真相;若 AI 面板内出现文件入口,转接到 MNote document pane / FileTree。
- OpenHub KnowledgeManager:不作为知识库 UI;如入口存在,转接到 MNote 知识库展示层或隐藏。
- OpenHub Admin / Team / Scheduler / SmartEntity:默认隐藏或不可达,除非后续明确纳入 Page AI 管理面。
- OpenHub Git snapshot / restore:默认关闭,避免改写 MNote local-first workspace 版本语义。
### 8.3 MNote 暴露给 OpenHub 的边界能力
MNote 不替 OpenHub 判断何时调用知识库、何时用 skill/MCP、如何组织 tool call;这些交给 OpenHub/opencode 已有 MCP/skill/agent 规范处理。MNote 只提供最小边界能力:
```text
- 当前页面 contextpage path / title / selection / rootUri / allowed roots
- OpenHub scopeuser/workspace/rootUri 派生的 session/skill/MCP/tool permission scope
- WeKnora MCP/CLI/API 配置:以 skill/MCP/tool 形式注册给 OpenHub/opencode
- open-reference bridge:把 provider citation/chunk/source 映射成 MNote 页面打开动作
- changed-file bridge:把 OpenHub/opencode 返回的 path 映射成 MNote document pane 打开/刷新
```
也就是说,MNote 是宿主、授权边界和回跳桥,不是 OpenHub agent 的 planner,也不是 OpenHub AI 面板的重写者。
## 9. 数据流:OpenHub 原生执行,MNote 注入边界
### 9.1 发送 Page AI 消息
```text
用户输入
-> MNote Page AI host 中的 OpenHub AI 面板
-> OpenHub 前端调用 OpenHub FastAPI
-> OpenHub FastAPI 使用 OpenHub session/message/skill/MCP/tool permission
-> OpenHub FastAPI 调 opencode serve
-> opencode 读写 MNote 授权 rootUri 内文件
-> OpenHub session/message/tool history 持久化
-> OpenHub AI UI 渲染回复、tool card、diff、changed files
-> MNote bridge 只处理文件打开、刷新、citation 回跳
```
MNote 不在消息主链里重写 OpenHub planner,也不解析知识需求后替 OpenHub 决定调用 WeKnora。MNote 只负责登录态、workspace 授权、scope 注入和结果回跳。
### 9.2 知识检索
```text
OpenHub/opencode 判断需要知识
-> 按 MCP/skill/tool 规范调用已注册的 WeKnora 工具
-> WeKnora MCP/CLI/API 返回 chunks/references
-> OpenHub/opencode 把结果纳入当前 session/tool result
-> 用户点击 citation/reference 时
-> MNote open-reference bridge 按 source registry / allowed roots 映射并打开对应页面
```
MNote 不负责替 OpenHub 判断 knowledge scopescope 在注册 WeKnora MCP/skill/tool 时由 MNote 根据当前用户、workspace、allowed roots 预先约束。
### 9.3 知识库生成与展示
```text
MNote 知识库展示层上传/添加资料
-> MNote canonical knowledge API
-> MNote 校验 user/workspace/rootUri/write permission
-> WeKnora file/manual/url ingest
-> 写 mnote_knowledge_sources registry
-> FileTree/Knowledge UI 显示 indexing 状态
-> 生成/更新可供 OpenHub/opencode 使用的 WeKnora MCP/skill/tool 配置
```
OpenHub 不管理知识库生成页面;它只消费已经按 MNote 授权边界配置好的 WeKnora 工具。知识库生成页面复用 WeKnora KB 页面体验,但 source 选择应以 MNote 本地文件/文件夹集合为入口。
## 10. 可执行 Checklist
### 10.1 设计与旧路径冻结
- [ ]`7-65` 标注官方 opencode iframe 只保留为 fallback,不再作为 Page AI 产品主线。
- [ ]`7-66` 标注自研 native UI 只保留为 fallback,不再继续扩自研聊天框。
- [ ]`7-67` 标注已被本文覆盖:OpenHub 路线从“参考/重写”改为“嵌入 AI 面板 + 保留 FastAPI/Redis/opencode client”。
- [ ] 将本文保留在 `design/07-ai/process/`,作为当前 Page AI 嵌入式集成主设计。
- [ ] 在相关 design / bugs / testing 文档中统一术语:WeKnora 是唯一知识库底座,MNote 是知识库展示和文件真相层,OpenHub KnowledgeManager 不作为知识库页。
- [ ] 搜索并标记仍把 LightRAG 描述为默认知识库 provider 的文案,改成 legacy/fallback。
### 10.2 OpenHub 服务栈接入
- [ ] 确认 OpenHub 本机源码路径、启动命令、依赖文件和默认端口。
- [ ] 确认 OpenHub FastAPI 是否真实依赖 Redis;记录 Redis host/port/env 和启动顺序。
- [ ] 确认 OpenHub FastAPI 调 opencode 的配置项:opencode base URL、directory 参数、BasicAuth、模型/provider env。
- [ ]`scripts/desktop-hot.js` 或等价 dev-hot 链路中增加 OpenHub FastAPI、Redis、opencode serve 的启动/跳过/health check。
- [ ] 增加 OpenHub health endpoint 探针;失败时错误信息区分 FastAPI、Redis、opencode。
- [ ] 禁用 OpenHub launcher 的 kill-port 或 destructive workspace 行为,避免影响 MNote dev 进程。
- [ ] 明确 OpenHub Git snapshot / restore / revert 默认关闭,并在启动环境或配置中落实。
- [ ] 写 smokeOpenHub FastAPI 可列 sessionRedis 可达,opencode serve 可达。
### 10.3 MNote 登录态到 OpenHub Scope
- [ ] 定义 `openhub_user_key = stable_hash(mnote_user_id)`
- [ ] 定义 `openhub_workspace_key = stable_hash(workspace_id, root_uri)`
- [ ] 定义 session scope`mnote_user_id + workspace_id + root_uri + page_resource_id`
- [ ] 在 MNote 后端实现或扩展 OpenHub host/proxy bootstrap endpoint,输出 user/workspace/session/tool scope。
- [ ] 禁止前端持有 OpenHub JWT/localStorage 登录真相;OpenHub 用户态由 MNote 后端注入或代理。
- [ ] rootUri / workspace 切换时新建或切换 OpenHub session,旧 session 标记 stale/archived。
- [ ] 不同 MNote 用户访问同一页面时不得复用同一 OpenHub session、skill scope、MCP scope、tool permission scope。
- [ ] 写 control-plane 测试:binding 受 user/workspace/rootUri 隔离,跨用户查询失败。
### 10.4 OpenHub AI 面板嵌入
- [ ] 在 MNote Page AI sidebar host 中选择 iframe 或 reverse proxy 嵌入方式。
- [ ] 只暴露 OpenHub AI 页面路由;Login/Admin/Team/Scheduler/SmartEntity 默认不可达。
- [ ] OpenHub workspace selector 固定到 MNote 授权 rootUri 派生 workspace。
- [ ] 保留 OpenHub AI 主界面、history/session、MCP 设置、skill/agent 设置、tool/model permission、tool card、diff、changed files、运行日志。
- [ ] FileManager 入口若出现在 AI 面板内,跳转 MNote document pane / FileTree 或禁用。
- [ ] KnowledgeManager 入口若出现在 AI 面板内,跳转 MNote WeKnora 知识库页或隐藏。
- [ ] MNote context 通过 postMessage、proxy header 或 bootstrap JSON 注入 page path、title、selection、rootUri、allowed roots。
- [ ] 写浏览器 smokePage AI 显示 OpenHub AI 面板,刷新后 session/history 仍可恢复。
- [ ] 写浏览器 smoke:访问 OpenHub Login/Admin/FileManager/KnowledgeManager 非 AI 入口不会接管 MNote。
### 10.5 OpenHub 原生 opencode 链路
- [ ] 保持 OpenHub FastAPI 调用 `opencode serve`MNote 不重写 prompt/event/diff client。
- [ ] 确认 OpenHub 创建 session 时 directory 固定为 MNote 授权 rootUri。
- [ ] 确认 OpenHub 发送 prompt 后可监听 `/global/event` 并渲染 tool card / assistant message。
- [ ] 确认 OpenHub 可读取 `/session/{id}/diff` 或等价 changed files。
- [ ] MNote 只读取 changed file / diff artifact 的 path 和 session id,用于 open/refresh。
- [ ] changed file 点击调用 `window.__mnoteDocumentPaneRuntime.openResourceInActiveTab()`
- [ ] 当前打开页面被修改后走 watcher 或 `refreshPrimaryDocument()` 刷新。
- [ ] 写真实 smoke:让 OpenHub AI 修改 rootUri 内 MarkdownMNote 当前页面可看到变更。
### 10.6 WeKnora 知识库页面替换
- [ ] 盘点 WeKnora `KnowledgeBaseList.vue``KnowledgeBase.vue``KnowledgeBaseEditorModal.vue``knowledge-processing-timeline.vue` 的依赖。
- [ ] 决定复用方式:嵌 WeKnora frontend route、抽组件、或做 MNote adapter 页面复刻 WeKnora 交互。
- [ ] 知识库定义为 MNote 授权文件/文件夹/page resource 集合,不创建新的文件内容真相。
- [ ] 建立 source set 模型:kb id、workspace id、rootUri、source path/resource id、provider kb id、provider knowledge id、source hash。
- [ ] source 选择 UI 接 MNote FileTree / resource picker,而不是 WeKnora 自己的独立文件真相。
- [ ] 入库时 MNote 先校验 allowed roots,再调用 WeKnora file/manual/url ingest。
- [ ] WeKnora processing / reparse / failed / indexed 状态同步到 MNote registry 与 FileTree 灯号。
- [ ] 删除 source 时只删除知识库索引和 registry 映射,不删除本地原文件。
- [ ] 写浏览器 smoke:创建 KB、添加本地文件夹、看到 indexing 状态、完成后可检索。
### 10.7 WeKnora 检索替换 LightRAG
- [ ] 抽出 `KnowledgeProvider` 或等价 provider boundary,新增 `weknora` 实现。
- [ ] `/api/knowledge-rag/status` 改为检查 WeKnora health、KB 映射、source registry 状态。
- [ ] `/api/knowledge-rag/ingest` 改为 WeKnora ingest,并写入 provider KB / knowledge / source hash。
- [ ] `/api/knowledge-rag/search` 改为 WeKnora `/api/v1/knowledge-search` 或 KB 级 hybrid-search。
- [ ] `/api/knowledge-rag/query` 第一阶段由 WeKnora search results + citations 组成 query result,不启用 WeKnora chat session。
- [ ] `/api/knowledge-rag/section-context` 改为基于 WeKnora chunk + MNote 本地 locator,不读 LightRAG sidecar。
- [ ] `open-reference` 从 WeKnora `knowledge_id/chunk_id/knowledge_base_id` 回查 MNote registry,再生成 MNote locator。
- [ ] WeKnora RRF score 不沿用 LightRAG 阈值;UI 只显示排序分或弱化分值解释。
- [ ] 保留旧 LightRAG provider 为 legacy fallback,但默认隐藏且不作为 smoke 基线。
- [ ] 更新现有 knowledge-rag smoke,把 provider 断言从 `lightrag` 改为 `weknora`
- [ ] 写单元测试:WeKnora SearchResult 映射为 MNote search result / citation / open-reference。
### 10.8 WeKnora MCP/CLI Tool Bridge
- [ ] 选定第一阶段 MCP surfaceGo CLI `weknora mcp serve` 或 Python `mcp-server`,记录读写能力差异。
- [ ] 默认暴露只读工具:`mnote.weknora.search``mnote.weknora.list_sources``mnote.weknora.get_source_status``mnote.weknora.open_reference`
- [ ] 写工具 manifest / skill,使 OpenHub/opencode 能在当前 session scope 内调用 WeKnora。
- [ ] tool 调用前注入 KB/source allowlist,不让 OpenHub/opencode 查询未授权 source。
- [ ] tool result 返回 provider ids、quote、chunk metadata、MNote open-reference token。
- [ ] citation 点击由 MNote bridge 打开,不由 OpenHub 或 WeKnora 决定本地路径。
- [ ] 写真实 smokeOpenHub AI 通过 MCP/CLI tool 查询 WeKnora,回答中出现可回跳 citation。
### 10.9 dev-hot 与真实验收
- [ ] `npm run dev:hot` 启动或检查 MNote、OpenHub FastAPI、Redis、opencode、WeKnora。
- [ ] 登录 `mnote.e2e@example.com`,确认没有 OpenHub/WeKnora 登录跳转。
- [ ] 打开真实 Markdown 页面并打开 Page AI。
- [ ] Page AI 嵌入 OpenHub AI 面板,history/session/MCP/skill/agent/tool permission 入口仍可用。
- [ ] OpenHub Login/Admin/FileManager/KnowledgeManager 非 AI 入口被隐藏、404 或跳转 MNote。
- [ ] 发送消息后 OpenHub session/message/history 持久化,刷新浏览器可恢复。
- [ ] 让 AI 修改当前 rootUri 内 MarkdownMNote document pane 可刷新并显示变更。
- [ ] 在 WeKnora 知识库页用本地文件/文件夹集合建库,完成 ingest/index。
- [ ] OpenHub AI 通过 WeKnora MCP/CLI/API tool 检索该 KB,并返回 citation。
- [ ] 点击 citation 打开 MNote 对应页面或资源 tab。
- [ ] 保存 smoke 输出和关键截图;失败时标明 OpenHub FastAPI、Redis、opencode、WeKnora、MNote bridge 中哪一层失败。
## 11. 取舍矩阵
| 冲突点 | 直接用 OpenHub | 直接用 WeKnora | MNote 边界方案 | 决策 |
|---|---|---|---|---|
| 登录 | 第二套 JWT/localStorage | 第二套 tenant login/API key | MNote cookie -> scope/binding | 选 MNote 登录入口 + OpenHub 派生 scope |
| 消息历史 | OpenHub conversation/session | WeKnora chat session 暂弃用 | MNote binding + artifact index | 选 OpenHub session 为真相,MNote 只做绑定/索引 |
| 知识库 UI | OpenHub KnowledgeManager 暂不使用 | WeKnora KB list/detail/upload/status 体验可复用 | MNote shell + WeKnora KB UI adapter | 选 WeKnora 知识库页面体验,嵌入 MNote 外壳 |
| 知识库底座 | OpenHub 自带知识库暂不用 | 强 RAG/Wiki/Graph | MNote registry + WeKnora | 选 WeKnora |
| 文件打开 | workspace path | knowledge filename | MNote resource/open-reference | 选 MNote |
| 权限 | 模型/工具/skill/MCP | tenant RBAC | MNote scope + OpenHub/opencode 权限规范 + provider 防线 | 组合,以 MNote scope 为边界 |
| Agent 编辑 | opencode | WeKnora agent-chat 暂弃用 | opencode 编辑 + WeKnora MCP/CLI/API 知识工具 | 组合 |
| UI 成本 | 嵌入 AI 面板,裁剪非 AI 入口 | 不适合 Page AI 编辑侧栏 | MNote host + OpenHub AI 面板 | 选 OpenHub AI 面板嵌入 |
| 运维 | OpenHub FastAPI + Redis + opencode 运行 | WeKnora 服务 | MNote 启动/检查 OpenHub/opencode/Redis/WeKnora,提供 binding/tool 配置 | 选 OpenHub 栈运行 + MNote 边界控制 |
## 12. 风险与防线
### 12.1 风险:三套权限漂移
防线:MNote 是浏览器入口和授权边界;OpenHub/opencode 只能拿到 MNote 派生 scope、allowed roots、tool/MCP 配置,不直接获得未过滤 workspace/rootUri。
补充:WeKnora RBAC 只能作为 provider 防线,不作为 MNote 授权来源;RBAC 关闭、API key 复用或共享空间变化时,MNote 过滤结果仍必须保持一致。
### 12.2 风险:WeKnora citation 无法定位到本地文件
防线:ingest 时必须写 `source_uri/source_hash/provider_knowledge_id`;检索返回后以 provider id 回查 registry,失败时 UI 标记“定位降级”,不能伪造路径。
### 12.3 风险:OpenHub UI 迁移成本变成 fork
防线:第一阶段嵌入 OpenHub AI 面板,尽量保持 OpenHub 已有 AI 功能不动;只对非 AI 入口通过隐藏、404、反代拦截或跳转 MNote 做最小裁剪。后期如需精简或替换组件,再单独做 UI 迁移设计。
### 12.4 风险:opencode 工作目录过大导致找不到文件
防线:工作目录固定为当前打开 rootUricontext 中传相对 path、allowed roots、当前页面 path;切换 rootUri 新开 session;不把大仓根或 recycle 目录作为默认工作目录。
### 12.5 风险:旧 LightRAG 残留误导
防线:UI 文案和配置改 provider-neutral;默认 provider 改 WeKnora;旧 LightRAG tools/manifest 标记 legacy 或隐藏。
### 12.6 风险:OpenHub 自动 Git snapshot 污染 workspace
防线:只参考 OpenHub AI 页面与 event/diff 解析思路;不迁移 Git snapshot / restore 后端路径。若以后需要版本恢复,必须走 MNote DocumentBuffer / watcher / 显式用户确认的版本策略。
### 12.7 风险:MCP/CLI 写权限边界不清
防线:WeKnora 有 Go CLI `weknora mcp serve` 与 Python `mcp-server` 两套 surface,读写能力不同。MNote 接入前必须指定采用哪一套、默认只读还是允许 create/delete,并把写操作纳入 MNote 权限审批。
## 13. 验收标准
MVP 通过必须同时满足:
- 浏览器中 Page AI 嵌入 OpenHub AI 面板,并能显示 OpenHub 原生消息/tool/diff/history。
- MNote 登录态是唯一登录入口;无 OpenHub/WeKnora 登录跳转。
- 同一 MNote 用户跨浏览器可恢复绑定到当前页面/rootUri 的 OpenHub session。
- opencode 在当前 rootUri 下能真实读取/修改 Markdown。
- changed file chips 与 DiffViewer path 能用 MNote 打开。
- WeKnora search/MCP/CLI 工具返回真实引用,citation 经 registry 映射后可回跳 MNote 文件;失败时明确定位降级,且不能把 `knowledge_filename` 伪装成本地路径。
- `npm run dev:hot` 能拉起并检查必要 runtime;失败时错误页给出 OpenHub FastAPI、Redis、opencode、WeKnora 或 OpenHub session binding 哪个不可达。
- MNote Page AI 路径不触发 OpenHub 登录跳转;OpenHub FileManager/KnowledgeManager/Admin 等非 AI 入口被隐藏、404 或跳转 MNote;消息真相保留在受 MNote scope 隔离的 OpenHub session 中,不触发 Git snapshot/restore 自动写链。
## 14. 本轮源码依据
- OpenHub 源码:`/tmp/mnote-openhub-research/OpenHub`
- WeKnora 本机部署:`/mnt/Data1T/Mnote_data/weknora/WeKnora`
- MNote Page AI runtime`rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
- MNote document pane bridge`rust/crates/mnote-web/browser/document-editor-adapter-runtime.js`
- MNote opencode route`rust/crates/mnote-web/src/routes/page_ai_opencode.rs`
- MNote knowledge route`rust/crates/mnote-web/src/routes/knowledge_rag.rs`
- MNote auth/session route`rust/crates/mnote-web/src/routes/session.rs``rust/crates/mnote-web/src/routes/gateway.rs`