> ## Documentation Index
> Fetch the complete documentation index at: https://docs.befailproof.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 配置

> 配置文件格式、三级作用域系统与合并规则

failproofai 使用 JSON 配置文件来控制哪些策略处于启用状态、策略的行为方式，以及自定义策略的加载路径。配置设计上便于团队共享——将其提交到仓库，每位开发者都能获得相同的 AI 代理安全防护。

***

## 配置作用域

共有三个配置作用域，按优先级顺序评估：

| 作用域         | 文件路径                                      | 用途                       |
| ----------- | ----------------------------------------- | ------------------------ |
| **project** | `.failproofai/policies-config.json`       | 每个仓库的配置，提交到版本控制          |
| **local**   | `.failproofai/policies-config.local.json` | 个人的仓库级覆盖配置，已加入 gitignore |
| **global**  | `~/.failproofai/policies-config.json`     | 适用于所有项目的用户级默认配置          |

当 failproofai 收到 hook 事件时，会加载并合并当前工作目录下所有已存在的三个文件。

### 合并规则

**`enabledPolicies`** — 取三个作用域的并集。任意级别启用的策略都会生效。

```text theme={null}
project:  ["block-sudo"]
local:    ["block-rm-rf"]
global:   ["block-sudo", "sanitize-api-keys"]

resolved: ["block-sudo", "block-rm-rf", "sanitize-api-keys"]  ← 去重后的并集
```

**`policyParams`** — 对某个策略最先定义其参数的作用域完全胜出，不会对策略参数内的值进行深度合并。

```text theme={null}
project:  block-sudo → { allowPatterns: ["sudo apt-get update"] }
global:   block-sudo → { allowPatterns: ["sudo systemctl status"] }

resolved: { allowPatterns: ["sudo apt-get update"] }   ← project 胜出，global 被忽略
```

```text theme={null}
project:  （无 block-sudo 条目）
local:    （无 block-sudo 条目）
global:   block-sudo → { allowPatterns: ["sudo systemctl status"] }

resolved: { allowPatterns: ["sudo systemctl status"] }  ← 回退到 global
```

**`customPoliciesPath`** — 最先定义该字段的作用域胜出。

**`llm`** — 最先定义该字段的作用域胜出。

***

## 配置文件格式

```json theme={null}
{
  "enabledPolicies": [
    "block-sudo",
    "block-rm-rf",
    "block-push-master",
    "sanitize-api-keys",
    "sanitize-jwt",
    "block-env-files",
    "block-read-outside-cwd"
  ],
  "policyParams": {
    "block-sudo": {
      "allowPatterns": ["sudo systemctl status", "sudo journalctl"]
    },
    "block-push-master": {
      "protectedBranches": ["main", "release", "prod"]
    },
    "block-rm-rf": {
      "allowPaths": ["/tmp"]
    },
    "block-read-outside-cwd": {
      "allowPaths": ["/shared/data", "/opt/company"]
    },
    "sanitize-api-keys": {
      "additionalPatterns": [
        { "regex": "myco_[A-Za-z0-9]{32}", "label": "MyCo API key" }
      ]
    },
    "warn-large-file-write": {
      "thresholdKb": 512
    }
  },
  "customPoliciesPath": "/home/alice/myproject/my-policies.js"
}
```

***

## 字段参考

### `enabledPolicies`

类型：`string[]`

需要启用的策略名称列表。名称必须与 `failproofai policies` 显示的策略标识符完全匹配。完整列表请参见[内置策略](/zh/built-in-policies)。

不在 `enabledPolicies` 中的策略将处于非活跃状态，即便在 `policyParams` 中为其配置了条目也不例外。

### `policyParams`

类型：`Record<string, Record<string, unknown>>`

各策略的参数覆盖配置。外层键为策略名称，内层键为各策略专属的配置项。每个策略的可用参数请参见[内置策略](/zh/built-in-policies)。

如果策略有参数但未指定，则使用策略的内置默认值。未配置 `policyParams` 的用户其行为与之前版本完全一致。

策略参数块中未知的键在 hook 触发时会被静默忽略，但在运行 `failproofai policies` 时会作为警告标记出来。

#### `hint`（通用配置项）

类型：`string`（可选）

当策略返回 `deny` 或 `instruct` 时，附加到原因后面的消息。可用于在不修改策略本身的情况下，向 Claude 提供可操作的指引。

适用于任何类型的策略——内置策略、自定义策略（`custom/`）、项目约定策略（`.failproofai-project/`）或用户约定策略（`.failproofai-user/`）。

```json theme={null}
{
  "policyParams": {
    "block-force-push": {
      "hint": "Try creating a fresh branch instead."
    },
    "block-sudo": {
      "allowPatterns": ["sudo apt-get"],
      "hint": "Use apt-get directly without sudo."
    },
    "custom/my-policy": {
      "hint": "Ask the user for approval first."
    }
  }
}
```

当 block-force-push 拒绝操作时，Claude 会看到：*"Force-pushing is blocked. Try creating a fresh branch instead."*

非字符串值和空字符串会被静默忽略。若未设置 `hint`，行为保持不变（向后兼容）。

### `customPoliciesPath`

类型：`string`（绝对路径）

包含自定义 hook 策略的 JavaScript 文件路径。该值由 `failproofai policies --install --custom <path>` 自动设置（路径在存储前会被解析为绝对路径）。

每次 hook 事件触发时都会重新加载该文件，不进行缓存。有关编写自定义策略的详情，请参见[自定义策略](/zh/custom-policies)。

### 基于约定的策略

除了显式的 `customPoliciesPath` 外，failproofai 还会自动发现并加载 `.failproofai/policies/` 目录中的策略文件：

| 级别  | 目录                         | 作用域          |
| --- | -------------------------- | ------------ |
| 项目级 | `.failproofai/policies/`   | 通过版本控制与团队共享  |
| 用户级 | `~/.failproofai/policies/` | 个人使用，适用于所有项目 |

**文件匹配：** 仅加载匹配 `*policies.{js,mjs,ts}` 的文件（例如 `security-policies.mjs`、`workflow-policies.js`），目录中的其他文件会被忽略。

**无需配置：** 约定策略无需在 `policies-config.json` 中添加任何条目，只需将文件放入对应目录，下次 hook 事件触发时即可自动生效。

**联合加载：** 项目和用户约定目录都会被扫描，两个级别中所有匹配的文件都会被加载（与 `customPoliciesPath` 采用首个作用域胜出的方式不同）。

更多详情和示例请参见[自定义策略](/zh/custom-policies)。

### `llm`

类型：`object`（可选）

供策略进行 AI 调用的 LLM 客户端配置，大多数场景下无需配置。

```json theme={null}
{
  "llm": {
    "model": "claude-sonnet-4-6",
    "apiKey": "sk-ant-..."
  }
}
```

***

## 通过 CLI 管理配置

`policies --install` 和 `policies --uninstall` 命令会写入代理 CLI 的 hook 设置文件（hook 入口点），而 `policies-config.json` 是由你直接管理的文件，两者相互独立：

* **代理 CLI 设置** — 告知代理在每次工具调用时执行 `failproofai --hook <event>`：
  * **Claude Code**：`~/.claude/settings.json`（用户级）、`<cwd>/.claude/settings.json`（项目级）、`<cwd>/.claude/settings.local.json`（本地级）
  * **OpenAI Codex**：`~/.codex/hooks.json`（用户级）、`<cwd>/.codex/hooks.json`（项目级）——Codex 没有 `local` 作用域
  * **GitHub Copilot CLI *(beta)***：`~/.copilot/hooks/failproofai.json`（用户级）、`<cwd>/.github/hooks/failproofai.json`（项目级）——Copilot 没有 `local` 作用域。Hook 条目使用 Copilot 基于操作系统的 `bash`/`powershell` 命令字段，并带有 `timeoutSec`；文件顶层包含 `version: 1` 标记。Copilot CLI 支持目前为 **beta** 版本，我们正在针对更多实际会话验证 `events.jsonl` 记录模式（公开文档中未作说明）。
  * **Cursor Agent *(beta)***：`~/.cursor/hooks.json`（用户级）、`<cwd>/.cursor/hooks.json`（项目级）——Cursor 没有 `local` 作用域。Hook 条目使用 Claude 风格的 `{type, command, timeout}` 格式（无 `bash`/`powershell` 拆分），但按照 Cursor 的 [hooks schema](https://cursor.com/docs/hooks) 以驼峰式事件键（`preToolUse`、`beforeSubmitPrompt` 等）存储在扁平数组中；文件顶层包含 `version: 1` 标记。处理器通过 `CURSOR_EVENT_MAP` 将驼峰式键规范化为帕斯卡式，使现有内置策略能够正常触发。Cursor Agent 支持目前为 **beta** 版本，我们正在针对更多实际安装验证 Cursor 的磁盘上转录格式（公开文档未作说明）。
  * **OpenCode *(beta)***：`~/.config/opencode/opencode.json` + `~/.config/opencode/plugins/failproofai.mjs`（用户级）、`<cwd>/.opencode/opencode.json` + `<cwd>/.opencode/plugins/failproofai.mjs`（项目级）——OpenCode 没有 `local` 作用域。与其他五种 CLI 不同，OpenCode **没有外部命令 hook 系统**：它通过 `opencode.json` 的 `plugin: []` 数组显式注册来加载进程内 JS/TS 插件（自动发现 `.opencode/plugins/` 目录**不是** opencode v1.14.33 插件的加载方式）。安装时会生成一个小型插件 shim，以子进程方式调用 failproofai 二进制文件，并将二进制文件的 Claude 风格 JSON 响应转换回插件语义：工具事件拒绝时 `throw new Error()`（取消工具调用）；instruct 以及 `Stop` / `SubagentStop` 拒绝时调用 `client.session.prompt(...)`（将拒绝原因作为下一条用户消息提交——这是唯一的强制重试通道，因为 `session.idle` 仅用于通知，从中抛出异常无效）；allow 时不执行任何操作。该 shim 在转发给二进制文件前会规范化工具名称（小写 → 帕斯卡式，通过 `OPENCODE_TOOL_MAP`）和工具输入参数键（驼峰式 → 下划线式，通过 `OPENCODE_TOOL_INPUT_MAP` 适用于 `Read` / `Write` / `Edit`，例如 `filePath` → `file_path`、`oldString` → `old_string`），从而使 `block-read-outside-cwd`、`block-env-files` 和 `block-secrets-write` 等路径检查内置策略在 OpenCode 工具调用中正常触发。会话存储在 `~/.local/share/opencode/opencode.db` 的 SQLite 数据库中；仪表盘的会话查看器通过 `opencode db --format json` 和 `opencode export <id>` 读取它们。OpenCode 支持目前为 **beta** 版本，我们正在跨版本和更多实际会话中验证其行为。详见 [OpenCode plugins 文档](https://opencode.ai/docs/plugins/)。
  * **Pi *(beta)***：`~/.pi/agent/settings.json`（用户级）、`<cwd>/.pi/settings.json`（项目级）——Pi 没有 `local` 作用域。Pi 在启动时加载 TypeScript 扩展包；设置文件是一个扁平字符串数组 `{"packages": ["./relative/path", …]}`。failproofai 在 packages 数组中写入一个指向其捆绑的 `pi-extension/` 目录的条目。该扩展内部订阅 Pi 的 `tool_call` / `user_bash` / `input` / `session_start` 事件，并调用 `failproofai --hook <Event> --cli pi`；处理器通过 `PI_EVENT_MAP` 将下划线小写蛇形命名规范化为帕斯卡式，使现有内置策略正常触发。工具输入参数也通过 `PI_TOOL_INPUT_MAP` 规范化（Pi 的 Read / Write / Edit 使用 `path` 而非 `file_path`；映射顶层键后 `block-env-files` 和 `block-secrets-write` 可正常触发——`block-read-outside-cwd` 本身已有 `path` 的回退逻辑）。Pi 支持目前为 **beta** 版本，有待 Pi 的扩展 API 和会话日志格式稳定。
  * **Hermes (hermes-agent)**：`~/.hermes/config.yaml`（**仅用户作用域**——Hermes 没有项目/本地配置）。Hermes 是一个 Slack/Telegram **网关**，因此一次安装即可拦截来自所有平台（Slack/Telegram/cli/cron）**以及**内部子代理的工具调用。Hook 条目是 `hooks:` 映射下的 `{command, timeout}` 对（timeout 单位为**秒**），以 Hermes 的蛇形命名事件为键（`pre_tool_call` / `post_tool_call` / `on_session_start` / `on_session_end` / `subagent_stop`）；处理器通过 `HERMES_EVENT_MAP` 规范化事件，通过 `HERMES_TOOL_MAP` 规范化工具名称，使内置策略正常触发。配置通过保留注释的 YAML `Document` 往返编辑，以确保操作者的其他设置不受影响；安装时将 `hooks_auto_accept: true` 设置为 true，以便无头网关（无 TTY）在无需确认提示的情况下运行 hook。评估器输出 Hermes 的 `{"decision":"block","reason"}` stdout 约定（Hermes 忽略退出码）。**限制：** Hermes 没有会话结束 `Stop` 事件，因此 `require-*-before-stop` 内置策略永远不会为其触发（不适用，并非故障）；`instruct` 降级为允许并记录日志（无额外上下文通道）；输出密钥清理（`sanitize-*`）无法通过 shell hook 约定重写工具输出。Hermes **同时**也是一个离线**审计**数据源——仪表盘直接从 `~/.hermes/state.db` 读取其网关会话。
* **`policies-config.json`** — 告知 failproofai 评估哪些策略及其参数（在所有代理 CLI 之间共享）

通过 `--cli claude|codex|copilot|cursor|opencode|pi|hermes` 指定目标代理（可用空格分隔或重复指定多个）：

```bash theme={null}
failproofai policies --install --cli codex --scope project
failproofai policies --install --cli copilot --scope project
failproofai policies --install --cli cursor --scope project
failproofai policies --install --cli opencode --scope project
failproofai policies --install --cli pi --scope project
failproofai policies --install --cli hermes --scope user
failproofai policies --install --cli claude codex copilot cursor opencode pi
```

省略 `--cli` 时，`failproofai` 会自动检测已安装的代理 CLI（`which claude` / `which codex` / `which copilot` / `which cursor-agent` / `which opencode` / `which pi` / `which hermes`）：

* **检测到一个 CLI** — 自动选择该 CLI，无需提示。
* **在交互式终端中检测到多个 CLI** — 显示方向键单选菜单，分为 `Detected (N)` 区域（包含一个"为所有 N 个已检测到的 CLI 安装"的聚合行及各个已检测 CLI）和 `Not installed (M) · install hooks ahead of time` 区域（列出所有未检测到的受支持 CLI 作为提前安装选项）（↑↓ 移动，回车选择，^C 退出）。卸载流程仅显示 Detected 区域。
* **在非交互式运行中检测到多个 CLI**（CI、无 TTY）— 无需提示，为所有检测到的 CLI 安装。
* **未检测到任何 CLI** — 回退到 `claude`，并显示在 PATH 中未找到代理二进制文件的警告；hook 命令仍会被写入，一旦安装代理即可生效。

你可以随时直接编辑 `policies-config.json`，修改将在下一次 hook 事件触发时立即生效，无需重启。

***

## 示例：带团队默认配置的项目级配置

将 `.failproofai/policies-config.json` 提交到你的仓库：

```json theme={null}
{
  "enabledPolicies": [
    "block-sudo",
    "block-rm-rf",
    "block-push-master",
    "sanitize-api-keys",
    "block-env-files"
  ],
  "policyParams": {
    "block-push-master": {
      "protectedBranches": ["main", "release", "hotfix"]
    }
  }
}
```

每位开发者可以创建 `.failproofai/policies-config.local.json`（已加入 gitignore）进行个人覆盖配置，而不影响其他团队成员。
