Files
mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md
T
lix-2026 96e03645f7 chore: 收口 review 执行清单与 runtime 验证
- 补齐 design/10-review 执行清单、验收标准与相关设计治理记录

- 迁移已完成的 tree、mindmap、runtime fallback、AI kernel 等设计和缺陷条目

- 推进 Rust Web runtime、tree/sidebar、page aggregate、mindmap 与 OnlyOffice 路由侧验证支撑

- 增加 task177-task180 smoke/audit 脚本及前端相关测试覆盖
2026-05-14 05:52:08 +08:00

713 lines
28 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.
# 7 [recycle] mnote Kernel Phase 7 CLI-First 外置 Agent 统一执行面方案 v4
> 更新时间:2026-05-09
>
> 回收说明(2026-05-13):
> - 本稿的 `mnote-cli` 唯一长期 agent 执行面口径已被
> `/mnt/Data1T/mnote/design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
> 覆盖。
> - 后续长期方向改为:页面 AI 面板只是 Hermes 的页面内客户端;
> Hermes session/message/tool event/usage/model 才是会话真相;
> mnote 通过 Hermes skill/plugin 暴露页面、树、artifact 与 edge 能力。
> - 本稿仅作为历史决策记录保留。`mnote-cli` 仍可作为 mnote plugin 内部适配器,
> 但不再是页面 AI 的唯一长期执行面。
>
> 上位依据:
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md`
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/done/7-1-phase7-document-ai-minimum-loop-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v2.md`
> - `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v3.md`
>
> 本稿与前稿的关系:
> - `v2` 的价值仍然成立:它记录了文档页 AI 最小闭环已经真实完成
> - `v3` 把 CLI 抬到了重要位置,但仍保留了“产品内 AI 主链”和“CLI 外置层”双中心
> - 本稿进一步收口,明确长期只保留一个执行方向:`CLI-first`
>
> 当前判断修正:
> - 文档页 AI 面板不再被视为独立 AI 编排主线
> - `openai-agents-python` 不再被视为长期默认主编排,而是一个可插拔外置 agent
> - Hermes、Codex、后续其他 agent,应统一通过 `mnote-cli` 接入
> - `3000` 上的页面内 AI host 已属于 Rust `mnote-web` 主壳;`/api/ai-agent/run` 只保留 CLI host / adapter 口径
> - 文档页 AI 上下文读取已优先消费 Rust `page aggregate` snapshot,旧 TS builder / 旧 sidecar 只保留 fallback / 对照语义
---
## 1. 文档目的
这份稿只回答一个问题:
> **`mnote` 的长期 AI 执行面,到底要不要收口成一个唯一方向。**
当前答案是:
> **要。长期只保留一个 AI 执行方向:`Rust kernel + mnote-cli`。**
这里的关键不是“要不要有 AI UI”,而是:
- UI 可以存在
- 外置 agent 可以很多
- 但执行面只能有一个
一句话收口:
> **`mnote` 的长期主线不是“内置 AI + CLI 两套长期并存”,而是“CLI 作为唯一 agent 执行面,Web 面板只是其中一个客户端”。**
---
## 2. 为什么要改成 CLI-first
当前如果继续保留两套长期方向:
1. 产品内一套 AI 编排
2. 外置 agent 一套 CLI / 脚本 / Hermes / Codex 编排
那么后面一定会出现这些重复维护成本:
- 两套工具注册表
- 两套上下文装配逻辑
- 两套 session / trace / audit 语义
- 两套能力开关和权限边界
- 两套“哪些命令是真执行、哪些只是兼容层”的判断
这不是“体验和平台分层”,而是“长期双轨维护”。
如果改成 CLI-first
- Web AI 面板调用 CLI
- Hermes 调用 CLI
- Codex 调用 CLI
- 批处理脚本调用 CLI
- 定时任务调用 CLI
那么系统只维护:
1. 一个真执行方向:`mnote-cli`
2. 多个调用方:UI / agent / script / job
这才是真正减少长期维护成本。
---
## 3. 当前完成情况如何解释
### 3.1 `7-1` 已完成,但不代表它必须成为长期方向
当前文档页 AI 最小闭环已经完成,这个事实不变:
- Rust `mnote.page_aggregate.v1` snapshot 优先的页面上下文读取
- 标题改名与正文改写
- `openai-agents-python` sidecar
- `Hermes` fallback / 对照链
但这个完成状态只能说明:
> **我们已经验证过一条可工作的过渡主链。**
不能自动推出:
> **这条链就应该变成长期唯一主线。**
所以 `v2` 记录的工作不是白做,而是:
- 它证明了产品内写链需求真实存在
- 它帮助我们冻结了最小工具面和页面语义面
- 它可以作为 CLI-first 迁移时的行为基线
### 3.2 `mnote-cli` 已经足够接近长期入口
当前 `mnote-cli` 已经不是空壳,已有:
- `page`
- `block`
- `mindmap`
- `search`
- `sidebar`
- `editor`
- `tool`
同时还有这些关键参数:
- `--json`
- `--execute`
- `--session-id`
- `--reason`
- `--idempotency-key`
- `--validate-only`
- `--dry-run`
这说明真正应该继续投入的,不是再扩一条新的 Web 内 AI 主编排,而是把 CLI 补成稳定统一入口。
---
## 4. 新的长期架构边界
### 4.1 Rust kernel / Rust runtime
负责:
- 唯一事实源
- query / command / projection
- page / tree / graph / artifact 的正式语义
- 审计、回放、恢复、trace 基线
不负责:
- 直接承担 agent 编排
- 直接承担产品聊天壳
### 4.2 `mnote-cli`
负责:
- 唯一长期 agent 执行入口
- 人类、脚本、外置 agent、Web 面板的统一命令面
- 统一能力发现、统一参数面、统一输出结构
必须成为:
- CLI for humans
- CLI for agents
- CLI for jobs
- CLI for UI host
### 4.3 Web AI 面板
负责:
- 当前页面里的交互壳
- 流式展示
- 输入与审核
- 把页面上下文转成 CLI 可消费的标准输入
- 接收 CLI 事件并映射到 UI
不负责:
- 自己维护独立 agent 编排层
- 自己维护独立工具注册表
- 自己定义长期 AI 契约
### 4.4 外置 agent
包括但不限于:
- Hermes
- Codex
- `openai-agents-python`
- 后续任何别的 agent runtime
它们都负责:
- 决策
- 推理
- 工具调用策略
- 会话层 orchestration
但它们不应再直接拥有产品内部执行面。它们都应该统一调用 `mnote-cli`
---
## 5. `openai-agents-python` 的新定位
### 5.1 不再是默认主线
从本稿开始,`openai-agents-python` 不再被定义为:
- `mnote` 的长期默认主编排
- 产品内文档页 AI 的唯一推荐底座
### 5.2 改为可插拔外置 agent
它的新定位是:
> **一个可插拔外置 agent runtime。**
也就是说,它和 Hermes、Codex 的区别不再是“谁是产品内主线”,而只是:
- 不同的 agent 实现
- 不同的模型接法
- 不同的 orchestration 风格
是否继续保留它,取决于后续真实效果,而不是当前历史惯性。
### 5.3 可接受的长期状态
未来允许的状态有三种:
1. `openai-agents-python` 继续保留,作为可选外置 agent
2. `openai-agents-python` 仅保留在测试 / 回归 / 对照场景
3. `openai-agents-python` 完全移除
这三种都不影响长期主架构,只要唯一执行面还是 `mnote-cli`
---
## 6. CLI-first 下的产品口径
### 6.1 UI 仍然可以有 AI 面板
CLI-first 不等于没有产品内 AI UI。
允许继续有:
- 文档页 AI 面板
- mindmap AI 面板
- OnlyOffice AI 面板
但这些都只是:
> **CLI 的图形客户端。**
并且当前这些客户端默认都挂在 `3000` 的 Rust `mnote-web` 主壳里,而不是重新长出第二套产品内执行面。
### 6.2 页面语义仍然保留
虽然主执行面转到 CLI,但下面这些产品语义仍要保留:
- `model_key`
- `profile`
- `tool registry`
- `page aggregate`
- `page.head.updateTitle`
- `page.body.save`
- `tree.*`
区别只在于:
- 这些不再由 Web 内独立 AI service 主导
- 而是由 CLI 和 Rust runtime 共同定义
### 6.3 Web 不应再拥有第二套工具面
当前过渡工具面:
- `doc_get`
- `doc_find`
- `doc_insert_blocks`
- `doc_replace_range`
- `slash_run`
如果继续保留,后续也应通过 CLI 暴露,而不是继续作为 Web 内 service 的私有 tool surface。
---
## 7. CLI 必须补齐的能力
CLI-first 成立的前提,不是“已有命令很多”,而是这些能力必须补齐。
### 7.1 能力发现
至少要能稳定输出:
- 当前有哪些 command / query / job
- 每个能力属于哪个 scope
- 哪些可读,哪些可写
- 哪些是 plan-only
- 哪些是真执行
建议提供:
- `mnote-cli capabilities --json`
### 7.2 标准上下文输入
CLI 需要能接住标准化页面上下文,而不是让每个 agent 自己拼:
- `documentId`
- `blocks`
- `pageOptions`
- `editorRuntimePageOptions`
- `subtree`
- `outline`
- `evidence`
也就是说,页面上下文装配逻辑要从 Web 内 agent service 迁到 CLI 兼容输入协议。
### 7.3 标准事件输出
如果 Web 面板要流式显示,CLI 需要输出标准事件流,例如:
- `assistant_message`
- `tool_call`
- `tool_result`
- `completion`
- `error`
否则 UI 最后还是会倒逼出第二套 Web 内编排逻辑。
### 7.4 标准 session / trace / audit
CLI 必须把这些变成一等能力:
- `session_id`
- `trace_id`
- `request_id`
- `actor_id`
- `actor_type`
- `reason`
- `idempotency_key`
### 7.5 权限与 scope
CLI 要能明确限制:
- 当前只能改当前页
- 当前允许跨页还是不允许
- 当前允许树命令还是只允许页面命令
- 当前是只读、写入还是后台 job
---
## 8. 顺序执行 checklist
本节是后续实施时的主 checklist。执行时从上到下推进,完成一项后把对应 `- [ ]` 改成 `- [x]`,并在该项下补充实际命令输出摘要或截图路径。
### 8.1 冻结 CLI 基座
- [x] 确认 `mnote-cli` 顶层命令存在。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --help
```
- 通过标准:输出包含 `page`、`block`、`mindmap`、`search`、`sidebar`、`editor`、`tool`。
- [x] 确认 agent 必需的全局参数存在。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --help
```
- 通过标准:输出包含 `--json`、`--execute`、`--session-id`、`--reason`、`--idempotency-key`、`--validate-only`、`--dry-run`。
- [x] 确认 `mnote-cli` 当前单测全通过。
- 命令:
```bash
cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli
```
- 通过标准:所有 `mnote-cli` 测试通过;当前基线应覆盖 CLI JSON contract、tool registry、page/save/sidebar 等最小面。
### 8.2 冻结页面命令面
- [x] 验证页面读取命令。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json page get --page-id page_demo --workspace-id ws_demo
```
- 通过标准:输出包含 `domain: page`、`action: get`、`context.requestId`、`context.traceId`,并且 `operation.transport.functionName` 指向页面查询。
- [x] 验证页面标题更新命令。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json page title --page-id page_demo --workspace-id ws_demo --title "新标题"
```
- 通过标准:输出包含 `domain: page`、`action: title`、`operation.kind: command`,并且写入仍通过 Rust runtime / transport 计划表达。
- [x] 验证页面正文保存命令。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json page save --page-id page_demo --workspace-id ws_demo --content-json '[{"id":"block_1"}]'
```
- 通过标准:输出包含 `domain: page`、`action: save`、`operation.kind: command`,并且能看到正文写回 payload。
### 8.3 冻结块命令面
- [x] 验证块插入命令。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json block insert --page-id page_demo --workspace-id ws_demo --block-type paragraph --content "hello"
```
- 通过标准:输出包含 `domain: block`、`action: insert`、`operation.kind: command`、`operation.name: insert_block`。
- [x] 验证块 patch 命令。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json block patch --page-id page_demo --workspace-id ws_demo --block-id block_demo --snapshot-json '{"id":"block_demo","type":"paragraph","text":"updated"}'
```
- 通过标准:输出包含 `domain: block`、`action: patch`、`operation.kind: command`、`operation.name: blocks.patch`。
- [x] 第 1 步:固定块写链仍然存在的入口证据。
- 结果:`/api/ai-agent/run` 已不再直接恢复 `slash_run` / `doc_insert_blocks` / `doc_replace_range``DocumentAiAgentPanel.runtime.tsx` 也已不再声明 Web 内独立 agent owner。
- 验证命令:
```bash
rg -n "doc_insert_blocks|doc_replace_range|slash_run|startHermesRun|startDocumentAiOrchestratorRun|runCodexBridge" \
/mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts \
/mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx
```
- [x] 第 2 步:把块写链收口为 `mnote-cli` host / adapter。
- 结果:`/api/ai-agent/run` 已收口为薄 host route,块写链不再由 Web 私有 service 直接拥有。
- 依赖检查:`DocumentAiAgentPanel.runtime.tsx` 与 `/api/ai-agent/run/route.ts` 的 contract 说明已同步改成 `mnote-cli`。
- [x] 第 3 步:补块写链回归测试。
- 结果:`pnpm vitest run src/components/editor/DocumentAiAgentPanel.runtime.test.tsx` 与 `pnpm vitest run src/app/api/ai-agent/run/route.test.ts` 通过。
- 验证命令:
```bash
pnpm vitest run /mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.test.tsx
pnpm vitest run /mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.test.ts
```
- 通过标准:测试明确约束块写链不再暴露 Web 私有 tool surface,并且 route 只保留 CLI host / adapter 语义。
### 8.4 冻结搜索与侧栏投影
- [x] 验证文档搜索命令。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json search documents --workspace-id ws_demo --query "Hermes"
```
- 通过标准:输出包含 `domain: search`、`action: documents`,并且查询计划可由 Web host 消费。
- [x] 验证块搜索命令。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json search blocks --page-id page_demo --query "Hermes"
```
- 通过标准:输出包含 `domain: search`、`action: blocks`、`operation.name: search_blocks`。
- [x] 验证侧栏数据集命令。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json sidebar dataset --workspace-id ws_demo
```
- 通过标准:输出包含 `domain: sidebar`、`action: dataset`、`operation.name: sidebar.dataset.list`。
### 8.5 冻结 tool runtime 与安全模式
- [x] 验证 `tool run` 的只读 explain-plan。
- 命令:
```bash
cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --json --validate-only --dry-run --session-id ai-checklist-smoke --reason phase7-cli-first --idempotency-key phase7-cli-first-001 tool run --tool-name doc_get --kind query --mode explain-plan --args-json '{"pageId":"page_demo"}'
```
- 通过标准:输出包含 `domain: tool`、`action: run`、`context.sessionId: ai-checklist-smoke`、`context.idempotencyKey: phase7-cli-first-001`、`context.validateOnly: true`、`context.dryRun: true`。
- [x] 验证 tool registry 会拒绝未知工具。
- 命令:
```bash
cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli tool_run_rejects_unknown_tool
```
- 通过标准:测试通过,未知 tool 不会绕过 registry 进入执行链。
- [x] 确认 `--reason` 进入后续 audit / trace 输出规划。
- 当前状态:CLI 参数面已接住 `--reason`,并已在标准 JSON 输出的 `context.reason` 中展开。
- 通过标准:`cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli output_context_keeps_reason_for_audit_trace` 通过,断言 `reason` 出现在标准 audit / trace 输出上下文里。
### 8.6 将 Web AI 面板降为 CLI host
- [x] 检查 Rust Web checklist 的 Phase 5 口径。
- 文件:`/mnt/Data1T/mnote/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md`
- 通过标准:AI 面板只能写成 `mnote-cli` host/client 收口,不得写成 Hermes 或 `openai-agents-python` 长期主编排。
- 已核对:该文件明确写入“长期执行面应收口到 `mnote-cli`Web 只作为 CLI host / client`openai-agents-python` / Hermes / Codex 只作为可插拔外置 agent”。
- [x] 检查 legacy Next retirement gate 的 AI route 口径。
- 文件:`/mnt/Data1T/mnote/design/03-rust-web/done/3-11-rust-web-legacy-next-retirement-gates-v1.md`
- 通过标准:`/api/ai-agent/run` 只能写成 CLI host / adapter 兼容层,结构化写入必须经 Rust runtime。
- 已核对:该文件明确写入 `/api/ai-agent/run` 长期应降为 `mnote-cli` host / adapter,结构化写入必须经 Rust runtime,外置 agent 不得拥有第二执行面。
- [x] 检查 Wolai-aline E27 的 AI 编辑口径。
- 文件:`/mnt/Data1T/mnote/design/05-editor-mainline/process/5-9-wolai-aline-continuous-checklist-v1.md`
- 通过标准:E27 只能把 `openai-agents-python` sidecar / Hermes fallback 当作过渡基线,不得写成长期主线。
- 已核对:E27 明确写入 `/api/ai-agent/run` 后续应收口为 `mnote-cli` 唯一长期 agent 执行面上的 host / adapter`Hermes`、`Codex`、`openai-agents-python` 仅为可插拔外置 agent;当前 sidecar / fallback 只保留为过渡行为基线。
### 8.7 外置 agent 接入顺序
- [x] 第 1 步:冻结 Codex 接入方式。
- 结果:`/api/ai-agent/run` 已不再保留 `provider=codex` 默认系统入口;请求会明确失败,默认主链只保留 `mnote-cli` host。
- 验证命令:
```bash
rg -n "provider === \"codex\"|runCodexBridge" /mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts
```
- [x] 第 2 步:冻结 Hermes 接入方式。
- 结果:`/api/ai-agent/run` 已不再直接调用 `startHermesRun` / `streamHermesRunEvents`,Hermes 不再是长期主链入口。
- 验证命令:
```bash
rg -n "startHermesRun|streamHermesRunEvents|/api/hermes/bridge" /mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts
```
- [x] 第 3 步:冻结 `openai-agents-python` 接入方式。
- 结果:`DocumentAiAgentPanel.runtime.tsx` 已不再声明 `bridgeOwner: "openai-agents-python"``provider=online` 也不再直连 orchestrator。
- 验证命令:
```bash
rg -n "bridgeOwner: \"openai-agents-python\"|startDocumentAiOrchestratorRun|/api/v1/ai-agent/document/run" /mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx /mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts
```
- [x] 第 4 步:统一复核外置 agent 只剩 CLI host / adapter 口径。
- 结果:已通过全局复核,`/api/ai-agent/run` 与面板 contract 都只保留 `mnote-cli` host / adapter 口径。
- 验证命令:
```bash
rg -n "provider === \"codex\"|runCodexBridge|startHermesRun|streamHermesRunEvents|bridgeOwner: \"openai-agents-python\"|startDocumentAiOrchestratorRun|/api/v1/ai-agent/document/run" \
/mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts \
/mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx
```
### 8.8 完成判定
- [x] `mnote-cli` 的最小命令面在仓库内可复跑。
- 已验证:`cargo run --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -- --help` 与 `cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli` 可复跑。
- [x] `--json`、`--execute`、`--validate-only`、`--dry-run` 的输出语义稳定。
- 已验证:`--help` 暴露这些全局参数;`tool run --validate-only --dry-run --json` 输出包含 `context.validateOnly: true`、`context.dryRun: true`、`context.reason`、`context.sessionId` 与 `context.idempotencyKey`。
- [x] `page / block / search / sidebar / tool` 都能用 CLI 直接验证。
- 已验证:8.2 到 8.5 已分别覆盖 `page get/title/save`、`block insert/patch`、`search documents/blocks`、`sidebar dataset`、`tool run`。
- [x] 第 1 步:确认 Web AI 面板的 contract 已收口为 `mnote-cli` host / client。
- 结果:面板 contract 已改为 `bridgeOwner: "mnote-cli"``runtimeRole: "mnote_cli_host_client"`。
- 验证命令:
```bash
rg -n "bridgeOwner: \"openai-agents-python\"|startDocumentAiOrchestratorRun|startHermesRun|runCodexBridge" /mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx /mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts
```
- [x] 第 2 步:确认三类外置 agent 都只作为可插拔 runtime。
- 结果:`route.ts` 已收口为统一 host route,不再按 provider 分叉到三套执行实现。
- 验证命令:
```bash
rg -n "provider === \"codex\"|runCodexBridge|startHermesRun|startDocumentAiOrchestratorRun|bridgeOwner: \"openai-agents-python\"" /mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts /mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx
```
- [x] 第 3 步:补完成判定的最终回归。
- 结果:`cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli`、`pnpm vitest run src/components/editor/DocumentAiAgentPanel.runtime.test.tsx`、`pnpm vitest run src/app/api/ai-agent/run/route.test.ts` 均通过。
- 验证命令:
```bash
cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli
pnpm vitest run /mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.test.tsx
pnpm vitest run /mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.test.ts
```
- [x] 结构化写入仍然只能回到 Rust runtime / Rust kernel。
- 已核对:本稿 4.1、6.2、6.3 明确 Rust kernel / Rust runtime 负责事实源、query / command / projection 与结构化写入语义;`3-11` 与 Wolai-aline E27 均要求结构化写入经 Rust runtime 或 Rust tool / `page.body.save`。
### 8.9 当前测试账号写入验收
- [x] 确认当前测试账号默认允许 CLI 新建与编辑页面。
- 命令:
```bash
cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/server/mnote-cli-agent-host.test.ts
```
- 通过标准:`mnote-cli` host 默认下发 `MNOTE_CLI_ALLOW_CREATE_PAGE=1` 与 `MNOTE_CLI_ALLOW_EDIT=1`,并把当前 Web 用户身份透传给 CLI 子进程。
- [x] 确认当前测试账号的真实 workspace。
- 当前测试账号:
- `userId`: `a4b72c17-49e3-46d3-8456-24a0a7044d64`
- `email`: `dev@mnote.local`
- `name`: `开发用户`
- 当前真实 workspace
- `workspaceId`: `ws_req_1778035004501_15`
- `workspaceName`: `开发用户 的空间`
- 首个页面:`tree_1778036320856_1` / `新页面`
- 通过标准:`/api/sidebar?workspaceId=ws_req_1778035004501_15` 返回的 `documents` 至少包含 `tree_1778036320856_1`。
- [x] 确认旧的 CLI 直连 Convex identity 不能作为长期用户写入面。
- 事实:曾经写入 `tree_1777430834634_3` / `anonymous 的空间` 的页面不会出现在当前测试账号的 `ws_req_1778035004501_15`。
- 结论:CLI 不能长期依赖伪造 `DEV_USER_ID` 直连 Convex 来代表用户写入;需要由 Web host / mnote-web 持有当前会话、当前 workspace 与 capability 后再执行写入。
- [x] 确认 CLI host 会把当前页面 workspaceId 传入 CLI 输入。
- 命令:
```bash
cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/server/mnote-cli-agent-host.test.ts
```
- 通过标准:`--args-json` 包含 `workspaceId: "ws_req_1778035004501_15"`,避免 agent 在缺省 workspace 或错误 workspace 下创建页面。
- [x] 用当前浏览器会话创建当前账号可见页面。
- 执行动作:在已登录测试账号的浏览器会话中调用 `/api/tree/commands` 创建:
- `CLI 可见测试 A 1778057564`
- `CLI 可见测试 B 1778057564`
- 通过标准:创建响应的 `result.workspaceId` 必须是 `ws_req_1778035004501_15`。
- 当前验证结果:
- `tree_1778048047020_3` / `CLI 可见测试 A 1778057564`
- `tree_1778048047047_4` / `CLI 可见测试 B 1778057564`
- [x] 用当前浏览器会话读取 sidebar 确认可见。
- 命令等价动作:
```js
await fetch('/api/sidebar?workspaceId=ws_req_1778035004501_15', { credentials: 'include' })
```
- 通过标准:返回的 `documents`、`kernelSidebarProjection.items` 与 `kernelSidebarTree` 都包含 `CLI 可见测试 A 1778057564` 与 `CLI 可见测试 B 1778057564`。
- 当前验证结果:已在浏览器会话 API 返回中看到上述两个页面;同一返回还显示早先创建的 `cli_visible_ws_req_a_17780471` 与 `cli_visible_ws_req_b_17780471`。
- [ ] 后续权限收口:补“禁止/允许 CLI 编辑、允许 CLI 新建页面”的 capability 开关。
- 目标:默认测试账号允许 CLI 新建与编辑;后续真实用户可在设置中关闭关键页面的 CLI 编辑或新建权限。
- 通过标准:CLI 写操作必须能读到当前用户、当前 workspace、当前页面 capability;未授权时返回明确拒绝,不得 fallback 到 anonymous / dev identity 写入。
- [x] 确认 Web host 会把当前会话用户透传给 `mnote-cli` 作为默认执行身份。
- 命令:
```bash
cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/server/mnote-cli-agent-host.test.ts src/app/api/ai-agent/run/route.test.ts
```
- 通过标准:`spawn("cargo", ...)` 的环境变量包含 `DEV_USER_ID`、`DEV_USER_EMAIL`、`DEV_USER_NAME`,并默认下发 `MNOTE_CLI_ALLOW_CREATE_PAGE=1` 与 `MNOTE_CLI_ALLOW_EDIT=1`。
---
## 9. 迁移策略
### 阶段 0:口径冻结
先冻结新的长期判断:
- CLI 是唯一长期执行面
- Web 面板只是 CLI 客户端
- `openai-agents-python` 是可插拔外置 agent
- Hermes / Codex / 后续 agent 不再拥有默认产品内入口;是否保留仅通过显式接线决定
### 阶段 1CLI 能力补齐
优先补:
- capability manifest
- 标准 JSON 输出
- 事件流输出
- 标准上下文输入
- 标准 trace / session 参数
### 阶段 2Web 面板降为 CLI host
把当前文档页 AI 面板从:
- “调用内置 sidecar 主编排”
改成:
- “调用 CLI host / CLI adapter”
### 阶段 3:外置 agent 接入统一化
统一支持:
- Hermes -> 显式外置接线或停用
- Codex -> 显式外置接线或停用
- `openai-agents-python` -> `mnote-cli`
### 阶段 4:裁剪过渡层
当 CLI-first 成熟后,逐步评估:
- 是否保留 `openai-agents-python`
- 是否保留当前 Web 内 sidecar
- 是否完全移除旧过渡桥接
---
## 10. 非目标
本稿不做:
- 再造一套新的 agent 平台
- 让 Web 直接失去 AI UI
- 把所有 UX 都退回终端
- 让 Hermes 或 `openai-agents-python` 重新变成产品内部唯一中心
---
## 11. 完成判定
满足下面几项时,才算 CLI-first 真正成立:
- `mnote-cli` 是唯一长期 agent 执行入口
- Web AI 面板只是 CLI host,不再持有独立主编排
- Hermes / Codex / `openai-agents-python` 都通过 CLI 接入
- Rust kernel 仍然是唯一事实源
- 是否保留 `openai-agents-python` 不影响整体架构
一句话收口:
> **`mnote` 的长期 AI 方向应当收口成“Rust kernel 负责真相,`mnote-cli` 负责唯一执行面,外置 agent 全部可插拔,Web 只做交互壳”。**