Files
mnote/design/07-ai/done/7-69-page-ai-pi-first-lab-spike-record-v1.md
T

95 lines
8.4 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-69 Page AI Pi-first Lab Spike 验收记录 v1
状态:done(最小可验证 spike)
Owner07-ai / mnote-web / control-plane
日期:2026-07-03
## 1. 本轮结论
7-69 已完成 MNote 侧最小可验证 Pi-first Page AI Lab spikeOpenHub 仍是默认 Page AI 主线,Pi Lab 仅在 `MNOTE_PAGE_AI_PI_LAB=1` 下作为 dev-only/internal 入口启用。
当前结论不是“替换 OpenHub”,而是:Pi 值得进入下一阶段受控验证;扩大前必须补真实 Pi CLI/RPC、Turso/libSQL receipt 表、auth 下浏览器 smoke 和 watcher 端到端证据。
## 2. 已实现内容
- 新增 Pi Lab 后端路由:`/api/page-ai/pi/status``bootstrap``start``send``abort``events``tool-call``/page-ai/pi`
- 新增 MNote 托管 runtime`pi --mode rpc` subprocess 或 `MNOTE_PAGE_AI_PI_LAB_RUNTIME=mock`sessionDir 派生到 MNote 管控目录。
- 默认禁用 Pi builtin tools:启动参数包含 `--no-builtin-tools`,状态响应显式标注 `bash/read/write/edit` 不开放。
- 实现 MNote-owned tool facade`mnote.current_page.read``mnote.selection.read``mnote.allowed_roots.describe``mnote.local_file.read``mnote.local_file.patch``mnote.knowledge_rag.query``mnote.reference.open``mnote.tool_receipt.write`
- 文件读写受 allowed roots 限制,越界读写返回 deny reason 且写 receipt。
- LightRAG 保持唯一默认知识 provider;Pi 只通过 `knowledge_rag` facade 查询和 open-reference。
- receipt 先落 `provider_neutral_jsonl_adapter_v1`,字段含 session binding、tool event、deny reason、diff summary、file version、allowed roots snapshot,并写明迁移到 Turso/libSQL `ai_provider_sessions` / `ai_tool_events` / `ai_file_patches` 的条件。
- 文件 patch 响应声明 `mnote local-folder watcher / document-session external refresh`,并显式 `polling=false`,不新增高频轮询。
- 新增 Pi Lab runtime UI:状态机、SSE/EventSource、stream、tool card、citation、diff、abort、receipt 诊断。
- Pi Lab 已从简陋独立 debug 页改为当前 MNote Page AI 抽屉内的 dev-only provider 面板,并保留悬浮 `π` 入口;切回 OpenHub 时复用原抽屉与 iframeOpenHub 仍是默认入口。
- Pi RPC stream 中 `thinking_*` 事件默认不进入可见 assistant bubble;可见回复只渲染 `text_*` / assistant final text,避免 Omniroute/freefirst 的 reasoning_content 泄漏到页面。
- gateway 仅在 `enable_page_ai_pi_lab` 开启时注入 Pi Lab runtime,默认 body 标记为 hidden,不改变 OpenHub 默认入口。
- 新增 smoke:静态结构、禁用态 API endpoint、mock-mode API 验证脚本、浏览器 smoke 脚本。
- 新增 spike checklist`design/07-ai/done/7-69-page-ai-pi-first-lab-checklist-v1.md`,逐项标注 Phase A-E 已完成项与下一阶段 gap。
## 3. 未完成内容
- 系统 PATH 仍未安装全局 `pi`,但已通过 `npx -y @earendil-works/pi-coding-agent@0.80.3` 包装器完成真实 `pi --mode rpc` 端到端 smoke;后续需决定是否固定安装方式或配置 `MNOTE_PAGE_AI_PI_BIN`
- receipt 尚未写入 Turso/libSQL 正式表,仍为 provider-neutral JSONL adapter。
- `mnote.tool_receipt.write` 当前仍是最小 tool envelope 路径,未作为独立持久化入口深化。
- watcher 刷新已按 MNote 文件写入链路声明和返回 metadata,但还需登录态浏览器端 smoke 证明 tiptap 当前页被 watcher 刷新。
- 已基于最新 `@earendil-works/pi-web-ui@0.75.3` 源码重新评估,而不是使用 recycle 旧源码。其 `ChatPanel` / `AgentInterface` / `MessageEditor` / message-tool renderer 是成熟 UI 参考,但直接接入会同时引入浏览器端 `pi-agent-core`、IndexedDB session/provider key、API key prompt、proxy、attachments、artifacts 和 `Agent.state.tools` 事实源,与 MNote 托管 Pi RPC/SSE、AiAccessScope 和 MNote-owned tools 边界冲突。
- 本轮采用 MNote-native adapter:视觉与交互参考官方 `ChatPanel/AgentInterface` 的消息流、composer、tool timeline、model/runtime controls 和 artifacts-style railsession、API key、模型、工具权限、receipt 仍由 MNote 后端托管。若下一阶段要直接复用官方组件,迁移条件是先提供不依赖浏览器 API key/IndexedDB/Agent tools 的 remote-session adapter。
- API endpoint smoke 当前覆盖禁用态 3000;完整 mock-mode smoke 需要用 `MNOTE_PAGE_AI_PI_LAB=1 MNOTE_PAGE_AI_PI_LAB_RUNTIME=mock` 启动独立服务后运行。
## 4. Pi 是否进入下一阶段
建议进入下一阶段,但仍保持 internal/lab
- 通过:MNote 侧最小 runtime manager、tool facade、allowed roots、LightRAG facade、file patch、receipt adapter、SSE UI 已形成闭环。
- 未通过生产门槛:Turso receipt 表、正式登录态 browser smoke、watcher tiptap 刷新截图/断言仍未完整。
下一阶段应聚焦真实 runtime 和验收,不扩大默认入口。
## 5. OpenHub 状态
OpenHub 继续保持默认 Page AI 主线。Pi Lab 未替换、未破坏 OpenHub / LightRAG / Turso 路径;相关路由和 UI 均受 `MNOTE_PAGE_AI_PI_LAB=1` / `enable_page_ai_pi_lab` 控制。
## 6. 验证记录
- `node scripts/task-pi-lab-static-smoke.js`:通过,78 项。
- `node scripts/task-pi-lab-api-endpoint-smoke.js`:通过,10/10;当前 3000 服务未启用 Pi Lab,验证禁用态和 asset。
- `node scripts/task-pi-lab-mock-api-smoke.js`:通过;覆盖 mock runtime start、SSE events、allowed roots、当前页读取、越界拒绝、当前 `.md` patch、watcher refresh metadata、LightRAG facade、citation/open-reference facade、send、abort。
- `node scripts/task-pi-lab-browser-smoke.js`:通过;覆盖当前 MNote Page AI 抽屉、dev-only 悬浮 `π` 入口、Pi/OpenHub 切换、`omniroute/freefirst`、MNote tool rail、composer、status API 和截图;不再把独立 `/page-ai/pi` 当作主要 UI 验收面。
- Pi + Omniroute/freefirst 浏览器实测截图:`tmp/page-ai-pi-omniroute-freefirst-final-ok-20260704-v3.png`;证据 JSON`tmp/page-ai-pi-omniroute-freefirst-final-ok-20260704-v3.json`。最终 assistant 可见文本为 `OK``assistantStreaming=false``inDrawer=true`,模型为 `omniroute/freefirst`runtime 为 `rpc`
- `node scripts/task-pi-lab-mock-api-smoke.js`:通过;使用 mock mode 独立 3332 服务与 `MNOTE_PI_LAB_SMOKE_ROOT=/tmp/mnote-pi-lab-smoke`,覆盖 start/send/abort/events、allowed roots、越界拒绝、当前页 read、文件 patch、receipt。
- `node scripts/task-pi-lab-api-endpoint-smoke.js`:通过,10/10;支持启用态 + Bearer smoke auth,验证 status schema、disabled builtins、runtime asset 和端点存在。
- `codegraph sync . && codegraph status .`:通过;索引 up to date。
- 真实 Pi RPC smoke:通过;使用 `/tmp/mnote-pi-cli-wrapper.sh` 调用 `npx -y @earendil-works/pi-coding-agent@0.80.3`MNote 以 `MNOTE_PAGE_AI_PI_LAB_RUNTIME=rpc` / `MNOTE_PAGE_AI_PI_BIN=/tmp/mnote-pi-cli-wrapper.sh` 启动,验证 `start` 返回 `runtimePid`、当前页读取、越界拒绝、当前 `.md` patch、`send` accepted、`abort` 成功。
- `node scripts/task-pi-lab-rpc-api-smoke.js`:新增 RPC API smoke 固化脚本(2026-07-04 7-69 收尾),覆盖 runtimeMode=rpc status、runtimePid 真实子进程验证、start/send/abort 端点、tool-call MNote facadesallowed roots、越界拒绝、.md patch 含 watcher refresh/version change、LightRAG、citation)、SSE 事件流(runtime_started 含 pid/mode/disabledBuiltinTools)、多会话生命周期、Pi 进程清理。需要真实 Pi CLI + API key 完整测试 send 实际 AI 调用。
- `cd rust && cargo check -p mnote-web`:通过。
- `which pi`:无输出;本轮通过 `MNOTE_PAGE_AI_PI_BIN` 指向 wrapper 完成真实 RPC smoke。
## RPC 验证命令
```bash
# 启动 mnote-web(真实 Pi RPC 模式)
MNOTE_PAGE_AI_PI_LAB=1 \
MNOTE_PAGE_AI_PI_LAB_RUNTIME=rpc \
MNOTE_PAGE_AI_PI_BIN=/tmp/mnote-pi-cli-wrapper.sh \
MNOTE_PI_LAB_ALLOWED_ROOTS=/tmp/mnote-pi-rpc-smoke \
npm run desktop:hot
# 另一个终端运行 smoke
MNOTE_PI_LAB_BASE=http://127.0.0.1:3000 \
MNOTE_PI_LAB_AUTH="Bearer pi-lab-smoke" \
MNOTE_PAGE_AI_PI_BIN=/tmp/mnote-pi-cli-wrapper.sh \
MNOTE_PI_LAB_ALLOWED_ROOTS=/tmp/mnote-pi-rpc-smoke \
node scripts/task-pi-lab-rpc-api-smoke.js
```
mock 验证命令:
```bash
MNOTE_PAGE_AI_PI_LAB=1 \
MNOTE_PAGE_AI_PI_LAB_RUNTIME=mock \
MNOTE_PI_LAB_ALLOWED_ROOTS=/tmp/mnote-pi-lab-smoke \
MNOTE_PI_LAB_BASE=http://127.0.0.1:3000 \
node scripts/task-pi-lab-mock-api-smoke.js
```