Files
mnote/design/07-ai/done/7-70-page-ai-pi-lab-native-openhub-like-ui-record-v1.md
T

12 KiB
Raw Blame History

7-70 Page AI Pi Lab 原生 OpenHub-like UI 验收记录 v1

状态:doneUI spike + P2 Pi tool bridge 回归) Owner07-ai / mnote-web / page-ai-runtime 日期:2026-07-04

1. 结论

7-70 已完成 Pi Lab 从 7-69 的“OpenHub 抽屉内 provider 面板”到“默认显示的独立 π 悬浮入口 + MNote-native 抽屉”的切换。OpenHub 继续作为默认 Page AI 主线,Pi Lab 不复用 OpenHub 按钮、iframe 页面、provider tab 或抽屉容器。

Pi Lab 可以进入下一阶段受控验证,但仍不替换 OpenHub。2026-07-04 已补跑真实 Pi RPC + Omniroute/freefirst 的浏览器回归,证明真实 Pi text stream 能进入 MNote-native timeline 且 provider thinking 不进入 visible final answer。随后补齐了文档页 /documents/local-md:* 默认注入独立 Pi Lab、当前页读取、tiptap live selection snapshot、allowed roots deny、.md patch 后 watcher/refresh 刷新当前 tiptap 页的浏览器回归。

P2 已补齐真实 Pi 主动调用 MNote-owned tools 的最小链路:由于 Pi RPC 协议没有 host 回写 tool_result 的 stdin command,当前 spike 采用 per-session Pi extension bridge 注册 mnote_* custom tools。Pi 子进程仍使用 --no-builtin-tools 禁用原始 bash/read/write/edit,只允许 mnote_current_page_read 等 MNote 工具名;extension execute 通过内部 /api/page-ai/pi/tool-call-bridge 回到 MNote execute_tool,由 MNote 执行 allowed roots / LightRAG facade / receipt 写入。真实 Omniroute/freefirst API smoke 已观察到 Pi 发出 tool_execution_start: mnote_current_page_readMNote bridge 执行 mnote.current_page.read,并返回最终 marker。

2. 已实现内容

  • sidebar-page-ai-pi-lab-runtime.js 改为独立 drawer[data-page-ai-pi-lab-launcher] 默认显示,点击打开 [data-page-ai-pi-lab="drawer"],不再调用 attachPanelToDrawer / setOpenHubVisible,不再出现 data-page-ai-pi-lab-openhub-tab
  • UI 补齐 topbar、session/runtime/model/status、context strip、message timeline、composer、right rail receipts/tools、默认折叠 diagnostics2026-07-04 二次收敛为 OpenHub 平台式首屏:大标题、水平工具栏、白色对话卡片、居中空状态示例、底部大输入框与 Build/Plan/model/budget 控制条。
  • MNOTE_PAGE_AI_PI_LAB 改为默认开启,保留为 0/false 强制关闭开关;后端 status 返回 uiMode=independent_mnote_native_drawer
  • 保留 MNote-owned tool set 和 Pi builtin 禁用策略:bash/read/write/edit 仍默认不可用。
  • tool event UI 补充 deny reason、diff summary、file version、citation count、changed files chip。
  • mock runtime send 事件补充 LightRAG mock citation 与 diff event,用于浏览器截图验证 stream/citation/diff 的 UI 形态。
  • OpenHub 回归通过浏览器 smoke 验证:OpenHub drawer API 仍存在,OpenHub iframe host 仍独立打开,Pi drawer 不在 OpenHub drawer 内。
  • 文档页 shell 现在在 enable_page_ai_pi_lab 开启时注入 Pi Lab runtime,因此真实 /documents/local-md:* 页面默认也显示独立 π 入口。
  • Pi Lab start session 会绑定当前 rootUri / workspaceId / pagePath / pageTitle
  • “当前页”快捷按钮实际调用 mnote.current_page.read,并把当前 Markdown 内容注入 composer / system timeline。
  • “选区”快捷按钮消费 window.getSelection()mnote:leptos-tiptap-spike:selection 快照,已在真实 tiptap 页面验证选中文本注入 composer。
  • mnote.local_file.patch tool event 后会走 local-folder event bus + document pane refresh,不新增轮询;真实浏览器截图已验证当前 .md patch 后 .ProseMirror 显示新内容。
  • 每个 Pi session 生成独立 tool bridge extension,注册 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
  • bridge token 使用 /dev/urandom 生成,不随 session JSON 序列化,不写入 extension 文件;token 通过子进程 env 注入。
  • /api/page-ai/pi/tool-call-bridge 只接受匹配 bridge token 且仍处于 RuntimeRunning/TurnRunning 的 session。
  • session TTL、最大 session 数、start/send/tool 简单 rate limit 已补;过期 session 目录和空 rate bucket 会清理。

3. 第三方 UI 复用决策

  • @earendil-works/pi-web-ui 继续作为 ChatPanel / AgentInterface / ThinkingBlock / tool renderer / artifacts rail 的交互参考,不直接引入 MNote 主链。
  • @assistant-ui/react-pi 继续作为 HTTP/SSE thread contract 参考,不让其接管 MNote provider key、IndexedDB session、Pi browser Agent 或 tool 权限。
  • ai-elements 继续作为 message / reasoning / tool / source / prompt input 形态参考,不引入 React/Tailwind/shadcn 依赖。

4. 验证记录

  • node scripts/task-pi-lab-static-smoke.js:通过,119 checks。
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33170 MNOTE_PI_LAB_AUTH='Bearer pi-lab-smoke' node scripts/task-pi-lab-api-endpoint-smoke.js:通过,10/10。
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33170 MNOTE_PI_LAB_AUTH='Bearer pi-lab-smoke' MNOTE_PI_LAB_SMOKE_ROOT=/tmp/mnote-pi-lab-smoke node scripts/task-pi-lab-mock-api-smoke.js:通过。
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33170 MNOTE_PI_LAB_AUTH_HEADER='Bearer pi-lab-smoke' MNOTE_PI_LAB_BROWSER_ROOT=/tmp/mnote-pi-lab-browser-smoke MNOTE_PI_LAB_SCREENSHOT=/mnt/Data1T/mnote/tmp/page-ai-pi-lab-native-drawer-7-70.png node scripts/task-pi-lab-browser-smoke.js:通过。
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33172 MNOTE_PI_LAB_AUTH='Bearer pi-lab-smoke' MNOTE_PI_LAB_BROWSER_ROOT=/tmp/mnote-pi-real-rpc-smoke-1783130268 MNOTE_PI_LAB_SCREENSHOT=/mnt/Data1T/mnote/tmp/page-ai-pi-lab-rpc-browser-real-7-71.png node scripts/task-pi-lab-rpc-browser-smoke.js:通过;使用 MNOTE_PAGE_AI_PI_LAB_RUNTIME=rpcMNOTE_PAGE_AI_PI_BIN=/tmp/mnote-pi-cli-wrapper.sh 和 Omniroute freefirst
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33172 MNOTE_PI_LAB_AUTH='Bearer pi-lab-smoke' MNOTE_PI_LAB_BROWSER_ROOT=/tmp/mnote-pi-p1-rpc-smoke-1783133999 MNOTE_PI_LAB_SCREENSHOT=/mnt/Data1T/mnote/tmp/page-ai-pi-lab-rpc-browser-p1-7-71c.png node scripts/task-pi-lab-rpc-browser-smoke.js:通过;覆盖真实文档页 Pi Lab 默认入口、当前页读取、tiptap selection、allowed roots deny、.md patch、watcher/refresh 到 .ProseMirror、真实 Pi stream、abort、非 iframe;同时收紧 pagePath 绑定断言。
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33172 MNOTE_PI_LAB_AUTH='Bearer pi-lab-smoke' MNOTE_PI_LAB_SMOKE_ROOT=/tmp/mnote-pi-real-rpc-smoke-1783130268 MNOTE_PAGE_AI_PI_BIN=/tmp/mnote-pi-cli-wrapper.sh node scripts/task-pi-lab-rpc-api-smoke.js:通过;覆盖真实 Pi RPC subprocess、send、SSE、abort、allowed roots deny、.md patch、receipt。
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33172 MNOTE_PI_LAB_AUTH='Bearer pi-lab-smoke' OPENAI_API_KEY=<codex auth> node scripts/task-pi-lab-rpc-api-smoke.js:通过;覆盖真实 Pi custom tool 主动调用 mnote_current_page_read、MNote bridge 执行 mnote.current_page.read、marker 返回、send、SSE、abort、allowed roots deny、.md patch、receipt。
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33172 MNOTE_PI_LAB_AUTH='Bearer pi-lab-smoke' MNOTE_PI_LAB_SCREENSHOT=/mnt/Data1T/mnote/tmp/page-ai-pi-lab-rpc-browser-p2-visible-reply-7-70.png node scripts/task-pi-lab-rpc-browser-smoke.js:通过;覆盖真实浏览器 Pi Lab 默认入口、原生 drawer、当前页/选区、allowed roots deny、.md patch watcher refresh、真实 Pi stream、assistant bubble 可见回复、abort、非 iframe。该 smoke 已修正为断言 assistant bubble 本身包含 marker,而不是误用 drawer 全文里的用户 prompt。
  • MNOTE_PI_LAB_BASE=http://127.0.0.1:33172 MNOTE_PI_LAB_AUTH='Bearer pi-lab-smoke' MNOTE_PI_LAB_SCREENSHOT=/mnt/Data1T/mnote/tmp/page-ai-pi-lab-openhub-like-ui-collapsed-v2-7-70.png node scripts/task-pi-lab-rpc-browser-smoke.js:通过;覆盖 OpenHub-like 折叠 UI,设置/上下文/右侧 tools/receipts 默认收起,主对话区成为视觉重点,同时保留模型选择入口和真实 Pi 可见回复。
  • curl -H 'Cookie: foo=bar' http://127.0.0.1:33172/api/page-ai/pi/status:返回 401 page_ai_pi_lab_invalid_session_cookie,验证任意 Cookie 不能绕过 Pi Lab API 认证。
  • cd rust && cargo check -p mnote-web:通过。
  • codegraph sync . && codegraph status .:通过,index up to dateCodeGraph 提示索引由旧 engine 构建,可后续择机 full re-index。

5. 截图证据

  • Pi Lab 原生抽屉:tmp/page-ai-pi-lab-native-drawer-7-70.png
    • 显示独立 π 悬浮按钮。
    • 显示独立 Pi Lab drawer,不是 iframe。
    • 显示 omniroute/freefirst、runtime mock、aborted、context strip、tool rail、receipt rail。
    • 显示 prompt、mock stream、abort、LightRAG mock citation、patch/diff、changed files、allowed roots deny receipt。
  • OpenHub 默认入口回归:tmp/page-ai-pi-lab-openhub-default-7-70.png
    • 显示 OpenHub drawer 仍可独立打开。
    • 同屏保留独立 π 入口,说明 Pi Lab 未复用 OpenHub 抽屉容器。
  • 真实 Pi RPC 浏览器回归:tmp/page-ai-pi-lab-rpc-browser-real-7-71.png
    • 显示 runtime rpc、模型 omniroute/freefirst
    • 显示真实 Pi RPC text stream 返回 REAL_PI_BROWSER_OK_* 标记。
    • provider thinking 事件仅进入 diagnostics,不进入 visible final answer。
    • 同时验证 allowed roots deny、markdown patch receipt、abort UI 状态和非 iframe 独立 drawer。
  • P1 文档页功能回归:tmp/page-ai-pi-lab-rpc-browser-p1-7-71c.png
    • 显示真实 /documents/local-md:* 页面默认存在独立 π 入口。
    • 显示 Pi Lab drawer 内绑定当前页、选区、runtime rpc、模型 omniroute/freefirst
    • 显示当前页已由 patch 从 Browser RPC Original 刷新为 Browser RPC Patched
    • 显示 allowed roots deny receipt、patch diff receipt、changed files chip。
  • P2 真实 Pi RPC 浏览器回归:tmp/page-ai-pi-lab-rpc-browser-p2-visible-reply-7-70.png
    • 显示真实 MNote 文档页默认独立 π 入口与独立 Pi Lab drawer。
    • 显示 runtime rpc、模型 omniroute/freefirst、Pi builtin bash/read/write/edit disabled。
    • 显示当前页读取、选区、allowed roots deny receipt、patch diff receipt、changed files chip、assistant 可见回复。
  • OpenHub-like 折叠 UI 回归:tmp/page-ai-pi-lab-openhub-like-ui-collapsed-v2-7-70.png
    • 显示设置、上下文、MNote tools、Receipts 默认折叠。
    • 显示模型选择入口 omniroute/freefirst,主 timeline 和 composer 保持首屏重点。
  • OpenHub 平台式空状态回归:tmp/page-ai-pi-lab-openhub-platform-empty-v4-7-70.png
    • 显示独立 Pi Lab 抽屉使用 OpenHub 平台式标题、顶部工具栏、白色对话卡片、居中问号空状态和五个示例。
    • 设置、上下文、诊断默认不占首屏;通过齿轮展开。
    • 底部保留大输入框、发送按钮、Build/Plan、omniroute/freefirst 模型选择、预算和当前页/选区快捷入口。

6. 未完成内容与风险

  • receipt 仍落 provider_neutral_jsonl_adapter_v1,尚未迁移到 Turso/libSQL 正式 ai_provider_sessions / ai_tool_events / ai_file_patches 表。
  • 真实 Pi RPC + Omniroute/freefirst 已补浏览器回归;真实 Pi 主动调用 MNote tools 的最小 bridge 链路已在 API smoke 中通过。长期更正统方向仍是 in-process AgentSession / Node sidecar SDK,避免依赖 RPC subprocess + extension bridge。
  • watcher refresh 已通过真实文档页 .md patch 后 .ProseMirror 更新验证;仍需在后续产品化中补更细的 file version 冲突与多 pane 场景。
  • P1/P2 审查后已修复任意 Cookie 认证绕过、跨用户 session 查询、selection tool 缺少 host snapshot 标识、tool_receipt.write 空操作、browser smoke pagePath 误报、bridge token 可预测、aborted session bridge 调用、rate bucket 增长和 token 落盘风险。