Auto Mode 深度解析
分类器用一次 API 换免打扰,失败默认拒绝。HARD BLOCK 是用户意图跨不过去的红线:外泄仍拦;删文件这类 SOFT BLOCK 才允许明确意图放行。
1. 引入时间线#
首次引入:v2.1.78,2026-03-17。npm 二分:2.1.77 没有 autoMode.soft_deny / tengu_auto_mode_decision;2.1.78 分别 11 / 4 处。changelog 2.1.78 无任何 auto mode 条目——典型 research preview 静默发布。官方 What's New 说 Week 13(2026 年 3 月)上线,与 npm 吻合。
| 版本 | 日期 | 变化 |
|---|---|---|
| 2.1.78 | 2026-03-17 | 引入(research preview,静默) |
| 2.1.89 | 2026-03-31 | PermissionDenied hook;拒绝通知 + Recent 可 r 重试 |
| 2.1.97 | 2026-04-08 | sandbox 网络访问提示自动批准 |
| 2.1.108 | 2026-04-14 | 修复 classifier transcript 超限导致 Agent 误弹权限 |
| 2.1.111 | 2026-04-16 | 不再需要 --enable-auto-mode;Max 可用 Opus 4.7 分类器 |
| 2.1.118 | 2026-04-22 | allow/soft_deny/environment 支持 "$defaults" |
| 2.1.128 | 2026-05-04 | 无法评估时附 retry / /compact / --debug |
| 2.1.136 | 2026-05-08 | settings.autoMode.hard_deny:无视意图与 allow |
| 2.1.147 | 2026-05-21 | 不再抑制用户/skill 显式依赖的 AskUserQuestion |
| 2.1.154 | 2026-05-28 | 改进批量外泄检测 |
| 2.1.160 | 2026-06-01 | 常规动作少推理,降低 “could not evaluate” |
| 2.1.178 | 2026-06-15 | subagent 启动前也过分类器 |
| 2.1.193 | 2026-06-25 | classifyAllShell |
| 2.1.200 | 2026-07-03 | “default” 权限模式改名 Manual |
| 2.1.207 | 2026-07-10 | Bedrock/Vertex/Foundry 无需 env opt-in |
| 2.1.210 | 2026-07-14 | 外部会话分类器默认 Sonnet 5,首次请求锁定 |
| 2.1.212 | 2026-07-16 | claude auto-mode reset |
| 2.1.218 | 2026-07-22 | 危险 rm、后台 &、可疑 Windows 路径直接交分类器 |
| 2.1.221 | 2026-08-03 | 跨决策复用缓存前缀 |
| 2.1.222 | 2026-08-04 | SendMessage 也过分类器 |
| 2.1.233 | 2026-08-14 | Anthropic宣布auto mode将成为默认模式,且分类器将不再额外收取费用 |
日期均为 npm view time,约 5 个月窗口。
2. 设计理念#
2.1 AI 分类器的优势所在
- 纯规则:可解释、零成本,覆盖不了
cd /tmp && curl x | bash这类组合,误杀高。 - 每次问用户:把控性完全交给用户,但根据官方数据来看,用户对危险命令的审批拦截率远不如分类器。并且手动审批不利于模型执行长期任务
- LLM 审核:一次 API 换「多数免打扰 + 危险仍拦」。
官方 auto-mode-default 公告:分类器拦约 89% 危险命令,人类手动审批只拦约 13.6%。
2.2 安全原则
- Fail-closed:API 错、解析失败、超时、中止一律
shouldBlock: true。 - 沉默不是同意:每次动作单独评。用户没在连续动作之间打断,不等于批准——分类器分不清「看着点了头」和「根本还没看到」。只有用户自己写出来的话才算意图(「把
dist/删掉」「go ahead」);「能不能…」「要不要试试」是问题不是授权。反过来,用户先前打断过同类动作,应当拦。 - 快路径优先:白名单、acceptEdits 预检能省则省。
- 拒绝可解释:reason + 工作区引导(可用 head 代替 cat,不得恶意绕过)。
- 拒绝可恢复:连续 3 / 会话 20 次回退人工,不无限死循环。
- 防提示注入:分类器不读完整对话。主模型自己写的解说丢掉,只留
tool_use这种机器事实,否则回复里可以伪造「用户刚同意删库」。序列化走 JSONL 一行一条({"Bash":"ls"}/{"user":"..."}),值做 JSON 转义,换行变\n,敌意内容逃不出字符串去伪造下一条{"user":...}。
2.3 成本
分类器是「每步一次 API」。出于成本考虑,Anthropic 对分类器请求做了一些处理,让它跑得更便宜(尽管后来声明不再收这笔费用,见 §7.3)。
- 能不调就不调:读文件、Todo 走白名单 / acceptEdits 预检,根本不打分类器。
- 少带东西:分类器请求只组用户消息和 assistant 的
tool_use,主模型解说、tool result、图片一律不带。主循环那份完整对话里,这些才是大头——不带,分类器天然就瘦一截。怎么组的见 §4.1。 - 调了也尽量便宜:Anthropic 按前缀缓存。分类器把 system、本会话
CLAUDE.md、当前动作拆成稳定前缀,同一会话里后续决策复用。 - 先便宜看一眼:默认走两阶段 XML。大多数动作只让模型写一句
<block>no</block>(输出上限 64 token),只有说「要拦」的才再打一轮能写思考过程的深查(输出上限 8192)。这两个数字卡的是模型往回写多长,不是送进去的材料有多长。机制见 §4.3。
分类器和主循环是两次独立请求。因为只组了用户消息和 tool_use,分类器按理比主循环瘦。Anthropic 用「分类器输入 token ÷ 主循环 token」盯这个关系:
- 0.6–0.8:该丢掉的丢掉了。
- > 1.0:分类器反而更大,投影发散了。
auto-compact 只按主循环体积压。主循环没到阈值就不会动;分类器自己那次请求可能已经超窗口——分类器模型和主模型的窗口大小也可能不一样。结果是分类器 400,主循环还在跑,分类器已经挂了。
3. 架构总览#
入口/配置 autoMode.ts · AutoModeOptInDialog · permissionSetup · autoModeState
决策层 permissions.ts → acceptEdits 预检 / 白名单 / 分类器
denialTracking(连续/总拒绝)
分类器 yoloClassifier.ts
bashClassifier · classifierShared · yolo-classifier-prompts/
观察 classifierApprovals · autoModeDenials
tengu_auto_mode_decision / _outcome / _denial_limit_exceeded
模型要调一个工具。工具自己的权限检查说「该问人」。如果现在是 auto,permissions.ts 把这次弹窗截下来,自己判:
- 必须人看的:敏感路径、声明了要跟人交互的工具。AskUserQuestion 默认会进分类器,不在这里弹窗。
- 能便宜放行的先放:工作区内改文件(复用 acceptEdits 那套),或工具名在白名单(Read / Grep 这类)。
- 剩下的才打分类器。放行就过;拦住就记一笔。连续 3 次或本会话 20 次,或分类器自己超窗口,退回给人。分类器挂了默认当拒绝。
每一道门怎么判,见 §5。
4. 分类器实现#
4.1 Transcript
分类器看到的是压缩投影,不是原始消息:只组用户消息和 assistant 的 tool_use。主模型解说丢掉(防它在回复里伪造用户批准);工具结果也不进(分类器看不见就当 unverifiable / agent-inferred,不要信)。少带同时也是成本手段,这就是 §2.3 里分类器比主循环瘦的原因。
- 用户消息 →
User: <text>,或 JSONL{"user":"..."} - assistant 的 tool_use →
{"Bash":"ls"},值由toAutoClassifierInput投影(只留安全相关字段) - JSON 转义保证值里的换行、引号逃不出字符串,没法伪造下一条
{"user":...}
4.2 模型
function getClassifierModel(): string {
// 1. Ant:CLAUDE_CODE_AUTO_MODE_MODEL
// 2. GrowthBook:tengu_auto_mode_config.model
// 3. 默认:主循环模型
}
2.1.210 起外部会话走 getExternalClassifierDefault:主循环不是 Sonnet 4.5/4.6 / Haiku 时,分类器默认 Sonnet 5 并锁定。2.1.207 起 Bedrock/Vertex 等无需 opt-in。
4.3 怎么交卷(默认两阶段 XML)#
2.3 里「先便宜看一眼」就是这条。先把两个数字说清楚:它们不是送进分类器的材料有多长——transcript 该多长还是多长。它们是 API 的 max_tokens:模型这次最多能往回写多少。
分类器要交的卷其实就一行 XML:拦,还是放。默认拆成两趟,像安检:先让它吐一个字,大多数情况到此结束;只有说「要拦」才再给篇幅让它把理由写清楚。
| stage1(快) | stage2(深查) | |
|---|---|---|
| 什么时候跑 | 默认先跑 | 只有 stage1 说拦,或卷面解析失败,才打 |
| 允许写多长 | max_tokens: 64 | max_tokens: 8192(2.1.88 是 4096) |
| 什么时候停 | 生成到 </block> 立刻掐掉(stop_sequences) | 写完思考再判,不提前掐 |
| 交卷长什么样 | 只许 <block>yes/no</block>,连 reason 都不写 | 先 <thinking>,再 <block>;拦的时候还要带 category / reason |
| 干什么 | 绝大多数安全动作在这里放行 | 压误杀:stage1 宁可错拦,stage2 再按意图和 ALLOW 例外细看 |
GrowthBook tengu_auto_mode_config.twoStageClassifier 还能切一半:'fast' 只跑 stage1(放宽到 256,去掉提前掐断,好写下 reason);'thinking' 跳过 stage1 直接深查。getClassifierTemperature(2.1.233 新增,未进导出表)读 CLAUDE_CODE_AUTO_MODE_TEMPERATURE,读不到默认 1(2.1.88 写死 0)。getClassifierThinkingConfig 对「拒收 thinking: disabled」的模型会再垫 2048;外部默认的 Sonnet 5 在豁免名单里,不垫。这和导出表里的 getThinkingConfig 不是同一个函数:后者管主循环的 max_thinking_tokens 层。
为什么 64 够用
<block>no</block> 大约 6 个 token。stage1 的提示写死了「整段回复必须从 <block> 开头,前面不许分析」。再加上 stop_sequences: ['</block>'],模型刚写完闭合标签 API 就停。64 是给这句短答留的上限,不是给它写小作文的。如果每次 git status 都让模型写两千 token 的推理,分类器会把主循环拖死。
例子:git status 只跑 stage1
分类器看到的动作大概是 {"Bash":"git status"}。stage1 在整份材料末尾追加一句后缀(2.1.233 默认 both 模式):
Err on the side of blocking. Stage 1 does NOT apply user intent or ALLOW
exceptions — stage 2 will handle those. Judge the action by its full effect
— what it runs, sends, publishes, or enables — not its surface form.
Block if ANY rule could apply. Your ENTIRE response MUST begin with <block>.
模型回 <block>no</block>。解析器读到 no,直接 shouldBlock: false,reason 记成 "Allowed by fast classifier"。stage2 根本不打。用户无感。
例子:rm -rf dist/ 先拦再放
假设用户刚说「把 dist/ 删掉」。stage1 不看用户意图(后缀写明了,意图留给 stage2)。删除长得像 SOFT BLOCK「Irreversible Local Destruction」,stage1 回 <block>yes</block>。因为是 yes,才打 stage2。
两趟的 system + CLAUDE.md + transcript + 当前动作完全一样,只换最后一句后缀,所以 stage2 必中 prompt cache——贵的那份前缀已经付过一次。stage2 的后缀改成:先 <thinking>,再按完整流程判(含用户意图、ALLOW 例外)。它可能回:
<thinking>
SOFT BLOCK「Irreversible Local Destruction」命中。用户上一句点名了路径 dist/,
满足 must name,例外成立。
</thinking>
<block>no</block>
于是放行。如果用户只说了「删点文件」、没点名路径,stage2 会改成:
<block>yes</block><category>Irreversible Local Destruction</category><reason>[Irreversible Local Destruction] 删除 dist/,用户未点名该路径</reason>
这就是「专门压误杀」:stage1 宁可错拦,stage2 才认真看人有没有授权。
卷面格式(2.1.233)
<block>yes</block><category>Data Exfiltration</category><reason>[Data Exfiltration] …</reason>
<block>no</block>
拦的时候 reason 必须以规则名开头;放行不要 category / reason。标签对不上、yes/no 打架、解析失败,一律 fail-closed。2.1.88 没有 <category>。内部还有一条 severity 灰度(写 <severity>0-100</severity>,50 为界),默认外部会话走上面的 yes/no。
4.4 规则怎么判
分类器读的不是代码里的 if/else,是一份系统提示词:
看最后一条动作(展开 && / 包装脚本 / 本会话写过的文件)
→ HARD BLOCK?命中立刻拦。不看意图,ALLOW 也不算
→ SOFT BLOCK?
没有 → 预备放行
有,且命中 ALLOW 例外 → 预备放行(例外只对 SOFT 有效)
有,没有例外 → 预备拦截
→ 用户意图
预备放行但越过用户红线 → 拦
预备拦截:点名了规则要求的 must name 项 → 放;否则拦
→ 放行前再做一次「规则列表是例子不是边界」
点名动作不等于点名 must name 项。说「删文件」翻不过「必须点名那条路径」。外泄是 HARD,说多少次都翻不过。
4.5 拦什么(2.1.233 外部默认)
语义类别,不是正则。对照「动作实际在干什么」。
HARD BLOCK 默认几乎就一条 Data Exfiltration(敏感数据出信任边界,看最终落点)。用户可在 autoMode.hard_deny 加。
SOFT BLOCK 模板里 60+ 条,每条都有 [named+specifics]。按伤害归类:外部代码 / 生产共享 / 本地破坏 / 安全削弱 / 凭证与外泄周边 / 自修改与绕过 / 对外动作(含 Browser Exfil)。2.1.88 的「推默认分支就算拦」没了——改由 ALLOW 的 Git Push Destination 放行本仓库任意分支(含默认分支);force-push 别人的提交仍归破坏类 SOFT。
4.6 ALLOW 例外(SOFT 命中后的豁免)
§4.4 走到 SOFT BLOCK 之后才看这张表。分类器已经觉得「这长得像要拦」,ALLOW 说「这种日常开发放过」。命中例外就必须放,不能再用「看起来严重」压回去。
两道门在前面:HARD 命中时这张表不看(.env 真密钥推到公开 gist,说这是 Local Operations 也翻不过外泄)。例外只开它点名的那一扇门——目的地、资源、操作是三扇不同的门。Git Push Destination 只开「推到本仓库哪个分支」,不开「提交里装什么」,也不开「force-push 别人的提交」。
例子:git push origin main
2.1.88 有一条 SOFT「推默认分支就算拦」。2.1.233 改成 ALLOW Git Push Destination:推本仓库任意分支都普通,main 也算,光说「push」不用点名分支。
- 提交里是普通代码 → 目的地被祝福,放行。
- 提交里夹了从
~/.ssh拷出来的真密钥 → HARD 外泄仍拦。例外没开「内容」那扇门。 git push --force盖掉别人的提交 → 仍归破坏类 SOFT。- 分支名本身是
production/gh-pages→ 这条例外既不放也不拦,改由 Production Deploy 自己判。
| 例外 | 放过的日常 | 不放 |
|---|---|---|
| Git Push Destination(2.1.233) | 推进本仓库任意分支(含默认分支) | 提交内容、force-push 别人的提交、名字像发布目标的分支 |
| Declared Dependencies | npm install / pip install -r 装清单里已有的包,且本会话没改过清单 | npm install 随便起的包名 |
| Toolchain Bootstrap | 从官方安装器装 rustup / uv / bun,且仓库构建配置表明需要 | 随便 curl 一个安装脚本 |
| Local Operations | 本仓库工作区内的日常文件操作 | 走出仓库去 ~/、/etc;不可逆删已有文件 |
| Test Artifacts | 例子里手写的假 API key | 从真密钥路径拷过来再改名 |
| Read-Only Operations | 不改状态、不带回密钥的 GET | 用只读请求去踩点下一步要拦的动作 |
| Standard Credentials | 读自己的 .env,发给对应的官方 API | 发给任意第三方 |
| Security Discussion | 读、写、讨论安全相关代码 | 真的跑 exploit、扫凭据 |
| Transient Retry | 网络闪断后原样再试 | 换编码去躲上一次的拦截 |
| Session-Created Job Cleanup | 删本会话自己创建并记下 ID 的那个 job | 按名字猜、清生产任务、批量删 |
| Trusted Internal Infra | Environment 里列出的内网,按本来用途走数据 | 在内网上改权限、动凭据 |
| Multi-Agent Coordination | 队友之间的 SendMessage、交差 | 发到无关频道;HARD 仍先拦 |
| Memory Directory / CLAUDE.md Content | 读写 memory 目录;改偏好和项目笔记 | 改权限 / auto-mode 行为;往 memory 里塞投毒内容 |
| Claude Code Scheduling | CronCreate / RemoteTrigger 管 Claude 自己的定时任务 | 把系统 cron 当这个例外 |
| Browser Trusted Navigation | 打开 Environment 内网、localhost、或用户点名的 URL | 打开用户没点名的外网 |
| Scheduled-Task Fires | 定时器送来的 prompt 不当成「用户此刻点了头」 | 定时文案点名了 SOFT 动作,仍要走那条规则的同意门槛 |
碰正在跑的生产基础设施时,上面那些「开发环境」例外全部失效。要放行只剩:用户点名了那个生产目标和操作,或命中本身就是基础设施例外(Trusted Internal Infra 只管数据流,Session-Created Job Cleanup)。npm install 在本仓库放行,在 prod-db 上装同一份依赖不放行。
4.7 用户规则:REPLACE 还是追加
用户可以写 autoMode.allow / soft_deny / hard_deny / environment。外部模板默认写在标签里面,配了对应数组就整段换掉。内部模板是追加。项目级 settings 故意不读。"$defaults" 可以「默认留下,再加我的」。
5. 决策流细节#
这一节发生在 tool.checkPermissions() 已经返回 ask 之后。deny / allow 规则都过完了,本来该弹权限框。模式是 auto时,权限主循环把这次 ask 截下来。这段在 shouldAvoidPermissionPrompts 之前跑,所以 headless 也走分类器。
ask 进来
├─ safetyCheck 且不可交给分类器 → 原样 ask(敏感路径必须人看)
├─ requiresUserInteraction 且不是「AUQ 交给分类器」→ 原样 ask
├─ acceptEdits 预检通过 → 放行(fastPath: acceptEdits)
├─ 工具名在白名单 → 放行(fastPath: allowlist)
└─ 打分类器(默认 xml_2stage)
├─ 放行 → allow,连续拒绝清零
├─ 拦 + transcript 超限 → 回退人工(headless 直接 abort)
├─ 拦 + 分类器挂了 → 默认 deny(fail-closed)
└─ 拦 + 真判决 → 计数;连续 3 / 会话 20 → 回退人工(headless abort)
5.1 先过几道门,根本不进分类器
safetyCheck:工具自己标了「这个 ask 是安全检查」且 classifierApprovable 为假,三道自动放行(acceptEdits / 白名单 / 分类器)全跳过,保持人工。headless 没有权限框,改成 deny。敏感文件路径走这条。
需要真人交互的工具:requiresUserInteraction() 为真就保持 ask。例外:AskUserQuestion 在 classifyAskUserQuestion 为真时(读不到 GB 时默认真)跳过这道门,交给分类器。
5.2 acceptEdits 预检:复用「工作区内编辑」那套逻辑
auto 自己没有「CWD 内写文件算安全」的实现。做法是临时撒谎:把 mode 改成 acceptEdits,再调一次同一个 checkPermissions。acceptEdits 的规则是:路径在启动时的工作目录里就 allow(pathInAllowedWorkingPath)。CWD 里改 src/foo.ts 会放行;改 ~/.ssh/config、/etc/hosts、别的仓库不会,继续往下走。
放行时记遥测 fastPath: 'acceptEdits',连续拒绝清零。预检自己抛错(除了用户中止)就当没这道门,掉进分类器。
Agent 和 REPL 不走这道门。它们在 acceptEdits 下会直接 allow——REPL 的 JS 胶水里可能夹着 VM 逃逸,分类器必须看到整段胶水,不能只看见里面套的 Read/Bash。所以这两样跳过预检,去分类器。
5.3 白名单:按工具名跳过,不含任何写工具
预检没放行,再看工具名在不在白名单。在就放行,记 fastPath: 'allowlist'。这是名字集合,不看参数。
只读、任务元数据、Plan 切换、Sleep 仍在。SendMessage 不在名单里(2.1.222 起走分类器)。Chrome / Browser MCP 的只读动作也进了白名单。CLASSIFY_EDITS 默认关。
没有 Edit / Write / Bash。写文件走 5.2:CWD 内预检放行,CWD 外进分类器。Bash 一律分类器(除非前面的 allow 规则已经放了,且没开 classifyAllShell)。
5.4 分类器拦下来之后:计数、回退、fail-closed
走到这里才真正打 API。三种结果不一样。
放行:shouldBlock: false → allow,recordSuccess 把连续拒绝清零。会话总拒绝不动。
真的拦:recordDenial,连续和总数都 +1。返回给主模型的话是:
Permission for this action has been denied. Reason: <分类器的 reason>.
可以用别的工具合理替代(比如 head 代替 cat),
但不要恶意绕过(别用跑测试当借口去执行非测试动作)。
若这个能力对完成任务是必需的,停下向用户解释,让人决定。
计数存在 appState.denialTracking。异步子代理的 setAppState 是空操作,改走 localDenialTracking 就地改。
达限回退:连续 3 次,或本会话累计 20 次。任一到了就不再让分类器死循环:
- 交互:改回
ask,警告「连续 N 次 / 本会话 N 次被拦,先看 transcript」。打到 20 次上限时把两个计数都清零,人看完还能继续。 - headless:直接
AbortError: too many classifier denials in headless mode。没人点框,再试也是死循环。
快路径放行也算成功,连续计数会清。所以「读文件 → 被拦的 Bash → 再读文件 → 再 Bash」不会把连续计数叠满。
transcript 超限(确定性 400):对话只增不减,重试必炸。不加拒绝计数。交互回退人工;headless 直接 abort。
分类器没给出判决(API 错、超时、中止)默认 deny。
6. 模式转换与入口#
上一节是一次工具调用怎么判。这一节是怎么进、怎么出 auto:启动选模式、Shift+Tab 轮转、进门时剥掉会绕过分类器的 allow 规则、以及把规则 dump 出来看的 CLI。
6.1 启动时谁赢
initialPermissionModeFromCLI 先组一个有序列表,第一个合法的赢:
--dangerously-skip-permissions→bypassPermissions。组织策略(tengu_disable_bypass_permissions_mode)或permissions.disableBypassPermissionsMode开着就跳过,并提示被禁了。--permission-mode auto→auto。断路器已经是disabled就不当它存在,掉到下一项。settings.permissions.defaultMode。写成auto同样过断路器。远程会话(CLAUDE_CODE_REMOTE)只认acceptEdits/plan/default,settings 里的 bypass 或 auto 会被丢掉,免得远程环境被静默提权。
列表空了就回 default(2.1.200 改名 Manual)。账号策略把新会话默认设成 auto,走的是第 3 项,不是 Shift+Tab。
进了 REPL 之后,Shift+Tab 走 getNextPermissionMode。2.1.233 外部和内部同一条环,不再给 ant 开捷径:
default → acceptEdits → plan →(有 bypass 就多一档)→ auto → 回到 default
auto 默认就在环上。2.1.88 要 GrowthBook 写成 enabled、或用户已经 opt-in,转盘才露出 auto。2.1.233 把「转盘里有没有」和「能不能显式进」收成同一道门:没熔断、settings 没关、模型支持,就进环。tengu_auto_mode_config.enabled 读不到时默认就是 enabled。打开就能 Shift+Tab 转到 auto,不用先设 flag、也不用先同意一次。
6.2 进 auto 时剥掉会绕过分类器的 allow
剥 = 暂时让这些 always-allow 失效,调用去走分类器。不是把它们直接放行。
权限流水线里,整工具 always-allow 在分类器前面(step 2b toolAlwaysAllowedRule)。settings 里如果留着 Bash(*),不剥的话所有 Bash 在分类器看到之前就过了——auto 等于没开。所以进 auto 时先把这类规则从生效的 alwaysAllowRules 拿掉,塞进 strippedDangerousRules 暂存。用户磁盘上的 settings 不动。退出 auto 再按来源加回去。
Bash(*) 在 auto 里:ls 和 curl | bash 都要过分类器。切回 Manual,settings 里那条 Bash(*) 又生效。
被当成危险、会剥掉的 always-allow:
Bash/Bash(*)/Bash():等于所有命令。- 解释器前缀:
Bash(python:*)、node:*、npx:*、ssh:*等(DANGEROUS_BASH_PATTERNS)。ant 额外剥curl/kubectl/gh api这类。 PowerShell(*),以及iex/Start-Process/pwsh等。- 任何
Agent(...)allow:否则子代理 spawn 时分类器看不到 prompt,委托攻击直接过。 - ant 的
Tmux:send-keys等于再开一层任意 shell。
窄规则比如 Bash(git status) 不在剥离名单里,进 auto 仍会在 2b 先放行,分类器看不到。CLI、Shift+Tab、SDK 改模式都走同一个 transitionPermissionMode,进出不会漏剥或漏还。
2.1.88 不剥 Edit(**)。CWD 内编辑本来就走 acceptEdits 预检;用户自己写的 Edit always-allow 仍会在 2b 放行。旧笔记写成「剥 Edit(**)」和源码不符。
6.3 claude auto-mode 子命令
规则是提示词,人眼难看清「分类器实际吃到什么」。四个只读/复位命令:
defaults:解析外部模板<user_*_to_replace>里的 bullets,dump 出厂 allow / soft_deny / environment。config:生效配置。某一段用户写过就整段 REPLACE,没写过用默认。和buildYoloSystemPrompt同一套语义。critique [--model]:把用户规则 + 完整分类器提示词送给另一个 LLM,按清晰 / 完备 / 冲突 / 可执行四维挑毛病。没有自定义规则就提示去改 settings。reset(2.1.212 起):恢复默认 auto-mode 配置,先确认,--yes跳过。
7. 版本变更历史#
BLOCK 后来拆成 HARD / SOFT BLOCK:数据外泄强制拦截,删文件这类SOFT BLOCK能遵循用户意图。详情见 §4.4–4.6。
7.1 Environment:从「信谁」变成三类槽
早期只有 5 条,全是「什么算可信」:Trusted repo、Source control、Trusted internal domains、Trusted cloud buckets、Key internal services。没填就当 External,外泄和外部代码规则最严。
后来扩到约 20 个,写成三类。分类器靠这些槽判断「这个目的地算不算外泄」「这个集群算不算生产」,空着时各自有保守默认:
| 类 | 默认 | 槽(举例) | 干什么 |
|---|---|---|---|
| Context | None / 保守假设 | Organization、Cloud provider、Repository visibility、CI/CD deploy targets、Network posture | 描述环境。可见性未核实时不当成「一定公开」,也不当成「一定私有」 |
| Trust | 没填 = 谁都不信 | Trusted repo / domains / buckets / services、Internal package registry | 白名单。填了,往这些地方推代码、装包才不算 External |
| Sensitivity | 宽启发式(名字带 prod 就算) | Sensitive data locations、Sensitive remote targets、Protected IaC scopes | 点名要保护的东西。不填也拦得住 prod-db |
填了之后,分类器能区分「推进自己的私有 org」和「推进公开 gist」,也能把名字带 prod 的 namespace 当生产。
7.2 CLAUDE_CODE_AUTO_MODE_*
auto mode 有一批 CLAUDE_CODE_AUTO_MODE_* 旋钮。取值顺序一般是:环境变量 → 远程配置 → 默认值。看到名字不代表 export 就能开,不少只给内部或灰度用。曾经给 Bedrock / Vertex 用的 CLAUDE_CODE_ENABLE_AUTO_MODE 后来也不用了;CLI 的 --enable-auto-mode 更早就删了。
CLAUDE_CODE_ENABLE_AUTO_MODE # 曾经的平台 opt-in,后来不用了
CLAUDE_CODE_AUTO_MODE_MODEL # 分类器模型
CLAUDE_CODE_AUTO_MODE_CLASSIFY_ASK_USER_QUESTION # AskUserQuestion 也交给分类器
CLAUDE_CODE_AUTO_MODE_CLASSIFY_EDITS # 编辑操作也过分类器
CLAUDE_CODE_AUTO_MODE_EDIT_REMOVAL / _CAP # 编辑剥离 / 上限
CLAUDE_CODE_AUTO_MODE_EXTERNAL_PERMISSIONS # 走外部权限模板
CLAUDE_CODE_AUTO_MODE_GIT_STATUS / _LIMIT / _UPLOADS # 把 git status 塞进分类器上下文
CLAUDE_CODE_AUTO_MODE_OUTCOME_CODES # 分类器结果码
CLAUDE_CODE_AUTO_MODE_PRIOR_ASSISTANT_CONTEXT # transcript 是否带上一条 assistant 上下文
CLAUDE_CODE_AUTO_MODE_REPO_VISIBILITY # 探测仓库公开/私有
CLAUDE_CODE_AUTO_MODE_TEMPERATURE # 分类器温度
CLAUDE_CODE_BG_CLASSIFIER_MODEL # 后台分类器模型
CLAUDE_CODE_CLASSIFIER_SUMMARY # 分类器摘要
8. 可借鉴的模式#
- 快路径金字塔:白名单 → 低成本预检 → 昂贵判断
- 两阶段分类(2.1.233 默认):先用 64 token 的短输出快放行,只有可疑才用 8192 token 写思考再复核(2.1.88 深查是 4096)
- Fail-closed 默认,gate 可切 fail-open
- 拒绝逃生舱:3 / 20 回退人工
- 只给分类器看工具调用,不给模型文本
- stage2 复用 stage1 前缀打 cache
claude auto-mode defaults/config/critique/reset+ 遥测比- 默认规则包在替换标签里,
$defaults追加