# pi 前端调研（只调研，先不实现）

> 目的：为"第三步：搭前端"决策提供依据。回答三件事——① 官方有没有给前端组件/模板？② 社区有没有人做了 pi 的 Web 页面、分别是什么？③ TS 全栈前后端到底怎么对接（对比以往 Python 后端 + 分离前端）。
>
> 定位：**调研存档，不实现。** 综合判断后再决定接哪个。
> **状态更新（2026-10-10）：前端已经落地，但走的是本文选型②（assistant-ui），不是第 5 节推荐的 `pi-web-ui` / agegr 壳。** 结论速览与对比部分作为决策依据保留，不再随版本更新；现状以 `web/` 源码与 [`assistant-ui.md`](assistant-ui.md) 的状态说明为准。
> 信源：官方 npm 包本地源码 + 官方文档 + 网络搜索（多信源交叉）。注：本环境无法直连 github.com（DNS 解析失败），涉及具体仓库文件/API 的细节标注为「待核实」。

---

## 0. 结论速览（TL;DR）

1. **官方有前端，但不是"完整应用"，而是 Web 组件库**：`pi-mono`（badlogic / earendil-works，作者 Mario Zechner）里有独立的 `pi-web-ui` 包，多个信源一致描述为 **"AI 对话 Web 组件 / Web 聊天组件"**，与 Agent 逻辑**完全解耦**（和 `pi-tui` 对称：一个是终端组件库，一个是 Web 组件库）。官方本体应用**只有 TUI（终端）**，没有"跑起来就能用的网页版 pi"。
2. **命名大坑：有两个都叫 "pi-web-ui" 的东西**，必须分清（见 §1.3）——官方 Mario 的组件库 `@mariozechner/pi-web-ui`（`@earendil-works/` 域下同源），与第三方作者 xingshuyin 的完整驾驶舱 `pi-web-ui`（npm，v0.94）。**我们仓库 `docs/能力与边界.md` 一直对照的是后者（那套 130 文件的重型 cockpit），不是官方的组件库。**
3. **社区完整 Web 应用首选 `agegr/pi-web`**：Next.js + TypeScript + Tailwind，MIT，`npx @agegr/pi-web@latest` 即可跑，star 最高（各信源 2.4k–6.7k 不等）。它读 pi 自己的会话文件 + 服务端起 AgentSession + SSE 推流。
4. **TS 全栈对接 ≠ Python 分离式的关键点**：前后端**共享同一份 TS 类型**（我们的 `protocol.ts` + `lib.ts` 就是这份"合约"），不是靠手写接口清单去对。但**"要自己写对接 + 润色"你的判断完全正确**——无论拿官方组件还是第三方应用，接到 pi-starter 的**自定义 WS 协议 + 独立会话目录**都得写适配层。

---

## 1. 官方到底给了什么前端

### 1.1 官方 pi-mono 的分层（展示层有两个库）
monorepo 包结构（依赖单向：`pods → ai → agent → coding-agent/slack-bot → tui/web-ui`）：

| 包 | 域名 | 性质 |
|---|---|---|
| `pi-ai` | `@earendil-works/pi-ai`（旧 `@mariozechner/pi-ai`） | 统一多模型 LLM API |
| `pi-agent-core` | `@earendil-works/pi-agent-core` | Agent 运行时，无 UI |
| `pi-coding-agent` | `@earendil-works/pi-coding-agent` | 编码 Agent CLI + SDK（我们的底座） |
| `pi-tui` | `@mariozechner/pi-tui` | **终端 UI 组件库**（差分渲染） |
| **`pi-web-ui`** | `@mariozechner/pi-web-ui`（`@earendil-works/` 同源） | **Web 聊天组件库**（与 agent 逻辑解耦） |

信源交叉：百度百科"Pi 的结构包括 pi-ai、pi-agent-core、pi-coding-agent、pi-tui、**pi-web-ui**"；johng.cn"web-ui @mariozechner/pi-web-ui — AI 对话 Web 组件"；smzdm/什么值得买"UI 组件库包含 pi-tui（终端）和 **pi-web-ui（Web 组件），与 Agent 逻辑完全解耦**"；dev.to"@earendil-works/pi-web-ui — **web components for AI chat interfaces**"；163"如果你要给自己的 Agent 工具做前端，不需要从零写 React 或 Vue，直接用 pi-mono 的现成组件"。

### 1.2 官方本地包里的实情（我们装的那份）
`node_modules/@earendil-works/pi-coding-agent` 里 **没有** web 前端：`dist/` 顶层是 `bun/cli/core/extensions/modes/utils`，`modes/` 下是 `interactive`(TUI)/`rpc`/`print`，docs 里 UI 相关只有 `tui.md`（终端组件），没有 `web.md`。README 里的"custom UI / interactive selector"全指 **TUI** 组件与浏览器 OAuth 回调。**→ 官方 coding-agent 包本身不含 Web 前端，Web 组件是 `pi-mono` 里另一个独立包 `pi-web-ui`，需要单独引入。**

### 1.3 命名消歧（务必分清）

| | 官方 Mario 的 | 第三方 xingshuyin 的 |
|---|---|---|
| npm | `@mariozechner/pi-web-ui` / `@earendil-works/pi-web-ui` | `pi-web-ui`（v0.94.1，MIT，作者 xingshuyin） |
| 性质 | **Web 组件库**（拼装用，解耦 agent） | **完整 browser cockpit**（chat/code/files/terminal/git，pi+DSH 双引擎） |
| 规模 | 组件级 | 约 130 TS 文件，`agent-service.ts` 约 1.5 万行（即我们 `能力与边界.md` 的对照对象） |
| 分发 | 作为库 import | 作为 **pi extension**（`pi install npm:pi-web-ui`，manifest 声明 `extensions`），agent 跑服务端、**WebSocket** 推浏览器 |

**我们仓库里"对照 pi-web-ui@0.99.0"指的是右列（xingshuyin 的重型 cockpit），不是官方组件库。** 之前调研时容易混，这里钉死。

> 待核实：官方 `pi-web-ui` 的确切 npm 域名下线状态、导出组件清单、期望的输入接口（事件流/会话适配器）。需能访问 npm/github 后 `npm view` + 读源码确认。

---

## 2. 第三方 / 社区 Web UI 候选

| 项目 | 技术栈 | 传输 | 对接方式（它怎么连 pi） | 规模/热度 | license | 复用适配 pi-starter 的难度 |
|---|---|---|---|---|---|---|
| **agegr/pi-web** ⭐最推荐作参考 | Next.js + TS + Tailwind | **SSE** | 读 `~/.pi/agent/sessions/*.jsonl` + `models.json`，Next.js API 路由**服务端起 AgentSession**，SSE 推流 | 6.7k star（趋势 2k+），`npx @agegr/pi-web@latest` | MIT | **高**：假设 pi CLI 会话约定 + 自持 AgentSession，与 pi-starter 自定义协议/独立会话目录不符 |
| **xingshuyin/pi-web-ui**（重型 cockpit） | Node+Express+WS + 前端 | **WebSocket** | 自成一整套前后端；agent 服务端、~100 条 WS 命令、terminal/scm/plugin | 26.5k 下载/月；即对照对象 | MIT | **很高**：它是另一套完整前后端 + 自己的 WS 协议，不是给外部后端用的壳 |
| **@narumitw/pi-webui**（narumiruna/pi-extensions） | pi extension | 事件流到 loopback 页 | "当前终端 pi 会话"的私有伴侣页，`/webui` 一次性链接，流语义消息+工具活动 | 小、轻 | MIT | **中**：绑定 pi 终端会话，非面向自定义后端 |
| **pi-desktop** | Electron（Main + utilityProcess 跑 pi-coding-agent + React 19/Vite renderer） | 类型化 MessagePort IPC + revisioned Browser RPC | 桌面壳，agent 在 utilityProcess | 桌面 cockpit | 待核实 | **高**：Electron 桌面形态，且自带 contract 层 |
| PI Code Agent（VSCode） | VSCode 扩展（封装 pi-web） | iframe + 本地 server | 把 pi-web 搬进 webview | marketplace | 待核实 | 参考"如何封装" |

其它周边（非直接前端）：`badlogic/pi-skills`（官方 skills 仓库：web search/browser automation 等）、`@mariozechner/pi-mom`（Slack bot 委托 agent）。

**架构共识（值得借鉴）**：几乎所有社区 Web UI 都遵循一句话——"agent 跑服务端、事件流推浏览器、会话磁盘事实源是 JSONL、内存进程可弃可恢复"。这正是 pi-starter 已有的方向（快照服务端唯一真源 + 会话落盘）。

---

## 3. TS 全栈前后端到底怎么对接（对比你之前的 Python 分离模式）

你之前的做法：**Python 后端把所有接口列出来（OpenAPI/接口清单）→ 前端照着对**。前后端是两个进程、两种语言，靠"手写契约"对齐，类型对不上只能运行时发现。

TS 全栈有三种常见形态，**别被"全栈"这个词唬住**：

**形态 A｜仍是你熟悉的前后端分离**（换语言而已）
Node/Express 后端 + 独立 SPA（Vite+React/Vue）。浏览器 `fetch`/`WebSocket` 打后端 HTTP/WS。
→ **pi-starter 现在就是这个形态**：Express REST + `ws` 的 `/ws`。对接心智和 Python+前端**一模一样**，只是两边都是 TS。

**形态 B｜全栈框架合体**（Next.js 那类，agegr/pi-web 就是）
一个 Next.js 应用：`app/api/**` 路由=后端，**服务端进程内直接 import pi SDK 起 AgentSession**，用 SSE 把事件流推给同一应用的页面。**没有独立的"后端服务器"要你去对接**——前后端在同一代码库、同一进程域。
→ 这条路和"我先有后端、再对接前端"不同；用它意味着后端也得按它的形态走（而 pi-starter 后端已按形态 A 建好）。

**形态 C｜共享类型的合约**（TS 真正的红利）
前后端**引用同一份 TS 类型定义**，wire 消息的形状在编译期两端一致。
→ **pi-starter 已经具备这份资产**：`src/protocol.ts`（`ClientMessage` A 类命令 / `ServerMessage` B 类推送 / `UiState` 快照 / `PROTOCOL_VERSION`）+ `src/lib.ts` 导出。前端 `import type` 这些，后端加/改字段，前端 type-check 立刻报错——**不用手写接口清单，也不用运行时才发现对不上**。这就是你"接口全部列出来前端去对"的 TS 升级版：**那份"清单"变成了可 import 的活类型**。

**所以 pi-starter 的前端 = 形态 A 后端 + 形态 C 合约**：一个消费 `/ws`（JSON 帧）+ 少量 REST 的浏览器端，`import` protocol.ts 类型。对接面见 §4。

---

## 4. pi-starter 后端接口面 × 各候选的对接评估

**pi-starter 已暴露的"接口面"（前端要吃的东西）：**
- **WS `/ws`**（主）：`hello`→`ready`(首帧+capabilities+protocolVersion)；`snapshot`+`rev` 链 + `snapshot_delta` 增量；命令面 `prompt`/`steer`/`follow_up`/`abort`/`new_conversation`/`open_conversation`/`switch_conversation`/`rename_conversation`/`rollback_conversation`/`edit_message`/`fork_conversation`/`set_label`/`list_models`/`set_model`/`cycle_model`/`set_thinking`/`get_capabilities`/`set_tool_enabled`/`search_knowledge`/`approval_response`/**`extension_ui_response`(HITL 反问)**/`set_plan_mode`/`get_settings`/`set_settings`/`compact_context`；推送 `run_start`/`run_end`/`turn_start`/`turn_end`/`tool_status`/`tool_delta`/`models`/`capabilities`/`conversations`/`edit_ready`/`knowledge_hits`/`approval_request`/**`extension_ui_request`**/`notice`/`error`。类型全在 `protocol.ts`。
- **REST**（辅助/无模型探针）：`/health` `/health/ready` `/info` `/metrics`；`POST /chat`(SSE) `POST /chat?format=jsonl`；`/db/query`；文件路由；`/approval/rules` CRUD；`/providers`；`/prompt-templates`；`/model/cycle`；`/context/compact`；`/sessions/import`。
- **`lib.ts`** 把 `protocol` 类型 + 解析器 + 桥都导出，前端/嵌入方直接 import。

**对接难点（也是你预判的"要自己写"的根据）：**
- **会话目录不同**：agegr/pi-web 默认读 `~/.pi/agent/sessions/`（pi CLI 约定），而 pi-starter **刻意不共用**（落在 SDK 目录的兄弟目录 `.pi-starter` + 自己的 `index.json`）。→ 复用 agegr 的 UI 壳时，**不能让它直接读会话文件**，得改成打 pi-starter 的 WS/REST。
- **协议不同**：xingshuyin cockpit 与 agegr 各有各的消息形状；pi-starter 有**自己封闭联合的 protocol.ts**。→ 无论用哪个现成 UI，都要写一层 **adapter：候选 UI 期望的事件/命令 ⇄ pi-starter 的 `ServerMessage`/`ClientMessage`**。

**各候选的对接策略：**
- **官方 `pi-web-ui` 组件库**：最契合"拿组件、自己接"——它解耦、只管渲染聊天界面；你写一个 hook 把 pi-starter 的 WS 帧喂给它、把用户操作转成 `ClientMessage`。**改动集中在适配层，UI 不用从零画**。← 与你"至少接口对接起来、稍改就能用"的意图最贴。
- **agegr/pi-web**：想整包用，得把它的"读 jsonl + 自持 AgentSession + SSE"后端换成"连 pi-starter WS"。**能当架构范本 + 组件参考**，整包搬改动大。
- **xingshuyin cockpit**：它本身就是"另一个完整前后端 + 另一套协议"，与 pi-starter 定位重叠且更重，**不适合作为 pi-starter 的前端**（对照价值 > 复用价值）。

---

## 5. 初步判断（留给你综合决策，不擅自开工）

1. **能拿、且值得拿的是"官方 `pi-web-ui` 组件库"或"agegr/pi-web 的 UI 壳/组件"**——两者都验证了"官方/社区有现成前端零件"，你的直觉对。
2. **无论拿哪个，接到 pi-starter 都要写适配层**（因为 pi-starter 有自定义 WS 协议 + 独立会话目录 + 封闭类型合约）。这层恰好就是形态 C 的红利：适配层两端都 import `protocol.ts`，类型编译期锁死。
3. 建议的落地路线（等你拍板再动）：**先做一个极小的 WS 骨架页（形态 A + C）**打通 `hello/ready/snapshot`，证明后端合约能被前端正确消费；**再决定**要不要引入官方组件库或 fork agegr 壳来替换手写的聊天渲染。
4. 你原来那句"就算拿过来也不代表能直接用，但肯定要用的"——完全成立，本文就是把"拿谁的、要改哪几处"落到纸面。

---

## 6. 待核实清单（需能访问 npm / 目标仓库源码后确认）
- 官方 `pi-web-ui` 确切 npm 包名（`@earendil-works/` vs `@mariozechner/`）、版本、导出组件、期望的输入接口（是否给"会话适配器/事件流"注入点）。
- `agegr/pi-web` 的 API 路由/协议细节（决定"换后端"的具体改动面）。
- xingshuyin `pi-web-ui` 与 `pi-desktop` 的 license、包边界（前者已知 MIT；desktop 待确认）。
- pi-starter 若要兼容"读 pi CLI 会话文件"的第三方 UI，是否需要提供一个可选的 `~/.pi/agent/sessions` 只读适配层（涉及隐私与会话目录隔离原则，谨慎）。
