Files
mnote/design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md
T

16 KiB
Raw Blame History

7-18 [process] local-first agent 文件编辑控制面 v1

2026-07-03 口径更新:本文中的 Hermes / Reasonix 主路径口径已退为历史参考。当前 Page AI / agent runtime 主线以 OpenHub / native agent + LightRAG + Turso/libSQL 为准;7-68-openhub-weknora-mnote-deep-fusion-v1.md7-69-weknora-native-embed-mnote-openhub-governance-v1.md 的 WeKnora 默认 provider 口径已标记 stale。本文仅保留 local-first 文件授权、watcher 同步和冲突模型的参考价值。

创建时间:2026-05-22

当前状态:PROCESS

上位依据:

  • /mnt/Data1T/mnote/ARCHITECTURE.md
  • /mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md
  • /mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md
  • /mnt/Data1T/mnote/design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md
  • /mnt/Data1T/mnote/design/07-ai/done/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md
  • /mnt/Data1T/mnote/design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md

覆盖旧稿:/mnt/Data1T/mnote/design/old/07-ai/process/7-14-local-first-ai-markdown-editing-convergence-v1.md

口径说明:旧 7-14 仍受 CLI Main / Lark Doc / tool-first 写入模型影响。CLI Main 可以参考 agent workflow 纪律,但不作为 MNote local-first Markdown 编辑架构主参考。

1. 第一结论

MNote 不提供普通 Markdown 正文编辑工具。

MNote 在 local-first 下只提供:

  • 页面和文件定位。
  • agent target 选择与冻结。
  • AiAccessScope、allowed roots、allowed files、readonly / dirty / permission context。
  • selection、当前光标附近上下文、相关资源引用。
  • agent runtime session、审计、changed files / diff 回收。
  • watcher、BufferStore、Page Aggregate、ProseMirror 前台同步。

Hermes / Reasonix 是已知 agent,使用自身文件 read / write / patch / diff 能力在授权目录内完成普通 .md 编辑。

2. 明确退役项

2.1 mnote.doc.markdown_edit

mnote.doc.markdown_edit 不再作为:

  • local-first 普通 Markdown 编辑默认入口。
  • local-first 普通 Markdown 编辑 fallback。
  • remote fallback。
  • 新 agent runtime 的推荐工具。
  • 新 checklist 的验收目标。

已有代码和 smoke 若仍覆盖 markdown_edit,只代表历史在线 / compat 路径的回归证据;不得再由它反推出“local-first 应继续提供 Markdown 编辑工具”。

2.2 mnote.doc.fetch

mnote.doc.fetch 不作为普通正文读取主入口。

local-first 默认路径中,agent 直接读取授权文件。MNote 可以把 selection、文件摘要、block projection 或结构化上下文放进 run input,但不要求 agent 通过 mnote.doc.fetch 再读正文。

2.3 page_ai_workflow / mnote.block.* / mnote.page.save

  • page_ai_workflow 只保留为 debug / 历史兼容门面,不进入 local-first 普通编辑主路径。
  • mnote.block.* 只保留为复杂结构辅助,例如拖拽排序、精确块删除、非 Markdown 结构资源辅助。
  • mnote.page.save 只保留为页面级兜底写入工具,不作为精确 Markdown 编辑入口。

3. 主路径数据流

Page / active editor
  -> Agent Target Resolver freezes selected target package
  -> resolve canonical .md file / resource file
  -> build AiAccessScope
  -> freeze allowed roots / allowed files / readonly / dirty / selection
  -> start Hermes / Reasonix run with file refs and context
  -> agent native patch / diff / write inside allowed roots
  -> MNote collects changed files / diff / audit
  -> watcher observes file changes
  -> BufferStore arbitrates clean / dirty / conflict state
  -> Page Aggregate rebuilds projection
  -> ProseMirror / tiptap island refreshes visible document

关键点:

  • MNote 只给位置、权限和上下文,不给普通 Markdown 编辑工具。
  • 多工作区、多 tab、多资源同时打开时,默认 target 只能来自最后获得编辑焦点的 editor / resource tab;不允许用“第一个 tab”“树选中项”或 URL documentId 静默猜测。
  • 写入动作发生在 agent 自身文件能力内,并受 allowed roots / allowed files 限制。
  • 前台同步由 watcher / BufferStore / Page Aggregate 接回,不由 agent 直接推 UI 状态。
  • dirty buffer 时不得静默覆盖,必须进入冲突或 review 状态。

4. Agent Target Resolver

Agent Target Resolver 是 MNote 发送给 agent 前的唯一目标解析层。它的职责是把当前 UI 状态冻结为一份明确的 targetPackage,而不是让 agent 或 API 自行猜“当前页”。

4.1 选择原则

  • workspace 是权限边界:默认一次 run 只属于一个 workspace;跨 workspace 必须由用户显式多选,并生成多个 allowedRoots
  • tab 是视图,不是真相:tab 只帮助判断用户正在看的对象;最终 target 必须落到 objectIdentityresourceKind 和 canonical path。
  • last focused editor 优先:默认 target 是最后获得编辑焦点的 editor / resource tab。
  • selection 覆盖 whole file:存在真实选区时,默认 scope 为 selection;没有选区时才是 whole file / current resource。
  • 树选中项只作为候选FileTree / PageTree selection 不自动成为 agent target,除非用户在 target picker 中选择“树选中项”。
  • 歧义时不发送:如果焦点不明确、target dirty 且会被写入、或跨 workspace 未显式确认,发送按钮进入需要选择状态。

4.2 targetPackage 合同

{
  "schema": "mnote.agent_target_package.v1",
  "primaryTargetId": "target-1",
  "targets": [
    {
      "targetId": "target-1",
      "workspaceId": "local:design",
      "workspaceSource": "local_folder",
      "workspaceRoot": "/workspace/design",
      "editorGroupId": "main",
      "tabId": "tab-design-md",
      "focusRank": 1,
      "objectIdentity": "local_folder:/workspace/design/design.md",
      "resourceKind": "markdown_page",
      "title": "design.md",
      "canonicalPath": "/workspace/design/design.md",
      "uri": "file:///workspace/design/design.md",
      "version": "mtime:size:hash",
      "scope": "selection",
      "selection": {
        "text": "当前选区文本",
        "anchor": "pm:block:p_1:0..12"
      },
      "dirty": false,
      "readonly": false,
      "ownerPage": null,
      "relatedResources": []
    }
  ],
  "allowedRoots": ["/workspace/design"],
  "allowedFiles": ["/workspace/design/design.md"],
  "policy": {
    "writeMode": "agent_native_file_patch",
    "requiresConfirmation": false,
    "crossWorkspace": false
  }
}

4.3 resourceKind

resourceKind 至少需要区分:

  • markdown_page:普通 .md 页面正文。
  • mindmapmindmap 资源文件;可带 ownerPage,但不伪装成正文。
  • officeOnlyOffice 资源;默认只读上下文,除非明确进入 office 编辑 agent flow。
  • raw_file:普通文件。
  • folder:目录范围;只在用户显式选择“把这个文件夹作为上下文”时出现。

5. UI 设计

当前右侧页面 AI 抽屉已有 runtime / profile / context scope,但“上下文:当前页/选区/块/页面设置”不够表达多工作区、多 tab 和资源 tab。需要把它升级为明确的 target UI。

5.1 默认展示

AI composer 上方固定显示 target chip,不放到设置页深处:

[工作区: design] [design.md] [当前选区] [可写]

如果当前焦点是截图中的右侧 mindmap,则显示:

[工作区: local] [新页面165037 / KMIND] [mindmap] [可写]

chip 必须可点击,打开 target picker。chip 内容优先显示用户能识别的名称,不优先显示内部 id;hover / detail 再显示 path、workspace root、object identity。

5.2 target picker

点击 target chip 后打开轻量 popover,不跳转页面。结构为:

发送给 Agent

当前焦点
  ● design.md                  markdown_page   当前选区   /workspace/design/design.md

打开的 Tab
  ○ 新页面223322               markdown_page   全文
  ○ design.md                  markdown_page   当前选区
  ○ 新页面165037 / KMIND       mindmap         当前资源

树选中项
  ○ 3 个文件                   只读上下文

范围
  ( ) 当前选区   ( ) 当前文件   ( ) 当前资源   ( ) 多选 tab   ( ) 文件夹

[取消] [使用这个目标]

交互规则:

  • 单选是默认;多选必须用户点击“多选 tab”或勾选多个候选。
  • 多 workspace 候选默认折叠在各自 workspace 组下,跨 workspace 多选时出现权限提示。
  • dirty target 显示 未保存 标记;如果 agent 可能写入,发送前需要确认。
  • readonly target 显示 只读,发送按钮文案变成“只读提问”或禁用写入型 intent。
  • resource target 不能显示成“当前页”;必须显示 mindmap / office / raw file

5.3 composer 中的 context scope

context scope select 不应继续承担 target 选择职责。它只控制 primary target 内部范围:

  • selection:当前选区。
  • file:当前文件。
  • resource:当前资源文件。
  • related:当前页面引用的相关资源,只读附加上下文。

不再把 page / block / options 混成同一层 target 选择。页面设置属于 target 的辅助上下文,不是普通 Markdown 编辑范围。

5.4 发送前状态

发送按钮按 targetPackage 状态变化:

  • 发送:单 target、clean、可写或只读问答。
  • 选择目标:没有明确 last focused target,或当前焦点和 URL / active tab 冲突。
  • 确认发送:跨 workspace、多 target、dirty write、folder scope。
  • 只读提问readonly target 或用户选择只读上下文。

发送后第一条用户消息旁边显示冻结的 target 摘要,避免 run 过程中用户切 tab 后误以为 agent 改了新 tab

目标:design.md / 当前选区 / local_folder / 12:31:05 冻结

6. SQLite control-plane 职责

Rust SQLite control-plane 是默认控制面,负责:

  • auth / session / membership。
  • share grants / sync state。
  • AI policy、workspace policy、allowed roots / allowed files。
  • ACP / Hermes / Reasonix runtime session。
  • targetPackage 审计与用户确认记录。
  • permission decision、approval decision、tool / file access audit。
  • changed files / diff / command context 记录。

它不持有普通 Markdown 正文真相。本地 .md 文件仍是正文真相。

7. 运行时输入合同

每次从页面触发 agent 编辑时,run input 至少应包含:

{
  "workspaceSource": "local_folder",
  "targetPackage": {
    "schema": "mnote.agent_target_package.v1",
    "primaryTargetId": "target-1",
    "targets": []
  },
  "currentFile": {
    "path": "/workspace/page.md",
    "uri": "file:///workspace/page.md",
    "version": "mtime:size:hash"
  },
  "allowedRoots": ["/workspace"],
  "allowedFiles": ["/workspace/page.md"],
  "selection": {
    "text": "当前选区文本",
    "anchor": "可选的 editor/block/offset 引用"
  },
  "context": {
    "readonly": false,
    "dirty": false,
    "resourceRefs": []
  }
}

后续可以补充 page id、object identity、resource refs、outline、nearby context,但不得把普通正文写入重新包装成 mnote.doc.markdown_edit

8. 验收清单

Phase A0target selection 与 UI

  • Page AI composer 可见 target chip,显示 workspace、target title、resourceKind、scope、readonly / dirty 状态。
  • 点击 target chip 打开 target picker,可在当前焦点、打开 tabs、树选中项之间选择。
  • 默认 target 来自 last focused editor / resource tab,不来自第一个 tab、URL documentId 或树选中项。
  • mindmap / office / raw file target 显示真实 resourceKind,不伪装成 markdown page。
  • 多 tab / 跨 workspace / dirty write 时发送前需要显式确认。
  • 发送后消息记录冻结 target 摘要,切换 tab 不影响已启动 run。

Phase A:运行时输入与权限边界

  • 从文档页发起 Hermes / Reasonix run 时,request body 包含 currentFileallowedRootsallowedFiles、selection 和 dirty / readonly context。
  • run body 包含 targetPackage,且 allowedRoots / allowedFiles 从 targetPackage 推导。
  • local-first 普通 Markdown 编辑 run 不包含 mnote.doc.markdown_edit 推荐工具。
  • allowed roots 外文件写入被 agent runtime 或 MNote 审计层拒绝。
  • readonly 页面不会启动可写 run,或只启动只读问答 run。

2026-06-01 进展:已补第一版 mnote.agent_target_package.v1 运行时输入。前端 Page AI run body 会从当前 editorTarget / WorkspacePath 生成 targetPackage;后端会 sanitize 该包,并把 local-first aiAccessScope.allowedFiles / allowedFilePathsagentRunEnvelope.allowedFiles 从该包派生。task502-page-ai-agent-selector-context-smoke.js 已覆盖 page / mindmap / OnlyOffice target picker 与 payload 冻结;task520-page-ai-raw-resource-target-smoke.js 已覆盖真实 raw local resource tab,并断言 objectIdentity 不退化为 [object Object]。当前仍未完成跨 workspace 多选确认和真实 agent 写入回收 smoke,因此本文继续保持 process

Phase B:写入回收与前台同步

  • agent 修改当前 .md 后,MNote 能回收 changed files / diff。
  • clean buffer 下 watcher -> BufferStore -> Page Aggregate -> ProseMirror 可见更新通过 browser smoke。
  • dirty buffer 下外部 agent 写入不会静默覆盖,必须出现冲突或 review 状态。
  • changed files / diff 与 session / run / actor 关联写入审计。

2026-06-06 执行拆分:Phase B/C 的真实 smoke 与审计收口已拆到 design/07-ai/process/7-18-agent-edit-clean-dirty-smoke-checklist-v1.md。该拆分只覆盖 local-first 普通 Markdown agent 原生文件编辑,不碰 LightRAG / Knowledge RAG / OCR / evidence index。

2026-06-07 进展:task535 已证明 clean buffer 下 agent 原生文件 patch 写入真实 .md 后,receipt 触发当前文档与 filetree 刷新,前台 tiptap 无需 reload 即可见更新;task536 已证明 dirty buffer 在启动可写 run 前阻塞且磁盘不变;local_agent_audit targeted tests 已覆盖 allowedFiles 优先、folder context 不扫全 root、超限 auditScope 可见。

Phase C:旧工具退役

  • local-first 普通 Markdown 编辑 smoke 断言未调用 /api/documents/save
  • local-first 普通 Markdown 编辑 smoke 断言未调用 mnote.doc.markdown_edit
  • Hermes / Reasonix manifest 或 system prompt 不再鼓励普通 Markdown 编辑调用 mnote.doc.markdown_edit
  • markdown_edit 测试只保留为历史 online / compat 回归,并在文档中标明不指导新实现。

2026-06-07 进展:Page AI / Hermes run guidance 明确禁止 local-first 普通 Markdown 编辑调用 mnote_doc_markdown_editmnote_page_savemnote.doc.markdown_edit manifest 描述已收口为 compat / remote / cloud fallback 或结构校验辅助;Reasonix ACP wrapper selftest 已断言 prompt 包含禁止使用 MNote doc/page write tools 的口径;task535 同时断言未调用 /api/documents/savemnote.doc.markdown_editmnote.page.save

Phase D:结构辅助边界

  • mnote.block.* 只在复杂结构辅助场景出现,例如结构块排序、非 Markdown 资源辅助。
  • mnote.page.save 只作为页面级兜底写入,不作为默认精确编辑入口。
  • mnote.doc.fetch 不作为 local-first 正文读取主入口;需要结构化上下文时由 MNote 在 run input 中提供摘要或 projection。

9. 不做事项

  • 不参考 CLI Main 的 Lark Doc 在线文档写入架构来设计 local-first 普通 Markdown 编辑。
  • 不新增 MNote 普通 Markdown 编辑工具。
  • 不把 Convex 恢复为默认正文或 AI 会话全文主存储。
  • 不实现 Phase C Review Mode / StreamApplyController / GhostTextOverlay。
  • 不让前端维护第二份正文真相。
  • 不把所有打开 tab 默认发送给 agent。
  • 不把跨 workspace 自动合并为一个隐式上下文。

10. 归档条件

满足以下条件后,本稿可归档到 design/07-ai/done/

  • Phase A0 target UI 有真实浏览器 smoke 证据。
  • Phase A / B / C 至少各有一条真实浏览器或 targeted test 证据。
  • local-first agent 文件编辑主路径已能在 clean buffer 下完成真实 .md 修改并同步回 tiptap。
  • dirty conflict 不静默覆盖。
  • 文档和 manifest 不再把 mnote.doc.markdown_edit 描述为 local-first fallback。