Skip to content

17 · 类别开关与信任目录

Tri-state category switches & trusted directories

静态引擎(§03)回答「这一次调用危不危险」,类别层回答「这一类操作要不要问」。工具与 shell 命令被归入 11 个类别,每类可配 auto / ask / deny 三态;未配置 = inherit,行为与没有这层时完全一致。全部实现是纯函数(src/auto/category.ts,647 行),宿主在两个接线点各自从零调用。

17.1 十一个类别与优先级 category.ts:L47-59

优先级类别典型内容配置约束
11privilegesudo/su、set-executionpolicy 等提权LOCKED:仅可 ask
10deleterm/del/Remove-Item 等删除LOCKED:仅可 ask
9diskformat/bcdedit/磁盘镜像写LOCKED:仅可 ask
8protected触碰受保护/关键路径的写改LOCKED:仅可 ask
7networkExeccurl/wget/iwr 外联下载执行三态可配
6gitPushgit push 及等价远端变更三态可配
5publishnpm publish/deploy 等发布动作三态可配
4gitLocal本地 git 变更(commit/branch…)三态可配
3fileEdit工作区文件写改三态可配
2build构建/测试/包管理例行命令三态可配
1readOnly只读查询三态可配

类别清单 CATEGORY_KEYS(L33-37)、锁定名单 LOCKED_CATEGORIES = ['delete','protected','privilege','disk'](L40)。另有 harnessInternalunknown 两个非类别归宿:它们没有配置键、恒为 inheritcategory.ts:L638)——看不懂的东西不给你开自动。

17.2 三态语义

语义关键边界
auto≡ 按 LOW 档走,LLM 复审仍是最后一关只对「本来就要进语义分类器」的调用生效(ask + classifierEligible,category.ts:L649-652);降档不越 HIGH——原判 HIGH/DENY 原地不动(category.ts:L695-697
ask无条件转人工,status-less、无宿主倒计时pre-execute 快径直接返回(index.ts:L1911-1913),LLM 分类器永远没机会回答一次类别 ask;answerer 侧同样直达人工(index.ts:L2541-2546
deny绝对拒绝,提权重试不可绕过与 denyList 同构的终端拒绝(index.ts:L2512-2526);applyCategoryDirective 里 DENY 是地板,任何配置都压不住它(category.ts:L692

17.3 双接点机制

两个接点各自调 categoryDirectiveFor 从零重算类别与指令,无任何状态跨越(函数注释明言,category.ts:L663-668);同一次调用被两层检查,但不存在「上层记住下层结论」的耦合。guard 层只做硬拒,从不参与类别判定。

17.4 LOCKED 四类与三重保险

delete / protected / privilege / disk 四类在配置面上只能收 ask。保险有三道:

  1. schema 层categoryPolicy 的 zod 定义只允许 auto|ask|deny 三值字典(index.ts:L167);
  2. resolveConfig 层:未知键 warn+丢弃,LOCKED 类别收到非 ask 值一律钳回丢弃并告警(index.ts:L199-217);
  3. 决策层常量兜底:即便有漏网配置进了运行时,categoryDirective 对 locked 类别的分支也只会给出 askinherit,绝无 auto/deny(category.ts:L643-646)。

17.5 复合命令:类别取先、指令取严

一条 bash 可能串了多段命令。categorizeCommandSegments 先做词法分解再逐段归类;读不懂的整行(opaque)退化为单个 unknown 段——不瞎判(category.ts:L605-615)。合并规则 mergeCommandDecisionscategory.ts:L582-598)双轨取值:

  • 类别取先:按 §17.1 优先级表,最高优先级类别的标签胜出(git push && rm x 归 gitPush?不——delete 10 > gitPush 6,归 delete);
  • directive 取严deny > ask > auto > inherit,任一段最严的指令决定整行待遇。

17.5b 写重定向与写向量族判类

  • 写重定向目标参与判类:含 >/>>/>|/&>/N> 写重定向(非 discard sink)的命令段按目标先行判类——命中敏感名/受保护路径 → protected,否则 → fileEdit,与显式写工具同语义;按 §17.1 优先级取严合并,delete/privilege 永不被拖低。
  • POSIX 写向量族五头teedd of=sed -i(含后缀/--in-place 形态)、truncateinstall 以操作数目标参与全部按目标闸门——直写运行态文件(history/audit 等)无条件硬拒,敏感/受保护目标与 cp/mv 同流;sed 不带 -iddof= 保持读语义;dd 恒属 disk 锁定类。

17.6 信任目录模式 categoryMode 与敏感名熔丝

位置谓词 isEffectiveRoutine(target, roots)category.ts:L140-145)决定「工作区内的例行放行」认哪些地方:

模式判定
standard(默认)workspace ∪ trustedDirs 内才认(category.ts:L143-144
aggressive直接 return true——位置不限(category.ts:L142

aggressive 下三个内置类别 ['networkExec','gitPush','publish']AGGRESSIVE_BUILTINcategory.ts:L65)在未显式配置时隐式取 autocategory.ts:L647)——这就是「切激进会自动放行网络读写/git push/发布」的出处;显式配置过则听你的。

危险度门全部不动:敏感名熔丝 sensitiveBasenameAtcategory.ts:L127-132)对任意位置的 .gitconfig/.netrc/.npmrc/.pypirc/.mcp.json/.bash*/.env(非 example).ssh/.gnupg/.aws/.azure/.kube 目录段生效(名单 L110-120)——换什么模式都拦着;插件运行态文件硬拒、symlink realpath 复检同样与模式无关(§3.2/§3.4)。

17.7 trustedDirs 配置面

  • 校验:仅收绝对路径;凭据树(.ssh/.gnupg/.aws/.azure/.kube)、home、dshHome、critical 路径内的条目 warn+丢弃,余下归一化入库(resolveConfig,index.ts:L223-248)。
  • host-only:九员 host-only 键之一(decision.ts:L224-234)——只能写在 settings.yaml / patch,设置卡保存不会抹掉它,也没有它的控件。
  • 复检扩区:symlink 守卫把 trustedDirs 并入受信复检区(workspace ∪ 插件区 ∪ trustedDirs,index.ts:L1827)——文本上落进信任目录的目标照样做真实路径逃逸检查。

配置示例(默认零变化)

yaml
auto-approval-llm:
  # 什么都不写 = 全部 inherit = 行为与本层不存在时一致
  # categoryPolicy: {}
  # categoryMode: standard
  # trustedDirs: []
  # ---- 以下为主动收紧/放宽的样子 ----
  categoryPolicy:
    fileEdit: auto      # 工作区文件写改:降为 LOW 档(仍送 LLM 复审)
    networkExec: ask    # 外联下载:无条件转人工
    gitLocal: deny      # 本地 git 变更:绝对拒绝
    # delete/protected/privilege/disk 写 auto/deny 会被 warn+丢弃,仅 ask 有效
  categoryMode: aggressive   # 取消位置白名单:任意位置视为常规位置(危险度门、敏感名 fuse 不动)
  trustedDirs:
    - D:\work\shared-lib     # 绝对路径;落在凭据树/home/critical 内会被丢弃