# 仓库全景：公开投影与两个上游

> 31 个包根的职责、规模与依赖方向，@mavis 私有包名的由来，公开投影的四道关，以及 pi-mono 与 sandbox-runtime 两个上游。

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

读这份源码之前，先要知道它不是一个普通仓库。`AGENTS.md` 的第一句（`AGENTS.md:3`）：

> This repository is the reviewed public projection of an internal monorepo, not an ordinary workspace.

它是内部 monorepo 的“公开投影”：公开哪些文件、包含哪些包、对应内部仓库的哪个提交，都写在 `release/` 下几个机器可读的文件里，构建、类型检查、测试与源码检查全都从那里读。这一篇先把包的全景摊开，再看投影机制和两个上游。

## 31 个包根

包的范围只有一个出处：`release/extraction.json` 的 `packageRoots`（`AGENTS.md:37`）。这个文件还记着内部源码基线 `sourceRevision`（`release/extraction.json:3`）和产品基线 0.4.12。`packageRoots` 一共 31 项：

- **第一方 26 个**：`packages/` 下 14 个一级包，加上 `packages/agent-modules/` 下的 12 个子包。`agent-modules` 本身只是一层目录，不是包（`AGENTS.md:7`）。
- **第三方 5 个**：`third_party/pi-mono/packages/` 下的 `agent`、`ai`、`coding-agent`、`tui`，以及 `third_party/sandbox-runtime`。

`pnpm-workspace.yaml` 里列的正好是同样的 31 项。新增工作区包时两处都要登记，再重新生成类型路径和源码清单（`AGENTS.md:47`）。

## 规模与职责

口径：git 跟踪的 `.ts`、`.tsx`、`.mts` 文件，排除测试目录与 `*.test.*`、`*.spec.*`，按物理行计。全仓 2758 个文件、695432 行；其中第一方 `packages/` 2348 个文件、556219 行，`third_party/` 410 个文件、139213 行。

| 包 | 行数 | 职责 |
| --- | ---: | --- |
| `local-runtime-v2` | 194988 | 现役运行时：进程内入口 `CliService`、应用层（会话、队列、交互），以及会话、回合、Agent、模型、插件、MCP 等服务和 SQLite 存储 |
| `local-runtime` | 138122 | 第一代运行时，如今主要提供被复用的宿主设施（数据库、文件工具、权限、存储）和旧会话的只读读取器 |
| `tui`（`@minimax/code`） | 105010 | 产品入口：命令行、交互式 TUI（含分叉来的 TUI 引擎）、headless 与 ACP 适配、账号、更新、遥测 |
| `agent-modules/*`（12 个） | 39383 | 可插拔的 Agent 能力：权限、插件钩子、系统提醒、目标、定时任务、上下文压缩、MCP、技能、失控保护、后台任务、会话报告，以及会话、队列、回合的服务契约 |
| `agent-tools` | 22551 | 平台工具的定义：文件、搜索、任务、浏览器、网页抓取等 |
| `shared` | 13558 | 各层共用的小工具与数据结构 |
| `browser-core` | 12682 | 与提供方无关的浏览器核心，Electron 与原生 headless Chrome 共用 |
| `agent-core` | 10120 | Agent 运行时契约、pi 事件到运行时事件的桥，以及回合执行器 `PiTurnRunner` |
| `config` | 7091 | 配置文件的加载，以及各子系统的配置类型 |
| `agent-extension` | 5395 | 把各个 agent-modules 接到 `agent-runtime` 扩展接口上的内置适配器 |
| `protocol` | 2168 | CLI 的数据结构（`local.ts`），Agent 的配置与事件（`runtime.ts`） |
| `oauth-core` | 2020 | MiniMax OAuth 的本地凭据、刷新与跨进程协调 |
| `agent-runtime` | 1417 | 每回合的扩展 SPI、注册表与 `assembleTurn` 装配 |
| `oauth-lease-protocol` | 896 | 本地 OAuth 访问令牌的租约协议 |
| `mcode-tools-host` | 818 | 给内嵌的 mcode-tools 子进程发短期令牌的租约代理与启动器 |
| `third_party/pi-mono` | 120731 | pi 的 `agent`、`ai`、`coding-agent`、`tui` 四个包 |
| `third_party/sandbox-runtime` | 18482 | 沙箱运行时的分叉，连同原生辅助程序的源码 |

12 个 agent-modules 里，`permission` 一个就有 16599 行，占了四成多。两代运行时加起来超过 33 万行，约占第一方代码的六成；真正驱动模型的 `agent-core` 只有一万行出头——Agent 循环本身在 pi-mono 里。

还要留意，这些包服务的不只是命令行。以 `local-runtime` 为例，它最大的目录 `src/channels`（约 3.2 万行）是飞书、Telegram、微信等 IM 渠道的接入；V2 的服务目录里也有 `miniapp`、`canvas`、`host-connector-system` 这样从代码看服务于桌面端或云端的模块。本书以命令行为主线，这些模块只在和 CLI 路径相关时才讲。

## 依赖方向

官方的分层说明只有一行（`docs/architecture.md:3`）：

> `TUI / exec / ACP → CliService → local Applications → Session / Turn / Agent services → Pi / model providers / local tools`.

按各包 `package.json` 的运行时依赖画出来：

```mermaid
flowchart TB
    tui["tui（@minimax/code）<br/>命令行、TUI、exec、ACP"]
    lr2["local-runtime-v2<br/>CliService、会话、回合"]
    lr1["local-runtime<br/>第一代运行时、宿主设施"]
    ext["agent-extension"]
    art["agent-runtime"]
    tools["agent-tools"]
    mods["agent-modules（12 个）"]
    core["agent-core<br/>PiTurnRunner"]
    auth["oauth-core、oauth-lease-protocol、<br/>mcode-tools-host"]
    browser["browser-core"]
    pi["third_party/pi-mono<br/>agent、ai、coding-agent、tui"]
    sbx["third_party/sandbox-runtime"]

    tui --> lr2
    tui --> tools
    tui --> browser
    tui --> auth
    tui -.->|仅 pi-coding-agent| pi
    lr2 --> lr1
    lr2 --> ext
    lr2 --> art
    lr2 --> tools
    lr2 --> mods
    lr2 --> core
    lr2 --> sbx
    lr1 --> core
    lr1 --> tools
    lr1 --> mods
    ext --> art
    ext --> mods
    art --> core
    tools --> core
    tools --> mods
    mods --> core
    core --> pi
```

图里省略了指向 `shared`、`protocol`、`config` 的边（几乎每个包都依赖它们），也省略了其他包对 pi 的直接依赖——`agent-runtime`、`agent-tools`、两代运行时，以及 `context-manager`、`goal` 两个模块，都直接依赖 pi 的包。三处值得留意：

1. **入口只认 V2**。`tui` 的 workspace 依赖里只有 `@mavis/local-runtime-v2`（`packages/tui/package.json:28`），没有第一代运行时；V2 再依赖 V1，并在启动时顺手创建一个 V1 宿主（`packages/local-runtime-v2/src/runtime.ts:254`）。两代怎么分工，见[下一篇](https://daiw.net/manual/minimax-code/reading-the-source)。
2. **能力模块不碰运行时**。12 个 agent-modules 要么不依赖任何 workspace 包，要么只依赖 `agent-core`（外加 `shared` 与 pi 的包），包描述里反复出现“never imports runtime packages”这样的约定（如 `packages/agent-modules/permission/package.json:4`）。它们经 `agent-extension` 适配到 `agent-runtime` 的扩展接口，由运行时在每个回合装配，见 [Agent 模块](https://daiw.net/manual/minimax-code/agent-modules)。
3. **界面不用 pi-tui**。`tui` 从 pi 只拿了 pi-coding-agent 的两样东西：执行 Shell 命令的 `createLocalBashOperations`（`packages/tui/src/host/bash-command.ts:17`）和缩放图片的 `resizeImage`（`packages/tui/src/host/image-preview-worker.ts:2`）。画界面用的是自己分叉的引擎，后文再说。

## @mavis：私有包名

第一方包都叫 `@mavis/*`。架构文档对这个前缀的解释（`docs/architecture.md:12`）：

> `@mavis/*` names identify private workspace packages in this repository. The build resolves their source directly rather than downloading them from an internal registry. These packages are not independently published npm APIs.

“直接解析源码”落在 `scripts/build.mjs` 的 esbuild 插件 `standalone-workspace-sources` 上（`scripts/build.mjs:38`）：凡是包根里登记过的包名，就按该包 `package.json` 的 `exports` 找到目标文件，再把指向构建产物的路径改回源码（`build.mjs:60`）：

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

仓库里因此并存四套包名：第一方的 `@mavis/*`、产品包 `@minimax/code`、沿用上游名字的 `@earendil-works/pi-*`，以及沙箱 `@minimax/mcode-sandbox-runtime`。31 个清单全部标着 `private: true`，真正发布到 npm 的名字 `@minimax-ai/code` 是构建时才写进产物目录 `dist` 里的 `package.json` 的（`build.mjs:146`）。

## 公开投影的四道关

归纳起来，投影靠四道关把守。

**文件清单**。`release/public-source.json` 逐个登记公开的文件，这个快照里是 4178 个，和 git 跟踪的文件一一对应。`pnpm check:source`（`scripts/source-inventory.mjs`）先扫描整个工作树做内容检查：根 `LICENSE` 的 SHA-256 必须等于审过的 MIT 文本（`scripts/source-inventory.mjs:36`，防止源码同步时被上游的许可证悄悄替换，见 `LICENSE-STATUS.md:21`）；不许出现内部域名、内部 IDL 与 `thrift-gen` 生成代码之类的引用，也不许有疑似密钥（`source-inventory.mjs:46`）；不许有已退役的路径和 `.env` 文件；各包声明的 workspace 依赖与 `exports` 指向的源文件都得存在；沙箱原生辅助程序的哈希要对得上（`source-inventory.mjs:99`）。内容检查通过后，再拿实际文件和清单比对，多一个少一个都报错，要求先审再更新（`source-inventory.mjs:122`）。加 `--write` 才会重写清单，而 `AGENTS.md` 提醒：登记进清单不等于适合公开（`AGENTS.md:23`）。

**退役路径**。`scripts/lib/retired-sources.mjs` 有两张表。`retiredSourceRoots` 列了 14 个绝不能出现在公开源码里的路径（`scripts/lib/retired-sources.mjs:9`），包括 `packages/thrift-gen/`、两代运行时各自的 `src/http/`、`packages/protocol/src/generated/`、会话移交 `session-handoff`、工作区索引，以及未使用的 `@mavis/team` 循环引擎（`docs/architecture.md:14`）。`nonBundledSources` 只有一项：可以作为源码存在，但不许进入 CLI 的构建图（`retired-sources.mjs:28`）。

**构建图**。`pnpm build` 会把 esbuild 的 metafile 写到 `dist/metafile.json`，`pnpm check:standalone`（`scripts/check-standalone-boundary.mjs`）读它做两件事：确认退役路径一个都没被打包进去（`scripts/check-standalone-boundary.mjs:8`）；反过来，还列了 12 个必须真正出现在产物里的文件——登录、账号、签到、反馈、更新、mcode-tools 集成、插件注册表与云端应用、模型预设、联网搜索、云端权限等客户端（`check-standalone-boundary.mjs:11`）。前者挡住私有实现，后者防止有人为了让检查通过而删掉能力，`AGENTS.md` 明确禁止这种做法（`AGENTS.md:65`）。

**源码同步**。内部仓库的改动不会被 merge 进来。`scripts/prepare-source-sync.mjs` 在仓库外生成候选：对清单里的每个文件，拿旧基线、目标提交和当前公开版本做三方合并，冲突保留标记，删除只报告，新文件只列路径（`docs/source-sync.md:14`）；维护者逐个审过、应用到公开分支，并且只有全部差异审完，才把 `sourceRevision` 推进到新提交（`docs/source-sync.md:22`）。所以 `AGENTS.md` 开头就提醒：在这个仓库里移动或重命名文件代价很高，下次同步会变成冲突或未审的新文件，能改内容就别改布局。

## 两个上游

| 目录 | 上游 | 基线 | 许可 | 本地改动记在哪 |
| --- | --- | --- | --- | --- |
| `third_party/pi-mono` | `earendil-works/pi-mono` | `v0.79.1`，2026-06-16 导入 | MIT | `MINIMAX_CHANGES.md` |
| `third_party/sandbox-runtime` | `anthropic-experimental/sandbox-runtime` | `v0.0.74`，本地版本号 `0.0.74-mcode.2` | Apache-2.0 | `README.md` |

**pi-mono** 只引入了 `agent`、`ai`、`coding-agent`、`tui` 四个包（上游基线见 `third_party/pi-mono/MINIMAX_CHANGES.md:7`），同口径下分别是 8264、34346、66352、11769 行。导入时没有改动任何上游文件（`MINIMAX_CHANGES.md:14`），之后的每处修改都按日期记进这本台账，到这个快照已有三十多条：Windows PowerShell 受限语言模式的兼容、工具钩子里终止整个 Agent 的出口、向宿主暴露解析后的提供方流事件等。哪些是上游原样、哪些是补丁，见[内置的 pi-mono](https://daiw.net/manual/minimax-code/pi-mono)。

**sandbox-runtime** 是 Anthropic 开源的沙箱运行时的分叉（`third_party/sandbox-runtime/upstream.json:2`）。`README.md` 说明了保留的扩展，例如每次调用单独清洗的 `baseEnv`、调用方提供的临时目录、网络代理关闭时仍然生效的文件保护（`third_party/sandbox-runtime/README.md:7`）。`vendor/` 下连原生辅助程序的源码一起带着，其哈希记在 `native-integrity.json` 里，由上面说的源码检查逐个核对；构建时再把 `seccomp`、`srt-win`、`java-proxy-agent` 三组辅助程序拷进产物（`scripts/build.mjs:123`）。见[沙箱](https://daiw.net/manual/minimax-code/sandbox)。

**还有一个不在 `third_party/` 里的上游**：`packages/tui/src/tui/engine`，MCode 的 TUI 引擎。它导入自 pi 仓库的 `packages/tui/src`，版本 0.84.2，2026-08-18 导入、次日正式分叉（`packages/tui/src/tui/engine/UPSTREAM.md:7`），同口径约 1.8 万行。`BASELINE.json` 逐文件记着上游与适配后的哈希，`LOCAL_CHANGES.md` 逐条记着本地改动。`UPSTREAM.md` 把它定位为 MCode 自己的源码，pi 只是出处和将来选择性同步的来源（`UPSTREAM.md:13`）。于是仓库里其实有两份 pi 的 TUI：`third_party` 里随 pi-mono 引入的 0.79.1 版，和 `packages/tui` 里分叉出去的 0.84.2 版，产品启动路径只用后者。见 [TUI 引擎](https://daiw.net/manual/minimax-code/tui-engine)。

下一篇：[怎么读这份源码](https://daiw.net/manual/minimax-code/reading-the-source)——从源码构建、验证门与类型路径的生成，两代运行时各管什么，以及一条推荐的阅读路线。
