Files
mnote/design/old/07-ai/done/7-25-acp-session-runtime-enhancement-plan-v1.md
T
Agent Board b798f628ee chore: land tree view-state, vault, Pi module split, and repo hygiene
Persist PageTree expand state via control-plane view-state and align
chevron/DOM with restored expansion; keep Sidex-style shallow page-tree
scan and drop the unused recursive scanner that only added cargo noise.

Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi
into a module package, and retire Hermes/ACP/OpenHub recycle + root
harness evidence from the index while gitignoring recycle and local
diag dumps.

Archive superseded design/bugs docs under old/, point architecture at
ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor
regressions so the working tree can stay clean.
2026-07-21 05:13:05 +08:00

26 KiB
Raw Blame History

[recycle] 7-25 [process] ACP Session Runtime 增强规划 v1

更新时间:2026-05-17 参考:hermes-vscode-main (ACP client) / hermes-web-ui-0.5.18 (HTTP API)

2026-05-22 口径补充:本文是 ACP session runtime 增强的历史执行记录,当时曾选择根 convex/ 作为 ACP-local runtime store。后续 2-8 已把默认 auth / ACP-Hermes runtime session / share / sync / AI policy 控制面替换为 Rust SQLite control-plane;根 convex/ functions 源码已软删除到 recycle/20260522-convex-runtime-retirement/convex/scripts/run-convex-deploy.js 已归档到同批 recycle 目录。下文中的 Convex store / 根 convex/ 部署描述仅作为历史记录,不再是当前实现方向。

1. 当前状态

当前 mnote-web 的 ACP 实现是半成品,但已有的 route 和 runtime 壳并不等于“本地会话持久化已完成”:

领域 已有 缺失
Wire protocol AcpClientJSON-RPC 2.0 over stdio
Session lifecycle AcpSessionManagercreate/prompt/cancel 持久化、多会话管理
Event dispatch AcpSessionEvent 枚举 + SSE 输出
Session CRUD POST /client/sessions 创建;GET /client/sessionsGET /client/sessions/{session_id}POST /client/sessions/{session_id}/resume 已注册,但当前仍是 Hermes proxy / runtime 壳语义 列表/查询/重命名/删除 的本地持久化 API
Run lifecycle create_run / stream_events / abort_run
Session persistence Hermes HTTP 路径仍遵循 Hermes 持有会话真相;ACP 路径目前没有本地后端 store ACP-local runtime/session cache 或独立持久化边界未定
Message history 消息不持久化
Session search 无搜索能力
Usage tracking usage_update 已能映射为 SSE usage.updated 无 session 级持久聚合、查询 API 和 UI
Permission request acp_client.rs 仅日志(bug 3-20 无协议响应
Session resume route 已注册,resume_session 目前仅回显 get_session 结果 真实 resume / 历史注入 / 续跑语义不完整
Auto-title 无自动命名
Conversation export 无导出
Session list UI 当前只有页面 AI 的本地短期历史列表(localStorage),不是后端 ACP 会话列表 后端会话列表 / 详情 / 搜索 还未接到 UI

1.1 正确性结论

本文对“ACP runtime 已接入但 session runtime 仍不完整”的判断成立;但“在 mnote-web 中新增本地 sessions/messages 并替代内存表”不能直接理解为新的 AI 聊天真相层。

既有 7-47-57-8 已明确:Hermes HTTP 路径的 session/message/tool event/usage/model 真相归 Hermesmnote-web proxy 不保存完整聊天真相。因此本计划若引入本地存储,默认只能用于:

  • ACP-local run payload、runtime registry、断线重连、短期恢复所需的技术状态;
  • mnote 自己产生的 tool audit、业务写入结果、artifact / page / tree 事实;
  • 经单独架构决策确认后的 ACP 会话缓存或索引。

如果要把 mnote 的存储升级为跨 Hermes HTTP / ACP 的长期会话真源,必须先更新 7-5 / 7-8 的边界,而不能作为本计划的隐含前提。并且 mnote 是多用户系统,AI session 必须按用户、workspace、document 进行隔离;当前默认数据库边界已改为 Rust SQLite control-plane,不再新增 Convex functions 作为默认 runtime store。

2. 参考实现分析

hermes-vscode-mainACP 客户端参考)

  • acpClient.ts — JSON-RPC 2.0 over stdio,支持 request/response/notification/incoming request。 基础结构已移植到 acp_client.rs
  • sessionManager.ts — Session 生命周期管理 + session/update 事件分发。基础结构已移植到 acp_session_manager.rs
  • sessionStore.ts关键缺失部分。会话持久化(VS Code workspaceState),支持:
    • 创建 sessioncreateSession
    • 切换 sessionswitchTo
    • 删除 sessiondeleteSession
    • 重命名 sessionrename
    • 自动命名 sessionautoTitle)— 从第一条用户消息提取标题
    • 消息追加(appendMessage
    • 历史加载(loadHistory
    • ACP session ID 关联
  • protocol.ts — 事件解析 + 去重。deduplicateChunk 逻辑已移植。
  • types.tsChatSessionStoredMessageTodoState 等类型。

hermes-web-ui-0.5.18HTTP REST API 参考)

  • controllers/hermes/sessions.ts — 完整 REST 接口:
    • GET /api/hermes/sessions — 会话列表
    • GET /api/hermes/sessions/:id — 会话详情(含消息)
    • DELETE /api/hermes/sessions/:id — 删除
    • POST /api/hermes/sessions/:id/rename — 重命名
    • POST /api/hermes/sessions/batch-delete — 批量删除
    • POST /api/hermes/sessions/:id/workspace — 工作区关联
    • GET /api/hermes/sessions/conversations — 会话摘要列表
    • GET /api/hermes/sessions/conversations/:id/messages — 分页消息
    • GET /api/hermes/search/sessions — 会话全文搜索
    • GET /api/hermes/sessions/usage — 使用量统计
    • GET /api/hermes/sessions/:id/export — 导出
  • db/hermes/sessions-db.ts — 参考实现中的会话存储,支持:
    • 完整 CRUD
    • 全文搜索(FTS5
    • 用量统计
    • 消息分页
  • db/hermes/session-store.ts — 本地 JSON 文件备选存储

3. 建议新增功能

按优先级分三阶段:

Phase ASession 持久化与管理(P0 — 缺少则 ACP 无实用价值)

  1. 会话持久化存储

    • 默认先建设 ACP-local runtime store / TTL cache,不改 Hermes HTTP 会话真相归属
    • 持久化如需数据库,默认落到 Rust SQLite control-plane;不新增根 Convex functions 作为默认 runtime store
    • 表结构先服务 ACP_RUN_PAYLOADS / ACP_ACTIVE_RUNS 的可靠生命周期,并且所有记录必须带 userId / workspaceId / documentId 作用域
    • sessions/messages 如要保存完整聊天历史,必须先完成架构决策并标明只覆盖 ACP 路径还是统一覆盖 Hermes HTTP + ACP
  2. 会话管理 API

    • GET /client/sessions — 会话列表(分页、排序)
    • GET /client/sessions/{session_id} — 会话详情(含消息)
    • DELETE /client/sessions/{session_id} — 删除
    • POST /client/sessions/{session_id}/rename — 重命名
    • POST /client/sessions/{session_id}/auto-title — 自动命名
  3. 消息持久化

    • ACP stream_events 执行过程中按架构决策写入 user message + agent response,或只写 runtime/event 索引
    • 支持追加消息到已有 ACP-local sessionHermes HTTP 历史仍从 Hermes 读取
  4. 会话搜索

    • GET /client/sessions/search?q=... — 全文搜索(基于 Convex 查询能力,必要时再接专用搜索索引)
    • 返回匹配的 session 摘要 + snippet
  5. Permission request 响应修复bug 3-20

    • 已知 incoming request 应返回结构化 error 而非仅日志
    • session/request_permission 至少需要 auto-deny + 通知前端
    • 配合 Phase C 的 UI 实现人工确认

Phase B:历史消息与管理增强(P1 — 常用功能)

  1. 会话恢复(Resume

    • 当前 POST /client/sessions/{session_id}/resume 已有 route,需实现
    • 从 ACP-local store 或 Hermes session store 加载历史消息,作为后续 prompt 的 context 来源
    • 支持新模型继续已有会话
  2. 分页消息查询

    • GET /client/sessions/{session_id}/messages?cursor=...&limit=...
    • 支持时间范围和角色过滤
  3. 用量记录

    • 从 ACP usage_update 事件提取 contextUsed / contextSize
    • 写入 sessions 表的 usage 字段
    • GET /client/sessions/usage — 聚合统计
  4. 会话导出

    • GET /client/sessions/{session_id}/export?format=json|markdown
    • JSON 格式:完整结构化导出
    • Markdown 格式:人类可读对话导出
  5. ACP run payload 生命周期修复bug 3-21

    • 停止在 stream_events.remove(run_id) 释放 payload
    • 改为基于 Convex session/runtime 记录或 TTL 缓存

Phase C:前端交互与高级功能(P2 — 用户体验提升)

  1. 前端会话列表

    • Sidebar 或独立面板展示历史会话
    • 支持切换、删除、重命名
    • 支持继续已有会话
  2. 会话搜索 UI

    • 搜索框,全字段搜索(标题、消息内容)
    • 结果高亮 + 跳转
  3. Token 用量可视化

    • 每次 AI 调用后展示 token 消耗
    • 会话级别统计累计用量
  4. 权限请求 UI

    • Agent 发起 session/request_permission 时前端弹窗确认
    • 支持 auto-allow / auto-deny 配置
  5. 多会话并发

    • 支持同时运行多个 ACP session
    • 前端标签页切换

4. 技术方案

4.0 历史 Convex 源目录口径

当前仓库状态下:

  • recycle/20260522-convex-runtime-retirement/infra/convex/ 只保留自托管 Convex backend/dashboard 的 Docker 与说明作为历史对照,不是 functions/schema 源目录;
  • wolai-frontend/convex/ 已不在当前工作区可读路径中,不能作为新的实现落点;
  • recycle/wolai-frontend/convex/ 只作为历史参考;
  • 本轮历史执行曾在仓库根 convex/ 建立 ACP-local runtime store 的最小 functions/schema,并让 scripts/run-convex-deploy.js 优先以仓库根作为 Convex CLI cwd;当前这些源码和脚本已退役到 recycle/20260522-convex-runtime-retirement/

后续若要恢复 Convex cloud / compat source,必须先明确新的 cloud/compat 部署边界,再从 recycle 恢复必要 functions;不能把根 convex/ 重新作为默认 active deploy source。

4.1 存储方案

注意:以下结构是历史 Convex-backed ACP-local store 候选形状,不默认推翻 Hermes 持有聊天真相的既有边界。当前默认 store 已转向 Rust SQLite control-plane;多用户隔离字段仍是硬要求。

// sessions 记录
struct SessionRow {
    id: String,          // 主键
    user_id: String,     // 多用户隔离
    workspace_id: Option<String>,
    document_id: Option<String>,
    title: Option<String>,
    profile: String,
    model: Option<String>,
    source: String,       // "acp" | "hermes-http"; hermes-http 默认只保存引用/索引,不保存聊天真相
    started_at: i64,
    ended_at: Option<i64>,
    end_reason: Option<String>,
    message_count: u32,
    input_tokens: u64,
    output_tokens: u64,
    preview: Option<String>,  // 首条消息摘要
}

// messages 记录
struct MessageRow {
    id: i64,             // 自增
    user_id: String,     // 多用户隔离
    session_id: String,  // FK -> sessions
    role: String,        // "user" | "assistant" | "tool"
    content: String,
    reasoning: Option<String>,
    tool_calls: Option<Value>,  // JSON
    token_count: Option<u32>,
    created_at: i64,
}

4.2 现有代码改动范围

  • 历史项:曾新增/修改根 convex/ 相关 schema / functions,承接 ACP-local session/runtime 记录、用户隔离查询、TTL 清理;当前默认应改 Rust SQLite control-plane schema/APIinfra/convex/ 已软删除到 recycle,只负责历史对照,不再作为 active 服务部署
  • 修改 acp_session_manager.rs — 按架构决策写入 ACP-local 消息或只写 runtime/event 索引
  • 修改 hermes_client.rs — 新增 session 管理 route handlers
  • 修改 routes/mod.rs — 注册新 route
  • 修改 control-plane / compat transport 调用点 — mnote-web 默认通过 SQLite control-plane 读写 session/runtime 索引,Convex 只作为显式 legacy compat

4.3 与现有 bug 的关系

Bug Phase 说明
3-20 ACP incoming request 无响应 A permission request 必须有协议响应
3-21 ACP run payload 消费后移除 B Convex-backed store / TTL cache 化后 payload 由 session 管理,不再依赖内存 map
7-19 Hermes 指导优先 apply_block_ops C 需统一到 markdown_edit 口径
7-20 page_ai_workflow 绕过 tool executor C 建议统一到 ACP tool executor

5. 实施建议顺序

Phase AP0 基础可用)
  ├── 5.1 Convex-backed ACP-local Session Store(多用户隔离 + TTL 策略)
  ├── 5.2 会话列表 / 详情 / 删除 / 重命名 API
  ├── 5.3 消息或 runtime/event 索引持久化(stream_events 写入)
  ├── 5.4 会话搜索
  └── 5.5 Permission request 响应(bug 3-20

Phase BP1 常用增强)
  ├── 5.6 会话恢复
  ├── 5.7 分页消息查询
  ├── 5.8 用量记录
  ├── 5.9 会话导出
  └── 5.10 Payload 生命周期修复(bug 3-21

Phase CP2 用户体验)
  ├── 5.11 前端会话列表
  ├── 5.12 搜索 UI
  ├── 5.13 用量可视化
  ├── 5.14 权限请求 UI
  └── 5.15 多会话并发

6. 详细 checklist

6.1 先确认数据契约

  • 先确认是否允许 mnote-web 保存完整 AI session/message 真相;若不允许,本文存储范围必须限定为 ACP-local runtime cache / metadata index。
    • 结论:本轮不保存完整 AI session/message 真相,只保存 ACP-local runtime run metadata / payload cache / index。
  • 明确 Hermes HTTP 路径继续由 Hermes 持有 session/message/tool event/usage/model 真相,mnote-web 只保存引用、audit 或业务结果。
  • 明确所有 AI session 记录必须带 userId,并按 userId + workspaceId + documentId 过滤,禁止跨用户读取。
  • 明确 Convex sessions 记录的唯一键、排序键、软删除策略和保留周期。
    • 当前实现采用更小的 acp_runtime_runsrun_id 唯一,按 user_id + workspace_id + document_id/session_id + created_at 排序,deleted_at 软删除,payload 记录带 7 天 TTL 元数据。
  • 明确 Convex messages 记录是否允许同一 session_id 下多次 resume 追加写入。
    • 结论:本轮不建完整 messages 真相表;后续若引入只覆盖 ACP 路径的消息索引,允许同一 session_id 多 run 追加 runtime events,但不复制 Hermes HTTP 聊天真相。
  • 明确 session_idrun_id 的映射是否需要单独持久化,避免仅靠内存 map。
    • 当前已在 acp_runtime_runs 中持久化 session_id/run_id 映射。
  • 明确 profilemodelsource 三个字段在 ACP/Hermes HTTP/Reasonix 三种路径下的取值规则。
    • 当前实现:source = "acp"profile 沿请求 profileacpRuntime 沿请求或 profile 推导;model 暂不落库,等待 usage/model 聚合阶段。
  • 明确 usage 统计的口径:只记 usage_update,还是合并 run.completed.usage
    • 结论:usage_update 作为运行中实时增量;run.completed.usage 作为最终校正值。两者都只落 ACP-local runtime/usage index,不复制 Hermes HTTP 聊天真相。

6.2 先做后端持久化骨架

  • 历史阶段曾为 ACP-local session/runtime store 设计 Convex schema;当前默认已改为 SQLite control-plane。
  • 历史阶段曾在 Convex 中建 sessionsmessages 或更小的 runtimeRuns / runtimeEvents 记录,补齐索引和权限过滤。
    • 历史实现为根 convex/schema.tsacp_runtime_runs / acp_runtime_events,以及 convex/aiSessions.tsupsertRuntimeRun/getRuntimeRun/listRuntimeRuns;当前根 convex/ 已退役。
  • ACP_RUN_PAYLOADS / ACP_ACTIVE_RUNS 中必须保留的数据拆到持久化层或短 TTL 层。
    • 历史阶段 create_run 会把 ACP payload/runtime 写入 Convex;当前默认写入 SQLite control-plane,旧 Convex store 仅在显式 legacy compat 下使用。
  • create_run 产出的 sessionIdprofiletraceIdruntime 写入 sessions 记录。
    • 当前写入目标为更小的 acp_runtime_runs,而不是完整聊天 sessions 真相表。
  • stream_events 的 ACP 路径里,按架构决策写入 messages 或 runtime/event 索引,不能把 Hermes HTTP 聊天真相复制进 mnote。
    • 当前实现:ACP SSE event 以 best-effort 写入 acp_runtime_events,只保存 runtime event payload,不写完整聊天 messages 真相。

6.3 再补 session 读接口

  • GET /client/sessions 返回当前用户可见的 Convex store 或 Hermes upstream 会话摘要,而不是只依赖前端 localStorage。
    • 当前实现:ACP profile / source=acpaiSessions:listRuntimeRunsHermes HTTP profile 仍走 upstream。
  • GET /client/sessions/{session_id} 返回会话元数据、消息列表、runtime 状态。
    • 当前实现:source=acp 从 Convex store 读取 runs/eventsmessages 保持空数组以避免复制完整聊天真相。
  • POST /client/sessions/{session_id}/resume 真正从持久化历史恢复,而不是简单复用 get_session
    • 当前实现:source=acp 返回 Convex runtime history,并标记 resumed=true / resumeSource=convex_acp_runtime_store
  • DELETE /client/sessions/{session_id}POST /client/sessions/{session_id}/renamePOST /client/sessions/{session_id}/auto-title 补齐路由与处理器。
    • 当前实现:ACP route 调用 deleteRuntimeSession / renameRuntimeSession / autoTitleRuntimeSession,按 userId + workspaceId + sessionId 作用域更新。
  • 为搜索接口加分页和最小 snippet,避免一次返回过长正文。
    • 当前实现:GET /client/sessions/search?source=acp&q=...&limit=... 调用 searchRuntimeSessions,最多返回 50 条 session 摘要和 snippet。

6.4 再修 ACP 协议行为

  • session/request_permission 生成结构化响应,不再只打日志。
  • 给未知 incoming request 返回明确 error,避免 agent 一直等超时。
  • 把 permission 结果同步到前端,至少先支持 auto-allow / auto-deny。
    • 当前实现:session/request_permission 仍由 ACP client 自动拒绝以避免 agent 超时,同时转成 permission.denied SSE;页面 AI 侧展示权限弹窗/卡片,包含 tool 名、参数摘要、允许/拒绝动作入口。
  • usage_updaterun.completed 的 usage 汇总到 session 级统计。
    • 当前实现:appendRuntimeEvent 在收到 usage.updated 或带 usagerun.completed 时,同步更新 acp_runtime_runs.usage
  • 验证 thought.delta 仍不会落入最终 assistant 正文。
    • 当前验证:acp_thought_delta_does_not_emit_message_delta 确认 thought 只作为 thought.delta 转发。

6.5 再补前端入口

  • 在页面 AI 面板中加入后端会话列表,而不是只显示 localStorage 的短期历史。
    • 当前实现:页面 AI history 面板打开时会请求 GET /api/hermes/client/sessions?source=acp&workspaceId=...&documentId=...,并与 localStorage 短期缓存合并。
  • 会话列表支持切换、重命名、删除、恢复。
    • 当前实现:history 行内提供恢复、重命名、删除按钮;切换会话时会拉取 Convex-backed detail/resume。
  • 会话详情页或抽屉支持查看消息分页与搜索结果。
    • 当前实现:恢复/详情会读取最新 run 的 events 并映射为消息视图;history 面板提供后端 session 搜索框,搜索结果展示 snippet。当前消息详情读取后端最近 200 条 event,尚未做 cursor 翻页。
  • 权限请求弹窗至少展示 tool 名、参数摘要、允许/拒绝动作。
    • 当前实现:permission.requested / permission.denied / permission.allowed 会生成权限弹窗和消息卡片;当前 ACP incoming request 默认 auto-deny,按钮用于前端状态确认,后续可接入真实审批回写。
  • Token / 用量信息在会话条目和运行状态上可见。
    • 当前实现:会话条目和当前 session 状态展示 usage.updated / run.completed.usage 汇总。

6.6 最后做回归验证

  • 新建 ACP 会话后刷新页面,仍能从 Convex-backed store 或 Hermes session store 重新拿到同一会话。
    • 当前实现:ACP profile 创建 session 时写入 aiSessions:upsertRuntimeRunsession.created 索引记录;刷新后列表可从 Convex-backed store 找回该 session。
  • 用两个测试用户分别创建 AI session,确认 session 列表、详情、resume 都只返回当前用户自己的记录。
    • 当前实现:Convex functions 使用 ctx.auth.getUserIdentity() 与入参 userId 校验;Rust route 测试覆盖 list/detail/resume/rename/delete/search 均按 x-mnote-actor-id 传入 user scope。尚未跑真实双账号浏览器 smoke。
  • 断线重连后,stream_events 能从历史继续恢复,而不是丢失 payload。
    • 当前最小验证覆盖 payload lookup 不再 remove;完整断线重连仍需浏览器 smoke。
  • session/request_permission 会返回可验证的协议响应。
  • message.deltathought.deltatool.*run.completed 的 SSE 映射在浏览器里都能看见正确结果。
    • 当前实现:页面 AI stream handler 映射 message.deltathought.deltausage.updatedtool.*run.completed;Rust/JS 静态测试覆盖前端入口,尚未完成真实浏览器截图级验证。
  • 运行一次真实浏览器 smoke,确认会话列表、恢复、重命名和删除链路都能闭环。
    • 当前验证:启动 mnote-web127.0.0.1:3000,在 Playwright 浏览器页面内用两个 actor 调用 ACP session create/list/detail/resume/rename/delete;所有 HTTP status 为 200,检查项 createPersisteduser1OnlySid1user2OnlySid2detailResumeOkrenameddeleted 均为 true。

7. 执行记录

2026-05-18

  • 新增根 convex/schema.tsacp_runtime_runs / acp_runtime_events 表和索引。(历史记录;当前已退役到 recycle)
  • 新增根 convex/aiSessions.tsupsertRuntimeRungetRuntimeRunlistRuntimeRuns,按 userId 校验 identity 作用域。(历史记录;当前已退役到 recycle)
  • 修改 scripts/run-convex-deploy.js:优先以仓库根 convex/ 作为 Convex deploy cwd,避免继续指向已不存在的 wolai-frontend/。(历史记录;当前脚本已退役到 recycle,desktop:hot / dev:hot 不部署 Convex
  • 后续历史修正:根 convex/ 当时只包含 ACP session runtime store,不能直接覆盖完整本地 Convex 部署;scripts/run-convex-deploy.js 曾改为只有根 convex/ 具备完整 schema 时才用根目录,否则继续使用现有完整 Convex 源。当前根 convex/ 和该 deploy 脚本均已退役到 recycle。
  • 修改 rust/crates/mnote-web/src/routes/hermes_client.rs
    • ACP create_run 写入 aiSessions:upsertRuntimeRun(历史 Convex store;当前默认 SQLite);
    • ACP GET /client/sessions 读取 aiSessions:listRuntimeRuns(历史 Convex store;当前默认 SQLite);
    • ACP GET /client/sessions/{session_id} 读取 runs/events
    • ACP POST /client/sessions/{session_id}/resume 返回 runtime history 恢复来源;
    • ACP DELETE /client/sessions/{session_id}renameauto-title 写入 runtime store
    • ACP GET /client/sessions/search 返回 runtime store 搜索摘要和 snippet
    • ACP stream_events 将 SSE event 写入 runtime event store
    • stream_events 使用 clone lookup 保留 ACP payload,不再 .remove(run_id)
  • 验证:
    • cd rust && cargo test -p mnote-web hermes_client_acp -- --nocapture10 passed。
    • cd rust && cargo test -p mnote-web test_incoming_permission_request_gets_response -- --nocapture1 passed。
    • cd rust && cargo test -p mnote-web acp_permission -- --nocapture1 passed。
    • cd rust && cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --nocapture1 passed。
    • cd rust && cargo test -p mnote-web acp_thought_delta_does_not_emit_message_delta -- --nocapture1 passed。
    • node -c scripts/run-convex-deploy.js:历史验证通过;当前脚本已退役到 recycle。
    • node scripts/run-convex-deploy.js:历史验证通过;当时部署入口已避免用根最小 convex/ 覆盖完整本地 Convex schema。当前默认启动链路不再部署 Convex。
    • node 提取并 new Function(SIDEBAR_TREE_JS):通过。
    • npx convex --version1.39.1。
    • 历史验证:CONVEX_TMPDIR=/mnt/Data1T/mnote/.convex-tmp npx convex codegen --dry-run --typecheck try 曾通过;当前 .convex-tmp 已软删除到 recycle/20260522-convex-runtime-retirement/local-state/.convex-tmp,不再作为 active codegen 临时目录。
    • Playwright browser smoke127.0.0.1:3000 页面内 fetch 创建两个用户的 ACP session,并验证 list/detail/resume/rename/delete 与用户隔离,全部通过;测试数据已调用 delete 清理。
    • 回归收口:tree_command_purge_uses_tree_command_protocol 中 purge 请求未携带排序,返回 sortOrder: null 符合当前 route 语义,已同步修正测试断言。
    • 回归收口:hermes_client / hermes_tools / page_ai_workflow 测试统一使用 crate 级 Hermes 环境锁,避免完整并发测试时互相修改 HERMES_HOME / MNOTE_WEB_HERMES_*
    • cd rust && cargo test -p mnote-web tree_command_purge_uses_tree_command_protocol -- --nocapture1 passed。
    • cd rust && cargo test -p mnote-web hermes_tools_call_rejects_profile_disabled_tool -- --nocapture1 passed。
    • cd rust && cargo test -p mnote-web hermes_client_profile_skill_and_memory_routes_use_local_bff_without_upstream -- --nocapture1 passed。
    • cd rust && cargo test -p mnote-web block_edit_workflow_respects_disabled_markdown_edit_tool -- --nocapture1 passed。
    • cd rust && cargo test -p mnote-web -- --nocapture336 passed / 0 failedmain tests 0 passed / 0 faileddoc tests 4 ignored。
  • 依赖记录:
    • package.json / lockfile 增加 convex@1.39.1,用于根 convex/ codegen/typecheck。
    • npm install --save-dev convex@1.39.1 曾因既有 node_modules 布局报 ENOTDIR,未保留该失败命令产生的临时 symlink;随后用 pnpm 指定仓库 store 安装,并恢复被 pnpm 移入 .ignored 的既有 Playwright / electron-builder 目录。