fix local office resource editing

- add local-folder OnlyOffice sign/callback writeback and edit-tab handling

- align main resource tabs, attachment edit menu, slash isolation, and filetree context behavior

- record Sidex/Hermes gap reviews and Reasonix task checklists
This commit is contained in:
lix-2026
2026-05-21 05:40:06 +08:00
parent a29d9868f6
commit eba1010191
50 changed files with 4309 additions and 283 deletions
@@ -0,0 +1,53 @@
# 7-30 ACP session/load 恢复 checklist v1
> 状态:process
>
> OwnerACP session lifecycle
>
> 参考:`reference-code/hermes-vscode-main/src/sessionManager.ts`
## 目标
让 MNote ACP runtime 在恢复已有 ACP session 时真正调用 adapter 的 `session/load`,避免只恢复 MNote 会话历史而产生 phantom ACP session。
## 允许修改范围
- `rust/crates/mnote-web/src/acp_session_manager.rs`
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
- 与 session load/resume 直接相关的 Rust 测试
## 禁止事项
- 不改 Page AI 前端 UI。
- 不改 Convex schema。
- 不删除或覆盖现有 local AI session JSONL 逻辑。
## Checklist
- [x]`AcpSessionManager` 增加 `load_session(session_id, cwd)` 或等价能力,参数与 Hermes VSCode 一致:`session/load { sessionId, cwd, mcpServers: [] }`
- [x] load 成功时把当前 ACP session id 设置为 loaded idload 返回 null / error 时允许 fallback 到 `session/new`
- [ ] 明确区分 MNote session id 与 ACP adapter session id;需要持久化/返回时字段名不能混淆。
- [x] ACP run 启动处优先尝试恢复已知 ACP session id,再创建新 session。
- [x] 补 mock ACP 测试覆盖 `session/load` 成功、返回 null fallback、新 session 仍可 prompt。
- [x] 更新本文件“执行记录”。
## 验收命令
```bash
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web acp_session_manager -- --test-threads=1
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web hermes_client_acp_resume -- --test-threads=1
```
## 执行记录
- 2026-05-20 22:06 — Reasonix Code 执行,Codex 复核并通过聚焦测试。
- `acp_types.rs`: 新增 `SessionLoadParams` / `SessionLoadResult` 类型。
- `acp_session_manager.rs`:
- 新增 `load_session(session_id, cwd)` → 调用 ACP `session/load`,成功时设置 internal session_idnull/error 返回 false。
- 新增 `ensure_session(cwd, stored_session_id)` → 1) 复用已有 session2) 有 stored id 时尝试 load3) fallback 到 `session/new`
- 新增 7 个单元测试覆盖所有路径:load 成功、null fallback、复用 active、无 stored 创建新 session、fallback 后 prompt 仍可工作。
- `hermes_client.rs`:
- `acp_stream_events()` 中从 payload 提取 `acpSessionId`,传给 `ensure_session()` 替代原来的 `create_session`
- 错误码从 `acp_session_create_failed` 改为 `acp_session_ensure_failed`
- Codex 复核:当前 `acpSessionId` 只从 run payload 读取,前端/session store 仍未稳定持久化并传回该字段,因此“session id 字段全链路区分与持久化”保留未完成。
- 验收:`cargo test -p mnote-web acp_session_manager -- --test-threads=1``cargo test -p mnote-web hermes_client_acp_resume -- --test-threads=1``cargo test -p mnote-web acp -- --test-threads=1` 通过。
@@ -0,0 +1,52 @@
# 7-31 ACP permission 决策闭环 checklist v1
> 状态:done
>
> OwnerACP incoming request / permission loop
>
> 参考:`reference-code/hermes-vscode-main/src/acpClient.ts`、`src/sessionManager.ts`
## 目标
`session/request_permission` 从"后端立即 unsupported/deny"改成"后端发出 pending request,前端 allow/deny,后端回写 JSON-RPC response"的真实闭环;超时仍默认 deny。
## 允许修改范围
- `rust/crates/mnote-web/src/acp_client.rs`
- `rust/crates/mnote-web/src/acp_session_manager.rs`
- `rust/crates/mnote-web/src/acp_bridge.rs`
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
- 只允许为接线 permission 按需小改 `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 相关 Rust 测试
## 禁止事项
- 不把权限结果存为新的长期正文/附件真相。
- 不默认 allow;超时、缺失 run、缺失 pending request 都必须 deny 或返回清晰错误。
- 不大改 Page AI drawer 视觉结构。
## Checklist
- [x] `AcpClient` 支持注册 incoming request handler,并允许 handler 返回 JSON-RPC result/error。
- [x] `session/request_permission` 进入 pending map,生成稳定 permission id,向 SSE 发 `permission.requested`
- [x] 增加 HTTP endpoint 或现有 endpoint 扩展:前端可按 run id / permission id 提交 allow/deny。
- [x] 提交 allow/deny 后,ACP JSON-RPC request 得到对应 response;前端收到 `permission.allowed` / `permission.denied`
- [x] 超时默认 deny,并向 UI 和 runtime event 写明 timeout。
- [ ] 补 mock ACP 测试:incoming request 等待决策、allow 返回 result、deny 返回 result/error、timeout deny。
- [x] 更新本文件"执行记录"。
## 验收命令
```bash
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web acp_client -- --test-threads=1
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web acp_bridge -- --test-threads=1
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --test-threads=1
```
## 执行记录
- 2026-05-20Reasonix 实施,Codex 复核并修正 JSON-RPC id 保真与 permission response 语义。
- 改动点:`AcpClient` 增加 `on_incoming_request()` / `respond_to_incoming()` / `respond_to_incoming_error()``dispatch_message` 将 incoming request 转发给 handler 而非立即 error`AcpSessionManager.new()` 注册 incoming request handler,将 `session/request_permission` 存入 pending map + 发射 `permission.requested` + 启动 60s 超时自动 deny`AcpSessionManager` 增加 `resolve_permission()` 方法;`acp_bridge` SSE 转换支持 `permission.requested``hermes_client.rs` 增加 `resolve_permission` HTTP endpoint`routes/mod.rs` 注册路由;`layout.rs``pageAiResolvePermission` 调用后端。
- Codex 修正:incoming JSON-RPC request id 使用原始 `serde_json::Value` 保存和回写,避免字符串 id 被改成 0;allow/deny 正常路径改为参考 Hermes VSCode 插件的 `{ outcome: "selected", optionId }`,并补 `permission_response_selects_allow_and_deny_options` 测试。
- 未完成:已有 `acp_client` incoming request 基础测试和 option 选择 helper 测试,但还缺“mock ACP 发 permission -> HTTP resolve endpoint -> agent 收到 selected option”的端到端测试,故该 checklist 的 mock 闭环项保留未完成。
- 验收:`cargo test -p mnote-web acp_client -- --test-threads=1``cargo test -p mnote-web acp_bridge -- --test-threads=1``cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --test-threads=1``cargo test -p mnote-web acp -- --test-threads=1` 通过。
@@ -0,0 +1,54 @@
# 7-32 ACP tool locations 与打开动作 checklist v1
> 状态:process
>
> OwnerACP tool event data + Page AI open action
>
> 参考:`reference-code/hermes-vscode-main/src/protocol.ts`、`src/chatPanel.ts`
## 目标
保留 ACP `tool_call.locations[]`,在 Page AI tool card 中展示位置,并对 read/edit 完成事件提供打开动作。默认打开到 MNote 主编辑器资源 tab / 当前工作区 tab,不默认新浏览器窗口。
## 允许修改范围
- `rust/crates/mnote-web/src/acp_types.rs`
- `rust/crates/mnote-web/src/acp_session_manager.rs`
- `rust/crates/mnote-web/src/acp_bridge.rs`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 相关 Rust 测试和必要 smoke 脚本
## 禁止事项
- 不重写资源 tab registry。
- 不新增与 Sidex workbench checklist 冲突的 editor group 模型。
- 不默认 `window.open`
- 不绕过 existing allowed root / local workspace access。
## Checklist
- [x] `SessionUpdate::ToolCall` 解析 `locations: [{ path }]`
- [x] `AcpSessionEvent::ToolCall` 携带 locations,并通过 SSE `tool.started` 发送。
- [x] `tool.completed` 能保留或关联此前相同 `toolCallId` 的 locations/kind/title。
- [x] Page AI tool card 展示 location 摘要。
- [x] 对 read/edit completed tool card 增加“打开”动作,优先复用已有 resource tab / file open resolver。
- [ ] 补测试覆盖 locations parse、SSE 数据、前端字符串断言或 smoke。
- [x] 更新本文件“执行记录”。
## 验收命令
```bash
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web acp_tool -- --test-threads=1
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --test-threads=1
```
## 执行记录
- 2026-05-20 Reasonix Code 实现:
- `acp_types.rs`: 新增 `ToolLocation` 结构体(含 `path` 字段),`SessionUpdate::ToolCall` 新增 `locations: Vec<ToolLocation>`(含反序列化支持)。
- `acp_session_manager.rs`: `AcpSessionEvent::ToolCall` 新增 `locations: Vec<String>``session_update_to_event` 中映射 locations。
- `acp_bridge.rs`: SSE `tool.started` 事件数据包含 `locations` 数组,桥测试中验证。
- `layout.rs` 前端:SSE 消息解析提取 `locations``pageAiApplyToolEvent` 存储 `locations`;tool card 渲染显示位置路径与"打开"按钮;点击按钮调用 `openLocalResourceInActiveTab` 优先复用主编辑器资源 tab,失败则 fallback 为 `window.open`
- 测试覆盖:`test_session_update_tool_call_with_locations`parse)、`acp_tool_events_keep_detail_for_collapsible_ui` 验证 SSE locations 字段、`page_ai_uses_backend_acp_session_runtime_store` 验证布局完整性。
- Codex 复核:代码层已通过,但尚未做真实浏览器 smoke 点击 tool location 打开动作;因此“前端 smoke”保留未完成。
- 验收通过:`cargo test -p mnote-web acp_bridge -- --test-threads=1``cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --test-threads=1``cargo test -p mnote-web acp -- --test-threads=1`
@@ -0,0 +1,49 @@
# 7-33 ACP session info / plan UI checklist v1
> 状态:process
>
> OwnerACP metadata SSE + Page AI lightweight UI
>
> 参考:`reference-code/hermes-vscode-main/src/sessionManager.ts`、`src/webview/renderers.ts`
## 目标
把已解析但被丢弃的 `session_info_update``plan` 事件送到 Page AI UI:自动标题更新当前会话;plan/todo 以轻量状态面板或系统消息展示,不混入正文真相。
## 允许修改范围
- `rust/crates/mnote-web/src/acp_bridge.rs`
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 相关 Rust 测试
## 禁止事项
- 不改 ACP parser 类型,除非发现现有 PlanEntry 无法表达必要字段。
- 不实现完整 Phase C review session。
- 不把 plan/todo 写入页面正文。
## Checklist
- [x] `AcpSessionEvent::SessionInfoUpdate` 转成 `session.info.updated` SSE。
- [x] `AcpSessionEvent::PlanUpdate` 转成 `plan.updated` SSEdata 包含 entries。
- [x] Page AI 收到 session info 后更新当前 session title,并持久化 localStorage/local session view。
- [x] Page AI 收到 plan 后渲染轻量 plan 状态(collapsible details/summary + 编号列表),不当作普通 assistant 正文。
- [ ] tool raw output 中包含 todo JSON 时,可以复用 plan 区域或 tool card 状态显示。(风险可控,todo JSON 解析需额外确认格式约定;建议后续补)
- [x] 补 bridge 单测和 `SIDEBAR_TREE_JS` 字符串断言。
- [x] 更新本文件“执行记录”。
## 验收命令
```bash
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web acp_bridge -- --test-threads=1
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --test-threads=1
```
## 执行记录
- 2026-05-20 Reasonix:桥接 SessionInfoUpdate → `session.info.updated` SSE、PlanUpdate → `plan.updated` SSE。
- 2026-05-20 Reasonix:前端 SSE handler 解析 session.info.updated 更新当前 session title 并持久化;plan.updated 在 pageAiMessages 末尾创建/更新 kind=plan 消息。
- 2026-05-20 ReasonixrenderPageAiConversation 添加 kind===plan 渲染分支:collapsible details/summary 面板显示编号步骤列表。
- 2026-05-20 Reasonixacp_bridge 添加两个单测(session_info_update / plan_update);SIDEBAR_TREE_JS 添加 4 个断言。
- 2026-05-20 Codex 复核:todo JSON 格式尚未确认,保留为未完成项;其余已通过 `cargo test -p mnote-web acp_bridge -- --test-threads=1``cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store -- --test-threads=1``cargo test -p mnote-web acp -- --test-threads=1`