Перейти к основному содержанию
Пользовательские политики позволяют вам написать правила для любого поведения агента: соблюдать соглашения проекта, предотвращать отклонения, ограничивать деструктивные операции, обнаруживать зависших агентов или интегрироваться с Slack, рабочими процессами утверждения и многим другим. Они используют ту же систему событий хука и решения allow, deny, instruct, что и встроенные политики.

Быстрый пример

// my-policies.js
import { customPolicies, allow, deny, instruct } from "failproofai";

customPolicies.add({
  name: "no-production-writes",
  description: "Block writes to paths containing 'production'",
  match: { events: ["PreToolUse"] },
  fn: async (ctx) => {
    if (ctx.toolName !== "Write" && ctx.toolName !== "Edit") return allow();
    const path = ctx.toolInput?.file_path ?? "";
    if (path.includes("production")) {
      return deny("Writes to production paths are blocked");
    }
    return allow();
  },
});
Установите её:
failproofai policies --install --custom ./my-policies.js

Два способа загрузки пользовательских политик

Вариант 1: На основе соглашения (рекомендуется)

Поместите файлы *policies.{js,mjs,ts} в .failproofai/policies/ и они будут загружены автоматически — не требуются флаги или изменения конфигурации. Это работает как git hooks: положите файл, и всё работает.
# Уровень проекта — заложено в git, поделено с командой
.failproofai/policies/security-policies.mjs
.failproofai/policies/workflow-policies.mjs

# Уровень пользователя — личное, применяется ко всем проектам
~/.failproofai/policies/my-policies.mjs
Как это работает:
  • Сканируются оба каталога проекта и пользователя (объединение — не первый приоритет)
  • Файлы загружаются в алфавитном порядке в каждом каталоге. Префиксируйте 01-, 02- для управления порядком
  • Загружаются только файлы, соответствующие *policies.{js,mjs,ts}; другие файлы игнорируются
  • Каждый файл загружается независимо (открытое поведение при ошибке для каждого файла)
  • Работает вместе с явным --custom и встроенными политиками
Политики на основе соглашения — это самый простой способ создать стандарт качества для вашей организации. Зафиксируйте .failproofai/policies/ в git и каждый член команды автоматически получит одинаковые правила — никакой предварительной настройки для каждого разработчика не требуется. По мере того, как ваша команда обнаруживает новые сбои, добавляйте политику и выполняйте push. Со временем они станут живым стандартом качества, который постоянно улучшается с каждым вкладом.

Вариант 2: Явный путь к файлу

# Установка с пользовательским файлом политик
failproofai policies --install --custom ./my-policies.js

# Замена пути к файлу политик
failproofai policies --install --custom ./new-policies.js

# Удаление пути пользовательской политики из конфигурации
failproofai policies --uninstall --custom
Разрешённый абсолютный путь сохраняется в policies-config.json как customPoliciesPath. Файл загружается заново при каждом событии хука — кэширования между событиями нет.

Использование обоих способов вместе

Политики на основе соглашения и явный файл --custom могут сосуществовать. Порядок загрузки:
  1. Явный файл customPoliciesPath (если настроен)
  2. Файлы соглашения проекта ({cwd}/.failproofai/policies/, по алфавиту)
  3. Файлы соглашения пользователя (~/.failproofai/policies/, по алфавиту)

API

Импорт

import { customPolicies, allow, deny, instruct } from "failproofai";

customPolicies.add(hook)

Регистрирует политику. Вызывайте это столько раз, сколько необходимо для нескольких политик в одном файле.
customPolicies.add({
  name: string;                         // обязательно - уникальный идентификатор
  description?: string;                 // показывается в выводе `failproofai policies`
  match?: { events?: HookEventType[] }; // фильтр по типу события; опустите, чтобы совпадать со всеми
  fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
});

Вспомогательные функции решения

ФункцияЭффектИспользуйте когда
allow()Разрешить операцию беззвучноДействие безопасно, сообщение не требуется
deny(message)Заблокировать операциюАгент не должен выполнять это действие
instruct(message)Добавить контекст без блокировкиДать агенту дополнительный контекст, чтобы оставаться в курсе
deny(message) — сообщение отображается для Claude с префиксом "Blocked by failproofai:". Один deny прерывает всю дальнейшую оценку. instruct(message) — сообщение добавляется в контекст Claude для текущего вызова инструмента. Все сообщения instruct накапливаются и доставляются вместе.
Вы можете добавить дополнительное руководство к любому сообщению deny или instruct, добавив поле hint в policyParams — изменение кода не требуется. Это работает для пользовательских (custom/), соглашений проекта (.failproofai-project/) и соглашений пользователя (.failproofai-user/) политик тоже. See Configuration → hint для подробностей.

Информационные сообщения allow

allow(message) разрешает операцию и отправляет информационное сообщение обратно Claude. Сообщение доставляется как additionalContext в ответе stdout обработчика хука — тот же механизм, используемый instruct, но семантически другой: это обновление статуса, а не предупреждение.
ФункцияЭффектИспользуйте когда
allow(message)Разрешить и отправить контекст ClaudeПодтвердить успешность проверки или объяснить, почему проверка была пропущена
Примеры использования:
  • Подтверждения статуса: allow("All CI checks passed.") — сообщает Claude, что всё в порядке
  • Объяснения открытого поведения: allow("GitHub CLI not installed, skipping CI check.") — сообщает Claude, почему проверка была пропущена, чтобы он имел полный контекст
  • Несколько сообщений накапливаются: если несколько политик каждая возвращают allow(message), все сообщения объединяются новыми строками и доставляются вместе
customPolicies.add({
  name: "confirm-branch-status",
  match: { events: ["Stop"] },
  fn: async (ctx) => {
    const cwd = ctx.session?.cwd;
    if (!cwd) return allow("No working directory, skipping branch check.");

    // ... проверка статуса ветки ...
    if (allPushed) {
      return allow("Branch is up to date with remote.");
    }
    return deny("Unpushed changes detected.");
  },
});

Поля PolicyContext

ПолеТипОписание
eventTypestring"PreToolUse", "PostToolUse", "Notification", "Stop"
toolNamestring | undefinedВызываемый инструмент (например, "Bash", "Write", "Read")
toolInputRecord<string, unknown> | undefinedВходные параметры инструмента
payloadRecord<string, unknown>Полная необработанная полезная нагрузка события из Claude Code
sessionSessionMetadata | undefinedКонтекст сеанса (см. ниже)

Поля SessionMetadata

ПолеТипОписание
sessionIdstringИдентификатор сеанса Claude Code
cwdstringРабочий каталог сеанса Claude Code
transcriptPathstringПуть к файлу стенограммы JSONL сеанса

Типы событий

СобытиеКогда оно срабатываетСодержимое toolInput
PreToolUseПеред запуском инструмента ClaudeВходные данные инструмента (например, { command: "..." } для Bash)
PostToolUseПосле завершения инструментаВходные данные инструмента + tool_result (выходные данные)
NotificationКогда Claude отправляет уведомление{ message: "...", notification_type: "idle" | "permission_prompt" | ... } - хуки должны всегда возвращать allow(), они не могут блокировать уведомления
StopКогда сеанс Claude заканчиваетсяПусто

Порядок оценки

Политики оцениваются в этом порядке:
  1. Встроенные политики (в порядке определения)
  2. Явные пользовательские политики из customPoliciesPath (в порядке .add())
  3. Политики соглашения из проекта .failproofai/policies/ (файлы по алфавиту, .add() порядок внутри)
  4. Политики соглашения из пользователя ~/.failproofai/policies/ (файлы по алфавиту, .add() порядок внутри)
Первый deny прерывает все последующие политики. Все сообщения instruct накапливаются и доставляются вместе.

Транзитивные импорты

Файлы пользовательских политик могут импортировать локальные модули, используя относительные пути:
// my-policies.js
import { isBlockedPath } from "./utils.js";
import { checkApproval } from "./approval-client.js";

customPolicies.add({
  name: "approval-gate",
  fn: async (ctx) => {
    if (ctx.toolName !== "Bash") return allow();
    const approved = await checkApproval(ctx.toolInput?.command, ctx.session?.sessionId);
    return approved ? allow() : deny("Approval required for this command");
  },
});
Все относительные импорты, достижимые из файла входа, разрешаются. Это реализуется путём переписывания импортов from "failproofai" на фактический путь dist и создания временных файлов .mjs для обеспечения совместимости ESM.

Фильтрация типов событий

Используйте match.events для ограничения срабатывания политики:
customPolicies.add({
  name: "require-summary-on-stop",
  match: { events: ["Stop"] },
  fn: async (ctx) => {
    // Срабатывает только при завершении сеанса
    // ctx.session.transcriptPath содержит полный журнал сеанса
    return allow();
  },
});
Опустите match полностью, чтобы срабатывало на каждый тип события.

Обработка ошибок и режимы отказа

Пользовательские политики открыты при ошибке: ошибки никогда не блокируют встроенные политики и не приводят к сбою обработчика хука.
ОтказПоведение
customPoliciesPath не установленЯвные пользовательские политики не выполняются; политики соглашения и встроенные политики продолжают работать нормально
Файл не найденПредупреждение записано в ~/.failproofai/hook.log; встроенные политики продолжают работать
Синтаксическая/ошибка импорта (явная)Ошибка записана в ~/.failproofai/hook.log; явные пользовательские политики пропущены
Синтаксическая/ошибка импорта (соглашение)Ошибка записана; этот файл пропущен, другие файлы соглашения всё ещё загружаются
fn выбрасывает исключение во время выполненияОшибка записана; этот хук рассматривается как allow; другие хуки продолжают работать
fn занимает дольше 10sTimeout записан; рассматривается как allow
Каталог соглашения отсутствуетПолитики соглашения не выполняются; нет ошибки
Для отладки ошибок пользовательской политики смотрите файл журнала:
tail -f ~/.failproofai/hook.log

Полный пример: несколько политик

// my-policies.js
import { customPolicies, allow, deny, instruct } from "failproofai";

// Запретить агенту писать в каталог secrets/
customPolicies.add({
  name: "block-secrets-dir",
  description: "Prevent agent from writing to secrets/ directory",
  match: { events: ["PreToolUse"] },
  fn: async (ctx) => {
    if (!["Write", "Edit"].includes(ctx.toolName ?? "")) return allow();
    const path = ctx.toolInput?.file_path ?? "";
    if (path.includes("secrets/")) return deny("Writing to secrets/ is not permitted");
    return allow();
  },
});

// Держать агента в курсе: проверить тесты перед коммитом
customPolicies.add({
  name: "remind-test-before-commit",
  description: "Keep the agent on track: verify tests pass before committing",
  match: { events: ["PreToolUse"] },
  fn: async (ctx) => {
    if (ctx.toolName !== "Bash") return allow();
    const cmd = ctx.toolInput?.command ?? "";
    if (/git\s+commit/.test(cmd)) {
      return instruct("Verify all tests pass before committing. Run `bun test` if you haven't already.");
    }
    return allow();
  },
});

// Запретить незапланированные изменения зависимостей во время заморозки
customPolicies.add({
  name: "dependency-freeze",
  description: "Prevent unplanned dependency changes during freeze period",
  match: { events: ["PreToolUse"] },
  fn: async (ctx) => {
    if (ctx.toolName !== "Bash") return allow();
    const cmd = ctx.toolInput?.command ?? "";
    const isInstall = /^(npm install|yarn add|bun add|pnpm add)\s+\S/.test(cmd);
    if (isInstall && process.env.DEPENDENCY_FREEZE === "1") {
      return deny("Package installs are frozen. Unset DEPENDENCY_FREEZE to allow.");
    }
    return allow();
  },
});

export { customPolicies };

Примеры

Каталог examples/ содержит готовые к использованию файлы политик:
ФайлСодержимое
examples/policies-basic.jsПять начальных политик, охватывающих распространённые режимы отказа агента
examples/policies-advanced/index.jsПродвинутые шаблоны: транзитивные импорты, асинхронные вызовы, очистка выходных данных и хуки конца сеанса
examples/convention-policies/security-policies.mjsПолитики безопасности на основе соглашения (блокировка записей .env, предотвращение переписывания истории git)
examples/convention-policies/workflow-policies.mjsПолитики рабочего процесса на основе соглашения (напоминания о тестах, запись файлов аудита)

Использование примеров явных файлов

failproofai policies --install --custom ./examples/policies-basic.js

Использование примеров на основе соглашения

# Копирование на уровень проекта
mkdir -p .failproofai/policies
cp examples/convention-policies/*.mjs .failproofai/policies/

# Или копирование на уровень пользователя
mkdir -p ~/.failproofai/policies
cp examples/convention-policies/*.mjs ~/.failproofai/policies/
Команда установки не требуется — файлы подбираются автоматически при следующем событии хука.