95 lines
8.4 KiB
Markdown
95 lines
8.4 KiB
Markdown
# 7-69 Page AI Pi-first Lab Spike 验收记录 v1
|
||
|
||
状态:done(最小可验证 spike)
|
||
Owner:07-ai / mnote-web / control-plane
|
||
日期:2026-07-03
|
||
|
||
## 1. 本轮结论
|
||
|
||
7-69 已完成 MNote 侧最小可验证 Pi-first Page AI Lab spike:OpenHub 仍是默认 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 时复用原抽屉与 iframe,OpenHub 仍是默认入口。
|
||
- 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 rail;session、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 facades(allowed 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
|
||
```
|