Skip to main content

title: Архитектура description: “Как работают обработчик хука, загрузка конфигурации и оценка политик” icon: sitemap

Этот документ объясняет, как failproofai работает внутри: как система хуков перехватывает вызовы инструментов агента, как загружается и объединяется конфигурация, как оцениваются политики и как панель мониторинга отслеживает активность агента.

Обзор

failproofai состоит из двух независимых подсистем:
  1. Обработчик хука — быстрый CLI подпроцесс, который Claude Code вызывает при каждом вызове инструмента агента. Оценивает политики и возвращает решение.
  2. Agent Monitor (Dashboard) — веб-приложение Next.js для мониторинга сеансов агента и управления политиками.
Обе подсистемы используют общие файлы конфигурации в ~/.failproofai/ и директории .failproofai/ проекта, но запускаются как отдельные процессы и взаимодействуют только через файловую систему.

Обработчик хука

Интеграция с Claude Code

Когда вы запускаете failproofai policies --install, она записывает записи вроде этой в ~/.claude/settings.json:
Затем Claude Code вызывает failproofai --hook PreToolUse как подпроцесс перед каждым вызовом инструмента, передавая JSON-полезную нагрузку на stdin.

Формат полезной нагрузки

Для событий PostToolUse полезная нагрузка также содержит tool_result с выводом инструмента. Обработчик устанавливает лимит stdin в 1 МБ. Полезные нагрузки, превышающие это значение, отбрасываются и все политики неявно разрешают.

Формат ответа

Deny (PreToolUse):
Deny (PostToolUse):
Instruct (любое событие кроме Stop):
Событие Stop instruct:
  • Код выхода: 2
  • Причина записана в stderr (не stdout)
Allow:
  • Код выхода: 0
  • Пустой stdout
Allow с сообщением: allow(message) позволяет политике отправить информационный контекст обратно Claude, даже когда операция разрешена. Обработчик хука записывает следующий JSON в stdout (не в файл конфигурации — это ответ обработчика Claude Code, как и ответы deny и instruct выше):
  • Код выхода: 0 (операция разрешена)
  • Когда несколько политик возвращают allow с сообщением, их сообщения объединяются с переводами строк в одну строку additionalContext
  • Если ни одна политика не предоставляет сообщение, stdout пустой (как раньше)

Конвейер обработки

src/hooks/handler.ts реализует полный конвейер:
Весь процесс выполняется менее чем за 100 мс для типичных полезных нагрузок без вызовов LLM.

Загрузка конфигурации

src/hooks/hooks-config.ts реализует загрузку конфигурации с тремя областями.
Логика объединения:
  • enabledPolicies — дедуплицированное объединение всех трех файлов
  • policyParams — по ключу политики, первый файл, который его определяет, побеждает полностью
  • customPoliciesPath — первый файл, который его определяет, побеждает
  • llm — первый файл, который его определяет, побеждает
Веб-панель использует readHooksConfig() (только глобальная) для чтения и записи, так как она не вызывается с cwd проекта.

Оценка политик

src/hooks/policy-evaluator.ts запускает политики по порядку. Для каждой политики:
  1. Ищет схему params политики (если она есть).
  2. Читает policyParams[policy.name] из объединенной конфигурации.
  3. Объединяет предоставленные пользователем значения над значениями по умолчанию схемы для создания ctx.params.
  4. Вызывает policy.fn(ctx) с разрешенным контекстом.
  5. Если результат deny, останавливается немедленно и возвращает это решение.
  6. Если результат instruct, накапливает сообщение и продолжает.
  7. Если результат allow, продолжает к следующей политике.
После выполнения всех политик:
  • Если был возвращен какой-либо deny, выдает ответ deny.
  • Если были собраны возвращаемые instruct, выдает один ответ instruct со всеми сообщениями объединены.
  • В противном случае выдает ответ allow (пустой stdout, выход 0).

Встроенные политики

src/hooks/builtin-policies.ts определяет все 39 встроенных политик как объекты BuiltinPolicyDefinition:
Политики, которые принимают params, объявляют PolicyParamsSchema с типами и значениями по умолчанию для каждого параметра. Оценщик политик внедряет разрешенные значения в ctx.params перед вызовом fn. Функции политик читают ctx.params без защиты от нулевых значений, потому что значения по умолчанию всегда применяются сначала. Сопоставление паттернов внутри политик использует проанализированные токены команды (argv), а не сопоставление по сырой строке. Это предотвращает обход с помощью инъекции оператора оболочки (например, паттерн для sudo systemctl status * не может быть обойден добавлением ; rm -rf / к команде).

Пользовательские политики

src/hooks/custom-hooks-registry.ts реализует реестр, основанный на globalThis:
src/hooks/custom-hooks-loader.ts загружает файл политики пользователя:
  1. Читает customPoliciesPath из конфигурации; пропускает, если отсутствует.
  2. Разрешает абсолютный путь; проверяет, существует ли файл.
  3. Переписывает все импорты from "failproofai" на фактический путь dist, чтобы customPolicies разрешился на один и тот же реестр globalThis.
  4. Рекурсивно переписывает переходящие локальные импорты для обеспечения совместимости ESM.
  5. Записывает временные файлы .mjs и import() файл записи.
  6. Вызывает getCustomHooks() для получения зарегистрированных хуков.
  7. Очищает все временные файлы в блоке finally.
При любой ошибке (файл не найден, синтаксическая ошибка, ошибка импорта) ошибка регистрируется в ~/.failproofai/hook.log и загрузчик возвращает пустой массив. Встроенные политики не затрагиваются. Пользовательские политики оцениваются после всех встроенных политик. deny пользовательской политики все еще вызывает короткое замыкание для дальнейших пользовательских политик (но все встроенные уже выполнены к этому моменту).

Логирование активности

После каждого события хука обработчик добавляет строку JSONL в ~/.failproofai/hook-activity.jsonl:
По одной строке на политику, которая приняла решение, отличное от allow. Решения allow не регистрируются (чтобы сохранить размер файла маленьким).

Архитектура панели

Панель является приложением Next.js 16 с использованием App Router с React Server Components и Server Actions.
Поток данных:
  • Компоненты страниц вызывают lib/projects.ts и lib/log-entries.ts для чтения данных проекта/сеанса непосредственно из файловой системы (нет уровня API для чтения).
  • Страница Policies использует Server Actions для всех изменений (переключение, обновление параметров, установка/удаление).
  • Просмотрщик сеанса парсит формат стенограммы JSONL Claude и отображает хронологию сообщений и вызовов инструментов.
Ключевые дизайн-решения:
  • Нет базы данных — все постоянное состояние находится в обычных файлах (~/.failproofai/, ~/.claude/projects/).
  • Server Actions для изменений — REST API не требуется для операций CRUD.
  • React Server Components для страниц чтения — более быстрая начальная загрузка, нет клиентского пакета для выборки данных.
  • Клиентские компоненты только там, где требуется интерактивность (переключатели политик, поиск активности, просмотр журнала).

Структура файлов