Part IV · Interface & Extensions

12 扩展系统 同进程、全权限、能覆盖内建工具——这是能力也是代价

已发布事件 25 类文档 2988 行权限 与 pi 进程相同
§1 Mental Model 一句话心智模型

先记住这一句

pi 的扩展不是插件,是同进程运行的 TypeScript 模块,拥有和 pi 一样的全部权限。它能覆盖内建工具、改上下文、加命令、接管渲染——代价是它也能做任何别的事。

这个设计与 第 15 章「不做权限系统」是同一个决定的两面。扩展系统的能力之所以能这么大,正是因为 pi 放弃了在进程内划边界;反过来,正因为扩展能力这么大,进程内的边界也确实没意义了。

文档在「扩展位置」一节开头就把这件事挑明了。

§2 Evidence 代码实证

安全提示写在最前面

packages/coding-agent/docs/extensions.md原文 L111
> **Security:** Extensions run with your full system permissions and can execute
  arbitrary code. Only install from sources you trust.
项目本地的 .pi/extensions 只在项目被信任后才加载——这正是 第 15 章里 project trust 唯一真正管的事。extensions.md:111

四类接入点

类别能做什么
事件(25 类)观察生命周期:会话、agent、turn、消息、工具、模型、用户 bash、输入
工具pi.registerTool() 注册新工具,或覆盖同名内建工具(Gondolin 就靠这个)
命令与快捷键registerCommand / registerShortcut / registerFlag
渲染registerMessageRenderer / registerEntryRenderer / registerMarkdownTransformer

事件类型的权威列表是一个 TypeScript 联合类型,共 25 个成员。extensions/types.ts:1034(计数由 tools/stats.sh 从该联合类型程序化统计。)

有几个事件不只是观察,还能改

典型是 context 事件——它就是 第 05 章那道 transformContext 闸门的全部内容:coding-agent 把它原样转交给扩展。返回 { messages } 就能改写这次请求的上下文。

还有 tool_call 事件可以 block 一次工具执行并附 terminate——对应 第 04 章的 beforeToolCall。

注意这里的边界:扩展能改「喂进去什么」和「要不要执行」,但改不了循环的形状(第 03 章 §3)。这不是疏漏,是 CONTRIBUTING.md 写明的克制。

会话操作也开放了

ctx.newSession() / ctx.fork(entryId) / ctx.navigateTree(targetId) / ctx.switchSession(path) ——第 07 章那棵会话树的操作对扩展是开放的。文档专门有一节叫「Session replacement lifecycle and footguns」,本身说明这组 API 容易用错。

§3 Design 设计动机与权衡

「同进程 + 全权限」换来了什么

换来的是扩展能做的事没有上限:覆盖内建工具、把执行路由进 micro-VM、接管渲染、改上下文、注册新的交互模式。第 15 章里 Gondolin 那套隔离方案本身就是一个扩展——这在一个有沙箱的插件系统里是做不到的。

代价直白:装一个扩展等于给它 shell 权限。文档没有回避,把它放在最显眼的位置。

核心最小化这条规则的可执行性

第 02 章提到,「不属于核心的做成扩展」这条规则要可执行,取决于扩展系统够不够强。从 25 类事件 + 工具覆盖 + 渲染接管来看,它确实够强——被拒的 PR 有真实的去处。

但也有明确做不到的:改循环控制流、加新的 LLM provider(那是 pi-ai 的事,走的是另一条路 docs/custom-provider.md)、以及任何需要修改 packages/agent 语义的事情。

事件命名暴露了执行模型

25 个事件里同时有 agent_end 和 agent_settled。这个区分直接来自 第 03 章的机制:agent_end 意味着循环不再发事件,而「settled」还要等所有被 await 的订阅者跑完。把这两个时刻分成两个事件,是因为它们真的不同——想在「一切都结束了」之后做事的扩展,要用后者。

§4 Limits 边界与失效条件
§5 Takeaway 拿走什么
§ Self-check 自测

给场景,判断 pi 会怎么做

场景你装了一个第三方 pi 扩展。它能读你的 ~/.ssh 私钥吗?

看答案

B。extensions.md:111 这与「不做权限系统」是同一个决定的两面:进程内不存在边界,所以也没有给扩展划边界。

场景Gondolin 扩展能把内建工具路由进 micro-VM 执行。它靠的是扩展系统的哪个能力?

看答案

A。覆盖内建工具是这个扩展系统与多数插件系统的关键差异——隔离方案本身能作为扩展存在,前提就是扩展能替换 read/bash 这些名字。

基于 earendil-works/pi @ ac4ac9e(2026-08-07 快照)。
仓库 commit 速度约 428/月,行号引用仅对该 SHA 有效。