pi-web-side
v0.2.0
Published
/side: ask side questions in pi without touching the main conversation. Works in Pi Web (@agegr/pi-web), RPC mode and the TUI; optional right-side side-chat panel for Pi Web (userscript).
Maintainers
Readme
pi-web-side
给 pi 用的 /side 旁路提问扩展,在 Pi Web(@agegr/pi-web)、
pi 的 RPC 模式和终端 TUI 里都能用。
主代理正在干活时,用 /side <问题> 随手问一个问题:
- 回答**流式显示在输入框上方的扩展小部件(widget)「side」**里,不依赖 TUI 浮层;
- 问答不会进入主对话的 LLM 上下文,主代理完全看不到;
- 主代理不会被打断,也不会被阻塞(steer / follow-up 照常可用);
- 旁路线程支持追问(会带上之前的旁路问答),会话重载、Pi Web 空闲回收或重启后可恢复。
为什么需要它:现有 /btw 扩展在 Pi Web 里不能用
社区里的 /btw 扩展(pi-btw、@narumitw/pi-btw、@lanlance/pi-btw 等)都是为 TUI 写的:答案画在
ctx.ui.custom() 的终端浮层里,并按 pi 官方文档的建议用 ctx.mode === "tui" 做判断。
Pi Web 在服务端进程内创建 AgentSession,并以 mode: "rpc" 绑定扩展(lib/rpc-manager.ts),于是:
| 扩展 | 在 Pi Web 里的实际表现(本机实测,pi-web 0.9.3 / pi 0.87.1) |
| --- | --- |
| @narumitw/pi-btw 0.61.1 | 直接报错 "/btw requires interactive TUI mode" |
| @lanlance/pi-btw(L2ncE/pi-btw) | 源码同样是 if (ctx.mode !== "tui") 报错(未单独实测) |
| pi-btw(dbachelder)0.6.1 | 不显示浮层,退化为把问答作为 btw-note 消息写进主会话记录;主代理忙时排队为 follow-up,结束后额外触发了一轮主代理回复 |
本扩展只使用 pi RPC 扩展 UI 协议里 Pi Web 能渲染的调用:setWidget(字符串数组)、notify、editor。
命令名用 /side,因此可以和终端里的 /btw 扩展同时安装而不冲突。
安装
要求:pi ≥ 0.87(Pi Web 0.9.x 自带 pi 0.87.1),Node ≥ 22.19。
# 从 npm 安装(发布后可用)
pi install npm:pi-web-side
# 或从本地打包好的 tarball 安装(注意要写成 包名@file:绝对路径 的形式;
# 直接写 npm:/path/to/xxx.tgz 能装上,但 pi 0.87.1 按路径推断包名,之后加载不到扩展)
pi install npm:pi-web-side@file:/path/to/pi-web-side-0.1.0.tgz
# 或直接使用本地源码目录(不复制,改代码后 /reload 即生效)
pi install /path/to/pi-web-sidepi install 会写入 ~/.pi/agent/settings.json 的 packages;也可以手动添加:
{ "packages": ["npm:pi-web-side"] }Pi Web 与 pi 共用 ~/.pi/agent(或 PI_CODING_AGENT_DIR)。装好后重启 pi-web,或在 Pi Web 里对当前会话执行
/reload、新开会话即可。可以在 Pi Web 的 Settings → Plugins 里确认已加载。
用法
| 命令 | 作用 |
| --- | --- |
| /side <问题> | 旁路提问(在当前旁路线程里追问);主代理运行中也可以直接发 |
| /side | 重新显示当前旁路线程的最新回答 |
| /side:new [问题] | 开一个新的旁路线程(可同时提问) |
| /side:show | 用对话框查看完整旁路线程(只读,点 Cancel 关闭,不会发送任何内容) |
| /side:inject [补充说明] | 把旁路问答作为一条用户消息交给主代理(主代理忙时排队到本轮结束后) |
| /side:stop | 取消正在进行的旁路请求 |
| /side:clear | 清空旁路线程并隐藏小部件 |
| /side:panel on\|off | 切换到右侧面板模式 / 切回小部件(通常由浏览器面板脚本自动发送) |
在 Pi Web 中:回答完成后会弹出提示;点击输入框上方的 「▲ side」 标签展开面板查看
(Pi Web 只会自动展开 ≤3 行的小部件,面板最高约 144px,可滚动;长回答用 /side:show 看全文)。
在 TUI 中,小部件直接显示在输入框上方。
可选环境变量(在启动 pi / pi-web 的环境里设置)
| 变量 | 说明 |
| --- | --- |
| PI_SIDE_MODEL=provider/modelId | 旁路问题使用的模型(默认用当前会话模型) |
| PI_SIDE_THINKING=off\|minimal\|low\|medium\|high\|xhigh\|inherit | 旁路问题的思考等级(默认 inherit,跟随主会话) |
| PI_SIDE_MAX_TOKENS=数字 | 旁路回答最大输出 token |
| PI_SIDE_LANG=en | 界面文字改为英文(默认中文) |
| PI_SIDE_UI=panel | 所有会话默认使用右侧面板模式 |
Pi Web 右侧侧聊面板(0.2.0,可选)
默认情况下回答显示在输入框上方的小部件里。如果想要类似 Codex「侧聊」的体验——页面右侧一个独立面板, 有自己的对话线程和输入框——再装一个浏览器用户脚本即可(不需要修改或重新构建 Pi Web):

- 浏览器安装用户脚本管理器:Tampermonkey 或 Violentmonkey (新版 Chrome 需要在扩展详情页打开「允许用户脚本 / Allow User Scripts」或开发者模式)。
- 新建脚本,把
browser/pi-web-side-panel.user.js的内容整体粘贴进去保存。 通过 npm 安装的包里也有这个文件:~/.pi/agent/npm/node_modules/pi-web-side/browser/pi-web-side-panel.user.js。 - 脚本默认只匹配
http://localhost:*和http://127.0.0.1:*。如果你通过其他地址访问 Pi Web, 在脚本头部加一行// @match https://你的域名/*。 - 确认 pi 侧装的是 pi-web-side ≥ 0.2.0(面板依赖
/side:panel命令),刷新 Pi Web 页面。
面板功能:
| 操作 | 说明 |
| --- | --- |
| 右边缘「侧聊」标签 / Alt+S | 打开 / 关闭面板(状态记在 localStorage);拖动面板左边缘可调宽度 |
| 面板输入框 | Enter 发送(Shift+Enter 换行,兼容中文输入法);回答流式显示 |
| 主输入框里输入 /side <问题> | 同样进入侧聊线程,面板会自动打开 |
| 选中主对话里的文字 →「引用到侧聊」 | 把选中内容以引用形式填进面板输入框 |
| 新线程 / 交给主代理 / 清空 | 需要点两次确认;「交给主代理」= /side:inject,主代理忙时排到本轮结束后 |
| 停止 | 取消正在生成的侧聊回答(/side:stop) |
| 右上角状态 | 显示主代理是运行中还是空闲(主对话不会因为侧聊被阻塞) |
原理:用户脚本运行在 Pi Web 页面里,只调用 Pi Web 自己的同源接口
(POST /api/agent/<id> 发送 /side… 命令、GET /api/agent/<id>/events 订阅 SSE)。
打开会话时它先用 get_commands 确认扩展有 /side:panel,再发送 /side:panel on;
扩展随后把整个侧聊线程以 JSON 形式发布到名为 side-panel 的扩展小部件里(不再显示文本小部件、也不弹完成提示),
脚本解析后渲染到右侧面板,并用 CSS 隐藏这个小部件的标签。没装脚本时一切保持 0.1.0 的行为。
限制(面板):
- 面板模式按「会话运行时」记忆;同一会话在另一个没装脚本的浏览器标签里打开时,会看到一个
side-panel小部件标签 (内容是 JSON)。可以在那里执行/side:panel off切回文本小部件,但装了脚本的标签页一旦收到文本小部件会自动再切回面板模式。 - 面板依赖 Pi Web 0.9.x 的接口和 DOM(
.right-panel-container、小部件标签的title属性),Pi Web 大改版后可能需要更新脚本。 - 侧聊仍然是无工具的单次模型调用,和 Codex 的侧聊(可以读文件、跑命令的临时分叉会话)不同。
- 只在 Chromium(Playwright)+ Pi Web 0.9.3 + mock 模型上测试过;未在 Tampermonkey/Violentmonkey、Firefox、Safari、手机上实测。
工作原理
/side命令处理函数立即返回(fire-and-forget)。这很关键:Pi Web 在AgentSessionWrapper.send("prompt")里会一直持有 HTTP 请求和 prompt 准入锁,直到扩展命令处理函数返回; 如果在处理函数里等待模型回答,主代理的 steer/follow-up 都会被卡住。- 旁路请求是一次无工具的模型调用:
ctx.modelRegistry.streamSimple()。 上下文 = 主会话当前分支(sessionManager.buildSessionProjection()→convertToLlm,包含 pi 0.87 记录在 会话里的 system prompt 与工具声明)+ 之前的旁路问答 + 本次问题(外面包一段"这是旁路问题、不要调用工具"的说明)。 保持前缀与主会话一致,便于命中提供商的 prompt cache;工具声明保留但设置toolChoice: "none"。 - 主代理正在执行工具时,最后一条 assistant 消息里的 tool call 还没有结果;扩展会去掉这种悬空的 tool call, 避免提供商报错。
- 流式输出以 ~150ms 节流更新
ctx.ui.setWidget("side", lines);Pi Web 通过 SSE 实时显示,并保存在get_state.extensionWidgets里,刷新页面也能看到。 - 完成的旁路问答以
custom条目(customTypepi-web-side:turn,清空时写pi-web-side:reset)写入会话文件—— pi 构建上下文时会忽略 custom 条目,所以主代理看不到;主代理忙时推迟到agent_end再写, 避免插在 tool call 和 tool result 之间。Pi Web 回收空闲会话或重启后,旁路线程可从这些条目恢复。 - 请求进行中会向 Pi Web 的
session-liveness/v1注册,避免会话在回答途中被空闲回收。
限制
- 旁路没有工具(不能读文件/跑命令),只能基于主会话已有的上下文和模型自身知识回答。
- Pi Web 的小部件是纯文本(支持 ANSI,不渲染 Markdown),面板较小;长回答请用
/side:show。 - 在 Pi Web 中,主代理空闲时发送
/side,聊天区会短暂出现一个 "/side …" 的乐观气泡(Pi Web 前端行为), 刷新后消失;它不会写入会话,也不会进入上下文。 - 旁路问答作为 custom 条目保存在会话
.jsonl里(不进上下文)。 - 只在 pi 0.87.1 + Pi Web 0.9.3 上、用 mock 模型实测过;
streamSimple/buildSessionProjection在更老的 pi 版本上可能不存在。
开发与测试
测试脚本不随 npm 包发布,只在源码目录中。
npm install
npx tsc --noEmit # 类型检查
# 启动一个 OpenAI 兼容的 mock 模型服务
node test/mock-openai.mjs & # 监听 127.0.0.1:18555,请求日志写到 MOCK_LOG
# 准备一个测试用 agent 目录:models.json 指向 mock,settings.json 的 packages 指向本目录
# models.json: { "providers": { "mock": { "baseUrl": "http://127.0.0.1:18555/v1", "api": "openai-completions",
# "apiKey": "x", "models": [ { "id": "mock-model" } ] } } }
# settings.json: { "defaultProvider": "mock", "defaultModel": "mock-model", "packages": ["/path/to/pi-web-side"] }
# 1) 真实 pi --mode rpc 端到端测试
PI_CODING_AGENT_DIR=/tmp/agent node test/rpc-cli.test.mjs
# 2) 真实 Pi Web 端到端测试(先用同一个 PI_CODING_AGENT_DIR 启动 pi-web)
PI_CODING_AGENT_DIR=/tmp/agent npx @agegr/[email protected] -p 30199 --no-open &
BASE=http://127.0.0.1:30199 TEST_CWD=$PWD node test/piweb-e2e.mjs --strict
# 3) 右侧面板协议端到端测试(按用户脚本的方式调用 Pi Web 接口)
BASE=http://127.0.0.1:30199 TEST_CWD=$PWD node test/panel-e2e.mjstest/piweb-e2e.mjs 支持 CMD= / WIDGET= 环境变量,也可以用来对比其他旁路提问扩展在 Pi Web 里的表现。
许可证
MIT
