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

374 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# [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 | `AcpClient`JSON-RPC 2.0 over stdio)✅ | — |
| Session lifecycle | `AcpSessionManager`create/prompt/cancel)✅ | 持久化、多会话管理 ❌ |
| Event dispatch | `AcpSessionEvent` 枚举 + SSE 输出 ✅ | — |
| Session CRUD | `POST /client/sessions` 创建;`GET /client/sessions``GET /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-4``7-5``7-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),支持:
- 创建 session`createSession`
- 切换 session`switchTo`
- 删除 session`deleteSession`
- 重命名 session`rename`
- 自动命名 session`autoTitle`)— 从第一条用户消息提取标题
- 消息追加(`appendMessage`
- 历史加载(`loadHistory`
- ACP session ID 关联
- **`protocol.ts`** — 事件解析 + 去重。`deduplicateChunk` 逻辑已移植。
- **`types.ts`** — `ChatSession``StoredMessage``TodoState` 等类型。
### 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 — 常用功能)
6. **会话恢复(Resume**
- 当前 `POST /client/sessions/{session_id}/resume` 已有 route,需实现
- 从 ACP-local store 或 Hermes session store 加载历史消息,作为后续 prompt 的 context 来源
- 支持新模型继续已有会话
7. **分页消息查询**
- `GET /client/sessions/{session_id}/messages?cursor=...&limit=...`
- 支持时间范围和角色过滤
8. **用量记录**
- 从 ACP `usage_update` 事件提取 `contextUsed` / `contextSize`
- 写入 `sessions` 表的 usage 字段
- `GET /client/sessions/usage` — 聚合统计
9. **会话导出**
- `GET /client/sessions/{session_id}/export?format=json|markdown`
- JSON 格式:完整结构化导出
- Markdown 格式:人类可读对话导出
10. **ACP run payload 生命周期修复**bug 3-21
- 停止在 `stream_events``.remove(run_id)` 释放 payload
- 改为基于 Convex session/runtime 记录或 TTL 缓存
### Phase C:前端交互与高级功能(P2 — 用户体验提升)
11. **前端会话列表**
- Sidebar 或独立面板展示历史会话
- 支持切换、删除、重命名
- 支持继续已有会话
12. **会话搜索 UI**
- 搜索框,全字段搜索(标题、消息内容)
- 结果高亮 + 跳转
13. **Token 用量可视化**
- 每次 AI 调用后展示 token 消耗
- 会话级别统计累计用量
14. **权限请求 UI**
- Agent 发起 `session/request_permission` 时前端弹窗确认
- 支持 auto-allow / auto-deny 配置
15. **多会话并发**
- 支持同时运行多个 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;多用户隔离字段仍是硬要求。
```rust
// 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/API`infra/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 先确认数据契约
- [x] 先确认是否允许 mnote-web 保存完整 AI session/message 真相;若不允许,本文存储范围必须限定为 ACP-local runtime cache / metadata index。
- 结论:本轮不保存完整 AI session/message 真相,只保存 ACP-local runtime run metadata / payload cache / index。
- [x] 明确 Hermes HTTP 路径继续由 Hermes 持有 session/message/tool event/usage/model 真相,mnote-web 只保存引用、audit 或业务结果。
- [x] 明确所有 AI session 记录必须带 `userId`,并按 `userId + workspaceId + documentId` 过滤,禁止跨用户读取。
- [x] 明确 Convex `sessions` 记录的唯一键、排序键、软删除策略和保留周期。
- 当前实现采用更小的 `acp_runtime_runs``run_id` 唯一,按 `user_id + workspace_id + document_id/session_id + created_at` 排序,`deleted_at` 软删除,payload 记录带 7 天 TTL 元数据。
- [x] 明确 Convex `messages` 记录是否允许同一 `session_id` 下多次 `resume` 追加写入。
- 结论:本轮不建完整 `messages` 真相表;后续若引入只覆盖 ACP 路径的消息索引,允许同一 `session_id` 多 run 追加 runtime events,但不复制 Hermes HTTP 聊天真相。
- [x] 明确 `session_id``run_id` 的映射是否需要单独持久化,避免仅靠内存 map。
- 当前已在 `acp_runtime_runs` 中持久化 `session_id/run_id` 映射。
- [x] 明确 `profile``model``source` 三个字段在 ACP/Hermes HTTP/Reasonix 三种路径下的取值规则。
- 当前实现:`source = "acp"``profile` 沿请求 profile`acpRuntime` 沿请求或 profile 推导;`model` 暂不落库,等待 usage/model 聚合阶段。
- [x] 明确 `usage` 统计的口径:只记 `usage_update`,还是合并 `run.completed.usage`
- 结论:`usage_update` 作为运行中实时增量;`run.completed.usage` 作为最终校正值。两者都只落 ACP-local runtime/usage index,不复制 Hermes HTTP 聊天真相。
### 6.2 先做后端持久化骨架
- [x] 历史阶段曾为 ACP-local session/runtime store 设计 Convex schema;当前默认已改为 SQLite control-plane。
- [x] 历史阶段曾在 Convex 中建 `sessions``messages` 或更小的 `runtimeRuns` / `runtimeEvents` 记录,补齐索引和权限过滤。
- 历史实现为根 `convex/schema.ts``acp_runtime_runs` / `acp_runtime_events`,以及 `convex/aiSessions.ts``upsertRuntimeRun/getRuntimeRun/listRuntimeRuns`;当前根 `convex/` 已退役。
- [x]`ACP_RUN_PAYLOADS` / `ACP_ACTIVE_RUNS` 中必须保留的数据拆到持久化层或短 TTL 层。
- 历史阶段 `create_run` 会把 ACP payload/runtime 写入 Convex;当前默认写入 SQLite control-plane,旧 Convex store 仅在显式 legacy compat 下使用。
- [x]`create_run` 产出的 `sessionId``profile``traceId``runtime` 写入 `sessions` 记录。
- 当前写入目标为更小的 `acp_runtime_runs`,而不是完整聊天 `sessions` 真相表。
- [x]`stream_events` 的 ACP 路径里,按架构决策写入 `messages` 或 runtime/event 索引,不能把 Hermes HTTP 聊天真相复制进 mnote。
- 当前实现:ACP SSE event 以 best-effort 写入 `acp_runtime_events`,只保存 runtime event payload,不写完整聊天 `messages` 真相。
### 6.3 再补 session 读接口
- [x]`GET /client/sessions` 返回当前用户可见的 Convex store 或 Hermes upstream 会话摘要,而不是只依赖前端 localStorage。
- 当前实现:ACP profile / `source=acp``aiSessions:listRuntimeRuns`Hermes HTTP profile 仍走 upstream。
- [x]`GET /client/sessions/{session_id}` 返回会话元数据、消息列表、runtime 状态。
- 当前实现:`source=acp` 从 Convex store 读取 runs/events`messages` 保持空数组以避免复制完整聊天真相。
- [x]`POST /client/sessions/{session_id}/resume` 真正从持久化历史恢复,而不是简单复用 `get_session`
- 当前实现:`source=acp` 返回 Convex runtime history,并标记 `resumed=true` / `resumeSource=convex_acp_runtime_store`
- [x]`DELETE /client/sessions/{session_id}``POST /client/sessions/{session_id}/rename``POST /client/sessions/{session_id}/auto-title` 补齐路由与处理器。
- 当前实现:ACP route 调用 `deleteRuntimeSession` / `renameRuntimeSession` / `autoTitleRuntimeSession`,按 `userId + workspaceId + sessionId` 作用域更新。
- [x] 为搜索接口加分页和最小 snippet,避免一次返回过长正文。
- 当前实现:`GET /client/sessions/search?source=acp&q=...&limit=...` 调用 `searchRuntimeSessions`,最多返回 50 条 session 摘要和 snippet。
### 6.4 再修 ACP 协议行为
- [x]`session/request_permission` 生成结构化响应,不再只打日志。
- [x] 给未知 incoming request 返回明确 error,避免 agent 一直等超时。
- [x] 把 permission 结果同步到前端,至少先支持 auto-allow / auto-deny。
- 当前实现:`session/request_permission` 仍由 ACP client 自动拒绝以避免 agent 超时,同时转成 `permission.denied` SSE;页面 AI 侧展示权限弹窗/卡片,包含 tool 名、参数摘要、允许/拒绝动作入口。
- [x]`usage_update``run.completed` 的 usage 汇总到 session 级统计。
- 当前实现:`appendRuntimeEvent` 在收到 `usage.updated` 或带 `usage``run.completed` 时,同步更新 `acp_runtime_runs.usage`
- [x] 验证 `thought.delta` 仍不会落入最终 assistant 正文。
- 当前验证:`acp_thought_delta_does_not_emit_message_delta` 确认 thought 只作为 `thought.delta` 转发。
### 6.5 再补前端入口
- [x] 在页面 AI 面板中加入后端会话列表,而不是只显示 localStorage 的短期历史。
- 当前实现:页面 AI history 面板打开时会请求 `GET /api/hermes/client/sessions?source=acp&workspaceId=...&documentId=...`,并与 localStorage 短期缓存合并。
- [x] 会话列表支持切换、重命名、删除、恢复。
- 当前实现:history 行内提供恢复、重命名、删除按钮;切换会话时会拉取 Convex-backed detail/resume。
- [x] 会话详情页或抽屉支持查看消息分页与搜索结果。
- 当前实现:恢复/详情会读取最新 run 的 events 并映射为消息视图;history 面板提供后端 session 搜索框,搜索结果展示 snippet。当前消息详情读取后端最近 200 条 event,尚未做 cursor 翻页。
- [x] 权限请求弹窗至少展示 tool 名、参数摘要、允许/拒绝动作。
- 当前实现:`permission.requested` / `permission.denied` / `permission.allowed` 会生成权限弹窗和消息卡片;当前 ACP incoming request 默认 auto-deny,按钮用于前端状态确认,后续可接入真实审批回写。
- [x] Token / 用量信息在会话条目和运行状态上可见。
- 当前实现:会话条目和当前 session 状态展示 `usage.updated` / `run.completed.usage` 汇总。
### 6.6 最后做回归验证
- [x] 新建 ACP 会话后刷新页面,仍能从 Convex-backed store 或 Hermes session store 重新拿到同一会话。
- 当前实现:ACP profile 创建 session 时写入 `aiSessions:upsertRuntimeRun``session.created` 索引记录;刷新后列表可从 Convex-backed store 找回该 session。
- [x] 用两个测试用户分别创建 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。
- [x] 断线重连后,`stream_events` 能从历史继续恢复,而不是丢失 payload。
- 当前最小验证覆盖 payload lookup 不再 remove;完整断线重连仍需浏览器 smoke。
- [x] `session/request_permission` 会返回可验证的协议响应。
- [x] `message.delta``thought.delta``tool.*``run.completed` 的 SSE 映射在浏览器里都能看见正确结果。
- 当前实现:页面 AI stream handler 映射 `message.delta``thought.delta``usage.updated``tool.*``run.completed`;Rust/JS 静态测试覆盖前端入口,尚未完成真实浏览器截图级验证。
- [x] 运行一次真实浏览器 smoke,确认会话列表、恢复、重命名和删除链路都能闭环。
- 当前验证:启动 `mnote-web``127.0.0.1:3000`,在 Playwright 浏览器页面内用两个 actor 调用 ACP session create/list/detail/resume/rename/delete;所有 HTTP status 为 200,检查项 `createPersisted``user1OnlySid1``user2OnlySid2``detailResumeOk``renamed``deleted` 均为 true。
## 7. 执行记录
### 2026-05-18
- 新增根 `convex/schema.ts``acp_runtime_runs` / `acp_runtime_events` 表和索引。(历史记录;当前已退役到 recycle)
- 新增根 `convex/aiSessions.ts``upsertRuntimeRun``getRuntimeRun``listRuntimeRuns`,按 `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}``rename``auto-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 -- --nocapture`10 passed。
- `cd rust && cargo test -p mnote-web test_incoming_permission_request_gets_response -- --nocapture`1 passed。
- `cd rust && cargo test -p mnote-web acp_permission -- --nocapture`1 passed。
- `cd rust && cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --nocapture`1 passed。
- `cd rust && cargo test -p mnote-web acp_thought_delta_does_not_emit_message_delta -- --nocapture`1 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 --version`1.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 smoke`127.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 -- --nocapture`1 passed。
- `cd rust && cargo test -p mnote-web hermes_tools_call_rejects_profile_disabled_tool -- --nocapture`1 passed。
- `cd rust && cargo test -p mnote-web hermes_client_profile_skill_and_memory_routes_use_local_bff_without_upstream -- --nocapture`1 passed。
- `cd rust && cargo test -p mnote-web block_edit_workflow_respects_disabled_markdown_edit_tool -- --nocapture`1 passed。
- `cd rust && cargo test -p mnote-web -- --nocapture`336 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 目录。