feat: align local-first workspace direction

Document the VSCode-like local-first product shape, demote Convex to a control-plane role, and retire stale architecture drafts.

Add local workspace migration/export references plus smoke coverage for no-Convex managed workspace startup, local markdown title/body/options persistence, asset upload behavior, and Convex fixture export.

Verification: git diff --cached --check; node scripts/check-local-first-convex-guard.js --staged; node scripts/task444-convex-workspace-export-local-fixture-smoke.js; node scripts/task166-local-first-managed-workspace-no-convex-smoke.js; node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
This commit is contained in:
lix-2026
2026-05-19 08:11:58 +08:00
parent 68d321e297
commit cdff672aa5
67 changed files with 5242 additions and 932 deletions
@@ -29,15 +29,16 @@
- `PageAIApplyController` 后续应收口为 review session / tool executor / readback controller,不能成为第二套 agent 编排中心。
- 当前 `usedHermesRun=false` 的快路径只能理解为 deterministic shortcut,不代表 mnote 新建长期 agent runtime。
## 1.0.1 当前主路径修正(2026-05-18
## 1.0.1 当前主路径修正(2026-05-19
`7-18``7-25` 修复后,本文中的页面 AI fast workflow 口径进一步收口
`2-2 local-first` 完成后,本文继续保留为页面块 / 结构性工具 checklist,但普通 Markdown 正文编辑主路径已经转为 VSCode-like 文件编辑
- 普通正文编辑主路径是 `mnote.doc.markdown_edit`,模型生成 markdown `search/replace``full_content`,再由统一 mnote tool executor 写入
- `/api/page-ai/block-edit-workflow` 仍是简单编辑 fast-path 入口,但不再把 `local_rule -> mnote.doc.apply_block_ops` 作为当前主路径。
- local-first 普通正文编辑默认给 Hermes / Reasonix 当前文件引用、selection 和 allowed roots,由 agent 使用自身 patch / diff / 文件编辑能力写本地 `.md`
- `mnote.doc.markdown_edit` 是 cloud / remote agent / compat fallback,不再是 local-first 普通 Markdown 编辑唯一主路径。
- `/api/page-ai/block-edit-workflow` 只保留为兼容门面或非 local source 的快捷路径,不作为 local-first 默认主路径。
- `mnote.doc.apply_block_ops` / `mnote.block.*` 保留为结构性块操作辅助,例如块移动、资源块、复杂子块或必须按 blockId 精确处理的场景。
- Phase C 的 review session / streaming apply 仍冻结;本文只继续跟踪基础工具合同、上下文、冲突校验、幂等和审阅面边界。
- 当前 page AI runtime 口径以 `design/07-ai/process/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md``design/10-review/done/10-current-mnote-ai-runtime-review-v1.md``bugs/07-ai/done/7-18``7-25` 为准。
- 当前 page AI runtime 口径以 `design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md``design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md``design/07-ai/process/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md` 为准。
## 1.1 当前执行状态(2026-05-16
@@ -262,14 +263,14 @@ cargo test -p mnote-web block_fetch
- [x] 支持 `command=block_replace`
- [x] 支持 `command=block_insert_after`
- [x] 支持 `command=block_move_after` dry-run。
- [x] `command=str_replace` 不再作为本 checklist 当前目标;普通正文 search/replace 已收口到 `mnote.doc.markdown_edit`,多重匹配/无法安全映射阻断由 markdown_edit 合同覆盖。
- [x] `command=str_replace` 不再作为本 checklist 当前目标;cloud / remote / compat 的普通正文 search/replace 已收口到 `mnote.doc.markdown_edit`,多重匹配/无法安全映射阻断由 markdown_edit 合同覆盖。local-first 普通 Markdown 编辑默认走授权文件 + agent 原生 patch/diff。
- [x] 缺少 `revision/conflictDetectionKey` 时只允许 dry-run。
- [x] 返回 `planId``diff``warnings``risk``blocked`
2026-05-18 口径修正:
2026-05-19 口径修正:
- `7-18``7-25` 收口后,普通正文替换不再继续扩 `mnote.doc.plan_update command=str_replace`当前实现路径是 `page_ai_workflow.rs -> mnote.doc.markdown_edit`
- `mnote.doc.markdown_edit` 已覆盖精确匹配、归一化匹配、多操作同块合并、无法安全映射时失败;相关测试由 `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture` 覆盖。
- `7-18``7-25` 收口后,普通正文替换不再继续扩 `mnote.doc.plan_update command=str_replace`compat 实现路径是 `page_ai_workflow.rs -> mnote.doc.markdown_edit`
- `mnote.doc.markdown_edit` 已覆盖精确匹配、归一化匹配、多操作同块合并、无法安全映射时失败;相关测试由 `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture` 覆盖。它不再代表 local-first 普通 Markdown 编辑默认入口。
验证命令:
@@ -1,6 +1,6 @@
# 7-12 [process] 页面 AI Hermes 工具路由与编辑审阅面设计 v1
> 更新时间:2026-05-16
> 更新时间:2026-05-19
>
> 当前状态:`PROCESS`
>
@@ -21,7 +21,8 @@
本稿仍作为 Hermes 工具路由与审阅面设计保留在 `process/`,但以下口径已经更新:
- `/api/page-ai/block-edit-workflow` 当前不再以 `local_rule -> apply_block_ops` 作为主路径;简单正文编辑主路径已切到模型生成 markdown `search/replace``full_content`,再调用 `mnote.doc.markdown_edit`
- `/api/page-ai/block-edit-workflow` 当前不再以 `local_rule -> apply_block_ops` 作为主路径;local-first 普通 Markdown 编辑默认给 Hermes / Reasonix 授权文件引用,由 agent 使用自身 patch / diff / 文件编辑能力写本地 `.md`
- `mnote.doc.markdown_edit` 保留为 cloud / remote agent / compat fallback;模型生成 markdown `search/replace``full_content` 后调用该工具,只适用于 agent 不能直接访问授权文件或需要受控代理写入的场景。
- `page_ai_workflow` 已复用统一 mnote tool executor,不再绕过 Hermes tool toggle / audit / write contract。
- `mnote.doc.apply_block_ops` / `mnote.block.*` 保留为结构性块操作辅助,不再作为普通正文 search/replace 的优先入口。
- 本稿中的 `PageAIReviewSession` 只定义 Phase C 的安全合同和状态机边界;当前 Phase C 仍冻结,不实施流式 apply 或新的审阅 UI。
@@ -4,13 +4,18 @@
>
> 更新时间:2026-05-16v3:深度参考 CLI Main skill 系统,补全成熟度采纳清单)
>
> 2026-05-18 口径补充:
> - local-first workspace 已成为早期产品默认形态;本地 `.md` 是默认 AI 编辑目标,在线 Convex 文档降级为可选 cloud / sync / share source。
> - 本文早期把 `mnote.doc.markdown_edit` 描述为统一主路径;最新口径改为:local-first 普通 Markdown 编辑优先给 agent 授权文件引用,由 agent 使用自身成熟的 diff / apply_patch / 文件编辑能力完成;`mnote.doc.markdown_edit` 保留为 cloud / remote agent / compat fallback。
> - 页面内图片 / 附件上传已在 local source 下写入 `{mdBase}.assets/` 并保存相对 Markdown 路径,AI 后续处理附件引用时也应保留相对路径,不改写为 Convex media asset。
>
> 当前状态:`PROCESS`
>
> 本稿目的:
> 1. 纠正 7-9 / 7-10 / 7-12 / 7-13 中隐含的「块级编辑是 AI 唯一写入路径」假设
> 2. 基于 CLI Main 参考实现,确立 mnote 的「文本级搜索替换为主 + 块级结构性操作为辅」两层模型
> 2. 基于 CLI Main 参考实现,确立 mnote 的「agent 原生文件 patch/diff 为 local-first 默认路径,MNote 文本级兼容工具 + 块级结构性操作为 fallback / 辅助」两层模型
> 3. 规划 BlockNote AI 流式/review 能力的远期方向(当前不实施)
> 4. 统一在线 Convex 文档和本地 `.md` 文件的 AI 写入路
> 4. 统一 cloud / remote / compat 文档和本地 `.md` 文件的 AI 读取、权限、冲突与回读口
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/07-ai/done/7-9-page-block-ai-tooling-roadmap-v1.md`
@@ -37,7 +42,7 @@
## 1. 结论
**在线 Convex 文档和本地 `.md` 文件本质上是同一个东西:一段 markdown 文本,Tiptap 只是块级 UI 表现层。** 当前 07-ai 设计把 AI 编辑契约钉死在 `EditorBlockDocument` 的块级操作上,导致三个连锁问题:
**在线 Convex 文档和本地 `.md` 文件本质上是同一个东西:一段 markdown 文本,Tiptap 只是块级 UI 表现层。** 进一步切到 local-first 后,本地 `.md` 已经是普通文件,因此不需要再为常规正文编辑发明一套 MNote 专用工具。当前 07-ai 设计把 AI 编辑契约钉死在 `EditorBlockDocument` 的块级操作上,导致三个连锁问题:
1. **AI 被迫在块级操作**:对「改写这段话」「补充一段总结」「调整语气」「把所有 TODO 改成 DONE」等自然请求,AI 必须产出 `{ op: "replace", blockId: "block_1", ... }` 格式的块操作,而不能直接产出修改后的文本或搜索替换对。这增加了 AI 的认知负担和出错概率。
@@ -55,15 +60,15 @@
| **BlockNote AI** | 纯块级 `add/update/delete`(依赖 blockId),流式 apply + suggest/review | ✅ **远期参考**:流式/review 能力,Phase C 规划 |
| **Tiptap AI Autocomplete** | 纯文本补全,单句接龙,无工具 | ⏳ 独立功能:内联 AI 补全(非本文讨论范围) |
**CLI Main 最关键的设计决策**:它不把 AI 钉死在某一层。`str_replace` 用于文本级修改(不需要 blockId),`block_*` 用于精确块结构调整。格式上,**XML 用于精确编辑场景,Markdown 用于整篇导入/导出**。mnote 的不同之处在于——在线文档和本地 `.md` 文件的共同分母是 **markdown 而非 XML**,因此我们的主格式是 markdown
**CLI Main 最关键的设计决策**:它不把 AI 钉死在某一层。`str_replace` 用于文本级修改(不需要 blockId),`block_*` 用于精确块结构调整。格式上,**XML 用于精确编辑场景,Markdown 用于整篇导入/导出**。mnote 的不同之处在于local-first 之后,默认对象就是本地 `.md` 文件,因此可以直接复用 Codex / Hermes / Reasonix 自身成熟的 diff、apply_patch、文件编辑能力;MNote 的职责收口为权限沙箱、文件引用解析、审计和刷新
### 正确方向
> mnote 的 AI 编辑路线:**CLI Main 的两层操作模型 + BlockNote AI 的流式/review 能力 + mnote 自己的 markdown 优先策略。**
> mnote 的 AI 编辑路线:**本地授权文件 + agent 原生 patch/diff 为主;MNote 兼容工具为 cloud/remote/结构化辅助;BlockNote AI 的流式/review 只作为远期交互参考。**
具体:
- **当前实施**Phase A/B):`mnote.doc.markdown_edit` 作为 AI 编辑主路径,文本级搜索替换为第一操作原语。`mnote.block.*` 保留为结构辅助。
- **当前实施**Phase A/B):local-first 页面 AI 只传当前文件引用、可选 selection 和用户指令;MNote 校验 `AiAccessScope` 后让 agent 在受限目录中使用原生 patch/diff 编辑 `.md``mnote.doc.markdown_edit` 作为兼容 / 远端代理 fallback`mnote.block.*` 作为复杂结构辅助。
- **远期规划**Phase C):BlockNote AI 的流式增量 apply + suggest/review 层。当前先设计,不实施。
---
@@ -377,36 +382,37 @@ mnote 当前:Hermes 只知道 workspace/document 模型,不知道本地文
AI 最自然的编辑方式是对文本进行操作。块是 UI 概念,不是 AI 概念。在线文档的持久化格式和本地文件的持久化格式都可以投影为 markdown。
### 4.2 两层操作模型
### 4.2 两层操作模型(兼容层)
| 层 | 工具 | 寻址方式 | 适用场景 | 占比 |
|----|------|---------|---------|------|
| **文级(主)** | `mnote.doc.markdown_edit` | search/replace 文本对 | "把这段改简洁"、"把所有 TODO 改 DONE"、"补充一段总结" | 80%+ |
| **文级(主)** | agent 原生 `diff/apply_patch/文件编辑` | 授权文件引用 / selection | local-first 普通 Markdown 改写 | 80%+ |
| **文本级(兼容)** | `mnote.doc.markdown_edit` | search/replace 文本对 | cloud / remote agent / 兼容旧页面 AI | 次要 |
| **块级(辅助)** | `mnote.doc.apply_block_ops` | blockId / matchText | "把第三块拖到第一块后面"、"精确删除引用块" | <20% |
### 4.3 在线和本地共用同一条写入路径
### 4.3 在线和本地的主写入路径
```
mnote.doc.markdown_edit
页面 AI / ACP
→ resolve_source(documentId) → Convex | LocalFS
读取当前 markdown
→ 应用 operations(搜索替换)
→ 写入目标(Convex 或文件系统)
返回 delta
local-first: 传授权文件引用给 agent runtime
→ agent 原生 patch/diff 写入文件
→ MNote 做权限 / 审计 / refresh
cloud / remote fallback: mnote.doc.markdown_edit
```
差异仅存在于 `resolve_source``write_target` 两个 adapter,中间的 markdown 操作逻辑完全共享。这和 CLI Main 的 `docs +update` 可以操作任何 `doc-token` 的设计一致。
差异主要在执行层:local-first 直接让 agent 修改授权文件;cloud 或受限 remote runtime 无法直接访问本地文件时,再走 `mnote.doc.markdown_edit` 代理。这和 CLI Main 的 `docs +update` 可以操作任何 `doc-token` 的设计一致,只是 mnote 进一步把“编辑算法”让渡给 agent runtime
### 4.4 Diff 是内部实现细节
AI **不**产出 unified diff(行号/上下文极易出错),也不调用独立的 diff/patch 工具。AI 产出两种形式之一:
对兼容 `mnote.doc.markdown_edit` 来说,AI **不**产出 unified diff(行号/上下文极易出错),而是产出两种形式之一:
| 形式 | 适用场景 | AI 负担 |
|------|---------|---------|
| `operations: [{ search, replace }]` | 局部修改 | 低:只需找原文片段 |
| `full_content: "..."` | 小文档全文改写 | 低:直接写完整 markdown |
服务端内部做 diff(用于 delta 推送和冲突检测),但 AI 无感知
而在 local-first 主路径中,agent runtime 自己可以安全使用成熟的 diff / apply_patch / 直接文件编辑能力;MNote 只要求这些写入被限制在授权路径内,并把 changed files / diff 摘要收回审计
---
@@ -446,7 +452,7 @@ AI **不**产出 unified diff(行号/上下文极易出错),也不调用
}
```
### 5.2 `mnote.doc.markdown_edit`(新增,主路径
### 5.2 `mnote.doc.markdown_edit`(新增,兼容 / fallback
```json
{
@@ -580,11 +586,13 @@ GhostTextOverlay(新增,编辑器)
## 8. 实施阶段(当前)
### Phase A`mnote.doc.fetch` 增强 + `mnote.doc.markdown_edit` 核心实现
### Phase A文件引用主路径 + `mnote.doc.*` 兼容层
- [x] `mnote.doc.fetch` 增加 `format: "markdown"`(在线文档 Page Aggregate → PageMarkdown
- [x] `mnote.doc.fetch` 增加本地文件 source 路由(自动检测 Convex vs 文件系统路径)
- [x] 实现 `resolve_source(documentId)` — 本地文件路径 `local_fs` vs 其余走 Convex
- [ ] 页面 AI / ACP 普通正文编辑默认只传当前文件引用、可选 selection 和用户指令,不再默认构造完整 page context
- [ ] 本地 agent runtime 在 `allowed_roots / allowed_file_paths` 内执行 patch/diff,并把 changed files / diff 摘要回传 MNote
- [x] 实现 `search_replace(text, operations)` — 四级匹配策略(精确→宽松→段落 fuzzy→失败)
- [x] 实现 Convex 写入 adapter(复用 `doc_apply_block_ops` 链路)
- [x] 实现本地文件写入 adapter`mnote.doc.markdown_edit` 检测到本地文件路径时直接 `fs::write` 写回,不经过 Convex
@@ -599,7 +607,7 @@ GhostTextOverlay(新增,编辑器)
### Phase B`page_ai_workflow.rs` 收口
- [x] 退役 `direct_block_edit_operations`(正则抠「」的快路径,代码保留但路由跳过)
- [x] `/api/page-ai/block-edit-workflow` 底层切换到 `mnote.doc.markdown_edit`
- [x] `/api/page-ai/block-edit-workflow` 不再被当作 local-first 主路径
- [x] 模型 system prompt 重构:从产块操作 JSON 改为产 search/replace 文本对
- [x] 补全 operation schema`extract_markdown_operations_from_model_text` 处理新旧格式
- [x] 浏览器 smoke`markdown_edit` 搜索替换通过,自然语言编辑路径可用
@@ -620,7 +628,7 @@ GhostTextOverlay(新增,编辑器)
| CLI Main 概念 | mnote 对应 |
|--------------|-----------|
| `str_replace`(文本级) | `mnote.doc.markdown_edit`主路径 |
| `str_replace`(文本级) | `mnote.doc.markdown_edit`兼容 / fallback |
| `block_replace/insert_after/delete/move_after`(块级) | `mnote.doc.apply_block_ops`(辅助路径) |
| XML 用于精确编辑 | mnote 不用 XML(没有 XML 存储层),direct path 退役后全部走 markdown |
| Markdown 用于导入/导出/对话引用 | mnote 全部 AI 交互走 markdown |
@@ -640,7 +648,7 @@ GhostTextOverlay(新增,编辑器)
| 设计稿 | 关系 | 修正状态 |
|--------|------|---------|
| 7-9 路线图 | 块级编辑降级为辅助,markdown_edit 为主路径 | ✅ 已修正 |
| 7-9 路线图 | 块级编辑降级为辅助,local-first 普通编辑改为授权文件 + agent patch/diffmarkdown_edit 退到兼容层 | ✅ 已修正 |
| 7-10 checklist | 新增 Phase 9 markdown_edit | ✅ 已修正 |
| 7-12 工具路由 | PageAICommandRouter 主输出改为 markdown_edit | ✅ 已修正 |
| 7-13 EditorRuntimeActor | 补充 markdown_edit 的 delta 适配 | ✅ 已修正 |
@@ -651,10 +659,10 @@ GhostTextOverlay(新增,编辑器)
## 10. 禁止项
- 不删除 `mnote.block.*` 工具(保留为辅助路径)。
-让 AI 产出 unified diff(行号/上下文极易出错)
-强迫 local-first agent 产出 MNote 自定义 diffHermes / Reasonix 可使用自身成熟 patch / diff / apply_patch 能力,MNote 负责白名单权限、文件版本冲突和审计
- 不要求本地文件有稳定的 `blockId`(本地文件没有 block identity)。
- 不在 markdown_edit 内部引入新的 AI 模型调用(diff 是确定性算法)。
-改变 Convex `documents:updateContent` 的持久化链路(markdown_edit 复用现有保存路径
- Convex `documents:updateContent` 重新提升为 local-first 正文主存储;`markdown_edit` 在 cloud / compat 场景可复用受控保存路径。
- 不把 `mnote.page.save` 重新描述为精确编辑主入口(它仍是兜底工具)。
- **不照搬 BlockNote AI 的纯 blockId 寻址模式**(与 mnote 的 markdown 优先策略冲突)。
@@ -663,10 +671,10 @@ GhostTextOverlay(新增,编辑器)
## 11. 成功标准
- [x] `mnote.doc.fetch(documentId, format: "markdown")` 对在线文档返回正确 markdown
- [ ] `mnote.doc.fetch(documentId, format: "markdown")` 对本地 `.md` 文件返回正确 markdown读取已实现,待浏览器 smoke
- [ ] `mnote.doc.fetch(documentId, format: "markdown")` 对本地 `.md` 文件返回正确 markdown兼容路径
- [x] `mnote.doc.markdown_edit` 的简单搜索替换(1 条 operation)浏览器 smoke 通过
- [ ] `mnote.doc.markdown_edit` 的复杂改写(3+ 条 operations)成功率 > 80%
- [ ] 本地 `.md` 文件通过页面 AI 面板可被读取和写入(读取已实现,写入待本地文件 adapter)
- [ ] 本地 `.md` 文件通过页面 AI 面板可被读取和写入,且默认通过 agent 原生 patch/diff 完成
- [x] 在线文档的 markdown_edit 不增加 Convex RTT(和当前块操作持平)
- [x] `direct_block_edit_operations` 已退役(路由跳过,代码保留)
- [x] `page_ai_workflow.rs` 的 system prompt 已补全 search/replace schema
@@ -682,7 +690,7 @@ GhostTextOverlay(新增,编辑器)
| # | CLI Main 模式 | mnote 实施项 | 状态 | 参考文件 |
|---|-------------|-------------|------|---------|
| 1 | 两层操作模型 | `markdown_edit`+ `apply_block_ops`(辅助)已在 7-10 Phase 9 登记 | 📋 设计完成,待实施 | `lark-doc-update.md` |
| 1 | 两层操作模型 | local-first 默认 agent 原生 patch/diff`markdown_edit`compat fallback+ `apply_block_ops`结构辅助)已在 7-10 Phase 9 登记 | 📋 设计完成,已按 2-2 改口径 | `lark-doc-update.md` |
| 2 | Scope 四级控制 | `mnote.doc.fetch` 增加 `scope: full/section/outline/keyword` | 🔜 Phase A | `lark-doc-fetch.md` |
| 3 | Detail 三级控制 | `mnote.doc.fetch` 增加 `detail: simple/with_ids/full` | 🔜 Phase A | `lark-doc-fetch.md` |
| 4 | 片段包装 | fetch 返回中标记 `<!-- fragment -->` / `<!-- excerpt -->` 告知 AI 部分视图 | 🔜 Phase A | `lark-doc-fetch.md`fragment/excerpt 模式) |
@@ -726,7 +734,7 @@ Phase A(当前立即)
├── #3 Detail 三级控制 ← mnote.doc.fetch 增强
├── #4 片段包装 ← fetch 返回值增强
├── #8 Markdown 优先 ← 已确认
└── mnote.doc.markdown_edit 核心实现
└── 授权文件引用 + agent 原生 patch/diff 主路径;mnote.doc.markdown_edit 作为 compat fallback
Phase BPhase A 完成后)
├── #5 Code-Act Loop ← Hermes plugin SKILL.md
@@ -11,9 +11,10 @@
> 4. 复用现有参考代码,最小化重复实现工作
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md`
> - `/mnt/Data1T/mnote/recycle/design/07-ai/retired-http-hermes/7-5-hermes-client-proxy-contract-v1.md`(已退役历史背景)
> - `/mnt/Data1T/mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/process/7-17-acp-session-convex-sharing-contract-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-vscode-main/`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/DeepSeek-Reasonix-main/`
@@ -35,10 +36,10 @@ mnote-web (Rust Axum)
│ get /api/hermes/runs/{id}/events
├─ hermes_tools.rs (mnote.doc.* / mnote.block.*)
│ └─ Rust 工具实现,通过 HTTP/Convex 读写文档
│ └─ Rust 兼容工具实现,通过 HTTP/Convex 或本地代理读写文档
└─ page_ai_workflow.rs (fast-path 块编辑)
└─ local_rule planner, 不经过 Hermes
└─ page_ai_workflow.rs (兼容门面)
└─ local-first 下不再是主路径
```
**问题:**
@@ -67,15 +68,21 @@ mnote-web (Rust Axum)
│ │ └─ session/update ≫ SSE 转发
│ └─ 代理层:向下游工具通知
├─ hermes_tools.rs (不变)
├─ hermes_tools.rs (兼容层)
│ └─ mnote.doc.* / mnote.block.* / mnote.page.*
└─ page_ai_workflow.rs (不变)
└─ fast-path 块编辑
└─ page_ai_workflow.rs (兼容保留)
└─ local-first 普通正文编辑默认不经过它
```
ACP 是整个架构的支点——它是一个**开放协议**,不是某个产品的私有接口。
> 2026-05-18 local-first 口径补充:
>
> - 目标不是把 Hermes / Reasonix 再包进一层重型 MNote 工具系统,而是让它们尽量像在 VSCode 中那样直接面对授权后的本地工作区。
> - MNote 主要负责:页面定位、白名单目录授权、ACP 会话管理、审计、文件变化同步到 tiptap / File Tree / Page Aggregate。
> - local-first 普通 Markdown 编辑默认不要求 runtime 调 `mnote.doc.markdown_edit`;兼容工具只为 cloud / remote / 复杂结构场景保留。
---
## 2. ACP 协议标准
@@ -216,6 +223,25 @@ profile "reasonix" → runtime "reasonix" (spawn node reasonix-acp-wrapper.mjs)
前端获取可用运行时列表:`GET /api/hermes/client/profiles`(现有接口,扩展字段)
### 4.1.1 白名单目录即 runtime 权限边界
对 local-first 而言,真正重要的不是再做一套“文档工具能力矩阵”,而是把 workspace 白名单准确传给 runtime
```text
登录用户
-> 解析 access-policy.json / owner / admin / grant
-> 得到 allowedRoots = ["/mnt/Data1T/Mnote_data/users/.../my-space", ...]
-> 启动 Hermes / Reasonix 时把 allowedRoots / cwd / read-write scope 传入 runtime
-> runtime 直接在这些目录里工作
```
这与 VSCode / Codex 的工作模型一致:
- runtime 看到的是一个受限 workspace,而不是一堆抽象页面 API。
- 页面 AI 只额外提供“当前文件是谁”以及可选选区信息。
-`.md` 的普通编辑不强制走 `mnote.doc.markdown_edit`
- 一旦文件写回磁盘,MNote 再负责把变化同步回前端显示。
### 4.2 会话生命周期 (ACP Session Manager)
```
@@ -244,9 +270,9 @@ profile "reasonix" → runtime "reasonix" (spawn node reasonix-acp-wrapper.mjs)
### 4.3 工具桥接
当前 `hermes_tools.rs` 中注册的 mnote 工具(`mnote.doc.fetch``mnote.doc.markdown_edit``mnote.block.*``mnote.page.*`)对 ACP 来说只是一组 HTTP 端点
当前 `hermes_tools.rs` 中注册的 mnote 工具(`mnote.doc.fetch``mnote.doc.markdown_edit``mnote.block.*``mnote.page.*`)对 ACP 来说只是一组 HTTP 兼容端点,而不是 local-first 普通 Markdown 编辑的唯一主路径
对于 Reasonix 作为 runtime 的场景,需要一个 Reasonix-side 的工具注册包装脚本,将 mnote 工具注册到 `ToolRegistry`
对于 Reasonix 作为 runtime 的场景,需要一个 Reasonix-side 的工具注册包装脚本,将 mnote 兼容工具注册到 `ToolRegistry`;但 local-first 默认应优先让 runtime 直接拿到授权文件引用,在受限 cwd 中使用自身成熟的 patch/diff/文件编辑能力。
```typescript
// reasonix-acp-wrapper.mjs — ACP 包装层
@@ -287,7 +313,27 @@ agent → tool call → (通过 TCP/localhost HTTP) → mnote-web Rust hermes_to
→ Convex / 文档系统
```
**不需要在 Rust 侧重新注册工具到 Reasonix。** mnote-web 的工具 HTTP 端点 (`/api/hermes/tools/mnote/call`) 不变,只通过 ACP 换掉了 agent runtime。
**不需要在 Rust 侧重新注册工具到 Reasonix。** mnote-web 的工具 HTTP 端点 (`/api/hermes/tools/mnote/call`) 仍可保留,只通过 ACP 换掉了 agent runtime;但这些端点主要承担 cloud / remote agent / compat fallback,而不是把所有本地文件编辑都重新包成 mnote 工具
### 4.3.1 local-first 默认工作流
local-first 页面 AI 的默认工作流应是:
```text
当前页面 URL / documentId
-> MNote 解析出真实 markdown 文件路径
-> MNote 校验该路径是否落在 runtime allowedRoots 白名单内
-> 把 currentFile / selection / allowedRoots 传给 runtime
-> runtime 直接读写该文件
-> watcher / refresh 触发前端 page aggregate 与 tiptap 更新
```
只有在以下情况,runtime 才需要走 mnote 兼容工具:
- runtime 本身无法直接访问本地文件
- 当前 source 是 cloud / sync replica
- 当前对象不是普通 markdown,而是 mindmap / table / 资源块 / 分享受限对象
- 需要显式审计某种结构化操作
### 4.4 前端 SSE 扩展
@@ -570,11 +616,11 @@ AiAgentPanel 增加下拉框 + 切换逻辑:
### 7.2 对 7-14 (markdown 编辑收敛) 的影响
7-14 确立的「两层操作模型」(`mnote.doc.markdown_edit` 主 + `mnote.block.*` 辅)不受影响——工具在 Rust 侧 `hermes_tools.rs` 实现不变。ACP 只是换掉 driver(从 Hermes 换成 Reasonix),不改 driver 调用的工具
7-14 的最新口径是:local-first 普通 Markdown 编辑优先走“授权文件引用 + agent 原生 patch/diff”,`mnote.doc.markdown_edit` / `mnote.block.*` 退到兼容与辅助层。ACP 只是换掉 driver(从 Hermes 换成 Reasonix),不改变这个权限与执行边界
### 7.3 对 `page_ai_workflow.rs` 的影响
不影响。`block_edit_workflow` 作为独立 fast-path 与 ACP 无关
最新口径下,`block_edit_workflow` 只保留为兼容门面;local-first 普通正文编辑不应再依赖它。ACP 主要服务 agent runtime 选择、权限隔离、事件桥接和审计
---
@@ -638,7 +684,7 @@ AiAgentPanel 增加下拉框 + 切换逻辑:
| 改动 | 文件 | 说明 |
|------|------|------|
| ACP 下拉选择器 | `layout.rs` | Agent 标签页新增 `<select data-page-ai-acp-runtime>`3 个选项:默认 (Hermes HTTP)、ACP · HermesACP · Reasonix |
| ACP 下拉选择器 | `layout.rs` | Agent 标签页新增 `<select data-page-ai-acp-runtime>`2026-05-18 起只保留 ACP · Hermes / ACP · Reasonix,移除“默认 (Hermes HTTP)”选项 |
| 状态存储 | `layout.rs` | `pageAiAcpRuntime` + `pageAiAcpRuntimes``/api/hermes/client/profiles` 加载 |
| 运行时切换 | `layout.rs` | `acpRuntime` 只表示运行时/传输层;Hermes ACP 继续保留当前 Hermes profileReasonix ACP 使用 `profile=reasonix` |
| UI 自适应 | `layout.rs` | ACP Hermes 模式下继续显示 Hermes profile 下拉;ACP Reasonix 模式下隐藏 Hermes profile 下拉;Agent 面板显示 ACP 配置 |
@@ -909,7 +955,14 @@ node /home/lix/.codex/skills/page-ai-browser-verify/scripts/verify_mnote_page_ai
### [ ] Step 16:退役旧的 Hermes HTTP proxy 代码
> 待 ACP 路径稳定后执行(至少 1 周灰度观察期)。
> 2026-05-18 已开始执行第一阶段退役:
>
> - 页面 AI 前端默认 `acpRuntime=reasonix`,不再以空 runtime 表示“默认 Hermes HTTP”;用户仍可在下拉中切换到 `ACP · Hermes`。
> - mnote-web 服务端默认把 `/api/hermes/client/runs` 的空 `acpRuntime` 归入 ACP 默认 runtime(默认 `reasonix`),避免继续落到 `configured_upstream_for_profile()` 的 HTTP proxy 分支。
> - `GET /api/hermes/client/gateway/health` 在 ACP 默认路径下返回 ACP transport 状态,不再探测 `8642/8644` HTTP gateway。
> - 旧 Hermes HTTP proxy 合同与 `/v1/runs` 默认主链设计稿已移入 `recycle/design/07-ai/retired-http-hermes/`,避免干扰后续 ACP 主线判断。
>
> 剩余工作:删除或进一步隔离 `hermes_client.rs` 内的 HTTP proxy 兼容分支;当前阶段只保留显式兼容开关,避免一次性删除影响 profile/memory/tools 管理能力。
### [ ] Step 17:基准测试 — Reasonix 缓存收益量化
@@ -17,7 +17,8 @@
本文只承接这些剩余 smoke,不新增 AI 功能面,不改变当前主路径:
- 简单正文编辑主路径仍是 `mnote.doc.markdown_edit`
- local-first 普通 Markdown 编辑主路径仍是“授权文件引用 + agent 原生 patch/diff + watcher 同步”;本文只验证 mnote tools 的结构性辅助和 compat fallback
- `mnote.doc.markdown_edit` 只作为 cloud / remote agent / compat fallback 的 smoke 对象。
- `mnote.block.*` 仍只作为结构性辅助。
- `mnote.page.save` 仍只作为页面级粗粒度兜底。
- Phase C review / streaming apply 仍冻结,当前只验证基础合同和安全边界。
@@ -0,0 +1,477 @@
# 7-17 [process] ACP Session 与控制面账号作用域 / 分享合同 v1
> 创建时间:2026-05-18
>
> 当前状态:`PROCESS / FUTURE`
>
> 本稿目的:
> 1. 明确 ACP session 必须绑定当前账号权限与控制面作用域,不能以内存态绕过权限。
> 2. 冻结后续“复制会话 / 只读分享 / 多人共同会话”的产品语义,避免当前实现误扩。
> 3. 区分 MNote 产品层 AI session 与 ACP Hermes / ACP Reasonix 执行层 session。
> 4. 为后续项目基本完成后扩展共享能力预留 schema、API 和验证边界。
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/07-ai/process/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/done/7-25-acp-session-runtime-enhancement-plan-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md`
> - `/mnt/Data1T/mnote/recycle/design/07-ai/retired-http-hermes/`
---
## 0. 当前结论
ACP 默认主链已经替代旧 Hermes HTTP 主链,但这不意味着 ACP runtime session 可以成为产品层会话真相。
当前必须成立的规则:
- **控制面持有产品层 AI session 的账号作用域真相;在当前实现里它可以暂时落在 Convex,但长期不应把“Convex”写死成唯一真相。**
- **ACP runtime session 只是执行层会话**,可以被控制面 session 绑定或索引,但不能直接作为跨用户共享对象。
- **ACP session 记录必须带 `userId / workspaceId / documentId / sessionId / runId / actorId / acpRuntime / profile`**。
- **写入控制面 session store 失败时,不允许静默降级为可继续写入的内存 session**。这会破坏账号隔离、审计和后续分享语义。
- 当前只实现单用户页面 AI session 基础路径;复制、分享、多人共同会话全部是后续功能,不在当前阶段实施。
---
## 1. 术语边界
### 1.1 MNote AI Session
MNote AI Session 是产品层会话对象,长期应该由控制面持有;当前实现可以暂存在 Convex,但目标不是把消息全文和权限真相永久绑死在 Convex。
它回答:
- 谁能看到这段 AI 会话?
- 这段会话属于哪个 workspace / document
- 它是否可被复制、分享、共同编辑?
- 每条 run 是哪个账号触发的?
- 哪些消息、工具调用、结果可以被其他用户看到?
MNote AI Session 的 id 可以稳定暴露给前端,例如 `mnote_{documentId}_{traceId}`,但它不是 ACP runtime 原生 session id。
### 1.2 ACP Runtime Session
ACP Runtime Session 是执行层对象,由 Hermes ACP 或 Reasonix ACP 子进程持有。
它回答:
- 某个 agent runtime 当前 prompt 属于哪个协议 session
- 运行时如何接收 `session/prompt`
- `session/update` 事件如何返回?
- 运行时内部是否有本地上下文、缓存、memory、tool state
ACP Runtime Session 不应作为产品层共享真相。它可以失效、重建、按用户隔离、按 runtime 隔离。
### 1.3 Run / Event / Tool Audit
Run 是一次用户触发的执行。
Event 是 runtime 或 mnote tool 在 run 中产生的结构化事件。
Tool Audit 是 mnote 侧工具执行与写入结果的审计记录,必须以触发账号为准,而不是 session owner 或 runtime owner。
---
## 2. 当前实现边界
当前代码可接受的最小边界:
- `POST /api/hermes/client/sessions` 在 ACP 默认路径下创建 Convex 侧 runtime session 索引。
- `POST /api/hermes/client/runs` 创建 run 并持久化到 Convex runtime store。
- `GET /api/hermes/client/events/{runId}` 将 ACP event 转成页面 SSE,并追加 runtime event。
- `GET /api/hermes/client/gateway/health` 在 ACP 默认路径下返回 `transport=acp`,不再探测旧 Hermes HTTP gateway。
当前不应声称已完成:
- 跨账号共享会话。
- 多人共同编辑同一个 AI session。
- 复制会话后的上下文重建。
- 完整消息历史作为产品层真相。
- Hermes ACP 与 Reasonix ACP 的统一长期 resume 语义。
- 分享链接、权限继承、脱敏导出。
---
## 3. 数据模型草案
### 3.1 `ai_sessions`
后续需要从当前 `acp_runtime_runs` 中抽出产品层 session 表。默认建议本地全文 + 控制面 metadata 的双层模型:
- 会话全文默认落本地 `ai-sessions/private/*.jsonl``ai-sessions/shared/*/*.jsonl`
- 控制面只保存 metadata、分享关系、审计索引、同步状态
- 只有显式开启同步或分享时,才把必要副本推到远端
建议字段:
| 字段 | 说明 |
|---|---|
| `id` | 控制面记录 id |
| `session_id` | MNote 产品层 session id |
| `owner_user_id` | 会话创建者 |
| `workspace_id` | 所属 workspace,可为空但必须显式 |
| `document_id` | 所属页面 / 文档 |
| `title` | 会话标题 |
| `visibility` | `private` / `workspace_read` / `shared_link` / `collaborative` |
| `source` | `page_ai` / `local_file_ai` / 其他 |
| `created_at` / `updated_at` / `deleted_at` | 生命周期 |
权限规则:
- 默认 `private`
- 任何 query/mutation 必须先按当前控制面 auth 解析 user,再判断 `owner_user_id`、membership 或 share grant。
- 不允许只凭客户端传入的 `userId` 授权。
### 3.2 `ai_session_members`
多人共同会话需要独立成员表,不应把共享用户塞进 session JSON。
| 字段 | 说明 |
|---|---|
| `session_id` | 产品层 session |
| `user_id` | 成员 |
| `role` | `owner` / `editor` / `commenter` / `viewer` |
| `added_by` | 添加者 |
| `created_at` | 加入时间 |
| `revoked_at` | 撤销时间 |
角色规则:
- `viewer` 只能读可共享消息和可共享 artifact。
- `editor` 可以继续发起 run,但每次 run 的 `actor_id` 必须是本人。
- `owner` 可以管理成员和删除会话。
### 3.3 `ai_runtime_bindings`
产品层 session 与运行时 session 的绑定必须独立建模。
| 字段 | 说明 |
|---|---|
| `binding_id` | 绑定 id |
| `session_id` | MNote 产品层 session |
| `run_id` | 当前 run,可选 |
| `user_id` | 运行时所属用户 |
| `runtime` | `hermes` / `reasonix` |
| `profile` | Hermes profile 或 Reasonix preset |
| `runtime_session_id` | ACP 对端 session id |
| `status` | `active` / `closed` / `expired` / `failed` |
| `created_at` / `expires_at` | 生命周期 |
关键规则:
- 同一个 MNote session 可以有多个 runtime binding。
- 不同用户默认不能复用同一个 runtime binding。
- 切换 Hermes / Reasonix runtime 时必须创建新 binding。
- binding 是执行层缓存,不是会话权限真相。
### 3.4 `ai_session_messages`
后续如需完整历史,应单独建消息表,而不是只依赖 runtime events。
| 字段 | 说明 |
|---|---|
| `message_id` | 消息 id |
| `session_id` | 产品层 session |
| `run_id` | 关联 run |
| `actor_user_id` | 触发者,可为空仅限系统消息 |
| `role` | `user` / `assistant` / `tool` / `system` |
| `content` | 可展示文本 |
| `visibility` | `private` / `members` / `owner_only` |
| `redaction` | 脱敏状态 |
| `created_at` | 创建时间 |
当前阶段可以先不落完整 messages,但必须保证已有 run/event store 能按 user 过滤。
### 3.5 `ai_session_events`
Runtime event 和 tool event 应保留原始结构,但查询时必须做权限过滤。
| 字段 | 说明 |
|---|---|
| `event_id` | 事件 id |
| `session_id` | 产品层 session |
| `run_id` | run |
| `actor_user_id` | 触发者 |
| `runtime` | `hermes` / `reasonix` |
| `event_type` | `message.delta` / `tool.started` / `tool.completed` 等 |
| `payload` | 原始或规范化 payload |
| `visibility` | 默认 `owner_only`,明确脱敏后才可扩大 |
| `created_at` | 创建时间 |
---
## 4. 权限模型
### 4.1 单用户私有会话
当前阶段只要求这个模式稳定。
规则:
- 创建 session 时使用当前 Convex Auth 解析出的真实 user id。
- `userId` 只可作为服务端派生字段,不可由浏览器任意指定。
- run / event / tool audit 都必须与同一 actor 绑定。
- 当前请求无法解析用户时,应返回 `401` 或稳定错误,而不是创建匿名共享 session。
### 4.2 复制会话
复制不是共享同一个 runtime session。
复制语义:
- 生成新的 `session_id`
- 新 session 的 `owner_user_id` 是复制者。
- 可以复制已脱敏、可共享的消息、摘要、artifact 引用。
- 不复制底层 `runtime_session_id`
- 不复制另一个用户的 Hermes profile memory、Reasonix cache handle、tool permission state。
- 复制后第一次继续对话时,为复制者创建新的 runtime binding。
适用场景:
- 用户把某段 AI 分析作为模板继续改。
- 从只读分享页复制到自己的 workspace。
### 4.3 只读分享
只读分享只读产品层 session 的可共享投影。
必须隐藏:
- 本地路径、profile 配置路径、API key 状态。
- tool args 中含有的敏感字段。
- 未授权页面内容、选区内容、私有 workspace 信息。
- actor 的内部 user id,除非产品明确展示成员身份。
分享链接不能恢复 ACP runtime session。
### 4.4 多人共同会话
多人共同会话是后续能力,不能直接复用当前 runtime session。
推荐语义:
```text
共享 MNote AI Session
-> 每个用户按自己的权限发起 run
-> 每次 run 记录 actor_user_id
-> runtime binding 默认按 actor_user_id 隔离
-> 前端展示同一个产品层 transcript
```
允许的实现策略:
- **每用户 runtime binding**:最安全,默认方案。每个成员继续对话时由自己的 runtime 处理。
- **共享 transcript 重建上下文**runtime 不共享,只把已授权 transcript 作为 prompt context 注入。
- **共享 runtime binding**:默认禁止。只有当 runtime 明确是服务端多租户安全实例,并且 tool permission 已按 actor 隔离时才可考虑。
---
## 5. ACP Hermes 与 ACP Reasonix 差异
### 5.1 ACP Hermes
特点:
- Hermes profile、memory、skills、tools 往往绑定本机用户配置。
- `mnoteai` profile 可能包含特定用户偏好、工具开关、记忆文件。
- Hermes ACP 的底层 session 适合“当前用户私有 agent runtime”。
规则:
- 默认按用户隔离 runtime binding。
- 共享会话不能让其他用户复用 owner 的 Hermes profile memory。
- 复制会话时只复制可展示 transcript,不复制 Hermes profile 状态。
- 如果后续允许团队 Hermes profile,必须单独建 workspace-level profile 权限模型。
### 5.2 ACP Reasonix
特点:
- Reasonix 强调 cache-first / preset / project cache。
- 缓存可能跨 prompt 复用,隐私边界比普通 stateless model 更敏感。
- Reasonix 的 session 与 cache handle 不应默认跨账号共享。
规则:
- 默认按 `userId + workspaceId + documentId + preset` 隔离缓存可见性。
- 分享 transcript 不等于分享 Reasonix cache。
- 多人共同会话如果要共享 Reasonix 缓存,必须先设计 cache grant。
- 只读分享不得暴露 cache hit/miss 细节,除非确认无隐私风险。
### 5.3 统一抽象
MNote 不应把 Hermes ACP / Reasonix ACP 的内部 session 当成统一真相。
统一层只定义:
- 产品层 session。
- run 与 actor。
- runtime binding。
- event/message 投影。
- 权限与分享合同。
---
## 6. API 草案
当前阶段不实现以下 API,只冻结形状。
### 6.1 Session
```http
GET /api/hermes/client/sessions
POST /api/hermes/client/sessions
GET /api/hermes/client/sessions/{sessionId}
DELETE /api/hermes/client/sessions/{sessionId}
POST /api/hermes/client/sessions/{sessionId}/rename
POST /api/hermes/client/sessions/{sessionId}/auto-title
```
要求:
- 所有接口服务端解析当前用户。
- 查询默认只返回当前用户有权限访问的 session。
- 删除默认软删除,不删除 runtime audit。
### 6.2 复制
```http
POST /api/hermes/client/sessions/{sessionId}/copy
```
请求:
```json
{
"targetWorkspaceId": "ws_1",
"targetDocumentId": "doc_2",
"copyMode": "summary_and_visible_messages"
}
```
响应:
```json
{
"ok": true,
"sessionId": "mnote_doc_2_copy_1",
"sourceSessionId": "mnote_doc_1_original",
"runtimeBindingCopied": false
}
```
### 6.3 分享
```http
POST /api/hermes/client/sessions/{sessionId}/shares
GET /api/hermes/client/sessions/{sessionId}/shares
DELETE /api/hermes/client/sessions/{sessionId}/shares/{shareId}
```
分享 grant 必须包含:
- `scope`: `user` / `workspace` / `link`
- `role`: `viewer` / `editor`
- `expiresAt`
- `redactionPolicy`
### 6.4 多人会话成员
```http
POST /api/hermes/client/sessions/{sessionId}/members
GET /api/hermes/client/sessions/{sessionId}/members
DELETE /api/hermes/client/sessions/{sessionId}/members/{userId}
```
---
## 7. 不变量
这些规则后续实现必须写成测试。
1. 账号 A 创建的 private session,账号 B 不能读取列表、详情、事件、消息。
2. 账号 B 复制账号 A 分享给他的 session 后,得到新的 `sessionId` 和新的 owner。
3. 复制后的 session 不包含源 session 的 `runtime_session_id`
4. 多人共同会话中,账号 B 发起 run 时 `actor_user_id=B`,不能写成 owner A。
5. 只读 viewer 不能发起 run。
6. Hermes profile memory 不随 session 分享。
7. Reasonix cache handle 不随 session 分享。
8. tool args 默认 `owner_only`,只有经过脱敏的摘要可进入 shared transcript。
9. Convex store 写入失败时,run 创建必须失败或返回明确可恢复错误,不能静默创建匿名内存会话。
10. 本地文件 AI session 分享必须重新核验目标用户是否能访问对应 local root;默认不支持跨用户分享本地文件内容。
---
## 8. 实施阶段
### Phase A:当前阶段,只做约束固化
状态:当前主线。
- ACP 默认主链可用。
- session/run/event 必须账号作用域写入 Convex。
- 旧 Hermes HTTP 主链进入 `recycle`
- 不实现复制、分享、多人会话。
- 文档与测试明确禁止内存降级绕过权限。
### Phase B:项目基本完成后,补产品层 session 表
目标:
- 增加 `ai_sessions` / `ai_session_members` / `ai_runtime_bindings`
- 将现有 `acp_runtime_runs` 从“运行态索引”升级为 session 下的 run 记录。
- UI 从 localStorage 历史逐步迁到 Convex session 列表。
### Phase C:复制与只读分享
目标:
- 实现 session copy。
- 实现只读分享投影。
- 建立脱敏策略。
- 不实现多人共同编辑。
### Phase D:多人共同会话
目标:
- 成员管理。
- 多 actor transcript。
- 每 actor runtime binding。
- 共享上下文重建策略。
### Phase Eruntime-specific 高级策略
目标:
- Hermes team profile 权限模型。
- Reasonix cache grant / cache visibility。
- workspace-level AI runtime policy。
---
## 9. 当前代码注意事项
当前代码中 `acp_runtime_runs` / `acp_runtime_events` 仍是运行态索引,不是完整产品层 session 模型。
因此后续修改时:
- 不要把 `ACP_RUN_PAYLOADS``ACP_ACTIVE_RUNS` 视为权限真相。
- 不要因为 Convex 写入失败就退回“可继续写”的内存 session。
- 不要把 `profile=mnoteai` 当成 user identity。
- 不要让 `acpRuntime=hermes` 自动表示可访问 Hermes profile memory;仍需当前账号授权。
- 不要把 Reasonix cache 命中结果写入共享 transcript,除非经过脱敏和权限确认。
---
## 10. 验收清单
后续实现本稿时,至少需要以下验证:
- 两账号隔离 smokeA 创建 sessionB 列表不可见。
- 分享 smokeA 授权 B viewerB 可读脱敏 transcript,不可 run。
- editor smokeA 授权 B editorB 可 runevent actor 为 B。
- copy smokeB 复制 A 的分享 session,产生新 session,新 owner 为 B。
- runtime binding smoke:复制后没有复用源 runtime session id。
- Hermes ACP smoke:共享后不暴露 owner profile memory。
- Reasonix ACP smoke:共享后不暴露 cache handle。
- 权限失败 smokeConvex auth 缺失或 user 不匹配时,API 返回 401/403,不创建内存会话。
@@ -2,6 +2,11 @@
> 更新:2026-05-18v2:整合 CLI Main 参考实现分析,确认方向,补充见解)
>
> 2026-05-19 local-first 口径补充:
> - 本文仍适用于 `convex_workspace` / 在线文档的 `mnote.doc.markdown_edit` 修复,但当前默认产品形态已切到 local-first workspace。
> - 本地 `.md` 路径的默认 AI 编辑主路径是“授权文件引用 + agent 原生 patch/diff + watcher 同步”;`mnote.doc.markdown_edit` 只作为本地受控代理 fallback、cloud / remote agent 或 compat 路径。
> - 后续新增 AI 编辑能力默认先保证本地 `.md` 与 `{mdBase}.assets/` 相对路径不被改写为 Convex media asset;在线 Convex 文档路径只作为可选 cloud / sync / share source。
>
> 当前状态:`PROCESS`
>
> 关联缺陷:`bugs/07-ai/done/7-24-markdown-edit-online-write-does-not-use-final-markdown-v1.md`