refactor: organize agent harness courses

This commit is contained in:
Haoran
2026-06-16 00:10:35 +08:00
parent 20e7cbb72c
commit 8af5c24e46
491 changed files with 7961 additions and 564 deletions

View File

@@ -0,0 +1,200 @@
# s11: Trust and Execution Boundary — 加载有 trust执行靠容器
> *加载在 core 里管,执行交给容器。*
> **Pi 边界**:执行权限边界 —— 资源加载看 trust执行边界不内置、靠部署层 containerization。
[上一节s10](../s10_runtime_modes/) → `s11` → [下一节s12](../s12_package_distribution/)
---
## 问题
core 会接触本地项目要加载项目资料s08工具也会执行本地动作s04
这两件事**风险差很多**:加载一份资料只是读,执行一个动作可能改动系统。所以加载该有个开关——不可信的项目,连资料都别加载,防恶意 AGENTS.md 或扩展混进来。
但"执行"这件事Pi 的真实取舍和我们直觉不同:**它不在 core 里限制执行权限**。文件系统、进程、网络全开放,权限等于启动它的用户。真要隔离执行,靠部署层把整个进程关进容器。
s11 就把这两件事的真实分工摆出来:加载在 core 里用 trust 管,执行边界交给容器。
---
## 解决方案
两个层次,分工明确:
| 层 | 在哪 | 管什么 |
| --- | --- | --- |
| **加载** | core 内trust | 不可信项目不加载资料,防恶意资源 |
| **执行** | 部署层containerization | 整个进程关进沙箱/容器,限制文件/进程/网络 |
containerization 有三种 pattern见 Pi 的 `containerization.md`
```text
OpenShell 整个 pi 进程跑在策略控制的沙箱
Gondolin pi 留主机,工具执行路由到 Linux 微虚拟机
Plain Docker 整个 pi 进程跑在本地容器
```
> **重要**:教学版**不再发明** `ExecutionPolicy`/`Executor` 那种"core 内 dryRun/allow 开关"——它在 Pi 里没有对应物。core 内唯一能拦住执行的,是 s05 的 `beforeToolCall` hook按工具 allow/block。系统级的执行隔离整体推给容器。
---
## 工作原理
**先定信任开关。**
```ts
export type ProjectTrust = "trusted" | "untrusted";
```
**资源加载看 trust。** `load(trust)` 在 untrusted 时直接返回空——core 拿不到任何项目资料。
```ts
load(trust: ProjectTrust = "trusted"): ContextResource[] {
if (trust === "untrusted") return [];
return this.resources.map((r) => ({ ...r }));
}
```
`createTurnSnapshot` 把 trust 透传给 load所以拍快照时就决定了本轮装不装资料。
**执行不靠 core 管。** 这里没有 `ExecutionPolicy`、没有 `Executor``executeToolCall` 的签名回到 s05 的样子(无 policy 参数):
```ts
export function executeToolCall(registry, hooks, call): ToolResultMessage {
const before = hooks.beforeToolCall?.(call) ?? { type: "allow" };
if (before.type === "block") return { /* blocked */ };
// ... 真正执行 handler错误捕获 ...
}
```
唯一能拦住执行的,是 `beforeToolCall` hook——它是扩展层的、按工具的拦截不是系统级权限。要系统级隔离执行去部署层用容器。
> 这一节真正建立的是**执行权限边界**,而且是对齐 Pi 的真实取舍:**加载**在 core 里用 trust 管(防恶意资源),**执行**不在 core 里管,整体交给部署层 containerization。core 保持轻量,权限的"重活"推给容器——这正是 README 里说的"Pi 不内置 permission system"。
---
## 试一下
运行(默认 trusted
```sh
npm run s11
```
输出类似:
```text
s11: Trust and Execution Boundary
[resources]
AGENTS.md
[execution boundary]
Pi 不在 core 内限制执行权限。执行边界靠部署层 containerization
- OpenShell整个 pi 进程跑在策略控制的沙箱
- Gondolinpi 留主机,工具执行路由到 Linux 微虚拟机
- Plain Docker整个 pi 进程跑在本地容器
core 内唯一的执行拦截点是 s05 的 beforeToolCall hook。
```
不可信项目(不加载资料):
```sh
npm run s11 -- --trust untrusted
```
```text
[resources]
noneuntrusted不加载任何资料
```
观察重点trust 只管"加载不加载资料"执行边界那段说明清楚——core 里没有 dryRun/allow 开关,真要限制执行得用容器。
---
## 接入主线
s11 在 s10 上累积。相对 s10 的变更:
| 组件 | s10 | s11 |
| --- | --- | --- |
| 新增类型 | — | `ProjectTrust` |
| `ResourceLoader.load` | `load()` | **`load(trust)`**U1默认 trusted |
| `createTurnSnapshot` | `(state, registry, loader)` | 多一个 `trust`(默认 trusted |
| 执行权限 | 只有 hook | **trust 控加载;执行靠 containerizationcore 内不内置 permission** |
**焊接点**`loader.load(trust)` 决定 context 装不装资料;`createTurnSnapshot` 透传 trust。`executeToolCall` 保持 s05 的签名(无 policy——执行拦截只有 beforeToolCall hook系统级隔离交给容器。
> 注:本节移除了早期教学版的 `ExecutionPolicy`/`Executor`。它们是为了"自演示执行边界"而发明的,但 Pi 真实没有这层——保留会让内核和 Pi 不一致。
---
## 接下来
现在工具、命令、项目资料都是零散定义的。想复用一组能力,没有个清单说明"这包里有什么"。
下一节会把它们整理成一个带清单的包,方便整体分发和加载。
进入下一节:[s12](../s12_package_distribution/)。
---
<details>
<summary>Pi 源码溯源:不内置 permission靠 containerization</summary>
教学版用 trust 控加载。Pi 的真实情况值得特别说明——**它不内置 permission 系统**,权限边界靠外部容器化。
### 源码在哪
- `packages/coding-agent/docs/containerization.md` — 三种容器化方案(官方文档)
- `packages/coding-agent/src/core/project-trust.ts:45``resolveProjectTrusted`
- `packages/coding-agent/src/core/extensions/runner.ts` — trust 事件
- `packages/coding-agent/src/tools/bash.ts:66` — bash 执行(无权限检查)
### 核实Pi 确实不内置 permission
README 说"Pi 不内置 permission system"。源码证实:`createLocalBashOperations``bash.ts:66`)直接 `spawn(shell, ...)`**没有任何权限检查**——文件系统、进程、网络全开放,权限等于启动它的用户。
### 那 trust 管什么
Pi 的 `ProjectTrust``project-trust.ts:45`)只管**资源加载**,不管执行:
```ts
async function resolveProjectTrusted(options): Promise<boolean> {
if (options.trustOverride !== undefined) return options.trustOverride;
if (!hasProjectTrustInputs(options.cwd)) return true; // 没有可信任输入,直接信任
const { result } = await emitProjectTrustEvent(...); // 问扩展 hook
if (result) return result.trusted === "yes";
const decision = options.trustStore.get(options.cwd); // 查历史决策
if (decision !== null) return decision;
switch (options.defaultProjectTrust ?? "ask") { // 默认问用户
case "always": return true;
case "never": return false;
case "ask": break;
}
}
```
trust 决定"要不要加载这个项目的扩展/资源"(防恶意 AGENTS.md 或扩展),**不限制**加载之后的执行。
### 三种容器化方案
`containerization.md` 给三种 pattern
| 方案 | 怎么做 | 适用 |
| --- | --- | --- |
| **OpenShell** | 整个 pi 进程跑在策略控制的沙箱 | 想全面限制 |
| **Gondolin 扩展** | pi 留在主机,工具执行路由到 Linux 微虚拟机 | 想保护 provider auth |
| **Plain Docker** | 整个 pi 跑在本地容器 | 简单隔离 |
### beforeToolCall 是唯一的执行拦截点
Pi 唯一能拦截执行的,是 s05 的 `beforeToolCall` hook——扩展可以在那里 block 某个工具。但这是扩展层的、按工具的,不是 core 内置的、系统级的权限系统。
### 一句话
教学版用 trust 控加载,和 Pi 对齐;执行边界也对齐——**不内置**,靠 containerization 三方案在部署层做。早期教学版发明过 `ExecutionPolicy`但那是为了自演示Pi 真实没有,所以本节移除了它。
</details>