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,持久層才允許前綴。策略改了,給模型的那句話必須一起改。