OpenAI Codex · 代码模式

审批策略:同一条命令,问不问看哪两颗旋钮

默认问不问跟沙箱种类绑在一起。问过一次之后,记住的是完整命令,还是一段前缀。

课程目标读完能说清两件事。同一条命令为什么会从直接放行变成必须有人点头。以及你点过一次 Yes 之后,下次还问不问,系统到底记住了什么。
先玩一遍 · 同一条命令,换策略看结局
四种策略,三种沙箱,看哪一格盖放行、问人、拒绝
命令
前三条走 Allow。第四条命中 execpolicy 的 Prompt。
策略
沙箱
旋钮
点格子也能跳到那一格。两套记住互斥。
十二格沙盘 · 只读和工作区常常盖同一张章Restricted 两行会撞车
CodexOnRequest · workspace-write
等待点播放,看同一条命令换策略之后盖哪张章。
给模型的说明书策略还没选定。
DSHask · workspace-write
等待两颗旋钮独立。全盘可写仍可继续提问。
记住什么只有 allowed-once。问过一次,下一次还问。
逻辑轨迹 · 动画每一步对应源码里的哪一段
  1. 读当前 turn 的 AskForApprovalprotocol.rs L924
  2. 看 FileSystemSandboxKind 是不是 Restrictedpermissions.rs L227
  3. Never 给 Skip,UnlessTrusted 给 NeedsApprovalsandboxing.rs L198
  4. OnRequest 或 Granular 只在 Restricted 时要问sandboxing.rs L200
  5. Granular 要问且关掉沙箱审批则 Forbiddensandboxing.rs L209
  6. execpolicy 的 Prompt 再过第二道闸exec_policy.rs L214
  7. 会话缓存只收 ApprovedForSession,认精确 keysandboxing.rs L108
  8. 策略改了,给模型的说明书一起改permissions_instructions.rs L271
点播放,看同一条命令从直接放行一路变到必须有人点头。
十二格里有几格一样read-only 和 workspace-write 都是 Restricted。OnRequest 在这两行盖同一张问人章。danger-full-access 把 kind 拧成 Unrestricted,默认函数不再问。
Never 也会拒绝策略要求提问时,Never 把提问升级成拒绝。关掉 sandbox_approval,只挡住本来要弹的窗。文件系统已经 Unrestricted,Forbidden 分支进不去。
两套记住按会话记住 npm run test,再跑 npm run lint,key 对不上还要问。按前缀记住才会把范围扩出去。常规弹窗默认露出的,是前缀那一条。
教学示意:舞台只演示判定函数和两套缓存的结构差异,不执行真实命令。逻辑轨迹右侧行号对应 openai/codex 仓库 commit 4f39251a01。
思路一 · 默认问不问,跟沙箱种类绑在一起
它解决什么问题

你把审批留在 on-request,沙箱从 workspace-write 拧到 danger-full-access。十分钟前那条出沙箱命令还会弹窗。现在不弹了。你没改审批旋钮。

审批策略 AskForApproval 是当前 turn 用哪套问人规则,类型里有四个变体。文件系统沙箱种类是三个,默认 Restricted,意思是只读或只能写工作区。这两颗旋钮在同一个判定函数里。改其中一颗,另一颗的行为会跟着走。出处:codex-rs/protocol/src/protocol.rs 第 924 至 947 行,以及 codex-rs/protocol/src/permissions.rs 第 227 至 232 行

思路是什么

一条命令先读审批策略,再看文件系统是不是 Restricted,再让 execpolicy 把 Allow、Prompt、Forbidden 叠上去。execpolicy 是按命令文本给出三态的规则文件。

默认问不问写在 default_exec_approval_requirement。Never 这一层给 Skip。UnlessTrusted 这一层给 NeedsApproval。OnRequest 和 Granular 只在 Restricted 时要问。read-onlyworkspace-write 都是 Restricted,所以这两格的默认结局一样。danger-full-access 把 kind 变成 Unrestricted,默认函数走 Skip。出处:codex-rs/core/src/tools/sandboxing.rs 第 198 至 230 行

Granular 还多一个闸。需要审批、且 sandbox_approval 关掉时,直接 Forbidden。文件系统如果已经是 Unrestricted,needs_approval 先变成假,Forbidden 分支进不去。关掉沙箱审批,只挡住本来就要弹的窗。

Never 遇到 execpolicy 的 Prompt,提问会升级成拒绝。它并不表示什么都准跑。出处:codex-rs/core/src/exec_policy.rs 第 214 至 236 行

命令到达 AskForApproval 文件系统 kind 是不是 Restricted 默认要求 Skip / 问人 / 拒绝 Allow 则开跑 Prompt 再过一闸 OnRequest 加 Unrestricted,默认 Skip。Never 加 Prompt,提问升级成拒绝。
教学化结构图:先出默认要求,再让 execpolicy 叠一层。

untrusted 写不进配置。类型还在,公开字符串已经退休。用户显式写出,整次加载失败。没写才看项目信任:已信任走 OnRequest,明确未信任走 UnlessTrusted。出处:codex-rs/core/src/config/mod.rs 第 3607 至 3625 行

还有一个加载期硬拒绝。requirements 不允许 danger-full-access,配置却写了 approval_policy = "never",加载器会先把权限档案回落到只读。只读加上从不提问,等于模型在窄沙箱里还没人可问。这种组合直接判非法。出处:codex-rs/core/src/config/mod.rs 第 3969 至 3979 行

为什么长期成立

全盘可写时,再问一次出沙箱没有意义。两颗旋钮绑在一起,少弹窗。代价是改沙箱会静默带走审批行为。换个语言重写,仍要先回答:全权模式还要不要问人。

策略枚举靠穷尽分支表达规则。新加一个变体,编译器会逼所有判定函数表态。这是类型在替运行时守门。

思路二 · 问过之后,记住什么
它解决什么问题

弹窗上两条记住长得很像。一条是本会话记住这条命令。一条是把前缀写进 execpolicy,跨会话生效。点错了,后面的边界题就会答反。

常规 exec 弹窗默认菜单里,甚至没有 ApprovedForSession。有网络上下文时才带上它。普通命令给一次批准,有前缀修正提案时再加按前缀记住,最后是取消。用户最常碰到的记住,是前缀那一条。出处:codex-rs/protocol/src/approvals.rs 第 314 至 347 行

思路是什么

会话缓存活在 ApprovalStore 里,跟会话同寿命。key 是规范化后的完整命令,外加 cwd、环境和 execpolicy 指纹。所有 key 都已经是 ApprovedForSession 才命中。Approved 一次放行不会进 map。出处:codex-rs/core/src/tools/sandboxing.rs 第 64 至 116 行

npm run test 批准进会话,npm run lint 是另一把 key。/bin/bash -lcbash -lc 在能拆出单一明文命令时,缓存成同一组 token。会话缓存认完整命令,不认 npm *。前缀扩张只走 execpolicy。

改审批策略不会清空这座 map。key 里没有 AskForApproval。中途从 OnRequest 改成 UnlessTrusted,已经缓存的批准仍算数。改 execpolicy 会改指纹,缓存才会失效。

用户点决定 ReviewDecision ApprovedForSession 精确 key 入会话抽屉 ApprovedExecpolicyAmendment 前缀写入规则文件 下次完整命令相同 才跳过弹窗 前缀匹配就放行 跨会话,范围更大 Approved 只放行这一次,不进 map。npm run test 和 npm run lint 是两把 key。 常规 exec 默认菜单常常只露出前缀这一条。
教学化对照:一层认完整命令,一层认前缀并落盘。
为什么长期成立

两套记住对应两种威胁。精确 key 挡不住换参数。前缀挡得住换参数,也容易把包装命令的范围扩太大。两层分开,才能各自配禁建议名单。一天弹窗不多的时候,先只做一次放行也成立。

思路三 · 策略改了,说明书一起改
它解决什么问题

策略改了,只改运行时分支不够。模型看到的说明必须同步改,否则它会按旧规则去要 require_escalated。Never 下面如果说明书还在教它提权,运行时会把提问升级成拒绝,模型只会反复撞墙。

思路是什么

权限说明是一段 developer 消息。先选沙箱模板,再选审批模板。Never、UnlessTrusted、OnRequest 各有现成 markdown。Never 那份只有一句:不要再给 sandbox_permissions,命令会被拒。Granular 没有第五份文件,按五个开关现场拼允许列表和拒绝列表。两套说明拼在同一段里,模型一次看到当前组合。出处:codex-rs/prompts/src/permissions_instructions.rs 第 271 至 291 行

钩子在人前面。钩子给出 Allow 或 Deny,弹窗就不会出现。钩子的 Allow 是一次 Approved,不写会话缓存。

策略改了,说明书必须一起改。
为什么长期成立

运行时和模型说明书是同一份合同的两面。判定函数改了,字典那一行必须一起改。把这两份放在同一个模块,用同一组测试喂两边,换语言也用得上。

横向对比 · 旋钮独立还是绑在一起

DSH:两颗旋钮,一次授权不扩大

DSH 的审批策略只有 asknevernever 在分派给回答者之前就返回 rejected,后挂的监听器改不了这个承诺。授权结果只有 allowed-once。没有本会话缓存,没有前缀修正。问过一次,下一次还问。出处:packages/interaction/user-approval/src/index.ts 第 84 至 94 行,以及第 304 至 312 行

沙箱和审批是两颗独立旋钮。用户看见的下拉框是预设表:workspace-writeaskdanger-full-accessnever。点下一档时分别调用两颗 setter。对不上表就显示 custom。所以 DSH 可以单独把审批留在 ask、把沙箱拧到全盘可写。Codex 的 OnRequest 在 Unrestricted 上默认 Skip,这个组合在默认函数里不可独立存在。出处:packages/interaction/permission-presets/src/index.ts 第 167 至 176 行

两侧均已核对源码 · 2026-08-22 · DSH · 审批与权限

Claude Code:记住写进规则表

Claude Code 对外的权限模式是五档,外加内部的 autobubble。规则来源包括 userSettingsprojectSettingssessioncliArg。判定先查整工具级 deny,再往下走。出处:restored-src/src/types/permissions.ts 第 16 至 29 行,以及 restored-src/src/utils/permissions/permissions.ts 第 1169 至 1181 行

alwaysAllowRules 可以按来源记下允许项,session 是其中一档。下次按规则匹配,不必完整 argv 相等。代价是匹配函数必须自己防包装。Codex 把扩大范围交给 execpolicy 前缀和禁建议名单。dontAsk 接近 Never。Codex 没有公开的全放行审批策略,Never 仍会被 execpolicy 拦住。出处:restored-src/src/types/permissions.ts 第 54 至 62 行,以及第 433 行

两侧均已核对源码 · 2026-08-22
课堂练习
01

拧沙箱,弹窗还在吗

审批留在 on-request,沙箱从 workspace-write 拧到 danger-full-access。再跑一条会写工作区外路径的命令。弹窗还在不在?为什么?

接着把同一条 npm run test 按会话批准,再提交 npm run lint。缓存该不该命中?如果点的是按前缀记住,答案会不会变?

Takeaway:同一条命令问不问,看审批策略和文件系统是不是 Restricted。Never 遇到必须提问的规则,提问会升级成拒绝。问过之后的记忆分两层:会话层认精确 key,持久层才允许前缀。策略改了,给模型的那句话必须一起改。