# 内置的 pi-mono：四个包与补丁台账

> MiniMax Code 把开源的 pi-mono 四个包按 v0.79.1 原样放进 third_party：各包规模、第一方代码实际用到哪几块、34 条本地补丁，以及为什么选择 vendor 源码。

- 作者：David（道雾轩）
- 专栏：MiniMax Code 源码解读（https://daiw.net/manual/minimax-code.md）
- 最后更新：2026-09-19
- 原文：https://daiw.net/manual/minimax-code/pi-mono
- 转载与引用：请注明出处并附原文链接（https://daiw.net/about/copyright）

MiniMax Code 的 Agent 循环、模型协议层，以及 read、write、edit、bash 这几个内置工具的底层实现，都不是从零写的，而是来自开源项目 pi-mono（包的作者署名 Mario Zechner，npm scope 为 `@earendil-works`）。它没有作为 npm 依赖安装，而是整份源码放进了 `third_party/pi-mono`，和第一方包一起编译进 `mcode`。

这一篇先把这块“内置底座”摸清楚：四个包各是什么、有多大，第一方代码到底用了哪几块，`MINIMAX_CHANGES.md` 里记了哪些本地补丁，以及为什么要 vendor 源码。后面三篇再分别拆 Agent 循环、模型协议层和模型系统。

## 怎么引进来的

引入基线记在 `third_party/pi-mono/.minimax-vendor.json:5`：

```json
  "upstream": {
    "type": "git",
    "url": "https://github.com/earendil-works/pi-mono.git",
    "ref": "refs/tags/v0.79.1",
    "commit": "28df940f0d07b65284849a483be7b06e2ca046ee"
  },
  "importedAt": "2026-06-16",
  "strategy": "source-vendor",
```

四个包在 `pnpm-workspace.yaml:2` 起被列为工作区包，第一方包用 `workspace:*` 引用它们（如 `packages/agent-core/package.json:73`）。构建时 `scripts/build.mjs` 里的 esbuild 插件把工作区包 `exports` 指向的 `./dist/*.js` 改写成 `./src/*.ts`，直接打包源码（`scripts/build.mjs:60`）：

```js
      const source = target
        .replace(/^\.\/dist\//, "./src/")
        .replace(/\.d\.ts$/, ".ts")
        .replace(/\.js$/, ".ts");
```

所以 pi 没有任何预编译产物参与，改一行源码、重新 `pnpm build` 就进了 `dist/cli.js`。公开仓库里这四个包的 `package.json` 都没有 `scripts` 字段，都标着 `"private": true`（如 `third_party/pi-mono/packages/ai/package.json:96`）；pi 自带的测试（`test/` 目录下 317 个文件）也不在本仓库的验证范围内，`AGENTS.md:8` 明说第三方目录的测试套件不属于本发行版的验证。

## 四个包

行数按全书统一口径（git 跟踪的 `.ts`/`.tsx`/`.mts`，排除测试目录与 `*.test.*`、`*.spec.*`）统计，四个包合计 369 个文件、120731 行：

| 目录 | 包名 | 文件 / 行数 | 内容 |
| --- | --- | --- | --- |
| `packages/agent` | `@earendil-works/pi-agent-core` | 27 / 8264 | `Agent` 类与 `agentLoop`，外加一套 harness（压缩、JSONL 会话、技能、提示词模板） |
| `packages/ai` | `@earendil-works/pi-ai` | 60 / 34346 | 统一的大模型调用层：9 种 API 形态的 provider、统一事件流、OAuth；其中 `src/models.generated.ts` 一个生成文件就有 17267 行 |
| `packages/coding-agent` | `@earendil-works/pi-coding-agent` | 254 / 66352 | pi 自己的命令行产品：`src/core` 64 个文件 26384 行，`src/modes`（交互模式等）45 个文件 16854 行，`examples/extensions` 14218 行 |
| `packages/tui` | `@earendil-works/pi-tui` | 28 / 11769 | 差分渲染的终端 UI 库 |

依赖方向是 `pi-coding-agent` 依赖其余三个包，`pi-agent-core` 依赖 `pi-ai`，`pi-tui` 不依赖任何兄弟包。

## 第一方代码用了哪几块

在 `packages/` 下的非测试源文件里，一共 117 个文件 import 了 pi 的包（含一处动态 import）：`local-runtime` 52 个、`local-runtime-v2` 34 个、`agent-core` 14 个、`agent-tools` 8 个、`agent-modules/context-manager` 5 个、`tui` 2 个，`agent-runtime` 与 `agent-modules/goal` 各 1 个。按被引用的 pi 包看：

| pi 包 | 引用它的文件 | 用得最多的符号 | 实际用途 |
| --- | --- | --- | --- |
| `pi-agent-core` | 86 | `AgentMessage`（66 处 import）、`StreamFn`（17）、`ThinkingLevel`（9） | 大多只借类型；`Agent` 类只在 `agent-core/src/pi-turn-runner` 的 7 个文件里出现 |
| `pi-ai` | 56 | `Api`（38）、`Model`（30）、`Tool` 与 `Context`（各 13） | 消息与模型类型、`streamSimple`（8 个文件）、模型目录查询 |
| `pi-coding-agent` | 19 | 四个工具工厂、`AuthStorage`、`convertToLlm` | 只取零件 |
| `pi-tui` | 0 | — | 第一方代码没有 import |

几个值得注意的事实：

- **Agent 只在一处被创建**。整个第一方代码里 `new Agent(...)` 只出现在 `packages/agent-core/src/pi-turn-runner/agent.ts:34`，其余地方都经由 agent-core 的 `PiTurnRunner` 使用它，详见[下一篇](https://daiw.net/manual/minimax-code/pi-agent)。agent-core 还特意把与 pi 相关的代码放在子路径里：`packages/agent-core/src/index.ts:11` 的注释说，这样只需要协议类型的消费者就不必承担加载 `@earendil-works/*` 的开销。
- **pi-coding-agent 最大，用得最少**。第一方只拿了它的零件：`createBashTool`、`createEditTool`、`createReadTool`、`createWriteTool` 四个工具工厂与 `createLocalBashOperations`、`getShellConfig`，Codex 登录用的 `AuthStorage`，消息转换 `convertToLlm`，压缩默认值 `DEFAULT_COMPACTION_SETTINGS`，以及 `calculateContextTokens`、`resizeImage`。pi 自己的会话外壳 `AgentSession`、交互模式、扩展系统和 JSONL 会话管理一概没用；`pi-agent-core` 里的 `AgentHarness` 同样没有被引用，只借了 harness 里的 `generateSummary`、`CustomMessage`、`CompactionSummaryMessage`。
- **pi-tui 实际上没进产品界面**。产品 TUI 用的是另一份 fork：`packages/tui/src/tui/engine`，基线是 pi-tui 的 0.84.2（`packages/tui/src/tui/engine/BASELINE.json:9`），比 `third_party` 里的 v0.79.1 新。`packages/tui/src/tui/engine/UPSTREAM.md:14` 这样定位它与 pi 的关系：

> Pi is provenance and a future selective-sync input, not a runtime boundary.

`third_party` 里的 pi-tui 于是只作为 `pi-coding-agent` 的依赖留在工作区（`third_party/pi-mono/packages/coding-agent/package.json:28`）。`docs/architecture.md:10` 说 `third_party/pi-mono` 提供 agent、模型协议与终端基础设施，前两项属实，终端这一项与代码不符：`UPSTREAM.md:21` 写明引擎是产品启动路径上唯一的终端、输入与渲染基础。引擎 fork 的细节见 [TUI 引擎](https://daiw.net/manual/minimax-code/tui-engine)。

```mermaid
flowchart LR
  subgraph PI["third_party/pi-mono（v0.79.1）"]
    AG["pi-agent-core"]
    AI["pi-ai"]
    CA["pi-coding-agent"]
    PT["pi-tui"]
  end
  CORE["agent-core（PiTurnRunner）"] --> AG
  CORE --> AI
  CORE --> CA
  RT["local-runtime 与 local-runtime-v2"] --> AI
  RT --> AG
  RT --> CA
  TOOLS["agent-tools（本地工具）"] --> CA
  CA --> PT
  ENGINE["packages/tui 引擎（另 fork 自 pi-tui 0.84.2）"]
```

## 补丁台账

`third_party/pi-mono/MINIMAX_CHANGES.md:14` 声明基线导入时没有改动任何上游源文件，此后每个本地补丁记一条，`MINIMAX_CHANGES.md:288` 规定每条要写清原因与受影响的包、属于可上游的通用改动还是 MiniMax 专用胶水、验证命令与结果。

截至快照共 34 条，最早 2026-06-16，最晚 2026-09-08，文件里的排列并不严格按时间。按 pi 的包计（跨包的条目分别计入）：`coding-agent` 17 条、`ai` 13 条、`agent` 6 条、`tui` 2 条，其中 3 条只改测试。只有 1 条自认是 MiniMax 专用胶水（`MINIMAX_CHANGES.md:53`），其余都标为可上游的通用改动。上游 PR 方面，只有 2026-07-31 保留 thinking 签名的那条是把已合并的上游 PR #6457 挑回来（`MINIMAX_CHANGES.md:156`），2026-07-15 的 GPT-5.6 回移注明通用行为已在上游，其余都写着“未提”，2026-09-05 与 2026-09-08 两条没写这一项。按时间顺序整理如下（均为 2026 年）：

| 日期 | 包 | 改了什么 | 上游 PR |
| --- | --- | --- | --- |
| 06-16 | ai | `build` 只编译已提交的模型目录，刷新目录改为单独的 `refresh-models` 脚本 | 未提 |
| 06-19 | ai | 测试：自适应思考模型的断言加入 Fable 5 | 未提 |
| 06-19 | coding-agent | 测试：删掉一条不稳定的会话 ID 断言 | 未提 |
| 07-04 | coding-agent | edit 模糊匹配只规范化命中片段，不再整文件改写全角字符与尾随空白 | 未提 |
| 07-07 | coding-agent | bash 停止与超时改为 SIGTERM、宽限期、SIGKILL，让清理 trap 能执行 | 未提 |
| 07-08 | ai | 从包入口导出 `convertResponsesMessages` 与 `convertResponsesTools` | 未提 |
| 07-09 | coding-agent | edit 模糊匹配加保真守卫，拒绝吞掉兼容字符的扩宽片段 | 未提 |
| 07-15 | agent | 在 `Agent` 上暴露已有的 `shouldStopAfterTurn` | 未提 |
| 07-15 | ai、agent、coding-agent | 回移 GPT-5.6 目录与 `max` 思考档 | 未提（上游已有） |
| 07-22 | ai | stream 选项新增 `fetch`，由宿主接管模型请求的出站 | 未提 |
| 07-26 | ai | 注入了 `fetch` 且传输为 `auto` 时，Codex 改走 SSE | 未提 |
| 07-28 | ai、coding-agent | OAuth token 刷新也可注入 `fetch` | 未提 |
| 07-29 | coding-agent | 加固 Windows 下 `taskkill` 的解析与 spawn 失败处理 | 未提 |
| 07-31 | ai | 回放时保留只有签名、内容为空的 Anthropic thinking 块 | 已合并（回移 #6457） |
| 07-31 | coding-agent | 测试：几条测试对组合与调度变化更稳健 | 未提 |
| 08-04 | coding-agent | 升级打包依赖 `undici` 与 `minimatch` | 未提 |
| 08-08 | ai | 新增 `onProviderError`，把 SDK 原始异常同步交给宿主 | 未提 |
| 08-10 | tui | OSC 8 超链接改用 BEL 结尾，修 macOS 终端的点击命中 | 未提 |
| 08-11 | coding-agent | 复制资源改用 Node 脚本，不再依赖 `shx` | 未提 |
| 08-12 | coding-agent | 打包后的 MCode 仍能读取本地图片（修正 Photon WASM 定位） | 未提 |
| 08-14 | tui | `CancellableLoader` 显式声明构造参数类型 | 未提 |
| 08-15 | coding-agent | Photon 不可用时，尺寸已合规的图片原样返回 | 未提 |
| 08-18 | ai | 新增 `onProviderStreamEvent`，向宿主暴露解析后的原生流事件 | 未提 |
| 08-18 | ai、coding-agent | Codex 浏览器登录的授权码交换也走宿主 `fetch` | 未提 |
| 08-20 | coding-agent | 可选的父进程死亡守护：宿主被杀后仍回收 bash 进程组 | 未提 |
| 08-22 | coding-agent | 内置 Bash 与 Read 在结果里保留分流输出与分页事实 | 未提 |
| 08-22 | agent | steering 受理后可优雅停止（`shouldStopAfterSteering`），唯一的专用胶水 | 未提 |
| 08-22 | agent | 工具钩子可返回 `terminateAgent`，结束整个 Agent 运行 | 未提 |
| 08-22 | agent | 新增 `onToolExecutionStart`，在参数校验与准入全部通过后才触发 | 未提 |
| 08-26 | ai | Codex SSE 响应头超时从 10 秒放宽到 30 秒 | 未提 |
| 08-27 | ai | Claude 默认思考模型不再发送显式 `thinking` 字段 | 未提 |
| 08-31 | coding-agent | 兼容 Windows PowerShell 的 ConstrainedLanguage 模式 | 未提 |
| 09-05 | coding-agent | Bash 可选的进程事实观察 `onProcessEvent` | 未写 |
| 09-08 | agent | `Agent.steerBatch`：一批 steering 消息在一次模型请求里消费 | 未写 |

落在 `ai` 包的补丁（Claude 默认思考、Codex SSE 超时、`fetch` 注入等）在 [pi-ai 一篇](https://daiw.net/manual/minimax-code/pi-ai)对照代码细讲，`agent` 包的几个接缝在 [pi-agent 一篇](https://daiw.net/manual/minimax-code/pi-agent)讲。这里只看那条唯一的专用胶水。`third_party/pi-mono/packages/agent/src/types.ts:215` 的注释直说它是给 MiniMax 宿主开的口子：

```text
Called immediately after steering messages have been polled and before another provider request.
This is a MiniMax host seam for a steering admission hook to stop the current run gracefully.
```

循环里的用法在 `third_party/pi-mono/packages/agent/src/agent-loop.ts:253`：

```ts
pendingMessages = (await config.getSteeringMessages?.()) || [];
if ((await config.shouldStopAfterSteering?.()) === true) {
	await emit({ type: "agent_end", messages: newMessages });
	return;
}
```

插件的 `UserPromptSubmit` 钩子若对一条 steering 消息返回 `continue: false`（`packages/local-runtime-v2/src/service/turn-system/agent-host/execution/user-input-control.ts:211`），宿主就让 `shouldStopAfterSteering` 返回真（同文件 `:113`），当前运行在下一次模型请求之前正常收尾，而不是报失败。受理与排队的上层逻辑见[回合系统](https://daiw.net/manual/minimax-code/turn-system)。

台账与代码有两处对不上：2026-08-04 那条写的是固定 `undici 8.10.0`，而 `coding-agent/package.json:41` 现在是 `8.10.2`，后续升级没有补记；`.minimax-vendor.json:14` 与 `MINIMAX_CHANGES.md:125` 都指向一份 `third_party/AGENTS.md`（vendor 代码的热修、验证与同步流程），公开仓库里没有这个文件。台账里的验证命令（如 `pnpm check:pi-vendor-resolution`）同样只存在于内部 monorepo。

## 为什么 vendor 源码

台账开头第一句就是答案（`MINIMAX_CHANGES.md:3`）：

> This directory vendors `pi-mono` as source so MiniMax can patch, validate, and ship agent-loop fixes without waiting on upstream release cadence.

从台账内容看，补丁主要有三类：宿主需要的循环接缝（`terminateAgent`、`onToolExecutionStart`、`steerBatch`、`shouldStopAfterSteering`），企业网络环境要求的出站控制（三条 `fetch` 注入与 Codex 传输选择），以及 Windows 与进程管理的稳健性修复；此外还有 edit 工具保真与图片读取的修复。它们要跟着产品版本一起发布，等上游发版就太慢；而源码直接参与构建，改完即生效。

代价同样清楚：基线停在 v0.79.1，上游后来的修复只能挑着回移（GPT-5.6 目录、#6457）；绝大多数补丁没有提上游 PR，本地分叉会越积越多，每次同步都要逐条核对台账。TUI 引擎另起了一份 0.84.2 的 fork，而不是在 `third_party` 里升级，从代码看，这两部分的同步节奏已经分开。

## 与 Kimi Code 的对照

[Kimi Code](https://daiw.net/manual/kimi-code) 也 vendored 了 pi-mono，但范围小得多。据[《Kimi Code 源码解读》的 pi-tui 一篇](https://daiw.net/manual/kimi-code/pi-tui)，它只把 pi-tui 复制成 `packages/pi-tui`（基线上游 0.80.2），本地分歧登记在包内的 `AGENTS.md`；Agent 循环是自研的（见其 [Agent 循环](https://daiw.net/manual/kimi-code/agent-loop)一篇）。

| | Kimi Code | MiniMax Code |
| --- | --- | --- |
| vendored 范围 | 只有 pi-tui | agent、ai、coding-agent、tui 四个包 |
| 基线 | 上游 0.80.2 | v0.79.1；产品 TUI 引擎另 fork 自 0.84.2 |
| 位置 | `packages/pi-tui` | `third_party/pi-mono`；引擎在 `packages/tui/src/tui/engine` |
| 改动登记 | 包内 `AGENTS.md` 列出必须保留的分歧 | `MINIMAX_CHANGES.md` 台账；引擎另有 `BASELINE.json` 逐文件哈希与 `LOCAL_CHANGES.md` |
| Agent 循环与模型层 | 自研 | 直接用 pi-agent-core 与 pi-ai |

两家在终端 UI 上殊途同归：都选了 pi-tui 的差分渲染，并直接改源码。区别在 Agent 与模型这两层：Kimi Code 只借了界面，MiniMax Code 连循环和协议层一起借，再在外面包自己的回合执行器与模型系统。

下一篇：[pi 的 Agent 循环与回合执行器](https://daiw.net/manual/minimax-code/pi-agent)——pi 的 `runLoop` 一轮怎么调模型、执行工具、停下来，MiniMax 又怎样把它包成一个回合。
