pi-web-side

/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).

Packages

Package details

extension

Install pi-web-side from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-web-side
Package
pi-web-side
Version
0.2.0
Published
Sep 29, 2026
Downloads
354/mo · 24/wk
Author
betago
License
MIT
Types
extension
Size
65.4 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./extensions/side.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

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-side

pi 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):

右侧侧聊面板

  1. 浏览器安装用户脚本管理器:Tampermonkey 或 Violentmonkey (新版 Chrome 需要在扩展详情页打开「允许用户脚本 / Allow User Scripts」或开发者模式)。
  2. 新建脚本,把 browser/pi-web-side-panel.user.js 的内容整体粘贴进去保存。 通过 npm 安装的包里也有这个文件:~/.pi/agent/npm/node_modules/pi-web-side/browser/pi-web-side-panel.user.js。
  3. 脚本默认只匹配 http://localhost:* 和 http://127.0.0.1:*。如果你通过其他地址访问 Pi Web, 在脚本头部加一行 // @match https://你的域名/*。
  4. 确认 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、手机上实测。

工作原理

  1. /side 命令处理函数立即返回(fire-and-forget)。这很关键:Pi Web 在 AgentSessionWrapper.send("prompt") 里会一直持有 HTTP 请求和 prompt 准入锁,直到扩展命令处理函数返回; 如果在处理函数里等待模型回答,主代理的 steer/follow-up 都会被卡住。
  2. 旁路请求是一次无工具的模型调用:ctx.modelRegistry.streamSimple()。 上下文 = 主会话当前分支(sessionManager.buildSessionProjection() → convertToLlm,包含 pi 0.87 记录在 会话里的 system prompt 与工具声明)+ 之前的旁路问答 + 本次问题(外面包一段"这是旁路问题、不要调用工具"的说明)。 保持前缀与主会话一致,便于命中提供商的 prompt cache;工具声明保留但设置 toolChoice: "none"。
  3. 主代理正在执行工具时,最后一条 assistant 消息里的 tool call 还没有结果;扩展会去掉这种悬空的 tool call, 避免提供商报错。
  4. 流式输出以 ~150ms 节流更新 ctx.ui.setWidget("side", lines);Pi Web 通过 SSE 实时显示,并保存在 get_state.extensionWidgets 里,刷新页面也能看到。
  5. 完成的旁路问答以 custom 条目(customType pi-web-side:turn,清空时写 pi-web-side:reset)写入会话文件—— pi 构建上下文时会忽略 custom 条目,所以主代理看不到;主代理忙时推迟到 agent_end 再写, 避免插在 tool call 和 tool result 之间。Pi Web 回收空闲会话或重启后,旁路线程可从这些条目恢复。
  6. 请求进行中会向 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/pi-web@0.9.3 -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.mjs

test/piweb-e2e.mjs 支持 CMD= / WIDGET= 环境变量,也可以用来对比其他旁路提问扩展在 Pi Web 里的表现。

许可证

MIT