第10章:权限模型 —— 六种模式与被外包的沙箱
约 12 分钟 · 更新于 2026-09-01
第10章:权限模型 —— 六种模式与被外包的沙箱
src/utils/permissions/ 有 24 个文件、9409 行。本章讲清三件事:六种权限模式、三类规则的优先级、以及一批连 bypass 模式也拦不住的检查。最后对照 Codex:Claude Code 把沙箱整个外包给了一个独立 npm 包,这是一个和 Codex 完全相反的取舍。
一、六种模式
ts
export const EXTERNAL_PERMISSION_MODES = [
'acceptEdits',
'bypassPermissions',
'default',
'dontAsk',
'plan',
] as const
export type InternalPermissionMode = ExternalPermissionMode | 'auto' | 'bubble'
export const INTERNAL_PERMISSION_MODES = [
...EXTERNAL_PERMISSION_MODES,
...(feature('TRANSCRIPT_CLASSIFIER') ? (['auto'] as const) : ([] as const)),
] as const
[源码 src/types/permissions.ts:16-36]
| 模式 | 标题 | 符号 | 语义 |
|---|
| default | Default | | 按规则来,不确定就问 |
| plan | Plan Mode | ⏸ | 只读,不许改文件 |
| acceptEdits | Accept edits | ⏵⏵ | 文件编辑自动放行 |
| dontAsk | Don't Ask | ⏵⏵ | 不弹窗(但 deny 规则仍生效) |
| bypassPermissions | Bypass Permissions | ⏵⏵ | 绕过权限 |
| auto [内部] | Auto mode | ⏵⏵ | 用一个模型当分类器决定放不放行 |
| bubble [内部] | —— | | 子 agent 的权限冒泡到父终端 |
bubble 不在 INTERNAL_PERMISSION_MODES 里(不可由用户选),它是 fork 子 agent 专用的 [源码 src/tools/AgentTool/forkSubagent.ts:52]:
permissionMode: 'bubble' surfaces permission prompts to the parent terminal.
isExternalPermissionMode() 的实现值得看一眼 [源码 src/utils/permissions/PermissionMode.ts:97]:
ts
export function isExternalPermissionMode(mode: PermissionMode): mode is ExternalPermissionMode {
// External users can't have auto, so always true for them
if (process.env.USER_TYPE !== 'ant') return true
return mode !== 'auto' && mode !== 'bubble'
}
外部构建里这个函数恒返回 true,因为那两个模式的代码根本没进产物。第 2 章那条 DCE 主线在这里又出现一次。
二、三类规则与它们的来源
ts
export type PermissionBehavior = 'allow' | 'deny' | 'ask'
export type PermissionRule = {
source: PermissionRuleSource // 从哪来
ruleBehavior: PermissionBehavior // allow / deny / ask
ruleValue: {
toolName: string
ruleContent?: string // 可选的内容匹配,如 Bash(git push:*)
}
}
[源码 src/types/permissions.ts:44, 73]
来源有 8 种(第 2 章那 5 个设置层,加上 3 个运行时来源):
ts
export type PermissionRuleSource =
| 'userSettings' | 'projectSettings' | 'localSettings'
| 'flagSettings' | 'policySettings'
| 'cliArg' | 'command' | 'session'
session 是"用户在这次会话里点了'总是允许'",不落盘。 第 2 章那个 OTel 映射就是靠它区分"临时批准"和"永久批准"的。
规则内容的语法是 工具名(内容):
ts
function permissionRuleValueFromString(ruleString: string): PermissionRuleValue {
const matches = ruleString.match(/^([^(]+)\(([^)]+)\)$/)
if (!matches) return { toolName: ruleString }
return { toolName: matches[1], ruleContent: matches[2] }
}
[源码 src/utils/sandbox/sandbox-adapter.ts:62]
所以 Bash 是"整个工具",Bash(git push:*) 是"内容匹配",mcp__server 是"这个 MCP 服务器的全部工具"。
空内容的 deny 规则会在工具进入模型视野之前就把工具过滤掉 [源码 src/tools.ts:257]:
ts
/**
* Filters out tools that are blanket-denied by the permission context.
* A tool is filtered out if there's a deny rule matching its name with no
* ruleContent (i.e., a blanket deny for that tool).
*
* Uses the same matcher as the runtime permission check (step 1a), so MCP
* server-prefix rules like `mcp__server` strip all tools from that server
* before the model sees them — not just at call time.
*/
两层防御:模型看不见 + 真调了也会被拦。注释特别强调"用的是同一个匹配器"——如果两处匹配逻辑不一致,就会出现"模型看不到但其实能调"或者反过来的漏洞。
这是权限系统的主函数,逻辑是一串带编号的步骤(源码里的编号就是 1a/1b/1c…)[源码 src/utils/permissions/permissions.ts:1158]:
text
1a. 整个工具被 deny 规则拦 → deny
1b. 整个工具有 ask 规则 → ask(除非沙箱可自动放行,见 §4)
1c. tool.checkPermissions() → 工具自己的判断
1d. 工具说 deny → deny
1e. 工具需要真人交互且说 ask → ask ★ bypass 也拦不住
1f. 内容级 ask 规则 → ask ★ bypass 也拦不住
1g. 安全检查(.git/ .claude/…) → ask ★ bypass 也拦不住
2a. 模式说 bypass → allow
2b. 整个工具被 allow 规则允许 → allow
3. passthrough → ask
顺序即优先级。 三个关键判断:
3.1 deny 永远第一
1a 在最前面,2a(bypass 模式)在很后面。所以:
bypassPermissions 模式绕不过 deny 规则。
这是整个模型最重要的一条性质。用户设了 Bash(rm -rf:*) 的 deny,那么无论开什么模式,它都不会执行。
3.2 三条 bypass 免疫
1e / 1f / 1g 都在 2a 之前,注释各自解释了为什么:
1e 需要真人交互的工具:
ts
// 1e. Tool requires user interaction even in bypass mode
if (tool.requiresUserInteraction?.() && toolPermissionResult?.behavior === 'ask') {
return toolPermissionResult
}
AskUserQuestion 这种工具的本质就是问用户——bypass 掉它没有任何意义。
1f 内容级 ask 规则:
ts
// Content-specific ask rules from tool.checkPermissions take precedence
// over bypassPermissions mode. When a user explicitly configures a
// content-specific ask rule (e.g. Bash(npm publish:*)), … This must be
// respected even in bypass mode, just as deny rules are respected at step 1d.
用户显式写了"npm publish 要问我",那就是要问。 逻辑是:用户配了 bypassPermissions 是在说"常规操作别烦我",不是在说"我刚才那条规则不算数了"。
1g 安全检查:
ts
// 1g. Safety checks (e.g. .git/, .claude/, .vscode/, shell configs) are
// bypass-immune — they must prompt even in bypassPermissions mode.
// checkPathSafetyForAutoEdit returns {type:'safetyCheck'} for these paths.
一批路径是硬保护的:.git/(改历史)、.claude/(改 agent 自己的配置)、.vscode/(改编辑器任务,能执行代码)、shell 配置文件(.bashrc 等,下次开终端就执行)。
注意这四类的共同点:它们都是"改一下就能获得持久的、超出本次会话的执行能力"。这是一个很清晰的威胁模型。
可迁移的判断 ㉑
"绕过所有检查"这个模式必须有一份显式的免疫清单,而且清单的判据要能一句话说清。
Claude Code 的判据是:"改一下就能获得持久执行能力的东西"(agent 自己的配置、编辑器配置、shell 配置、版本历史)+ "用户显式配过的规则" + "本质就是问用户的工具"。三类各有各的理由,没有一条是"感觉危险"。
3.3 plan 模式的一个后门
ts
const shouldBypassPermissions =
appState.toolPermissionContext.mode === 'bypassPermissions' ||
(appState.toolPermissionContext.mode === 'plan' &&
appState.toolPermissionContext.isBypassPermissionsModeAvailable)
如果用户本来是以 bypass 模式启动的,那么他进 plan 模式时也享受 bypass。 因为 plan 模式本身已经是只读了,再弹权限窗没意义。
3.4 checkRuleBasedPermissions:给钩子用的精简版
第 9 章那个 resolveHookPermissionDecision 调用的是另一个函数 checkRuleBasedPermissions() [源码 src/utils/permissions/permissions.ts:1071]。
对比一下就能看出设计:
| hasPermissionsToUseToolInner | checkRuleBasedPermissions |
|---|
| 1a deny 规则 | ✓ | ✓ |
| 1b ask 规则 | ✓ | ✓ |
| 1c 工具自查 | ✓ | ✓ |
| 1d 工具 deny | ✓ | ✓ |
| 1e 需要交互 | ✓ | ✗ |
| 1f 内容级 ask | ✓ | ✓ |
| 1g 安全检查 | ✓ | ✓ |
| 2a 模式 bypass | ✓ | ✗ |
| 2b allow 规则 | ✓ | ✗ |
| 3 passthrough→ask | ✓ | 返回 null |
它只保留"会反对"的那些步骤,不保留"会放行"的那些。 返回 null 表示"规则层没有异议"。
这正是第 9 章那张真值表的实现:钩子说 allow 时,只需要问"规则层有没有反对意见",而不需要重新走一遍完整的放行逻辑。函数的裁剪方式直接编码了它的语义。
四、沙箱:外包给一个独立包
这是本章和 Codex 教程差别最大的一节。
Codex 自己实现了四套沙箱(macOS Seatbelt、Linux Landlock+seccomp、Linux bwrap、Windows AppContainer),三套独立实现加一层统一抽象,是它"工程完备"哲学的最强证据。
Claude Code 的做法是 [源码 src/utils/sandbox/sandbox-adapter.ts:1]:
ts
/**
* Adapter layer that wraps @anthropic-ai/sandbox-runtime with Claude CLI-specific
* integrations. This file provides the bridge between the external sandbox-runtime
* package and Claude CLI's settings system, tool integration, and additional features.
*/
import {
SandboxManager as BaseSandboxManager,
SandboxRuntimeConfigSchema,
SandboxViolationStore,
} from '@anthropic-ai/sandbox-runtime'
整个沙箱是一个独立的 npm 包,这个文件只是适配层。
Claude Code 里剩下的只有配置 schema [源码 src/entrypoints/sandboxTypes.ts]:
ts
export const SandboxNetworkConfigSchema = lazySchema(() => z.object({
allowedDomains: z.array(z.string()).optional(),
allowManagedDomainsOnly: z.boolean().optional().describe(
'When true (and set in managed settings), only allowedDomains and WebFetch(domain:...) allow rules from managed settings are respected. ' +
'User, project, local, and flag settings domains are ignored. Denied domains are still respected from all sources.'),
allowUnixSockets: z.array(z.string()).optional().describe(
'macOS only: Unix socket paths to allow. Ignored on Linux (seccomp cannot filter by path).'),
allowAllUnixSockets: z.boolean().optional(),
allowLocalBinding: z.boolean().optional(),
httpProxyPort: z.number().optional(),
socksProxyPort: z.number().optional(),
}).optional())
从这些字段能反推出沙箱的能力面:网络域名白名单、Unix socket 控制、本地端口绑定、HTTP/SOCKS 代理端口,加上文件系统的 allowWrite / denyWrite。
两处细节暴露了实现差异:
- allowUnixSockets 标着 "macOS only … Ignored on Linux (seccomp cannot filter by path)" —— macOS 的 Seatbelt 能按路径过滤 socket,Linux 的 seccomp 不能。平台差异没有被抽象掉,而是直接写进了配置项的文档里。
- allowManagedDomainsOnly 是管理端专用的收紧开关:打开后只认管理端配的域名,用户/项目/本地/命令行配的一律忽略——但 deny 仍然从所有来源生效。 又是那条"收紧无条件、放宽受限"的不对称原则。
4.1 沙箱和权限的耦合点
沙箱不是独立于权限的另一套东西,它们在两处交汇。
交汇点一:沙箱内的命令可以自动放行
ts
const canSandboxAutoAllow =
tool.name === BASH_TOOL_NAME &&
SandboxManager.isSandboxingEnabled() &&
SandboxManager.isAutoAllowBashIfSandboxedEnabled() &&
shouldUseSandbox(input)
if (!canSandboxAutoAllow) {
return { behavior: 'ask', … }
}
// Fall through to let Bash's checkPermissions handle command-specific rules
[源码 src/utils/permissions/permissions.ts:1183]
"这条命令会在沙箱里跑"本身就是一个放行理由。 因为沙箱已经限制了它能碰的文件和网络。
交汇点二:allow 规则会喂给沙箱
配置项 allowWrite 的说明:Merged with paths from Edit(...) allow permission rules. —— 用户批准过编辑的路径,自动成为沙箱的可写路径。
4.2 excludedCommands 明确不是安全边界
ts
// NOTE: excludedCommands is a user-facing convenience feature, not a security boundary.
// It is not a security bug to be able to bypass excludedCommands — the sandbox permission
// system (which prompts users) is the actual security control.
function containsExcludedCommand(command: string): boolean
[源码 src/tools/BashTool/shouldUseSandbox.ts:18]
在代码里明确声明"这个不是安全边界,绕过它不算安全漏洞"。
这句话的价值在于降低安全响应的噪音:没有它,每个发现"我能绕过 excludedCommands"的人都会提一个安全 issue。
可迁移的判断 ㉒
在代码里显式声明哪些机制不是安全边界。
一个系统里总有一些"看起来像安全检查但其实是便利功能"的东西(前端校验、UI 隐藏、便利性黑名单)。不声明清楚,它们会同时造成两种损失:安全团队浪费时间在假漏洞上,以及有人真的把它们当成安全边界来依赖。
五、auto 模式:用模型当分类器 [内部]
auto 模式是 feature('TRANSCRIPT_CLASSIFIER') 门控的内部功能。它的实现文件 yoloClassifier.ts 有 1495 行,在快照里存在(不像第 7 章那些被 DCE 掉的),但它加载的提示词模板是 .txt 文件,那些不在。
ts
const BASE_PROMPT: string = feature('TRANSCRIPT_CLASSIFIER')
? txtRequire(require('./yolo-classifier-prompts/auto_mode_system_prompt.txt'))
: ''
[源码 src/utils/permissions/yoloClassifier.ts:55]
从周边代码能还原它的工作方式:
- 走 sideQuery(一个旁路模型调用),用一个专门的 ant 模型
- 输入包括:命令、当前 CLAUDE.md 内容、工具集、消息历史("transcript classifier" 这个名字说明它看整个对话)
- 输出是结构化的放行/拒绝判断
- 有 getBashPromptAllowDescriptions / getBashPromptDenyDescriptions 把用户的规则翻译成自然语言喂给分类器
这就是 Codex Guardian 的对位物——两家都走到了"静态规则拦不住,上一个模型当判官"这一步。差别在成熟度和公开程度:Codex 的 Guardian 策略是仓库里一份 8281 字节的公开风险分类学,Claude Code 的分类器提示词是内部构建才有的 .txt。
5.1 危险模式清单:allow 规则也可能是漏洞
auto 模式入口处会剥掉一批危险的 allow 规则 [源码 src/utils/permissions/dangerousPatterns.ts:1]:
ts
/**
* An allow rule like `Bash(python:*)` or `PowerShell(node:*)` lets the model
* run arbitrary code via that interpreter, bypassing the auto-mode classifier.
* These lists feed the isDangerous{Bash,PowerShell}Permission predicates in
* permissionSetup.ts, which strip such rules at auto-mode entry.
*/
export const CROSS_PLATFORM_CODE_EXEC = [
'python', 'python3', 'python2', 'node', 'deno', 'tsx', 'ruby', 'perl', 'php', 'lua',
'npx', 'bunx', 'npm run', 'yarn run', 'pnpm run', 'bun run',
'bash', 'sh',
'ssh',
] as const
export const DANGEROUS_BASH_PATTERNS = [
...CROSS_PLATFORM_CODE_EXEC,
'zsh', 'fish', 'eval', 'exec', 'env', 'xargs', 'sudo',
...(process.env.USER_TYPE === 'ant' ? ['fa run', 'coo', 'gh', 'gh api', 'curl', 'wget', 'git', 'kubectl', 'aws', 'gcloud', 'gsutil'] : []),
]
一个 Bash(python:*) 的 allow 规则等于"允许执行任意代码"。 用户配它的时候想的是"让 python 脚本跑起来别老问我",但它实际授予的是完整的代码执行权。
内部版那批更狠,注释解释了取舍:
These stay ant-only — external users don't have coo, and the rest are an empirical-risk call grounded in ant sandbox data, not a universal "this tool is unsafe" judgment.
"这是基于内部沙箱数据的经验性风险判断,不是普世的'这个工具不安全'的论断" —— 很克制的表述。git 上榜的理由写得很具体:git config core.sshCommand 和装 hook 都等于任意代码执行。
还有一条关于匹配器形状的注释:
gh api needs its own entry — the matcher is exact-shape, not prefix, so pattern 'gh' alone does not catch rule 'gh api:*' (same reason 'npm run' is separate from 'npm').
匹配器是按规则形状精确匹配的,不是前缀匹配,所以 gh 和 gh api 要各写一条。
5.2 分类器的止损:连续拒绝就回退到问人
ts
export const DENIAL_LIMITS = {
maxConsecutive: 3,
maxTotal: 20,
} as const
export function shouldFallbackToPrompting(state: DenialTrackingState): boolean
[源码 src/utils/permissions/denialTracking.ts:12]
连续拒绝 3 次,或累计拒绝 20 次 → 不再信任分类器,回退到弹窗问人。
这个设计的逻辑是:分类器连续拒绝,说明模型正在反复尝试一类它认为该做而分类器认为不该做的事。这时候争议应该上交给人,而不是让两个模型互相消耗。
和第 7 章那个 autocompact 断路器(连续失败 3 次就停)是同一个模式:任何"自动判断"的组件都要有一个"我判断不了了,交给上一层"的出口。
5.3 bypass 模式可以被远程关掉
ts
export async function checkAndDisableBypassPermissionsIfNeeded(…) {
if (bypassPermissionsCheckRan) return
bypassPermissionsCheckRan = true
if (!toolPermissionContext.isBypassPermissionsModeAvailable) return
const shouldDisable = await shouldDisableBypassPermissions() // Statsig gate
if (!shouldDisable) return
setAppState(prev => ({ ...prev, toolPermissionContext: createDisabledBypassPermissionsContext(prev.toolPermissionContext) }))
}
[源码 src/utils/permissions/bypassPermissionsKillswitch.ts:19]
一个远程开关可以在第一次请求之前关掉所有用户的 bypass 模式。 文件名直接叫 bypassPermissionsKillswitch.ts。
这是产品化 agent 才会有的东西:如果发现某个模型版本在 bypass 模式下会做危险的事,需要一个不用发版就能止血的手段。
六、决策原因的十一种类型
第 9 章列过,这里补全语义 [源码 src/services/tools/toolExecution.ts:214-244]:
| decisionReason.type | 含义 | OTel 归类 |
|---|
| rule | 规则命中 | 按规则来源映射(临时/永久/拒绝/配置) |
| hook | 钩子决定 | hook |
| mode | 权限模式决定(bypass) | config |
| classifier | auto 模式分类器 | config |
| subcommandResults | bash 子命令逐条判断的合并结果 | config |
| asyncAgent | 后台 agent(不能弹窗,自动拒) | config |
| sandboxOverride | 沙箱相关覆盖 | config |
| workingDir | 工作目录判断 | config |
| safetyCheck | 安全路径检查 | config |
| permissionPromptTool | SDK 宿主提供的权限工具 | 宿主自报,兜底 user_temporary / user_reject |
| other | 其它 | config |
subcommandResults 值得单独说:git status && rm -rf / 是一条 bash 命令但有两个子命令,权限系统要把它拆开逐个判断,然后合并结果。合并规则显然是"任何一个 deny 就整体 deny"。
asyncAgent 也很有意思——后台 agent 没有 UI,弹不了窗,所以它的"ask"等于"deny":
ts
/** When true, permission prompts are auto-denied (e.g., background agents that can't show UI) */
shouldAvoidPermissionPrompts?: boolean
[源码 src/Tool.ts 的 ToolPermissionContext]
七、和 Codex 的正面对照
| 维度 | Codex | Claude Code |
|---|
| 沙箱实现 | 自研 4 套(Seatbelt / Landlock+seccomp / bwrap / AppContainer) | 外包给 @anthropic-ai/sandbox-runtime,仓库里只有适配层和配置 schema |
| 沙箱是不是默认防线 | 是——审批策略和沙箱策略是一对 | 部分——沙箱是可选的,权限规则才是主防线 |
| 模型判官 | Guardian,策略是仓库里 8281 字节的公开风险分类学 | auto 模式分类器,内部构建专有 |
| 判官的输入 | 命令 + 上下文 | 整个 transcript(名字就叫 transcript classifier) |
| 判官失效时 | 回退到人工审批 | 连续 3 次 / 累计 20 次拒绝 → 回退到弹窗 |
| 危险 allow 规则 | —— | 有一份显式清单,进 auto 模式时剥掉 |
| 远程止血 | —— | bypass 模式有 killswitch |
| 权限规则来源 | 配置 + CLI | 8 种来源,每条规则记得自己的出身 |
两条路线的分歧点很清楚:
- Codex 把"隔离"当第一防线——先假设 agent 会干坏事,用内核机制限制它能干什么。所以必须自己实现,因为每个操作系统的机制不一样,抽象层做不干净。
- Claude Code 把"规则 + 审批"当第一防线——先定义什么可以做,沙箱是加固层。所以沙箱可以外包,因为它不承担主要的安全语义。
哪个更好取决于威胁模型:如果假设"模型可能被提示词注入劫持",隔离优先是对的;如果假设"模型基本可信但会犯错",规则优先更实用(也不会因为沙箱兼容性问题让工具全挂)。
Claude Code 显然选了后者——但它同时保留了 auto 模式分类器这条针对注入的防线。第 6 章末尾说这条防线"不算强",现在可以补完:它不强,但它不是唯一的一层。
八、动手复核
bash
cd claude-code-deep-dive/extracted-source
# 1. 六种模式
cat src/utils/permissions/PermissionMode.ts
sed -n '1,60p' src/types/permissions.ts
# 2. 决策流水线(带 1a/1b/1c… 编号)
sed -n '1158,1310p' src/utils/permissions/permissions.ts
# 3. 给钩子用的精简版,对比看裁剪了什么
sed -n '1071,1155p' src/utils/permissions/permissions.ts
# 4. 三条 bypass 免疫
grep -n -B3 -A8 'bypass-immune\|even in bypass mode\|requiresUserInteraction' src/utils/permissions/permissions.ts
# 5. 沙箱:只有适配层
head -50 src/utils/sandbox/sandbox-adapter.ts
cat src/entrypoints/sandboxTypes.ts | head -80
# 6. 「这不是安全边界」的声明
grep -n -B2 -A4 'not a security boundary' src/tools/BashTool/shouldUseSandbox.ts
# 7. 危险 allow 规则清单
cat src/utils/permissions/dangerousPatterns.ts
# 8. 分类器止损与 bypass killswitch
cat src/utils/permissions/denialTracking.ts
cat src/utils/permissions/bypassPermissionsKillswitch.ts
本机侧:
bash
# 看当前生效的规则及其来源
# (在 Claude Code 里敲)
/permissions
# 看沙箱状态
/sandbox-toggle
九、总结
- 六种模式(外部 5 + 内部 auto),加一个不可选的 bubble(fork 子 agent 把权限弹窗冒泡给父终端)
- 三类规则 × 8 种来源,每条规则记得自己的出身,一路带到遥测的四桶词表里
- 空内容的 deny 规则在工具进入模型视野之前就过滤掉工具,而且用的是和运行时检查同一个匹配器——两处不一致就是漏洞
- 决策流水线的顺序即优先级:deny 在最前,bypass 在很后面,所以 bypass 绕不过 deny
- 三条 bypass 免疫:需要真人交互的工具、用户显式配的内容级 ask 规则、安全路径(.git/ .claude/ .vscode/ shell 配置)——判据是"改一下就能获得持久执行能力"
- 给钩子用的 checkRuleBasedPermissions 只保留"会反对"的步骤,函数的裁剪方式直接编码了它的语义
- 沙箱整个外包给独立 npm 包,仓库里只有适配层;平台差异不抽象,直接写进配置项文档("macOS only, Linux seccomp 不能按路径过滤")
- 代码里显式声明 excludedCommands 不是安全边界——降低假漏洞噪音
- auto 模式是 Guardian 的对位物,输入是整个 transcript;有危险 allow 规则剥离清单(Bash(python:*) = 任意代码执行)和连续拒绝止损(3 次/20 次 → 回退问人)
- bypass 模式有远程 killswitch —— 不发版就能止血
- 和 Codex 的分歧是威胁模型的分歧:隔离优先(假设会被劫持)vs 规则优先(假设会犯错)
下一章讲那个在执行链上权力最大、也是本仓踩过坑的子系统:27 个事件的钩子。
- 第9章-工具执行链-一次调用要过多少道关
- 第11章-钩子系统-27个事件的治理层
- 第2章-工程骨架-一个npm包里的AgentOS —— 五层设置来源
- 第12章-Agent调度-fork与fresh两条路 —— bubble 模式的用处
- Codex 教程第 10 章 —— 自研沙箱的对照
- Codex 教程第 11 章 —— Guardian 的对照
- dsh 教程第 10 章 —— seam + 多后端的对照