chore: checkpoint turso and ai runtime work
This commit is contained in:
+44
-45
@@ -22,11 +22,14 @@
|
||||
|
||||
Turso 的收益不在于“SQLite 语法换个驱动”本身,而在于把控制面写入统一收口到受控 store / API 后端,避免脚本、测试、进程和运行时同时直接写同一个 SQLite 文件。若继续保留脚本直接 `sqlite3` 写库、测试临时覆盖 `MNOTE_CONTROL_PLANE_DB_PATH`、OpenHub 直接写自己的 SQLite 文件,那么换 Turso 只能解决一部分问题,不能解决当前最致命的并发写入纪律问题。
|
||||
|
||||
|
||||
2026-07-03 hard cutover 更新:用户已确认不再保留 mnote-web runtime SQLite fallback。当前策略改为 **Turso/libSQL-only runtime**:`dev-hot`、`desktop-hot`、prod 和 mnote-web 默认 `libsql-local`,显式 `MNOTE_CONTROL_PLANE_BACKEND=sqlite` 必须拒绝启动;SQLite 仅保留给 `control-plane-admin` 迁移/导出和 Rust 测试隔离。
|
||||
|
||||
2026-07-03 补充决策:已经获得真实 Turso `mnote` dev DB URL/token,并完成 remote dry-run 连接验证;但分叉讨论与本轮评估结论一致,**Turso Cloud 对当前 MNote 的意义不是“立刻把产品变成云同步应用”**。当前阶段仍以 local-first 文件真相和本机开发稳定性为主,云端能力只作为 control-plane 的受控验证、备份、迁移演练和后续多设备准备层。
|
||||
|
||||
因此默认路线调整为:
|
||||
|
||||
- `dev-hot` 继续优先使用 `libsql-local`,覆盖本地 store、migration、脚本纪律和 SQLite fallback。
|
||||
- `dev-hot`、`desktop-hot` 和 prod runtime 默认使用 `libsql-local`,覆盖本地 store、migration 和脚本纪律;不再保留 runtime SQLite fallback。
|
||||
- `desktop-hot` 不立即切到 `turso-remote` 或 `turso-local-replica`。
|
||||
- Turso remote 只做 dev DB dry-run、迁移演练、回滚演练和后续受保护 smoke,不作为当前本地功能测试的阻塞项。
|
||||
- local-first 文件正文、附件、mindmap、OnlyOffice 仍完全留在本地文件系统,不迁入 Turso。
|
||||
@@ -56,7 +59,7 @@ Turso 的收益不在于“SQLite 语法换个驱动”本身,而在于把控
|
||||
- external conversation bindings
|
||||
- `mnote-web` 对 control-plane 的构造、注入和测试 helper。
|
||||
- 所有直接 `sqlite3` 写 control-plane 的 smoke / 测试脚本。
|
||||
- 启动脚本和部署脚本里的 `MNOTE_CONTROL_PLANE_DB_PATH` 默认路径逻辑。
|
||||
- 启动脚本和部署脚本里的 `MNOTE_CONTROL_PLANE_DB_PATH` 默认路径逻辑必须移除;运行时只读取 libSQL/Turso env。
|
||||
|
||||
### 2.2 第一阶段不迁移
|
||||
|
||||
@@ -88,7 +91,7 @@ Rust mnote-web :3000
|
||||
v
|
||||
ControlPlaneStore trait
|
||||
|
|
||||
+-- SqliteControlPlaneStore 本地 fallback / 回滚 / 测试
|
||||
+-- SqliteControlPlaneStore 仅限 admin 迁移 / 导出 / Rust 测试隔离
|
||||
|
|
||||
+-- TursoControlPlaneStore 默认目标后端
|
||||
```
|
||||
@@ -96,8 +99,8 @@ ControlPlaneStore trait
|
||||
推荐环境变量:
|
||||
|
||||
```text
|
||||
MNOTE_CONTROL_PLANE_BACKEND=sqlite|turso|turso-local-replica
|
||||
MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.db
|
||||
MNOTE_CONTROL_PLANE_BACKEND=libsql-local|turso-remote|turso-local-replica|turso-synced
|
||||
MNOTE_TURSO_LOCAL_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane-libsql.db
|
||||
MNOTE_TURSO_DATABASE_URL=libsql://...
|
||||
MNOTE_TURSO_AUTH_TOKEN=...
|
||||
MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane-replica.db
|
||||
@@ -105,10 +108,10 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
|
||||
默认策略建议:
|
||||
|
||||
- 当前已落地:`dev-hot` 默认 `libsql-local`,`desktop-hot` 仍保守默认 `sqlite`。
|
||||
- 当前已落地:`dev-hot`、`desktop-hot` 和 prod runtime 默认 `libsql-local`。
|
||||
- 已配置真实 Turso dev DB:仅用于 remote dry-run、迁移演练、回滚演练和受保护 smoke,不改变 local-first 主路径。
|
||||
- Turso store 全测试通过后:CI 先跑 `sqlite` + `libsql-local`;`turso-dev` 作为需要 secret 的受保护 job。
|
||||
- 稳定后才评估:普通运行默认 `turso-local-replica`,保留 `sqlite` 作为离线和回滚模式。
|
||||
- 稳定后才评估:普通运行是否从 `libsql-local` 提升为 `turso-local-replica`;不恢复 SQLite runtime fallback。
|
||||
|
||||
本轮新增云端边界:
|
||||
|
||||
@@ -141,7 +144,7 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
- `rust/crates/mnote-web/src/app.rs`
|
||||
- 当前直接导入 `SqliteControlPlaneStore`。
|
||||
- `AppState.control_plane` 当前是 `Arc<SqliteControlPlaneStore>`,应改成 `Arc<dyn ControlPlaneStore>`。
|
||||
- `open_control_plane_store()` 读取 `MNOTE_CONTROL_PLANE_DB_PATH` 并打开 SQLite。
|
||||
- 历史状态:`open_control_plane_store()` 曾读取 `MNOTE_CONTROL_PLANE_DB_PATH` 并打开 SQLite;hard cutover 后该路径已删除。
|
||||
- `rust/crates/mnote-web/src/local_folder_watcher_registry.rs`
|
||||
- 字段已经是 `Arc<dyn ControlPlaneStore>`,但 `new()` 参数仍偏向具体 SQLite 类型,需要收口。
|
||||
- `rust/crates/mnote-web/src/routes/local_search_index.rs`
|
||||
@@ -156,7 +159,7 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
当前至少有两类脚本必须改:
|
||||
|
||||
- 直接调用 `sqlite3` CLI 写库。
|
||||
- 通过临时 `control-plane.sqlite3` 和 `MNOTE_CONTROL_PLANE_DB_PATH` 绕过真实 store。
|
||||
- 通过临时 `control-plane.sqlite3` 和 `MNOTE_CONTROL_PLANE_DB_PATH` 绕过真实 store;hard cutover 后运行时启动链不得再接受该方式。
|
||||
|
||||
这些脚本如果不改,即使 runtime 切 Turso,测试和开发链路仍会制造第二套写入口。
|
||||
|
||||
@@ -165,14 +168,14 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
## 5. Phase 0:风险登记和依赖确认
|
||||
|
||||
- [x] 确认 Turso / libSQL 的当前 Beta 风险:API 稳定性、SLA、区域、限流、连接数、备份能力、最大数据库大小。
|
||||
- 当前记录为运行手册中的准入风险:真实 remote / replica / synced 作为云端同步/备份/多设备准备层后续启用;默认保留 `sqlite` fallback,不把本地文件真相迁入 Turso。
|
||||
- 当前记录为运行手册中的准入风险:真实 remote / replica / synced 作为云端同步/备份/多设备准备层后续启用;不保留 mnote-web runtime SQLite fallback,也不把本地文件真相迁入 Turso。
|
||||
- [x] 固定 Rust `libsql` crate 版本,不使用浮动主分支依赖。
|
||||
- [x] 确认当前 8 个 migration SQL 在 libSQL 上可执行。
|
||||
- [x] 明确 remote-only、local replica、pure local 三种模式的行为差异。
|
||||
- [x] 明确 token 管理规则:只走环境变量或本机 secret 文件,不写入仓库。
|
||||
- [x] 记录故障边界:认证失败、网络超时、Turso 5xx、限流、schema migration 失败、local replica 同步失败。
|
||||
- 代码已覆盖 missing/empty URL/token、timeout/rate-limit error mapping、invalid local path;真实 5xx/限流/同步失败属于后续云端验证,不阻塞本地 control-plane 功能测试。
|
||||
- [x] 决定第一阶段是否允许离线写入。当前策略:`sqlite` fallback 允许离线写入,`turso-local-replica` 模式下本地 replica 可离线读、上线后同步;纯 `turso` remote 模式禁止离线写入,返回 `ServiceUnavailable`。
|
||||
- [x] 决定第一阶段是否允许离线写入。当前策略:`libsql-local` 允许本地离线写入;`turso-local-replica` 模式下本地 replica 可离线读、上线后同步;纯 `turso-remote` 模式依赖网络,失败时返回明确错误。
|
||||
|
||||
验收:
|
||||
|
||||
@@ -208,17 +211,17 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
- [x] `cargo test --manifest-path rust/Cargo.toml -p control-plane`
|
||||
- [x] `cargo test --manifest-path rust/Cargo.toml -p mnote-web` 已运行并确认本轮 control-plane 相关路径无新增失败。
|
||||
- 全量结果为 855 passed, 1 failed;唯一失败是既存 CSS 体积阈值 `ssr::styles::tests::mnote_css_is_reasonably_sized`,与 control-plane cutover 无关。
|
||||
- [x] `MNOTE_CONTROL_PLANE_BACKEND=sqlite npm run desktop:hot` 可正常启动。
|
||||
- [x] `MNOTE_CONTROL_PLANE_BACKEND=sqlite npm run desktop:hot` 已改为拒绝启动,防止运行时回落到 SQLite。
|
||||
|
||||
---
|
||||
|
||||
## 7. Phase 2:新增 TursoControlPlaneStore
|
||||
|
||||
目标:新增实现,不先删除 SQLite fallback。
|
||||
目标:新增 Turso/libSQL 实现,并在 mnote-web runtime 中移除 SQLite fallback。
|
||||
|
||||
- [x] `rust/crates/control-plane/Cargo.toml`
|
||||
- [x] 增加 `libsql` 依赖。
|
||||
- [x] 暂时保留 `rusqlite`,直到 SQLite fallback 和测试策略明确。
|
||||
- [x] 保留 `rusqlite` 仅服务 control-plane-admin 迁移/导出和 Rust 测试隔离。
|
||||
- [x] `rust/crates/control-plane/src/turso.rs`
|
||||
- [x] 新增 `TursoControlPlaneStore`。
|
||||
- [x] 支持 remote URL + token。
|
||||
@@ -290,7 +293,7 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
- [x] 支持 `MNOTE_CONTROL_PLANE_BACKEND` 和 Turso env。
|
||||
- [x] `scripts/desktop-hot.js`
|
||||
- [x] 明确 dev 默认后端。
|
||||
- [x] 防止多个 hot 进程同时写同一个 SQLite fallback。
|
||||
- [x] 防止 hot/prod runtime 再次写 SQLite control-plane 文件。
|
||||
- [x] `scripts/dev-hot.js`
|
||||
- [x] 同步 backend 环境变量策略。
|
||||
|
||||
@@ -324,7 +327,7 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
- [ ] 后续云端验证:空 Turso dev DB 可通过初始化命令启动 MNote(缺 `MNOTE_TURSO_DATABASE_URL` / `MNOTE_TURSO_AUTH_TOKEN`)。
|
||||
- [ ] 初始化后的测试账号 `mnote.e2e@example.com` 可登录。
|
||||
- 后续云端验证:同 Turso dev DB/token 缺
|
||||
- [x] 本地 SQLite fallback 可从 libSQL local 导出恢复;真实 Turso remote 导出待 dev DB/token。
|
||||
- [x] libSQL/Turso 可导出为 SQLite 备份;SQLite 备份不再作为 mnote-web runtime fallback。
|
||||
|
||||
---
|
||||
|
||||
@@ -365,13 +368,13 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
|
||||
## 11. Phase 6:默认切换
|
||||
|
||||
> dev-hot.js 已默认使用 `MNOTE_CONTROL_PLANE_BACKEND=libsql-local`(`dev-hot.js:75`);desktop-hot.js 仍默认 `sqlite`。remote / local-replica / synced 更接近云端同步、备份和多设备准备层,不影响当前 local-first 本地功能测试;它们作为后续云端验收项,不再阻塞本轮本地 libSQL cutover。
|
||||
> dev-hot.js 已默认使用 `MNOTE_CONTROL_PLANE_BACKEND=libsql-local`(`dev-hot.js:75`);desktop-hot.js 已默认 `libsql-local`。remote / local-replica / synced 更接近云端同步、备份和多设备准备层,不影响当前 local-first 本地功能测试;它们作为后续云端验收项,不再阻塞本轮本地 libSQL cutover。
|
||||
>
|
||||
> 2026-07-03 决策更新:真实 Turso dev DB/token 已可用,但云端价值对当前阶段有限;本轮只把它接入为“可验证但不默认”的控制面 dev target,不把 remote / replica 作为 local-first 功能测试前置条件。
|
||||
|
||||
- [x] dev 环境先切到本地 libSQL:
|
||||
- [x] `MNOTE_CONTROL_PLANE_BACKEND=libsql-local`
|
||||
- [x] 不依赖 Turso dev DB/token,覆盖本地 store、migration、script discipline 和 SQLite fallback。
|
||||
- [x] 不依赖 Turso dev DB/token,覆盖本地 store、migration 和 script discipline,不保留 runtime SQLite fallback。
|
||||
- [x] Turso dev DB/token 已配置为本机私有 env:
|
||||
- [x] `/mnt/Data1T/Mnote_data/control-plane/turso.env`
|
||||
- [x] 文件权限 `0600`
|
||||
@@ -395,7 +398,7 @@ MNOTE_TURSO_LOCAL_REPLICA_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plan
|
||||
|
||||
默认切换验收:
|
||||
|
||||
- [x] 本地 cutover:`libsql-local` store 行为、migration、admin roundtrip、seed helper、SQLite fallback 已验证。
|
||||
- [x] 本地 cutover:`libsql-local` store 行为、migration、admin roundtrip、seed helper 和 SQLite runtime 拒绝路径已验证。
|
||||
- [ ] 云端 cutover:`npm run desktop:hot` 使用 `turso-local-replica` 启动后可登录测试账号。
|
||||
- [ ] 云端 cutover:`/api/auth/session` 返回真实 Turso control-plane session。
|
||||
- [x] 文件树和文档页仍读取本地工作区文件。
|
||||
@@ -530,36 +533,36 @@ OpenHub 切换门槛:
|
||||
## 13. Phase 8:文档和运行手册
|
||||
|
||||
- [x] 更新 `ARCHITECTURE.md`:
|
||||
- [x] control-plane 从 `Rust SQLite` 改为 `Turso/libSQL + SQLite fallback`。
|
||||
- [x] control-plane 从 `Rust SQLite` 改为 `Turso/libSQL-only runtime`。
|
||||
- [x] 明确本地 `.md` 文件仍是正文真相。
|
||||
- [x] 更新 `CURRENT_ARCHITECTURE.md`:
|
||||
- [x] 当前默认后端、fallback、回滚方式。
|
||||
- [x] 当前默认后端、SQLite admin-only 边界和备份恢复方式。
|
||||
- [x] 更新 `AGENTS.md`:
|
||||
- [x] 常用命令增加 Turso backend env。
|
||||
- [x] smoke 基线说明不得直写 control-plane DB。
|
||||
- [x] 新增运行手册:`docs/operations/control-plane-turso.md`
|
||||
- [x] dev token 配置。
|
||||
- [x] dev DB reset / init。
|
||||
- [x] 本地 fallback 备份。
|
||||
- [x] SQLite 导出备份。
|
||||
- [x] Turso 到 SQLite 回滚。
|
||||
- [x] 故障排查。
|
||||
|
||||
---
|
||||
|
||||
## 14. 回滚策略
|
||||
## 14. 备份与恢复策略
|
||||
|
||||
必须保留一键回滚:
|
||||
hard cutover 后不再保留 mnote-web runtime SQLite 一键回滚;SQLite 只作为导出备份格式和迁移源。
|
||||
|
||||
```text
|
||||
MNOTE_CONTROL_PLANE_BACKEND=sqlite
|
||||
MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.db
|
||||
```bash
|
||||
cargo run --manifest-path rust/Cargo.toml -p control-plane --bin control-plane-admin -- \
|
||||
export-target-to-sqlite --backend libsql-local --output /tmp/mnote-control-plane-backup.sqlite --backup-existing
|
||||
```
|
||||
|
||||
回滚 checklist(仅在 remote / replica / synced 成为默认后需要完整执行):
|
||||
恢复 checklist(仅在 remote / replica / synced 成为默认后需要完整执行):
|
||||
|
||||
- [ ] 切回前从 Turso 导出最新 control-plane dump。
|
||||
- [ ] 备份当前本地 SQLite。
|
||||
- [ ] 导入 Turso 数据到本地 SQLite 或接受 dev 数据重置。
|
||||
- [ ] 从 Turso/libSQL 导出最新 control-plane 备份。
|
||||
- [ ] 保留当前 libSQL/Turso 目标库快照。
|
||||
- [ ] 将备份重新导入新的 libSQL/Turso 目标库,或接受 dev 数据重置。
|
||||
- [ ] 重启 `npm run desktop:hot`。
|
||||
- [ ] 验证登录、workspace、授权、sidebar、navigation、AI policy。
|
||||
- [ ] Turso DB 保留至少 30 天用于审计和补导出。
|
||||
@@ -580,7 +583,7 @@ MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.d
|
||||
8. 补并发写和故障注入测试。
|
||||
9. 再决定是否迁 OpenHub 会话库。
|
||||
|
||||
不建议一开始就删除 `SqliteControlPlaneStore`。它仍是本地 fallback、回滚、测试隔离和故障排查的必要后门。
|
||||
不删除 `SqliteControlPlaneStore` 实现本身,但它不再是 mnote-web runtime fallback;仅作为迁移源、导出备份格式和 Rust 测试隔离。
|
||||
|
||||
---
|
||||
|
||||
@@ -611,14 +614,14 @@ MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.d
|
||||
| `rust/crates/control-plane/src/migrations.rs` | 新增 `run_libsql_migrations()` async 迁移入口 |
|
||||
| `rust/crates/control-plane/src/error.rs` | 新增 `From<libsql::Error>`,区分 constraint/unauthorized/storage |
|
||||
| `rust/crates/control-plane/src/lib.rs` | 导出 `TursoControlPlaneConfig`、`TursoControlPlaneMode`、`TursoControlPlaneStore` |
|
||||
| `rust/crates/control-plane/src/sqlite.rs` | 保持 SqliteControlPlaneStore 作为本地 fallback,store 行为测试覆盖完整 |
|
||||
| `rust/crates/control-plane/src/sqlite.rs` | 保持 SqliteControlPlaneStore 作为 admin 迁移/导出与测试隔离实现,store 行为测试覆盖完整 |
|
||||
| `rust/crates/control-plane/src/model.rs` | 新增字段 |
|
||||
| `rust/crates/mnote-web/src/app.rs` | `AppState.control_plane: Arc<dyn ControlPlaneStore>` 解耦;5 种后端 factory |
|
||||
| `rust/crates/mnote-web/src/local_folder_watcher_registry.rs` | 构造器改为 `Arc<dyn ControlPlaneStore>` |
|
||||
| `rust/crates/mnote-web/src/routes/local_folder_source.rs` | best-effort 索引刷新适配 trait object |
|
||||
| `rust/crates/mnote-web/src/ssr/pages/admin.rs` | 适配 control-plane 后端状态展示 |
|
||||
| `docs/operations/control-plane-turso.md` | 新增 Turso/libSQL control-plane 初始化、迁移、回滚、验证和故障边界运行手册 |
|
||||
| `scripts/desktop-hot.js` | 支持 `MNOTE_CONTROL_PLANE_BACKEND`,默认 `sqlite` |
|
||||
| `scripts/desktop-hot.js` | 支持 `MNOTE_CONTROL_PLANE_BACKEND`,默认 `libsql-local`,拒绝 `sqlite` |
|
||||
| `scripts/dev-hot.js` | 支持 `MNOTE_CONTROL_PLANE_BACKEND`,默认 `libsql-local` |
|
||||
| `scripts/prod-build-start.js` | 支持 `MNOTE_CONTROL_PLANE_BACKEND` |
|
||||
| `scripts/lib/control-plane-test-env.js` | 新增统一 test env builder,支持全部后端模式 |
|
||||
@@ -626,7 +629,7 @@ MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.d
|
||||
| `scripts/task-control-plane-admin-libsql-roundtrip-smoke.js` | 新增 admin CLI SQLite → libSQL local → SQLite roundtrip smoke |
|
||||
| `rust/crates/mnote-web/src/routes/dev_seed.rs` | 新增 `/api/dev/seed` gated endpoint 与 targeted tests |
|
||||
| 17 个 smoke 脚本 | 从直接 `sqlite3` CLI 写库改为 API `POST /api/dev/seed` 和 `buildControlPlaneTestEnv` |
|
||||
| `ARCHITECTURE.md` | control-plane 从 `Rust SQLite` 改为 `Turso/libSQL + SQLite fallback` |
|
||||
| `ARCHITECTURE.md` | control-plane 从 `Rust SQLite` 改为 `Turso/libSQL-only runtime` |
|
||||
| `CURRENT_ARCHITECTURE.md` | 记录 ControlPlaneStore trait 架构、后端选择、env vars |
|
||||
| `AGENTS.md` | 常用命令增加 Turso backend env;脚本纪律不得直写 control-plane DB |
|
||||
|
||||
@@ -648,10 +651,10 @@ MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.d
|
||||
- 真实 Turso dev DB / token 未配置,remote、local-replica、synced、真实 5xx/限流/断网/同步失败作为后续云端验收
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web` 全量 855 passed, 1 failed(既有 CSS 体积阈值失败);本轮 control-plane 相关 targeted tests 已通过
|
||||
|
||||
**回滚命令(保持 Phase 1 前行为):**
|
||||
```text
|
||||
MNOTE_CONTROL_PLANE_BACKEND=sqlite
|
||||
MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.db
|
||||
**备份命令(SQLite 只作为导出格式,不作为 runtime rollback):**
|
||||
```bash
|
||||
cargo run --manifest-path rust/Cargo.toml -p control-plane --bin control-plane-admin -- \
|
||||
export-target-to-sqlite --backend libsql-local --output /tmp/mnote-control-plane-backup.sqlite --backup-existing
|
||||
```
|
||||
|
||||
**local-first 不迁移边界(已验证未踏入):**
|
||||
@@ -684,7 +687,7 @@ MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.d
|
||||
- `node --test scripts/desktop-hot.test.js` ✅
|
||||
- `node scripts/task-dev-hot-plan-test.js` ✅
|
||||
- `node scripts/task-control-plane-admin-libsql-roundtrip-smoke.js` ✅
|
||||
- `MNOTE_CONTROL_PLANE_BACKEND=sqlite npm run desktop:hot` 可正常启动 ✅
|
||||
- `MNOTE_CONTROL_PLANE_BACKEND=sqlite npm run desktop:hot` 拒绝启动 ✅
|
||||
|
||||
**已知失败:**
|
||||
|
||||
@@ -697,13 +700,9 @@ MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.d
|
||||
- `desktop:hot` 默认后端切换到 `turso-local-replica` 暂缓;当前本地开发默认已可用 `libsql-local`
|
||||
- OpenHub SQLite 会话库迁移(Phase 7)属于 OpenHub 独立评估,不绑定 Rust control-plane 切换
|
||||
|
||||
**SQLite fallback / rollback 环境:**
|
||||
**SQLite 导出备份 / admin-only 环境:**
|
||||
|
||||
```text
|
||||
# 回滚到纯本地 SQLite(保持 Phase 1 前行为)
|
||||
MNOTE_CONTROL_PLANE_BACKEND=sqlite
|
||||
MNOTE_CONTROL_PLANE_DB_PATH=/mnt/Data1T/Mnote_data/control-plane/control-plane.db
|
||||
|
||||
# 本地 libSQL 模式(无远程依赖,推荐开发测试)
|
||||
MNOTE_CONTROL_PLANE_BACKEND=libsql-local
|
||||
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
# 3-26 Local Folder FileChangeService 统一文件变化事件层 v1
|
||||
|
||||
> 创建时间:2026-06-29
|
||||
> 状态:`done`
|
||||
> Owner:`03-rust-web` 主控,协同 `05-editor-mainline`、`07-ai`
|
||||
> 来源:当前 Page AI / OpenHub / opencode 写入本地 `.md` 后,左侧当前文档页不会自动更新,整页刷新后才读取到新内容。
|
||||
> 参考:`reference-code/sidex-main` 的 watcher / `EventThrottler` / reaction 模型;`reference-code/vscode` 的 `FileService.onDidFilesChange`、Explorer 延迟刷新、TextFile editor reload/focus 补偿模型。
|
||||
|
||||
## 1. 结论
|
||||
|
||||
不要新增 OpenHub / opencode 专属“写完刷新当前页”链路,也不要完整重建 VSCode FileService。
|
||||
|
||||
本轮应把 MNote 现有 local-folder watcher / browser event bus 收口成一个轻量 `FileChangeService`:
|
||||
|
||||
- Rust watcher、OpenHub/opencode changedFiles、legacy Page AI tool receipt、MNote 自身保存回声都只能作为统一文件变化事件的输入源。
|
||||
- 当前文档、filetree、page tree、knowledge source status 只消费统一 FileChange reaction,不直接解析 provider 事件。
|
||||
- 当前打开文档只有在非 dirty / 非 saving / 非 recent input 时自动拉取最新 Page Aggregate;dirty 时进入外部冲突状态,不覆盖编辑器内容。
|
||||
- filetree/page tree 继续走现有 projection / watch_batch / sidebar refresh 链路,但由统一事件层负责去抖、合并、self-write 抑制和最小刷新范围。
|
||||
|
||||
## 2. 背景与现状
|
||||
|
||||
当前已经存在文件变化监控链路,不应另起一套:
|
||||
|
||||
1. `rust/crates/mnote-web/src/local_folder_watcher_registry.rs`
|
||||
- 使用 `notify` 监听 local folder root。
|
||||
- 生成 `relativePath`、`documentId`、`eventKind`、`revision`、`observedFileVersion`、`bufferFileVersion`、`selfWriteEcho`。
|
||||
- 调用 `BufferStore.mark_external_modified_if_version_changed_with_outcome` 区分自写回声与外部修改。
|
||||
- 同步触发本地搜索索引刷新;该职责保持在 Rust 侧,不迁到浏览器 runtime。
|
||||
|
||||
2. `rust/crates/mnote-web/src/routes/local_folder_events.rs`
|
||||
- `/api/local-folder/events?treeLive=true` 复用同一个 watcher。
|
||||
- 聚合 watcher payload,输出 `watch_batch`。
|
||||
|
||||
3. `rust/crates/mnote-web/browser/local-folder-event-bus-runtime.js`
|
||||
- 复用单连接,转发 `watch_batch`。
|
||||
- 派发 `mnote:local-folder:watch-batch`、`tree:local-folder-watch-batch`、`mnote:local-folder:filetree-parent-changed`、`mnote:local-folder:document-changed`、`mnote:local-folder:resource-changed`、`mnote:local-folder:sidebar-refresh-requested`。
|
||||
|
||||
4. `rust/crates/mnote-web/browser/document-session-runtime.js`
|
||||
- 消费 `mnote:local-folder:document-changed`。
|
||||
- 命中当前 document session 后调用 `refreshSessionFromExternalChange`,再 fetch `/api/page-aggregate/...`。
|
||||
|
||||
问题说明:整页刷新后能看到 agent 写入内容,说明 Page Aggregate 读盘链路有效;当前缺口在“真实文件变化 / agent 写入结果 -> 当前打开 document session reaction”。
|
||||
|
||||
## 3. 参考实现判断
|
||||
|
||||
### 3.1 Sidex 可移植部分
|
||||
|
||||
从 `reference-code/sidex-main` 可迁移的是模型,不是完整代码:
|
||||
|
||||
- `crates/sidex-workspace/src/watcher.rs`
|
||||
- `notify` 事件包装。
|
||||
- path 级别 pending map 去重。
|
||||
- ignore pattern。
|
||||
|
||||
- `crates/sidex-workspace/src/file_watcher_events.rs`
|
||||
- `EventThrottler.record_own_write(path)` 抑制自身写入回声。
|
||||
- `ingest()` 合并短时间多事件。
|
||||
- `flush()` 把文件事件映射成 reaction。
|
||||
- dirty 文件 + external modified 进入冲突 reaction。
|
||||
|
||||
- `src/vs/workbench/contrib/files/browser/explorerService.ts`
|
||||
- Explorer 用延迟合并处理文件事件。
|
||||
- 编辑中不刷新树。
|
||||
- 只在展开父节点受影响时刷新。
|
||||
|
||||
### 3.2 Sidex 复核补充
|
||||
|
||||
Sidex 的关键收益不是更换 watcher,而是在 watcher 与消费者之间增加稳定事件质量层:
|
||||
|
||||
| 能力 | Sidex 模型 | MNote 当前状态 | 本设计处理 |
|
||||
|---|---|---|---|
|
||||
| 两段去重 | watcher pending map + `EventThrottler` | 只有 `local_folder_events.rs` 的 120ms 连续 payload 聚合 | 增加 per-root FileChange throttler,跨批次合并同一路径 |
|
||||
| own-write 处理 | `record_own_write` + 过期窗口 | Rust 已产出 `selfWriteEcho`,但 browser consumer 仍要各自判断 | 统一 reaction 层消费 `selfWriteEcho`,避免各消费方重复判断 |
|
||||
| Reaction | `FileWatcherReaction` | 前端各 runtime 自己解析 JSON | 增加统一 reaction schema,再兼容输出旧事件 |
|
||||
| dirty 冲突 | throttler/working copy 层决定 reload 或 conflict | `BufferStore` 有版本仲裁,前端也有 conflict panel 壳 | reaction 层统一把 dirty/saving/recent input 转成冲突 |
|
||||
| ignore | watcher 层忽略构建产物 | 主要覆盖 `.mnote` / 索引相关路径 | 后续补 `.git`、`node_modules`、`target` 等 |
|
||||
|
||||
### 3.3 不直接移植部分
|
||||
|
||||
- 不迁 Tauri `app.emit("watch-batch")`,MNote 使用 Rust Web SSE/WS/browser event bus。
|
||||
- 不迁 VSCode extension host `FileSystemWatcher`。
|
||||
- 不让 watcher 携带文件内容;MNote 正文读取仍走 Page Aggregate。
|
||||
- 不把 Sidex 的 settings/extensions reload 规则原样迁入;MNote 后续可补自己的偏好、插件、knowledge source reaction。
|
||||
- 不新增第二条 broadcast/event stream。统一层必须复用现有 `/api/local-folder/events` 与 browser event bus。
|
||||
|
||||
## 4. 目标架构
|
||||
|
||||
### 4.1 事件输入
|
||||
|
||||
统一输入源:
|
||||
|
||||
| 来源 | 入口 | 说明 |
|
||||
|---|---|---|
|
||||
| Rust local-folder watcher | `/api/local-folder/events?treeLive=true` | 主来源,真实磁盘文件变化 |
|
||||
| MNote 编辑器保存 | `BufferStore` / existing save completion event | 用于 self-write echo 抑制和保存态同步 |
|
||||
| OpenHub / opencode | changedFiles adapter | 只能转成 FileChange input,不直接刷新 UI |
|
||||
| legacy Page AI tool receipt | `agentRunReceipt.changedFiles` | 只能转成 FileChange input |
|
||||
| local upload / resource save | existing local-upload event | 资源 tab 与附件场景 |
|
||||
|
||||
### 4.2 标准事件
|
||||
|
||||
统一标准事件命名为 `mnote.file_change.v1`:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "mnote.file_change.v1",
|
||||
"rootUri": "file:///...",
|
||||
"workspaceId": "local-ws:...",
|
||||
"relativePath": "新页面145827/新页面145827.md",
|
||||
"documentId": "local-md:...",
|
||||
"resourceKind": "markdown",
|
||||
"changeType": "modified",
|
||||
"fileVersion": "local-md:...",
|
||||
"source": "watcher|editor_save|openhub|opencode|agent_receipt|local_upload",
|
||||
"selfWriteEcho": false,
|
||||
"observedFileVersion": "...",
|
||||
"bufferFileVersion": "...",
|
||||
"createdAt": 1782738295000
|
||||
}
|
||||
```
|
||||
|
||||
批量事件命名为 `mnote.file_change_batch.v1`:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "mnote.file_change_batch.v1",
|
||||
"rootUri": "file:///...",
|
||||
"workspaceId": "local-ws:...",
|
||||
"source": "watcher",
|
||||
"changes": [],
|
||||
"affectedParents": [],
|
||||
"revision": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Reaction
|
||||
|
||||
统一 reaction 命名为 `mnote.file_change_reaction.v1`:
|
||||
|
||||
| Reaction | 触发条件 | 消费方 |
|
||||
|---|---|---|
|
||||
| `refresh_current_document` | 当前打开文档 clean 且命中 changed `.md` | `document-session-runtime.js` |
|
||||
| `external_conflict_current_document` | 当前打开文档 dirty/saving/recent input 且同文件外部变化 | `document-conflict-panel-runtime.js` / session status |
|
||||
| `refresh_filetree_parents` | 创建/删除/重命名/父目录变化 | `sidebar-tree-live-apply-runtime.js` / filetree |
|
||||
| `refresh_page_tree_projection` | `.md` 页面新增/删除/重命名 | sidebar page tree |
|
||||
| `refresh_resource_tab` | 当前资源文件变化 | `document-resource-tab-runtime.js` |
|
||||
| `refresh_knowledge_source_status` | knowledge source 文件变化 | knowledge RAG status |
|
||||
| `ignore_self_write_echo` | `selfWriteEcho=true` 且 session 已处于 saved/clean | 不触发 UI 刷新 |
|
||||
|
||||
### 4.4 Browser 侧 owner
|
||||
|
||||
当前最小实现放在 `rust/crates/mnote-web/browser/local-folder-event-bus-runtime.js`,作为现有 event bus 的升级,不新增并行 runtime。
|
||||
|
||||
后续如果文件变大,再拆为:
|
||||
|
||||
- `filechange-event-runtime.js`:标准化、去抖、source adapter、diagnostics。
|
||||
- `filechange-reaction-runtime.js`:reaction 计算。
|
||||
- `local-folder-event-bus-runtime.js`:只负责 transport 与连接复用。
|
||||
|
||||
Rust 侧后续可增加类型化结构,但不要求首批大改广播签名:
|
||||
|
||||
- Phase A 保持 SSE JSON 兼容,补齐字段透传与 browser 标准化。
|
||||
- Phase B/C 再评估是否在 `mnote-web/src/file_change_service.rs` 引入 `FileChangeEvent` / `FileChangeReaction` Rust 类型,并由现有 `Value` 序列化出口兼容输出。
|
||||
- throttler 必须是 per-root 实例,放在 `LocalFolderWatchChannel` 或等价 root-scoped state 中,避免不同 workspace 互相去重。
|
||||
|
||||
## 5. 核心流程
|
||||
|
||||
### 5.1 外部编辑器 / agent 直接写 `.md`
|
||||
|
||||
1. 文件系统变化进入 Rust watcher。
|
||||
2. watcher 更新 BufferStore 并生成标准字段。
|
||||
3. `/api/local-folder/events?treeLive=true` 输出 `watch_batch`。
|
||||
4. browser event bus 标准化为 `mnote.file_change_batch.v1`。
|
||||
5. reaction 层判断命中当前 document session。
|
||||
6. 当前文档 clean:调用 `refreshSessionFromExternalChange` 拉 Page Aggregate 并派发 tiptap 内容。
|
||||
7. 当前文档 dirty:设置 `external-change-conflict`,不覆盖内容。
|
||||
|
||||
### 5.2 OpenHub / opencode 写当前页面
|
||||
|
||||
1. provider 事件或 session changedFiles 进入 adapter。
|
||||
2. adapter 将绝对路径解析为当前 `rootUri` 下 `relativePath`。
|
||||
3. 生成 `mnote.file_change_batch.v1`,source 为 `openhub` 或 `opencode`。
|
||||
4. 后续与 watcher 事件走同一 reaction。
|
||||
5. 如果真实 watcher 随后也到达,同一路径同版本事件被去抖合并,不重复刷新。
|
||||
|
||||
### 5.3 MNote 自己保存
|
||||
|
||||
1. 保存链记录 write intent / save operation / fileVersion。
|
||||
2. watcher 回声到达时带出 `selfWriteEcho=true`。
|
||||
3. FileChangeService 产出 `ignore_self_write_echo` 或只同步 clean metadata,不触发外部冲突。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不引入周期轮询或 `setInterval` 检测文件是否变化。
|
||||
- 不把 OpenHub/opencode changedFiles 当作事实源;它只是 watcher 补偿输入。
|
||||
- 不让前端直接读取本地文件正文;正文仍走 Page Aggregate。
|
||||
- 不复活 BlockNote / Convex / Next 作为当前刷新主链。
|
||||
- 不把 filetree/page tree 的 projection 真相搬到前端。
|
||||
- 不在 AI runtime 中直接调用 document editor 私有刷新逻辑。
|
||||
|
||||
## 7. 实施 Checklist
|
||||
|
||||
### Batch A:现状断点复核
|
||||
|
||||
- [x] 复核 `LocalFolderWatcherRegistry` 产出的 `selfWriteEcho`、`observedFileVersion`、`bufferFileVersion` 是否完整进入 `watch_batch.changedPaths[]`。
|
||||
- [x] 复核 `/api/local-folder/events?treeLive=true` 是否对当前 workspace root 只有一个 watcher 连接。
|
||||
- [x] 复核 `local-folder-event-bus-runtime.js` 是否把 `documentId/eventKind/selfWriteEcho/fileVersion` 保留给 `document-session-runtime.js`。
|
||||
- [x] 复核 `document-session-runtime.js` 的命中条件:`documentId`、`relativePath`、`rootUri`、`workspaceId`。
|
||||
- [x] 复核当前截图场景中 Page AI/OpenHub 写入后是否有真实 watcher 事件、synthetic event、或两者都没有。
|
||||
- [x] 复核是否存在跨批次重复事件导致同一路径多次 refresh。
|
||||
- [x] 复核 ignore 规则是否放过 `.git`、`node_modules`、`target`、`dist` 等高频目录。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] 产出一份断点日志或 smoke 证据,能指出“事件未到达 / 字段丢失 / session 未命中 / dirty 拦截 / refresh 失败”的具体层级。
|
||||
|
||||
### Batch B:标准 FileChange batch
|
||||
|
||||
- [x] 在 browser event bus 增加标准化函数:从 watcher `watch_batch`、synthetic changedFiles、legacy receipt 转成 `mnote.file_change_batch.v1`。
|
||||
- [x] 统一绝对路径到 root-relative path 的解析规则,路径不在 rootUri 下时丢弃并记录 diagnostics。
|
||||
- [x] 合并同一 `rootUri + relativePath` 的短时间重复事件。
|
||||
- [x] 保留 `source`、`eventKind`、`selfWriteEcho`、`observedFileVersion`、`bufferFileVersion`。
|
||||
- [x] 暴露 diagnostics:最近 batch source、changed count、dropped count、last reaction。
|
||||
- [x] 保持旧 `watch_batch.changedPaths/affectedParents/eventKinds` 字段兼容,新增标准 batch 不破坏旧消费者。
|
||||
- [x] 如 Rust 侧改类型,保证最终 SSE/JS JSON 格式仍兼容旧 smoke。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] `task540-local-folder-event-bus-single-connection-smoke.js` 仍通过。
|
||||
- [x] 新增静态 smoke 确认 `mnote.file_change_batch.v1`、`selfWriteEcho`、`emitSyntheticWatchBatch` adapter 存在。
|
||||
|
||||
### Batch C:Reaction 分发
|
||||
|
||||
- [x] 新增 reaction 计算:当前文档 clean -> `refresh_current_document`。
|
||||
- [x] dirty/saving/recent input -> `external_conflict_current_document`。
|
||||
- [x] self-write echo -> `ignore_self_write_echo`。
|
||||
- [x] filetree affected parents -> `refresh_filetree_parents`。
|
||||
- [x] resource tab -> `refresh_resource_tab`。
|
||||
- [x] 保持旧事件兼容输出:`tree:local-folder-watch-batch`、`mnote:local-folder:document-changed`、`mnote:local-folder:resource-changed`。
|
||||
- [x] 不在 Rust watcher 回调中做新的重 I/O;reaction 计算应基于已有事件字段和 session state。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] 外部写当前 `.md` 后,当前 tiptap 页面无需浏览器刷新显示新文本。
|
||||
- [x] 当前页面 dirty 时,外部写同一 `.md` 不覆盖编辑器,显示 external conflict。
|
||||
- [x] filetree/page tree 仍按局部 parent 刷新,不整页 reload。
|
||||
|
||||
### Batch D:AI source adapter 收口
|
||||
|
||||
- [x] OpenHub/opencode changedFiles 只调用统一 FileChange input,不直接调用 `refreshPrimaryDocument`。
|
||||
- [x] legacy Page AI receipt 只调用统一 FileChange input。
|
||||
- [x] 移除或降级旧的 provider 专属直接刷新路径;保留兼容时必须标注为 fallback diagnostics。
|
||||
- [x] changedFiles 为空时不伪造 diff;只依赖真实 watcher 或 explicit changedFiles。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] agent/OpenHub/opencode 写当前 `.md` 后,当前页面自动更新。
|
||||
- [x] 若 provider 不报 changedFiles,真实 watcher 仍能驱动更新。
|
||||
- [x] 若 watcher 漏报但 provider 报 changedFiles,synthetic FileChange 可驱动同一 reaction。
|
||||
|
||||
### Batch E:回归与归档
|
||||
|
||||
- [x] 新增或更新 smoke:
|
||||
- [x] `task-local-folder-filechange-current-document-refresh-smoke.js`
|
||||
- [x] `task-page-ai-filechange-current-document-refresh-smoke.js`
|
||||
- [x] `task-local-folder-filechange-dirty-conflict-smoke.js`
|
||||
- [x] 运行 `cargo test -p mnote-web` 中相关单测。
|
||||
- [x] 运行 local-folder event bus、filetree lazy loading、page aggregate refresh 相关 smoke。
|
||||
- [x] `codegraph sync .` 并确认状态。
|
||||
- [x] 完成后把本设计从 `process/` 移到 `done/`,记录验证命令和剩余风险。
|
||||
|
||||
## 8. 风险与防线
|
||||
|
||||
- 事件重复:watcher 与 synthetic changedFiles 同时到达。防线:按 `rootUri + relativePath + fileVersion/source window` 去抖。
|
||||
- 自写回声误判外部冲突。防线:保留并消费 `selfWriteEcho`、write intent、save operation。
|
||||
- dirty 编辑器被覆盖。防线:dirty/saving/recent input 一律进入 conflict reaction。
|
||||
- filetree/page tree 首屏被 debug/diagnostics 挤占。防线:diagnostics 只写 DOM data attribute 或折叠开发态。
|
||||
- provider 事件格式不稳定。防线:OpenHub/opencode 只做 adapter,不作为系统事实源。
|
||||
- 前端拼第二份页面真相。防线:正文刷新只拉 Page Aggregate,不从 changedFiles 读取内容。
|
||||
- 第二条事件总线造成状态分裂。防线:只复用现有 `/api/local-folder/events` 与 browser event bus,不新增并行 broadcast。
|
||||
- throttler 误合并不同 workspace 事件。防线:per-root/per-workspace scoped state。
|
||||
|
||||
## 9. 验收定义
|
||||
|
||||
本设计完成的最低验收:
|
||||
|
||||
1. 真实外部写当前 `.md`,当前页面自动刷新正文。
|
||||
2. OpenHub/opencode/agent 写当前 `.md`,当前页面自动刷新正文。
|
||||
3. dirty 当前页遇到外部写入时不覆盖,显示冲突状态。
|
||||
4. 文件树/页面树仍使用同一 event bus 更新,没有新增并行轮询。
|
||||
5. OpenHub/opencode 没有直接持有编辑器刷新逻辑,只作为统一 FileChange input。
|
||||
|
||||
|
||||
## 10. 完成记录
|
||||
|
||||
完成时间:2026-06-29
|
||||
|
||||
实现摘要:
|
||||
|
||||
- Rust watcher batch 继续复用 `/api/local-folder/events?treeLive=true`,并在 `changedPaths[]` 保留 `documentId`、`eventKind`、`selfWriteEcho`、`observedFileVersion`、`bufferFileVersion`、`lastWriteIntentId`、`lastSaveOperationId`。
|
||||
- Browser `local-folder-event-bus-runtime.js` 升级为轻量 FileChangeService:标准化 `mnote.file_change_batch.v1`,80ms 去抖合并,输出 `mnote:file-change-batch` / `mnote:file-change-reaction`,并兼容旧 `tree:local-folder-watch-batch` / `mnote:local-folder:document-changed` / `resource-changed`。
|
||||
- `document-session-runtime.js` 统一消费 `selfWriteEcho`,clean 当前页走 Page Aggregate 自动刷新,dirty/saving/recent input 当前页进入 external conflict,不覆盖编辑器内容。
|
||||
- OpenHub/opencode/legacy receipt changedFiles 收口到 `emitChangedFiles` / FileChange input,不再直接调用 `refreshPrimaryDocument` 作为 provider 专属刷新链路。
|
||||
- watcher ignore 扩展到 `.git`、`node_modules`、`target`、`dist`、`build` 等高频目录。
|
||||
|
||||
验证命令:
|
||||
|
||||
- `node --check rust/crates/mnote-web/browser/local-folder-event-bus-runtime.js && node --check rust/crates/mnote-web/browser/document-session-runtime.js && node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js && node --check scripts/task540-local-folder-event-bus-single-connection-smoke.js && node --check scripts/task779-openhub-file-edit-document-pane-refresh-smoke.js && node --check scripts/task-local-folder-filechange-current-document-refresh-smoke.js && node --check scripts/task-local-folder-filechange-dirty-conflict-smoke.js && node --check scripts/task-page-ai-filechange-current-document-refresh-smoke.js`
|
||||
- `cargo fmt --check -p mnote-web`
|
||||
- `cargo test -p mnote-web local_folder -- --nocapture`
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3010 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3010 node scripts/task540-local-folder-event-bus-single-connection-smoke.js`
|
||||
- `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3010 node scripts/task-local-folder-filechange-current-document-refresh-smoke.js`
|
||||
- `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3010 node scripts/task-local-folder-filechange-dirty-conflict-smoke.js`
|
||||
- `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3010 node scripts/task-page-ai-filechange-current-document-refresh-smoke.js`
|
||||
- `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3010 node scripts/task776-openhub-changed-files-bridge-smoke.js`(live probe 因缺少 task774 真实文件编辑结果跳过,静态检查通过)
|
||||
|
||||
剩余风险:
|
||||
|
||||
- `task-local-folder-filechange-current-document-refresh-smoke.js` 与 dirty conflict smoke 复用现有 task436 主流程,覆盖真实外部写入、AI 写入、dirty conflict、rename/delete conflict,但不是独立新实现的更细粒度脚本。
|
||||
- FileChange reaction 当前通过 document session debug snapshot 判断当前页状态,并已暴露 `data-mnote-file-change-service-session-api` diagnostics;后续可把这层升级成正式 session registry API。
|
||||
- `mnote:page-ai-tool-write-completed` 仍保留为 changedFiles 为空时的 legacy fallback,不作为 OpenHub/opencode/receipt changedFiles 主路径。
|
||||
@@ -4,7 +4,7 @@
|
||||
>
|
||||
> 当前状态:`DONE`
|
||||
>
|
||||
> 2026-06-25 历史口径:本文记录 LightRAG provider 当时已完成的历史事实,不再代表当前新主线。当前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 将默认知识库 provider 切到 WeKnora;LightRAG 仅保留为 `lightrag_legacy` / fallback / 迁移对照边界。
|
||||
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文重新作为当前默认知识库 provider 的完成基线;此前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 中将 WeKnora 设为默认 provider 的口径已标记 stale,仅保留为历史设计、参考实现或备用 provider 边界。
|
||||
>
|
||||
> Owner:07-ai / knowledge-rag / plugin-ui / external-provider
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
>
|
||||
> 状态:`done`
|
||||
>
|
||||
> 2026-06-25 历史口径:本文记录 LightRAG hardening 当时已完成的历史事实,不再代表当前新主线。当前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 将默认知识库 provider 切到 WeKnora;本文只作为 `lightrag_legacy` / fallback / 迁移对照边界。
|
||||
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文重新作为当前默认 LightRAG provider 的 hardening 基线;此前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 中将 WeKnora 设为默认 provider 的口径已标记 stale。
|
||||
>
|
||||
> Owner:07-ai / knowledge-rag / 03-rust-web / plugin-ui
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
>
|
||||
> 当前状态:`DONE`
|
||||
>
|
||||
> 2026-06-25 历史口径:本文记录 LightRAG 图片 OCR 与搜索召回链路当时已完成的历史事实,不再代表当前新主线。当前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 将默认知识库 provider 切到 WeKnora;本文只作为 `lightrag_legacy` / fallback / 迁移对照边界。
|
||||
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文重新作为当前默认 LightRAG provider 的图片 OCR 与搜索召回链路 hardening 基线;此前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 中将 WeKnora 设为默认 provider 的口径已标记 stale。
|
||||
>
|
||||
> Owner:07-ai / knowledge-rag / local-search
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
>
|
||||
> 当前状态:`done`
|
||||
>
|
||||
> 2026-06-25 历史口径:本文记录 LightRAG DOCX 引用与定位合同当时已完成的历史事实,不再代表当前新主线。当前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 将默认知识库 provider 切到 WeKnora;本文只作为 `lightrag_legacy` / fallback / 迁移对照边界。
|
||||
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文重新作为当前默认 LightRAG provider 的 DOCX 引用与定位合同基线;此前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 中将 WeKnora 设为默认 provider 的口径已标记 stale。
|
||||
>
|
||||
> Owner:07-ai / knowledge-rag / 03-rust-web / office-preview
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
>
|
||||
> 当前状态:`done`
|
||||
>
|
||||
> 2026-06-25 历史口径:本文记录 LightRAG native DOCX sidecar 当时已完成的历史事实,不再代表当前新主线。当前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 将默认知识库 provider 切到 WeKnora;本文只作为 `lightrag_legacy` / fallback / 迁移对照边界。
|
||||
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文重新作为当前默认 LightRAG provider 的 native DOCX sidecar 定位基线;此前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 中将 WeKnora 设为默认 provider 的口径已标记 stale。
|
||||
>
|
||||
> Owner:07-ai / knowledge-rag / LightRAG native parser / office-preview
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
>
|
||||
> 当前状态:`DONE`
|
||||
>
|
||||
> 2026-06-25 历史口径:本文中 LightRAG 相关能力记录的是当时已完成的历史事实,不再代表当前新主线。当前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 将默认知识库 provider 切到 WeKnora;本文只作为 run/config/facade 参考与 `lightrag_legacy` 迁移对照边界。
|
||||
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文中的 Knowledge RAG facade 重新作为当前 LightRAG 默认 provider 的 run/config/facade 参考;此前 `7-68 OpenHub + WeKnora + MNote Page AI 深度融合` 中将 WeKnora 设为默认 provider 的口径已标记 stale。
|
||||
>
|
||||
> Owner:07-ai / Page AI runtime / Hermes client runs / Reasonix ACP / Knowledge RAG facade
|
||||
>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 7-18 [process] local-first agent 文件编辑控制面 v1
|
||||
|
||||
> 2026-06-28 覆盖说明:本文中的 Hermes / Reasonix 主路径口径已退为历史参考。当前 Page AI / agent runtime 主线以 OpenHub / opencode / WeKnora 融合设计为准,见 `7-68-openhub-weknora-mnote-deep-fusion-v1.md` 与 `7-69-weknora-native-embed-mnote-openhub-governance-v1.md`。本文仅保留 local-first 文件授权、watcher 同步和冲突模型的参考价值。
|
||||
> 2026-07-03 口径更新:本文中的 Hermes / Reasonix 主路径口径已退为历史参考。当前 Page AI / agent runtime 主线以 OpenHub / native agent + LightRAG + Turso/libSQL 为准;`7-68-openhub-weknora-mnote-deep-fusion-v1.md` 与 `7-69-weknora-native-embed-mnote-openhub-governance-v1.md` 的 WeKnora 默认 provider 口径已标记 stale。本文仅保留 local-first 文件授权、watcher 同步和冲突模型的参考价值。
|
||||
|
||||
> 创建时间:2026-05-22
|
||||
>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
> 状态补充(2026-06-25):本稿冻结为 opencode runtime / 官方 opencode WebUI iframe fallback 参考;Page AI 产品主线由 `7-68-openhub-weknora-mnote-deep-fusion-v1.md` 接管。官方 iframe 仅在 OpenHub AI 面板不可用、调试 opencode 原生行为或做回归对照时启用,不再作为默认产品路径。
|
||||
> 状态补充(2026-07-03):本稿冻结为 opencode runtime / 官方 opencode WebUI iframe fallback 参考;Page AI 产品主线为 OpenHub / native agent + LightRAG + Turso/libSQL。官方 iframe 仅在 OpenHub AI 面板不可用、调试 opencode 原生行为或做回归对照时启用,不再作为默认产品路径。此前指向 `7-68-openhub-weknora-mnote-deep-fusion-v1.md` 的 WeKnora 默认 provider 口径已标记 stale。
|
||||
|
||||
# 7-65 [process] Page AI opencode WebUI embed v1
|
||||
|
||||
> 创建时间:2026-06-23
|
||||
>
|
||||
> 当前状态:`FROZEN / 官方 opencode iframe fallback,OpenHub + WeKnora + MNote 深度融合为主线`
|
||||
> 当前状态:`FROZEN / 官方 opencode iframe fallback,OpenHub + LightRAG + MNote 深度融合为主线`
|
||||
>
|
||||
> Owner:07-ai / Page AI / opencode WebUI embed
|
||||
>
|
||||
@@ -21,11 +21,11 @@
|
||||
|
||||
## 1. 核心结论
|
||||
|
||||
废弃 MNote Page AI 自研 provider 接入与 Board-first 产品壳后,本稿曾把 **opencode 官方 runtime + 官方 WebUI** 作为主线;截至 `7-68`,该方案降级为 fallback。
|
||||
废弃 MNote Page AI 自研 provider 接入与 Board-first 产品壳后,本稿曾把 **opencode 官方 runtime + 官方 WebUI** 作为主线;当前该方案已降级为 fallback,默认产品主线为 OpenHub / native agent + LightRAG + Turso/libSQL。
|
||||
|
||||
```text
|
||||
fallback MNote Page AI = MNote 宿主壳 + opencode 官方 WebUI iframe + MNote 打开/刷新/上下文集成
|
||||
主线 Page AI = MNote 宿主壳 + OpenHub AI 面板 + OpenHub FastAPI/Redis/opencode client + WeKnora
|
||||
主线 Page AI = MNote 宿主壳 + OpenHub AI 面板 + OpenHub FastAPI/Redis/opencode client + LightRAG
|
||||
opencode = agent runtime / HTTP server / OpenAPI / SSE / SDK / opencodego provider
|
||||
```
|
||||
|
||||
@@ -53,7 +53,7 @@ MNote 不再直接维护 Reasonix / ZCode / Hermes / Chat-only / Board worker
|
||||
|
||||
### 2.2 仍可保留的边界
|
||||
|
||||
- `mnote.doc.*`、`mnote.block.*`、旧 LightRAG legacy facade 等工具可继续作为 fallback/compat 边界;默认知识库工具应改向 WeKnora。
|
||||
- `mnote.doc.*`、`mnote.block.*` 等工具可继续作为 fallback/compat 边界;默认知识库工具走 LightRAG + provider-neutral `mnote.knowledge_rag.*` facade。
|
||||
- Agent Board 仍可作为外部 workflow/QA/review 系统,不再作为 Page AI 默认聊天后端。
|
||||
- CodexMobile 可保留为备选 spike 或体验对照,不作为当前实现目标。
|
||||
|
||||
@@ -61,7 +61,7 @@ MNote 不再直接维护 Reasonix / ZCode / Hermes / Chat-only / Board worker
|
||||
|
||||
### 3.1 MNote 只做四件事
|
||||
|
||||
1. **上下文**:当前页、选区、页面标题、真实 `.md` 路径、workspaceId、allowed roots、WeKnora 引用;旧 LightRAG 只作为 legacy/fallback 口径保留。
|
||||
1. **上下文**:当前页、选区、页面标题、真实 `.md` 路径、workspaceId、allowed roots、LightRAG 引用;WeKnora 只作为历史设计、参考实现或备用 provider 边界。
|
||||
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。
|
||||
@@ -204,7 +204,7 @@ MNote open Page AI
|
||||
- 当前页真实 Markdown 路径。
|
||||
- selection 文本。
|
||||
- allowed roots 与读写权限。
|
||||
- WeKnora 引用摘要;旧 LightRAG 引用只作为 legacy/fallback。
|
||||
- LightRAG 引用摘要;WeKnora 引用只作为备用 provider / 历史参考。
|
||||
- 当前任务约束:优先编辑 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 边界。
|
||||
@@ -524,7 +524,7 @@ $MNOTE_DATA/users/<user-id>/opencode/<workspace-hash>/state/opencode/
|
||||
- 第一阶段只做 Chat/Session/Message/Diff/File open 五件事,不引入 OpenHub 登录/admin/知识库/team。
|
||||
### 11.9 OpenHub 知识库实现与 WeKnora 对照
|
||||
|
||||
用户最新判断:Page AI 要做深度融合;MNote 当前已放弃 LightRAG,知识库方向原计划是 WeKnora。因此需要单独核验 OpenHub 自带知识库是否能替代 WeKnora。
|
||||
2026-07-03 口径回正:Page AI 要做深度融合;当前默认知识库主线为 LightRAG + OpenHub tool facade,WeKnora 仅保留为历史设计、参考实现或备用 provider。因此本节只作为当时 OpenHub 自带知识库能力对照记录。
|
||||
|
||||
源码核验结论:**OpenHub 自带知识库是轻量 SQLite 文本知识库,不是完整 RAG/知识库底座;适合复用 UI、API 形状和 prompt 注入链路,不建议替代 WeKnora。**
|
||||
|
||||
@@ -542,7 +542,7 @@ OpenHub 知识库真实实现:
|
||||
|
||||
- WeKnora 应继续作为 MNote 长期知识库底座候选:负责文档解析、索引、检索、召回、引用、权限过滤与跨文档问答。
|
||||
- OpenHub 知识库不应替代 WeKnora;它更像“用户短记忆/轻量知识片段/企业公告文本”的 fallback。
|
||||
- 最优融合方式是 **OpenHub Knowledge UI + MNote Rust 知识库 adapter + WeKnora provider**:前端交互复用 OpenHub,后端接口形状兼容 OpenHub,但真正的 ingestion/search/citation 由 MNote 调 WeKnora。
|
||||
- 当前融合方式是 **OpenHub AI 面板 + MNote Rust 知识库 adapter + LightRAG provider**:OpenHub 负责 Page AI 对话与工具事件承载,真正的 ingestion/search/citation 由 MNote 通过 provider-neutral facade 调 LightRAG。
|
||||
- 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 映射,不能只塞纯文本。
|
||||
|
||||
@@ -551,5 +551,5 @@ OpenHub 知识库真实实现:
|
||||
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 / 用户手工短知识”,但不能称为默认知识库主线。
|
||||
4. 保留 OpenHub 轻量知识库作为“未配置 LightRAG 时的 local fallback / 用户手工短知识”,但不能称为默认知识库主线。
|
||||
5. 新设计稿应明确:`OpenHub UI` 负责交互,`MNote control-plane` 负责用户与权限,`WeKnora` 负责知识库索引与检索,`opencode` 负责 agent 执行。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 7-66 [process] Page AI opencode-native UI v1
|
||||
|
||||
> 状态补充(2026-06-25):本稿冻结为 native UI fallback / debug receipt 参考;Page AI 产品主线由 `7-68-openhub-weknora-mnote-deep-fusion-v1.md` 接管。不得继续扩写 MNote 自研聊天框作为默认 Page AI UI。
|
||||
> 状态补充(2026-07-03):本稿冻结为 native UI fallback / debug receipt 参考;Page AI 产品主线为 OpenHub / native agent + LightRAG + Turso/libSQL。不得继续扩写 MNote 自研聊天框作为默认 Page AI UI。此前指向 `7-68-openhub-weknora-mnote-deep-fusion-v1.md` 的 WeKnora 默认 provider 口径已标记 stale。
|
||||
|
||||
> 创建时间:2026-06-24
|
||||
>
|
||||
@@ -10,12 +10,12 @@
|
||||
|
||||
## 1. 结论
|
||||
|
||||
本稿提出的 MNote-native opencode UI 方向已冻结为 fallback。当前主线不是在 MNote 内复刻 opencode 消息流、tool UI、permission UI,也不是回到官方 opencode WebUI iframe 默认路径,而是 `7-68` 定义的 **MNote 薄宿主壳 + OpenHub AI 面板 + OpenHub FastAPI/Redis/opencode client + WeKnora**。
|
||||
本稿提出的 MNote-native opencode UI 方向已冻结为 fallback。当前主线不是在 MNote 内复刻 opencode 消息流、tool UI、permission UI,也不是回到官方 opencode WebUI iframe 默认路径,而是 **MNote 薄宿主壳 + OpenHub AI 面板 + OpenHub FastAPI/Redis/opencode client + LightRAG**。
|
||||
|
||||
因此 `7-66` 只作为反例和 fallback 保留:native message timeline / composer / permission cards 可以临时作为 debug receipt、故障诊断或 OpenHub 不可用时的降级参考,但不得成为 Page AI 主 UI,也不继续扩写自研聊天框。
|
||||
|
||||
```text
|
||||
主线 Page AI = MNote host chrome + OpenHub AI 面板 + OpenHub FastAPI/Redis/opencode client + WeKnora
|
||||
主线 Page AI = MNote host chrome + OpenHub AI 面板 + OpenHub FastAPI/Redis/opencode client + LightRAG
|
||||
native fallback = MNote debug receipt + opencode session/event/diff 摘要
|
||||
iframe fallback = 7-65 官方 opencode WebUI iframe
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
> 状态补充(2026-06-25):本稿为 OpenHub 初步融合稿,已被 `7-68-openhub-weknora-mnote-deep-fusion-v1.md` 覆盖。路线从“抽取/重写 OpenHub 组件 + MNote Rust adapter”改为“嵌入 OpenHub AI 面板 + 保留 OpenHub FastAPI/Redis/opencode client + WeKnora 工具/知识底座”。
|
||||
> 状态补充(2026-07-03):本稿为 OpenHub 初步融合稿,OpenHub AI 面板 / FastAPI / Redis / opencode client 方向仍保留参考价值;其中 WeKnora 默认知识库底座口径已被当前 OpenHub / native agent + LightRAG + Turso/libSQL 主线覆盖。`7-68-openhub-weknora-mnote-deep-fusion-v1.md` 已标记 stale,不再作为当前默认 provider 依据。
|
||||
|
||||
# 7-67 OpenHub Page AI 深度融合设计 v1
|
||||
|
||||
@@ -10,14 +10,14 @@ Owner:07-ai / mnote-web / control-plane
|
||||
|
||||
`7-65` 的 opencode 官方 WebUI iframe 路线验证了 opencode runtime、同源反代、session binding、文件打开/刷新等接缝,但 UI 深度融合受 iframe 与官方 WebUI 结构限制。用户目标已经调整为:尽量复用成熟社区项目 OpenHub 的前端、消息库、权限、知识库 UI 和 opencode 接入模式,把 MNote Page AI 做成类似 VSCode + Cline 的一体化侧边栏,而不是 MNote 自研一个简陋聊天框。
|
||||
|
||||
当前 Page AI 主路径以 `7-68` 为准:
|
||||
当前 Page AI 主路径已回正为 OpenHub / native agent + LightRAG + Turso/libSQL:
|
||||
|
||||
```text
|
||||
MNote Rust SSR / control-plane / local workspace
|
||||
-> OpenHub AI 面板(只暴露 AI 能力,裁剪非 AI 入口)
|
||||
-> OpenHub FastAPI + Redis + OpenHub SQLite session/skill/tool permission;MCP 由 MNote/WeKnora/opencode 工具配置承接
|
||||
-> OpenHub FastAPI + Redis + OpenHub SQLite session/skill/tool permission;MCP/tool 由 MNote/LightRAG/opencode 工具配置承接
|
||||
-> OpenHub opencode client / opencode serve runtime
|
||||
-> WeKnora knowledge provider(唯一知识库底座)
|
||||
-> LightRAG knowledge provider(默认知识库底座)
|
||||
```
|
||||
|
||||
`7-65` 降级为 opencode runtime / 官方 iframe fallback;`7-66` 降级为 native UI fallback;本稿的 Rust adapter / 自建 message store 路线也冻结为参考,不再作为执行主线。
|
||||
@@ -27,7 +27,7 @@ MNote Rust SSR / control-plane / local workspace
|
||||
- 不恢复 Reasonix / ZCode / Hermes / Board / CodexMobile 为 Page AI 默认后端。
|
||||
- 不新增 Page AI Leptos island;Page AI 仍属于 `mnote-web` host runtime 与普通前端资源。
|
||||
- 不直接引入 OpenHub 登录、admin、FileManager、KnowledgeManager 等非 AI 入口;用户、权限、workspace 真相归 MNote control-plane。
|
||||
- 不把 OpenHub 自带 SQLite 文本知识库当作 MNote 长期知识库底座;长期知识库 provider 是 WeKnora。
|
||||
- 不把 OpenHub 自带 SQLite 文本知识库当作 MNote 长期知识库底座;当前默认长期知识库 provider 是 LightRAG。
|
||||
- 不使用 `--dangerously-skip-permissions` 作为默认路径。
|
||||
- 不把 opencode 原生 session 列表裸露给前端;所有 session 必须绑定 MNote 用户、rootUri、page path。
|
||||
- 不通过高频轮询刷新消息或 changed files;优先使用 opencode event stream、MNote watcher、programmatic refresh。
|
||||
@@ -103,7 +103,7 @@ MNote 采用:
|
||||
|
||||
- UI:复用 OpenHub `KnowledgeManager` 交互。
|
||||
- API:提供 OpenHub-compatible `/api/knowledge/*`。
|
||||
- Provider:默认走 WeKnora。
|
||||
- Provider:默认走 LightRAG。
|
||||
- Fallback:未配置 WeKnora 时,可临时用 OpenHub-like SQLite 文本知识源做短知识。
|
||||
- Citation:WeKnora 结果必须保留 source/resource/open-reference 映射,方便点击回 MNote 文件或资源页。
|
||||
|
||||
@@ -137,12 +137,12 @@ MNote 采用:
|
||||
- [ ] DiffViewer 中所有 file path 点击都映射到 MNote resource/file open。
|
||||
- [ ] 文件路径必须限制在当前 rootUri / allowed roots 内。
|
||||
|
||||
### Phase E:Knowledge / WeKnora adapter
|
||||
### Phase E:Knowledge / LightRAG adapter
|
||||
|
||||
- [ ] 兼容 OpenHub `knowledgeService` 的 list/create/upload/search/stats API。
|
||||
- [ ] 后端默认调用 WeKnora ingestion/search。
|
||||
- [ ] 将 WeKnora 命中结果转成 OpenHub UI 可展示的 source/citation。
|
||||
- [ ] 未配置 WeKnora 时启用 SQLite fallback,并在 UI 明确标注 fallback。
|
||||
- [ ] 后端默认调用 LightRAG ingestion/search。
|
||||
- [ ] 将 LightRAG 命中结果转成 OpenHub UI 可展示的 source/citation。
|
||||
- [ ] 未配置 LightRAG 时启用 SQLite fallback,并在 UI 明确标注 fallback。
|
||||
|
||||
### Phase F:浏览器真实验证
|
||||
|
||||
@@ -151,7 +151,7 @@ MNote 采用:
|
||||
- [ ] Page AI 看到 OpenHub-derived UI,而不是旧简陋聊天框或官方 iframe。
|
||||
- [ ] 发送真实消息,模型能识别当前 rootUri 内文件。
|
||||
- [ ] 让 opencode 修改测试 Markdown,MNote changed chip 可打开,当前页可刷新。
|
||||
- [ ] Knowledge UI 可上传/检索,WeKnora provider 有真实命中与引用。
|
||||
- [ ] Knowledge UI 可上传/检索,LightRAG provider 有真实命中与引用。
|
||||
- [ ] 保存截图与 smoke 输出。
|
||||
|
||||
## 6. 验收标准
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# 7-68 OpenHub + WeKnora + MNote 深度融合执行 Checklist v1
|
||||
# [stale] 7-68 OpenHub + WeKnora + MNote 深度融合执行 Checklist v1
|
||||
|
||||
状态:process
|
||||
状态:stale / reference-only(不再作为当前 process 主线)
|
||||
Owner:07-ai / mnote-web / control-plane / knowledge-provider
|
||||
日期:2026-06-25
|
||||
|
||||
2026-07-03 stale 说明:当前 runtime 口径已改为 OpenHub / native agent + LightRAG + Turso/libSQL。本文 checklist 中将 WeKnora 定义为默认知识库 provider、LightRAG 退为 legacy 的条目不再作为当前主线执行;当前知识库主路径以 LightRAG provider 和 MNote provider-neutral `mnote.knowledge_rag.*` facade 为准。WeKnora 仅保留为历史设计、参考实现或备用 provider 边界。
|
||||
|
||||
上位设计:`design/07-ai/process/7-68-openhub-weknora-mnote-deep-fusion-v1.md`
|
||||
|
||||
## 0. 执行原则
|
||||
@@ -220,7 +222,7 @@ P1 WeKnora tool 记录:
|
||||
- Hermes tool manifest 与 dispatch 已新增 `mnote.weknora.search/open_reference/list_sources/get_source_status`。
|
||||
- Tool wrapper 要求 `rootUri` 以及 `scope/allowlist/allowedRoots/aiAccessScope/sourcePaths` 之一,否则返回 `mnote_weknora_scope_required`。
|
||||
- 当前通过 MNote HTTP facade 调用 WeKnora;没有把 WeKnora API key 暴露到浏览器/localStorage,也没有默认开放 Python mcp-server 写能力。
|
||||
- 2026-06-26 task782 可写 worker:OpenHub backend 新增 MNote host mode `/api/mnote/tools/call` 只读 facade,通过 MNote 反代受控 headers 与 server-side `X-MNote-Session-Cookie` 调 `/api/hermes/tools/mnote/call`,支持 `mnote.weknora.search/open_reference/list_sources/get_source_status`;OpenHub 不接触 WeKnora API key,也不把 MNote cookie 暴露给前端 localStorage。`stream.py` 在 MNote host mode 下对知识库/资料库/WeKnora/citation/source 类问题先调用 MNote WeKnora search,把结构化 tool result 注入 OpenHub/opencode prompt,并向 OpenHub 前端推送 `mnote.weknora.search` tool event。
|
||||
- 2026-06-28 主控收口:MNote 新增中性 tool executor alias `/api/mnote/tools/{manifest,call,audit}`,OpenHub backend 已改为调用 `/api/mnote/tools/call`;旧 `/api/hermes/tools/mnote/*` 只保留 legacy alias。OpenHub host mode 只读 facade 通过 MNote 反代受控 headers 与 server-side `X-MNote-Session-Cookie` 调 MNote tool executor,支持 `mnote.weknora.search/open_reference/list_sources/get_source_status`;OpenHub 不接触 WeKnora API key,也不把 MNote cookie 暴露给前端 localStorage。`stream.py` 在 MNote host mode 下对知识库/资料库/WeKnora/citation/source 类问题先调用 MNote WeKnora search,把结构化 tool result 注入 OpenHub/opencode prompt,并向 OpenHub 前端推送 `mnote.weknora.search` tool event。
|
||||
- 2026-06-26 task782 可写 worker:OpenHub 前端 `AssistantMessage` 从 WeKnora tool output 的 `citations/uiCitations/references` 渲染 `data-mnote-openhub-citation` 可点击 citation,点击经 `postMNoteOpenReference` 发 `mnote:open-reference`;MNote Page AI host 已接受 `openhub-citation` 并复用 document pane open-file bridge 打开本地页面。新增 `scripts/task782-openhub-weknora-tool-citation-bridge-smoke.js` 覆盖后端 facade、scope/cookie/API key 边界、stream auto tool bridge、citation button 与 postMessage runtime。主控复核:OpenHub `/openapi.json` 已暴露 `/api/mnote/tools/call`,`node scripts/task782-openhub-weknora-tool-citation-bridge-smoke.js` 通过;边界仍标 partial,不宣称 opencode 原生 MCP 自动选 tool 完成。
|
||||
|
||||
## 11. P1 验收与证据
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# 7-68 OpenHub + WeKnora + MNote Page AI 嵌入式集成设计 v1
|
||||
# [stale] 7-68 OpenHub + WeKnora + MNote Page AI 嵌入式集成设计 v1
|
||||
|
||||
状态:process
|
||||
状态:stale / reference-only(不再作为当前 process 主线)
|
||||
Owner:07-ai / mnote-web / control-plane / knowledge-provider
|
||||
日期:2026-06-25
|
||||
|
||||
执行说明:本文是 Page AI / OpenHub / WeKnora 深度融合的当前主设计,不表示代码已完成。具体落地按本文第 10 节与配套清单 `design/07-ai/process/7-68-openhub-weknora-mnote-deep-fusion-checklist-v1.md` 执行;若实现中发现源码能力与本文假设不一致,优先遵循“保留原系统已完成能力,只在产品真相冲突处做最小胶水/嫁接/裁剪”的总原则。
|
||||
2026-07-03 stale 说明:本文仍保留 Page AI / OpenHub / WeKnora 融合的历史设计上下文,但当前 runtime 口径已改为 OpenHub / native agent + LightRAG + Turso/libSQL。本文中将 WeKnora 定义为默认知识库 provider、LightRAG 退为 legacy 的内容不再作为当前主线执行;当前知识库主路径以 LightRAG provider 和 MNote provider-neutral `mnote.knowledge_rag.*` facade 为准。WeKnora 仅保留为历史设计、参考实现或备用 provider 边界。
|
||||
|
||||
执行说明:本文不再是当前主设计,仅作为 WeKnora 方向历史方案和 OpenHub 集成参考。当前实现与后续新增能力不得从本文推导出“WeKnora 是默认 provider”或“LightRAG 已退出主线”的口径。
|
||||
|
||||
## 0. 结论先行
|
||||
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
# 7-69 Page AI Pi-first Lab 设计 v1
|
||||
|
||||
状态:process
|
||||
Owner:07-ai / mnote-web / control-plane
|
||||
日期:2026-07-03
|
||||
|
||||
## 1. 结论
|
||||
|
||||
本稿确认一个新方向:**保留 OpenHub 作为当前生产基线,同时新开 Pi-first Page AI Lab,验证 Pi 是否能成为 MNote-native Page AI runtime**。
|
||||
|
||||
这里的 Pi 指当前官方仓库 `earendil-works/pi` / 旧 `badlogic/pi-mono`,当前 npm 主包已迁到 `@earendil-works/*`。本轮重新拉取的源码证据位于:
|
||||
|
||||
- `/mnt/Data1T/mnote/tmp/pi-research-20260703/pi-mono`
|
||||
- 当前核验 commit:`23d14626 fix(ai): rotate stale Codex websocket sessions`
|
||||
- 关键包:`@earendil-works/pi-coding-agent`、`@earendil-works/pi-ai`、`@earendil-works/pi-agent-core`、`@earendil-works/pi-tui`
|
||||
|
||||
Pi 不是 OpenHub 的直接替代品,但它是当前最值得优先 spike 的底层候选。原因是:Pi 已经具备 coding agent、RPC、session tree、tool event、extension / skill / package、provider auth、多模型和可扩展工具体系;这些比通用 UI 框架更贴近 MNote 的 local-first Page AI 目标。
|
||||
|
||||
硬边界:
|
||||
|
||||
- 不替换当前 OpenHub 主线。
|
||||
- 不把 Pi Lab 作为默认 Page AI 入口。
|
||||
- 不把 Pi JSONL session 直接当作 MNote 长期会话真相。
|
||||
- 不绕过 MNote `AiAccessScope` / allowed roots / Turso control-plane。
|
||||
- 不把第三方 Pi package 当成已验收能力;每个第三方包必须通过本地 smoke 后才能纳入。
|
||||
|
||||
## 2. 背景
|
||||
|
||||
当前 Page AI 主线已经回正为:
|
||||
|
||||
```text
|
||||
MNote local-first workspace
|
||||
-> OpenHub / native agent
|
||||
-> LightRAG knowledge provider
|
||||
-> Turso/libSQL control-plane
|
||||
```
|
||||
|
||||
OpenHub 的价值在于完整体验:Web 聊天 UI、消息流、tool card、diff/history、session/API、opencode/native agent 接入和当前已经实现的 MNote proxy/bootstrap/scope 注入。
|
||||
|
||||
但 OpenHub 仍有长期问题:
|
||||
|
||||
- UI、后端、session、agent runtime 是完整应用栈,MNote 深度融合需要持续 patch。
|
||||
- MNote 的权限、workspace、page selection、citation、watcher、Turso 审计只能通过宿主桥接注入。
|
||||
- OpenHub 自身会话与 MNote control-plane 的边界需要长期维护。
|
||||
|
||||
Pi 的候选价值不是“立刻复刻 OpenHub 并替换”,而是验证另一条更原生的路线:
|
||||
|
||||
```text
|
||||
MNote host chrome / sidebar
|
||||
-> Pi Lab gateway(MNote 托管)
|
||||
-> Pi RPC / SDK agent runtime
|
||||
-> MNote tools: current page / selection / allowed roots / LightRAG / file patch
|
||||
-> Turso/libSQL: session binding / tool audit / AI policy
|
||||
-> MNote watcher: editor refresh / changed file chips
|
||||
```
|
||||
|
||||
## 3. 本轮取证结论
|
||||
|
||||
### 3.1 官方已证实能力
|
||||
|
||||
从当前源码与 npm registry 核验,Pi 官方具备:
|
||||
|
||||
- `RPC mode`:`pi --mode rpc`,stdin/stdout JSONL 协议,适合由 `mnote-web` 或 sidecar 托管。
|
||||
- `SDK`:`createAgentSession()` / `AgentSession`,适合 Node/TS 应用直接嵌入。
|
||||
- `session persistence`:JSONL session,支持 tree / resume / fork / clone / import / export / share。
|
||||
- `agent event stream`:message / tool execution / compaction / queue 等事件。
|
||||
- `tool interception`:`beforeToolCall` / `afterToolCall`,以及 extension 事件 `tool_call` 可阻断或改写。
|
||||
- `extensions / skills / Pi packages`:`pi install npm:...`、`git:...`、本地路径,支持工具、命令、提示词、主题和热加载。
|
||||
- `project trust`:控制是否加载项目 `.pi` settings/resources/extensions。
|
||||
- `provider auth`:多 provider、API key、OAuth、订阅登录和 credential store。
|
||||
- `default file tools`:`read`、`write`、`edit`、`bash`。
|
||||
- `diff`:`edit` 工具与 session/export 链路能表达代码/文件变更。
|
||||
- `file watcher`:官方 extension 示例中已有 file-trigger / watcher 类能力。
|
||||
- `containerization guidance`:官方明确建议容器、VM、OpenShell 等外部隔离方案。
|
||||
|
||||
### 3.2 官方缺口
|
||||
|
||||
Pi 官方也明确不是完整 Web Agent 平台:
|
||||
|
||||
- 没有内建多租户 / 多用户控制面。
|
||||
- 没有内建 MNote 所需的 workspace membership / share grants / AI policy。
|
||||
- 没有内建 RAG / knowledge provider。
|
||||
- 没有内建 MCP,官方文档明确倾向通过 extension/package 自行接入。
|
||||
- 没有内建权限 sandbox;官方说明 Pi 默认继承启动进程权限。
|
||||
- 没有面向 MNote 的 tool audit 数据库。
|
||||
- 没有稳定的 HTTP 业务 API;官方主路径是 CLI / RPC / SDK。
|
||||
|
||||
### 3.3 已核验第三方 / 周边能力
|
||||
|
||||
以下包真实存在,但只作为候选,不直接成为主线依赖:
|
||||
|
||||
- `@earendil-works/pi-web-ui`:npm 上存在,提供 AI chat Web Components;当前 Git 主仓未见同名源码包,需要单独拉包验证。
|
||||
- `@hyperspaceng/neural-web-ui`:基于 Pi AI 的 Web UI 组件。
|
||||
- `pi-mcp-adapter`:Pi 的 MCP adapter extension。
|
||||
- `pi-workspace-history`:workspace undo/redo、snapshot、rewind 类扩展。
|
||||
- `pi-hermes-memory`:SQLite FTS5 memory / session search / secret scanning。
|
||||
- `@tintinweb/pi-subagents`:Pi subagents 扩展。
|
||||
- `safe-coder`:permission gate / protected paths 类安全 guardrail package。
|
||||
- `@cmdctrl/pi`:Pi coding agent daemon。
|
||||
- `pi-codemode-extension`:第三方 code mode / exec tool 扩展。
|
||||
|
||||
未证实:
|
||||
|
||||
- `Craft` 作为 Pi 官方或主流第三方项目名。
|
||||
- `Echo` 作为 Pi 官方或主流第三方项目名。
|
||||
- “Pi 官方一键提供 OpenHub 全套 Web UI + 多租户 + RAG + audit”的说法。
|
||||
|
||||
## 4. 目标与非目标
|
||||
|
||||
### 4.1 目标
|
||||
|
||||
Pi Lab 的目标是验证 MNote 原生融合,不是做第二个 OpenHub:
|
||||
|
||||
- 在 MNote sidebar 中跑出可用聊天 UI。
|
||||
- 通过 Pi RPC / SDK 驱动真实 agent turn。
|
||||
- 让 Pi 只通过 MNote tool facade 访问页面和文件。
|
||||
- 用 `AiAccessScope` / allowed roots 拦截文件读写和 bash。
|
||||
- LightRAG 作为 Pi tool 返回 citation,并能 open-reference 回 MNote。
|
||||
- session binding、tool event、diff summary 写入 Turso/libSQL。
|
||||
- 文件修改后由 MNote watcher 刷新 tiptap 页面。
|
||||
- 与 OpenHub 并存,可在同一测试页面对照体验。
|
||||
|
||||
### 4.2 非目标
|
||||
|
||||
- 不做 OpenHub 迁移。
|
||||
- 不做完整多用户 SaaS agent 平台。
|
||||
- 不复刻 OpenHub admin / login / FileManager / KnowledgeManager。
|
||||
- 不引入第二套 RAG provider。
|
||||
- 不把 Pi 的 `bash` 默认开放给普通 Page AI。
|
||||
- 不把 Pi package 的第三方能力不经审计直接暴露给用户。
|
||||
- 不把 UI 做成长期自研简陋聊天框;若 `pi-web-ui` 不够,继续评估 OpenHub UI 或 assistant-ui/CopilotKit。
|
||||
|
||||
## 5. 方案对比
|
||||
|
||||
| 方向 | 适合作用 | 优点 | 风险 | 当前定位 |
|
||||
|---|---|---|---|---|
|
||||
| OpenHub 继续收口 | 当前生产基线 | 体验完整,已接入 MNote scope / proxy / LightRAG 口径 | 长期 patch 成本,融合边界厚 | P0 |
|
||||
| Pi-first Lab | 新 Page AI 原生 runtime | coding agent + RPC + session tree + tool hook + package 生态 | Web/multi-tenant/RAG/audit 需 MNote 补齐 | P1 首选 spike |
|
||||
| OpenHub UI + Pi backend | 复用 OpenHub UI,替换 runtime | UI 完整,后端更可控 | OpenHub 前后端格式耦合可能很深 | P1b |
|
||||
| opencode serve direct | 去掉 OpenHub,保留 opencode | runtime 成熟 | UI/tool card/diff/session 需自研 | P2 |
|
||||
| assistant-ui / CopilotKit / AG-UI | 前端交互协议和 UI | 应用内 AI UI 生态强 | 不是 coding agent runtime,需要另配后端 agent | P2 UI 备选 |
|
||||
| LangGraph / Mastra / VoltAgent | agent/workflow 编排 | 编排能力强 | 与本地文件 patch / coding agent 体验不如 Pi 直接 | P2 backend 备选 |
|
||||
| 纯 Rust 自研 | 终极控制 | 单栈、权限可深度类型化 | agent/provider/tool/UI 工作量过大 | 暂不做 |
|
||||
|
||||
当前推荐:
|
||||
|
||||
```text
|
||||
P0:OpenHub + LightRAG + Turso 继续作为默认 Page AI
|
||||
P1:Pi-first Page AI Lab 做隔离 spike
|
||||
P1b:如果 pi-web-ui 不达标,评估 OpenHub UI + Pi backend
|
||||
P2:assistant-ui/CopilotKit/AG-UI 只作为 UI/协议备选,不作为 runtime 首选
|
||||
```
|
||||
|
||||
## 6. 目标架构
|
||||
|
||||
### 6.1 Runtime 拆分
|
||||
|
||||
```text
|
||||
Browser sidebar
|
||||
-> MNote Page AI host chrome
|
||||
-> Pi Lab panel(feature flag)
|
||||
-> /api/page-ai/pi/*
|
||||
|
||||
mnote-web
|
||||
-> auth/session truth: mnote_session
|
||||
-> AiAccessScope / allowed roots
|
||||
-> Pi runtime manager
|
||||
-> Turso/libSQL run + tool audit
|
||||
-> LightRAG provider facade
|
||||
-> local folder watcher / page aggregate refresh
|
||||
|
||||
Pi runtime
|
||||
-> RPC subprocess or Node SDK sidecar
|
||||
-> MNote-owned tool set
|
||||
-> no direct unbounded workspace access
|
||||
```
|
||||
|
||||
### 6.2 入口
|
||||
|
||||
第一阶段只加内部入口:
|
||||
|
||||
- URL:`/page-ai/pi` 或 sidebar 内 `data-page-ai-provider="pi-lab"`。
|
||||
- Env:`MNOTE_PAGE_AI_PI_LAB=1`。
|
||||
- 不在普通用户 UI 暴露“切换 OpenHub fallback”入口。
|
||||
- OpenHub 入口保持默认。
|
||||
|
||||
### 6.3 Pi 托管方式
|
||||
|
||||
Spike 阶段优先使用 RPC 子进程:
|
||||
|
||||
```text
|
||||
mnote-web -> spawn pi --mode rpc --session-dir <mnote scoped dir> --no-approve
|
||||
```
|
||||
|
||||
理由:
|
||||
|
||||
- Rust 侧容易托管进程、stdin/stdout、超时和 abort。
|
||||
- 不要求 MNote 主进程引入 Node SDK 生命周期。
|
||||
- 更接近未来 sandbox / container 边界。
|
||||
|
||||
备选:
|
||||
|
||||
```text
|
||||
mnote-web -> Node sidecar -> @earendil-works/pi-coding-agent SDK
|
||||
```
|
||||
|
||||
使用条件:
|
||||
|
||||
- 需要更细粒度地替换 Pi session manager / credential store。
|
||||
- 需要把 Turso 直接接进 AgentSession 生命周期。
|
||||
- RPC 事件格式无法满足 UI 或审计需求。
|
||||
|
||||
### 6.4 Session 存储
|
||||
|
||||
Pi JSONL 只能作为 runtime log / recovery source,不作为 MNote 会话主真相。
|
||||
|
||||
MNote control-plane 新增或复用 provider-neutral 表:
|
||||
|
||||
- `ai_runs`
|
||||
- `ai_messages`
|
||||
- `ai_tool_events`
|
||||
- `ai_file_patches`
|
||||
- `ai_provider_sessions`
|
||||
|
||||
字段必须包含:
|
||||
|
||||
- `mnote_user_id`
|
||||
- `workspace_id`
|
||||
- `root_uri`
|
||||
- `page_path`
|
||||
- `provider = pi`
|
||||
- `pi_session_id`
|
||||
- `pi_session_file`
|
||||
- `allowed_roots_snapshot`
|
||||
- `model_provider`
|
||||
- `model_id`
|
||||
- `created_at` / `updated_at`
|
||||
|
||||
Pi 的 `sessionDir` 必须派生到 MNote 管控目录,例如:
|
||||
|
||||
```text
|
||||
<workspace>/.mnote/ai/pi-sessions/<mnote-user-key>/
|
||||
```
|
||||
|
||||
或私有用户目录:
|
||||
|
||||
```text
|
||||
~/.mnote/ai/pi-sessions/<workspace-id>/<mnote-user-key>/
|
||||
```
|
||||
|
||||
具体路径由 privacy / sharing 设计决定,但不能写到全局 `~/.pi/agent/sessions` 后失去 MNote 归属。
|
||||
|
||||
### 6.5 Tool set
|
||||
|
||||
第一阶段只开放 MNote 自有工具:
|
||||
|
||||
- `mnote.current_page.read`
|
||||
- `mnote.selection.read`
|
||||
- `mnote.allowed_roots.describe`
|
||||
- `mnote.local_file.read`
|
||||
- `mnote.local_file.patch`
|
||||
- `mnote.knowledge_rag.query`
|
||||
- `mnote.reference.open`
|
||||
- `mnote.tool_receipt.write`
|
||||
|
||||
默认禁用或替换 Pi 内置工具:
|
||||
|
||||
- 禁用原始 `bash`。
|
||||
- 禁用不受 MNote allowed roots 约束的 `read` / `write` / `edit`。
|
||||
- 如必须使用 Pi `edit` 能力,必须在 `beforeToolCall` / extension `tool_call` 中做路径规范化、root containment、dirty version 检查。
|
||||
|
||||
第二阶段可评估:
|
||||
|
||||
- `pi-mcp-adapter`:只允许 MNote 注册的 MCP server。
|
||||
- `pi-workspace-history`:用于 agent undo/rewind。
|
||||
- `safe-coder`:作为权限 guardrail 参考。
|
||||
- `pi-hermes-memory`:只作为 session search / memory 参考,不作为 MNote 长期记忆真相。
|
||||
|
||||
### 6.6 权限与审计
|
||||
|
||||
所有 tool call 必须走 MNote control-plane 审计:
|
||||
|
||||
```text
|
||||
tool_call_start
|
||||
-> normalize args
|
||||
-> allowed roots check
|
||||
-> dirty buffer / file version check
|
||||
-> policy decision
|
||||
-> execute
|
||||
-> result / diff summary
|
||||
-> watcher refresh
|
||||
-> audit receipt
|
||||
```
|
||||
|
||||
审计 receipt 至少包含:
|
||||
|
||||
- actor:`mnote_user_id`
|
||||
- workspace / rootUri
|
||||
- page path / selection snapshot id
|
||||
- tool name
|
||||
- normalized file path
|
||||
- allowed / denied
|
||||
- deny reason
|
||||
- diff summary
|
||||
- before / after file version
|
||||
- provider session id
|
||||
- model provider / model id
|
||||
|
||||
## 7. UI 设计原则
|
||||
|
||||
Pi Lab UI 目标是接近 OpenHub 的使用体验,但不强行复制其实现。
|
||||
|
||||
优先级:
|
||||
|
||||
1. 真实任务区:消息、输入框、tool card、changed files、citation。
|
||||
2. 运行状态:model、session、allowed roots、abort/retry。
|
||||
3. 诊断信息:折叠在 diagnostics,不默认挤占首屏。
|
||||
|
||||
第一阶段 UI 候选:
|
||||
|
||||
- `@earendil-works/pi-web-ui`:优先验证。
|
||||
- `@hyperspaceng/neural-web-ui`:若官方 web-ui 不稳定,作为对照。
|
||||
- OpenHub React Chat 子组件:作为视觉和交互 baseline。
|
||||
- assistant-ui / CopilotKit:只在 Pi UI 不达标时评估。
|
||||
|
||||
最低 UI 要求:
|
||||
|
||||
- 流式文本。
|
||||
- reasoning 折叠。
|
||||
- tool call start / update / end 卡片。
|
||||
- 文件 patch / diff 摘要。
|
||||
- citation list + open-reference。
|
||||
- abort / retry。
|
||||
- session list / resume。
|
||||
- 错误态显示真实错误,不伪装成功。
|
||||
|
||||
## 8. Spike 阶段
|
||||
|
||||
### Phase A:源码与包可运行性
|
||||
|
||||
- [ ] 在 `tmp/pi-research-20260703/pi-mono` 跑通当前 Pi build / smoke。
|
||||
- [ ] 安装当前 `@earendil-works/pi-coding-agent` 与 `@earendil-works/pi-web-ui`。
|
||||
- [ ] 跑通 `pi --mode rpc` 最小 prompt。
|
||||
- [ ] 记录 RPC event schema,映射到 MNote `ai_tool_events`。
|
||||
- [ ] 验证 `abort` / `steer` / `follow_up` 行为。
|
||||
|
||||
### Phase B:MNote scoped Pi runtime
|
||||
|
||||
- [ ] 新增内部 runtime manager 设计,不先实现到生产入口。
|
||||
- [ ] 用临时脚本从 MNote workspace 派生 `sessionDir`。
|
||||
- [ ] 禁止全局 `~/.pi/agent/sessions` 作为默认落点。
|
||||
- [ ] 只开放 MNote mock tools,不开放 `bash`。
|
||||
- [ ] 路径越界读写必须被拒绝并写 receipt。
|
||||
|
||||
### Phase C:LightRAG tool
|
||||
|
||||
- [ ] 注册 `mnote.knowledge_rag.query`。
|
||||
- [ ] 调用现有 `/api/knowledge-rag/*` 或内部 provider facade。
|
||||
- [ ] 返回 source id、resource path、snippet、score。
|
||||
- [ ] UI 显示 citation。
|
||||
- [ ] citation 点击回 MNote open-reference。
|
||||
|
||||
### Phase D:文件 patch 闭环
|
||||
|
||||
- [ ] 当前页面解析到真实 `.md`。
|
||||
- [ ] Pi tool 请求 patch 当前文件。
|
||||
- [ ] MNote 检查 dirty buffer / version。
|
||||
- [ ] 写入文件后 watcher 刷新 tiptap。
|
||||
- [ ] changed file chip 能打开对应资源。
|
||||
|
||||
### Phase E:UI 对照
|
||||
|
||||
- [ ] 在 MNote sidebar 内嵌 `pi-web-ui` 或等价组件。
|
||||
- [ ] 与 OpenHub 当前面板并排截图对照。
|
||||
- [ ] 核验移动端 / 窄 sidebar 布局。
|
||||
- [ ] 核验 tool card、diff、citation、error、abort/retry。
|
||||
|
||||
## 9. 验收门槛
|
||||
|
||||
Pi Lab 进入下一阶段必须满足:
|
||||
|
||||
- `pi --mode rpc` 或 SDK 能由 MNote 稳定托管。
|
||||
- 当前页读取、LightRAG 查询、文件 patch、watcher 刷新形成闭环。
|
||||
- 所有文件读写都受 allowed roots 控制。
|
||||
- 所有 tool call 都有 Turso/libSQL receipt。
|
||||
- `bash` 默认不可用,或只能在显式 dev/debug scope 下使用。
|
||||
- citation 能点击回 MNote 文件或资源。
|
||||
- UI 不低于 OpenHub baseline 的核心交互:stream、tool card、diff/citation、abort/retry、session resume。
|
||||
- Pi Lab 与 OpenHub 并存,不影响默认 Page AI。
|
||||
|
||||
失败条件:
|
||||
|
||||
- 5 个工作日内无法跑通 RPC/SDK 最小 agent。
|
||||
- `pi-web-ui` 或候选 UI 无法稳定嵌入 MNote sidebar。
|
||||
- 文件 patch 难以在 MNote allowed roots / dirty buffer 模型下约束。
|
||||
- LightRAG citation 无法可靠回跳。
|
||||
- 第三方 package 需要过多不可控 patch 才能满足权限边界。
|
||||
|
||||
## 10. 决策点
|
||||
|
||||
### D1:Pi 是否作为新 Page AI runtime
|
||||
|
||||
通过条件:
|
||||
|
||||
- Phase A-D 全部完成。
|
||||
- UI 至少达到 OpenHub 核心体验的 70%。
|
||||
- 权限、审计、RAG、watcher 都由 MNote 控制。
|
||||
|
||||
若通过:
|
||||
|
||||
```text
|
||||
OpenHub = 默认生产入口
|
||||
Pi Lab = 内部/高级用户入口
|
||||
下一稿 = Pi Page AI runtime implementation checklist
|
||||
```
|
||||
|
||||
若未通过:
|
||||
|
||||
```text
|
||||
OpenHub 继续主线
|
||||
Pi 降级为 provider/runtime 参考
|
||||
assistant-ui / CopilotKit / AG-UI 进入 UI 备选评估
|
||||
```
|
||||
|
||||
### D2:Pi UI 选择
|
||||
|
||||
优先顺序:
|
||||
|
||||
1. `@earendil-works/pi-web-ui`
|
||||
2. `@hyperspaceng/neural-web-ui`
|
||||
3. OpenHub React UI 子集 + Pi backend
|
||||
4. assistant-ui / CopilotKit
|
||||
5. MNote-native debug UI
|
||||
|
||||
### D3:Pi 托管方式
|
||||
|
||||
优先顺序:
|
||||
|
||||
1. RPC subprocess
|
||||
2. Node SDK sidecar
|
||||
3. OpenHub backend adapter
|
||||
|
||||
不做:
|
||||
|
||||
- Rust 直接重写 agent loop。
|
||||
- 浏览器直接持有 provider API key 调 Pi。
|
||||
|
||||
## 11. 后续产物
|
||||
|
||||
本稿通过后新增:
|
||||
|
||||
- `7-69-page-ai-pi-first-lab-checklist-v1.md`
|
||||
- `scripts/task*-page-ai-pi-rpc-smoke.js`
|
||||
- `scripts/task*-page-ai-pi-lightrag-smoke.js`
|
||||
- `scripts/task*-page-ai-pi-file-patch-smoke.js`
|
||||
|
||||
临时研究目录只作为证据,不进入产品路径:
|
||||
|
||||
- `/mnt/Data1T/mnote/tmp/pi-research-20260703/pi-mono`
|
||||
- `/mnt/Data1T/mnote/tmp/pi-research-20260703/pi-skills`
|
||||
- `/mnt/Data1T/mnote/tmp/pi-research-20260703/openclaw`
|
||||
|
||||
## 12. 最终建议
|
||||
|
||||
当前最稳的路线是:
|
||||
|
||||
```text
|
||||
继续 OpenHub P0
|
||||
并行启动 Pi-first Page AI Lab P1
|
||||
用真实 smoke 决定是否扩大 Pi 权重
|
||||
```
|
||||
|
||||
Pi 不应被过早否定;它已经有足够多的 runtime 与生态基础,值得作为首选实验底座。
|
||||
Pi 也不应被过早宣布替换 OpenHub;MNote 需要自己补齐多用户控制面、Turso 审计、LightRAG tool、allowed roots 和 UI 产品化边界。
|
||||
Reference in New Issue
Block a user