原始内容
Pi Agent QQBot
English
Connect a QQ C2C conversation to the native Pi session that is already running in your terminal, using the official QQ Bot API.
Why This Exists
QQ is not a separate agent. It is a second input surface for the same Pi runtime: terminal and QQ share the working directory, model, tools, context, session history, and extension-created interaction UI.
Use the same live coding session from QQ when you are away from the terminal, without creating a second agent, duplicating context, or exposing a general-purpose remote command shell.
Requirements
- Pi
>=0.82.0 <1.0.0 - Node.js
>=22.19.0 - An official QQ bot with C2C direct messages enabled
- Native Windows, macOS, or Linux
WSL path translation and mixed Windows/WSL installations are not supported.
Highlights
- Bind one configured QQ C2C owner to the active native Pi session.
- Work from QQ with the same workspace, model, context, and session history.
- Use QQ Keyboard controls for models, thinking levels, and native Pi sessions.
- Deliver
ctx.ui.confirm(),select(), andinput()to both terminal and QQ. The first response from either side wins. - Operate extension-created
ctx.ui.custom()remotely through a TUI snapshot, keys, and text input. - Receive images, voice, documents, and ordinary files without extension-level file count, type, or size quotas.
- Send verified local files through QQ's official chunked-upload API.
- Transfer Gateway ownership between local Pi processes without stopping either process.
Install
Install the published npm package:
pi install npm:pi-agent-qqbot
You can also install a tagged GitHub release:
pi install git:github.com/gtiders/pi-agent-qqbot@v1.0.4
When HTTPS access to GitHub is unavailable, use SSH:
pi install git:git@github.com:gtiders/pi-agent-qqbot@v1.0.4
Completely exit and restart Pi after installation or upgrade.
Configure
Create ~/.pi/agent/pi-agent-qqbot.json. On Windows, this is %USERPROFILE%\.pi\agent\pi-agent-qqbot.json.
{
"schemaVersion": 5,
"appId": "YOUR_QQBOT_APP_ID",
"clientSecret": "YOUR_QQBOT_APP_SECRET",
"sandbox": true,
"ownerOpenId": "",
"link": { "conflictPolicy": "ask" },
"inboundMedia": { "deniedKinds": [], "deniedExtensions": [] },
"outboundMedia": {
"enabled": false,
"deniedRoots": [],
"deniedKinds": [],
"deniedExtensions": []
},
"logging": { "level": "info" }
}
Only schema 5 is accepted. Earlier configurations are neither read nor migrated. For first-time setup, leave ownerOpenId empty and use /qqbot-start to discover and confirm it locally.
| Field | Meaning |
|---|---|
appId |
QQ bot application ID. |
clientSecret |
QQ bot secret. Never commit a real value. |
sandbox |
Use QQ's sandbox endpoint when true. |
ownerOpenId |
The only QQ C2C user allowed to access Pi. Leave it empty only during first-time bootstrap. |
link.conflictPolicy |
ask requests terminal approval before takeover; takeover transfers ownership automatically. |
inboundMedia.deniedKinds |
Deny image, video, voice, or file; an empty list denies no type. |
inboundMedia.deniedExtensions |
Deny file extensions such as .exe; an empty list denies none. |
inboundMedia.stt |
Optional OpenAI-compatible speech-to-text settings: baseUrl, apiKeyEnv, and model. |
outboundMedia.enabled |
Master switch for local file delivery; defaults to false. |
outboundMedia.deniedRoots |
Real directory roots that must never be sent; an empty list permits every path readable by the current account. |
outboundMedia.deniedKinds |
Deny outbound media types; an empty list denies none. |
outboundMedia.deniedExtensions |
Deny outbound extensions; an empty list denies none. |
logging.level |
error, info, or debug. |
Media policies use denylist semantics. An omitted field or empty list adds no product-level restriction. QQ platform limits and file-integrity checks still apply.
Start and Link
The extension does not connect to QQ automatically. For normal use, run this command in the local terminal for each new Pi process:
/qqbot-link
For first-time setup only: save appId and clientSecret, leave ownerOpenId empty, run /qqbot-start, then send any C2C message to the bot. Pi displays the detected user_openid locally; after terminal confirmation it saves the value. Run /qqbot-link afterwards.
| Local command | Purpose |
|---|---|
/qqbot-start |
First-time bootstrap only. Temporarily starts the Gateway to discover the first private sender OpenID; terminal confirmation writes it to the config and stops bootstrap. |
/qqbot-stop |
Stop transport only; preserve the link and native Pi session. |
/qqbot-link |
Start the QQ Gateway and bind the configured owner to the current Pi runtime. |
/qqbot-unlink |
Remove the logical link and invalidate pending QQ replies. |
/qqbot-status |
Show Gateway, ownership, link, session, model, and queue state. |
/qqbot-takeover |
Take Gateway ownership from another local Pi process. |
QQ cannot run /qqbot-* commands.
QQ Commands
QQ commands are explicitly allowlisted and appear as a Keyboard when supported by the client.
| Command | Purpose |
|---|---|
/help |
Show the command menu. |
/status |
Show the active link and Pi state. |
/model [provider/model] |
Browse, search, or switch the active model. |
/thinking [level] |
Show or change the thinking level. |
/new [name] |
Create a native Pi session, optionally named. |
/sessions [query] |
List or search native Pi sessions. |
/resume <id-or-name> |
Switch to a uniquely matching native Pi session. |
/name <name> |
Rename the current session. |
/compact [instructions] |
Compact the active context. |
/stop |
Abort the active Pi turn. |
Normal QQ text is injected into the linked Pi session and uses Pi's native follow-up delivery while Pi is busy. Only output from QQ-originated turns is returned to QQ; output from terminal-originated turns remains in the terminal.
Shared Interaction UI
When a QQ-originated turn opens ctx.ui.confirm(), ctx.ui.select(), or ctx.ui.input(), both QQ and terminal receive the interaction. The first side to complete it wins and invalidates the other side.
For extension-created ctx.ui.custom(), both surfaces operate the same component instance. QQ receives a 72-column TUI snapshot with terminal control sequences removed, plus directional, paging, Home/End, Tab, space, backspace, delete, submit, cancel, and refresh controls. Normal QQ text is injected as terminal bracketed-paste input, including into password fields.
Pi core TUI, external PTY programs, and custom UI opened only by terminal-originated turns are outside this relay's scope.
Media
Inbound media has no extension-level count, type, individual-size, or total-size quotas. Images go to image-capable models; supported documents are extracted within remaining context budget; voice uses QQ ASR text when available; unknown files remain as temporary local resources for Pi tools.
Inbound downloads still enforce HTTPS, SSRF protection, bounded redirects, cancellation, and stalled-network detection.
With outbound media enabled, the qq_send_local_file tool can send a local file only to the original conversation of the current QQ turn. Candidates are checked with realpath, regular-file identity, hard-link, symlink/junction, and rename-race validation. QQ's 200 MB upload limit and passive-reply count remain platform constraints.
Current Boundaries
- One configured QQ C2C owner only. Group messages and other users are ignored before attachment handling and agent work.
- The Gateway does not start automatically with the extension. Run
/qqbot-linkfor normal operation. - Pi 0.82 can discover Skills, prompt templates, and extension commands, but does not expose a complete public input-dispatch API. QQ therefore cannot generically execute every Skill, template, or third-party slash command; it supports only the allowlisted commands above.
- Restart Pi completely after upgrading an installed package.
Troubleshooting
- Invalid configuration: verify
schemaVersion,appId,clientSecret, andownerOpenId. - No QQ response: run
/qqbot-status, then run/qqbot-linklocally. - Missing
ownerOpenId: leave it empty, run/qqbot-start, message the bot from QQ, and confirm the displayed OpenID in the terminal. - Gateway owned by another Pi: run
/qqbot-takeover, or retainconflictPolicy: "ask"and approve in the terminal. - Cannot send local files: set
outboundMedia.enabledtotrue, then restart Pi. - QQ buttons unavailable: send the corresponding text command shown on the card.
Development
See the development guide for architecture, development, and release procedures, and CHANGELOG for release history.
License
Apache-2.0
简体中文
通过 QQ 官方机器人 API,将一个 QQ 私聊连接到终端中正在运行的原生 Pi 会话。
为什么需要它
QQ 不是另起一个 Agent,而是同一 Pi runtime 的第二个输入端:终端与 QQ 共享工作目录、模型、工具、上下文、会话历史,以及扩展创建的交互界面。
离开终端时也能从 QQ 继续使用同一个实时编码会话,无需另起 Agent、复制上下文,也不会把 Pi 暴露成通用远程命令终端。
环境要求
- Pi
>=0.82.0 <1.0.0 - Node.js
>=22.19.0 - 已启用 C2C 私聊消息的 QQ 官方机器人
- 原生 Windows、macOS 或 Linux
不支持 WSL 路径转换,也不支持 Windows/WSL 混合安装。
主要能力
- 将唯一配置的 QQ C2C owner 绑定到当前原生 Pi 会话。
- 从 QQ 操作同一工作目录、模型、上下文和会话历史。
- 使用 QQ Keyboard 切换模型、思考等级和原生 Pi 会话。
- 将
ctx.ui.confirm()、select()、input()同时发送到终端和 QQ,任意一端先完成即生效。 - 通过 TUI 快照、按键和文字输入远程操作扩展创建的
ctx.ui.custom()。 - 接收图片、语音、文档及普通文件,不增加扩展层文件数量、格式或大小配额。
- 通过 QQ 官方分片上传 API 发送经过本地文件身份校验的文件。
- 在本机多个 Pi 进程间转移 Gateway 所有权,不终止任何 Pi 进程。
安装
安装 npm 正式版本:
pi install npm:pi-agent-qqbot
也可以从 GitHub 的版本标签安装:
pi install git:github.com/gtiders/pi-agent-qqbot@v1.0.4
如果本机无法通过 HTTPS 连接 GitHub,可使用 SSH:
pi install git:git@github.com:gtiders/pi-agent-qqbot@v1.0.4
安装或升级后必须完整退出并重新启动 Pi。
配置
创建 ~/.pi/agent/pi-agent-qqbot.json。Windows 路径为 %USERPROFILE%\.pi\agent\pi-agent-qqbot.json。
{
"schemaVersion": 5,
"appId": "YOUR_QQBOT_APP_ID",
"clientSecret": "YOUR_QQBOT_APP_SECRET",
"sandbox": true,
"ownerOpenId": "",
"link": {
"conflictPolicy": "ask"
},
"inboundMedia": {
"deniedKinds": [],
"deniedExtensions": []
},
"outboundMedia": {
"enabled": false,
"deniedRoots": [],
"deniedKinds": [],
"deniedExtensions": []
},
"logging": {
"level": "info"
}
}
扩展只接受 schema 5,不读取也不迁移过时配置。首次配置时可将 ownerOpenId 留空,通过 /qqbot-start 在本地发现并确认写入。
| 字段 | 含义 |
|---|---|
appId |
QQ 机器人应用 ID。 |
clientSecret |
QQ 机器人密钥,不得提交真实值。 |
sandbox |
true 表示使用 QQ 沙箱端点。 |
ownerOpenId |
唯一允许访问 Pi 的 QQ C2C 用户;仅首次引导时可留空。 |
link.conflictPolicy |
ask 在接管前请求终端确认;takeover 自动转移所有权。 |
inboundMedia.deniedKinds |
禁止 image、video、voice 或 file;空数组表示不禁止类型。 |
inboundMedia.deniedExtensions |
禁止 .exe 等扩展名;空数组表示不禁止扩展名。 |
inboundMedia.stt |
可选的 OpenAI 兼容语音转写配置:baseUrl、apiKeyEnv、model。 |
outboundMedia.enabled |
本地文件出站总开关,默认 false。 |
outboundMedia.deniedRoots |
永远禁止发送的真实目录根路径;空数组允许所有当前账户可读路径。 |
outboundMedia.deniedKinds |
禁止出站媒体类型;空数组表示不禁止类型。 |
outboundMedia.deniedExtensions |
禁止出站文件扩展名;空数组表示不禁止扩展名。 |
logging.level |
error、info 或 debug。 |
媒体配置统一采用黑名单语义:字段缺失或数组为空表示不增加产品限制。QQ 平台硬限制和文件完整性校验仍然生效。
启动与绑定
扩展加载时不会自动连接 QQ。正常使用时,每个新的 Pi 进程都需要在本地终端执行:
/qqbot-link
仅首次配置:先写入 appId 和 clientSecret,将 ownerOpenId 留空,执行 /qqbot-start,再从 QQ 私聊机器人发送任意消息。Pi 会在本地显示收到的 user_openid;终端确认后自动写入配置。随后执行 /qqbot-link。
本地专用命令:
| 命令 | 作用 |
|---|---|
/qqbot-start |
仅首次引导使用。临时启动 Gateway 发现第一位私聊用户的 OpenID;终端确认后写入配置并停止引导。 |
/qqbot-stop |
只停止传输,保留 link 和当前原生 Pi 会话。 |
/qqbot-link |
启动 QQ Gateway,并将配置的 owner 绑定到当前 Pi runtime。 |
/qqbot-unlink |
解除逻辑 link,并使未完成的旧 QQ 回复失效。 |
/qqbot-status |
查看 Gateway、所有权、link、会话、模型和队列状态。 |
/qqbot-takeover |
从本机另一个 Pi 进程接管 Gateway。 |
QQ 端不能执行任何 /qqbot-* 命令。
QQ 命令
QQ 命令采用显式白名单,并在客户端支持时显示 Keyboard:
| 命令 | 作用 |
|---|---|
/help |
显示命令菜单。 |
/status |
查看当前 link 和 Pi 状态。 |
/model [provider/model] |
浏览、搜索或切换当前模型。 |
/thinking [level] |
查看或切换思考等级。 |
/new [name] |
新建原生 Pi 会话,可同时命名。 |
/sessions [query] |
列出或搜索原生 Pi 会话。 |
/resume <id-or-name> |
切换到唯一匹配的原生 Pi 会话。 |
/name <name> |
重命名当前会话。 |
/compact [instructions] |
压缩当前上下文。 |
/stop |
中止当前 Pi 回合。 |
普通 QQ 文本会注入已绑定的 Pi 会话;Pi 忙碌时按原生 follow-up 投递。只有 QQ 发起的回合会回复 QQ,终端发起的输出只留在终端。
双端交互界面
QQ 发起的回合打开 ctx.ui.confirm()、ctx.ui.select() 或 ctx.ui.input() 时,终端与 QQ 会同时收到交互。任意一端最先完成即生效,另一端随即失效。
扩展创建的 ctx.ui.custom() 在两端共享同一个组件实例。QQ 会收到去除终端控制序列后的 72 列 TUI 快照,并提供方向键、翻页、Home/End、Tab、空格、退格、删除、确认、取消和刷新。普通 QQ 文本按终端 bracketed paste 输入,包括密码字段。
Pi 核心 TUI、外部 PTY 程序,以及仅由终端回合打开的自定义界面不在代理范围内。
媒体
入站媒体没有扩展层数量、格式、单文件或总大小配额。图片会传给支持图片的模型,已支持的文档按剩余上下文预算提取,语音优先使用 QQ ASR 文本,未知文件保留为临时本地资源供 Pi 工具读取。
入站下载仍强制 HTTPS、SSRF 防护、有限重定向、取消处理和网络停滞检测。
启用出站媒体后,qq_send_local_file 工具只能向当前 QQ 回合的原始会话发送本地文件。候选文件会经过 realpath、普通文件身份、hard-link、symlink/junction 和 rename-race 校验。QQ 的 200 MB 上传上限与被动回复次数仍是平台约束。
当前边界
- 只支持一个配置的 QQ C2C owner;群消息和其他用户会在附件处理与 Agent 工作前被忽略。
- Gateway 不会随扩展自动启动;日常运行请执行
/qqbot-link。 - Pi 0.82 可以发现 Skills、提示词模板和扩展命令,但没有公开完整输入分发 API。因此 QQ 不能通用执行任意 Skill、模板或第三方 slash command,只能使用上面的白名单命令。
- 升级已安装包后必须完整重启 Pi。
故障排查
- 提示配置无效:检查
schemaVersion、appId、clientSecret和ownerOpenId。 - QQ 没有响应:先运行
/qqbot-status,再在本地执行/qqbot-link。 - 缺少
ownerOpenId:将其留空,执行/qqbot-start,从 QQ 向机器人发消息,并在终端确认显示的 OpenID。 - Gateway 被其他 Pi 占用:执行
/qqbot-takeover,或保留conflictPolicy: "ask"并在终端批准。 - 无法发送本地文件:将
outboundMedia.enabled设置为true,然后重启 Pi。 - QQ 按钮不可用:直接发送卡片中显示的对应文字命令。
开发
开发、架构与发布流程见 开发指南,发布记录见 CHANGELOG。
License
Apache-2.0