> ## Documentation Index
> Fetch the complete documentation index at: https://exosphere-auto-translate-docs-20260624-1149.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

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

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

***

## Обзор

failproofai состоит из двух независимых подсистем:

1. **Обработчик крючков** — быстрый CLI подпроцесс, который Claude Code вызывает при каждом вызове инструмента агента. Оценивает политики и возвращает решение.
2. **Монитор агента (Панель)** — веб-приложение Next.js для мониторинга сеансов агента и управления политиками.

Обе подсистемы используют файлы конфигурации в `~/.failproofai/` и в директории проекта `.failproofai/`, но работают как отдельные процессы и взаимодействуют только через файловую систему.

***

## Обработчик крючков

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

Когда вы выполняете `failproofai policies --install`, он записывает записи вроде этой в `~/.claude/settings.json`:

```json theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "failproofai --hook PreToolUse"
          }
        ]
      }
    ],
    "PostToolUse": [ ... ]
  }
}
```

Claude Code затем вызывает `failproofai --hook PreToolUse` как подпроцесс перед каждым вызовом инструмента, передавая JSON-полезную нагрузку на stdin.

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

```json theme={null}
{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/myproject/sessions/abc123.jsonl",
  "cwd": "/home/user/myproject",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "sudo apt install nodejs" }
}
```

Для событий `PostToolUse` полезная нагрузка также содержит `tool_result` с выводом инструмента.

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

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

**Запретить (PreToolUse):**

```json theme={null}
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "Blocked by failproofai: sudo command blocked"
  }
}
```

**Запретить (PostToolUse):**

```json theme={null}
{
  "hookSpecificOutput": {
    "additionalContext": "Blocked by failproofai because: API key detected in output"
  }
}
```

**Инструктировать (любое событие кроме Stop):**

```json theme={null}
{
  "hookSpecificOutput": {
    "additionalContext": "Instruction from failproofai: Verify tests pass before committing."
  }
}
```

**Событие Stop с инструкцией:**

* Код выхода: `2`
* Причина записывается в stderr (не в stdout)

**Разрешить:**

* Код выхода: `0`
* Пустой stdout

**Разрешить с сообщением:**

`allow(message)` позволяет политике отправить информационный контекст обратно Claude даже когда операция разрешена. Обработчик крючков записывает следующий JSON в **stdout** (не в файл конфигурации — это ответ обработчика на Claude Code, так же как запрет и инструкции выше):

```json theme={null}
// Записано в stdout процессом обработчика крючков
{
  "hookSpecificOutput": {
    "additionalContext": "All CI checks passed on branch 'feat/my-feature'."
  }
}
```

* Код выхода: `0` (операция разрешена)
* Когда несколько политик возвращают `allow` с сообщением, их сообщения объединяются новыми строками в одну строку `additionalContext`
* Если ни одна политика не предоставляет сообщение, stdout пустой (как и раньше)

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

`src/hooks/handler.ts` реализует полный конвейер:

```text theme={null}
stdin JSON
  → parse payload (max 1 MB)
  → extract session metadata (session_id, cwd, tool_name, tool_input, etc.)
  → readMergedHooksConfig(cwd)    ← merges project + local + global config
  → register enabled builtin policies with resolved params
  → load custom policies from customPoliciesPath (if set)
  → register custom policies into policy registry
  → evaluate all policies (builtins first, then custom)
      → first deny short-circuits
      → instruct decisions accumulate
      → allow messages accumulate
  → write JSON decision to stdout
  → persist event to ~/.failproofai/hook-activity.jsonl
  → exit
```

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

***

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

`src/hooks/hooks-config.ts` реализует загрузку конфигурации с тремя уровнями видимости.

```text theme={null}
[1] {cwd}/.failproofai/policies-config.json        ← проект  (наивысший приоритет)
[2] {cwd}/.failproofai/policies-config.local.json  ← локальная
[3] ~/.failproofai/policies-config.json             ← глобальная   (наименьший приоритет)
```

Логика объединения:

* `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`, выдать ответ с запретом.
* Если собраны какие-либо `instruct`, выдать единый ответ с инструкцией со всеми сообщениями, объединенными вместе.
* В противном случае выдать ответ с разрешением (пустой stdout, выход 0).

***

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

`src/hooks/builtin-policies.ts` определяет все 39 встроенных политик как объекты `BuiltinPolicyDefinition`:

```typescript theme={null}
interface BuiltinPolicyDefinition {
  name: string;
  description: string;
  fn: (ctx: PolicyContext) => PolicyResult;
  match: {
    events: HookEventType[];
    tools?: string[];
  };
  defaultEnabled: boolean;
  category: string;
  beta?: boolean;
  params?: PolicyParamsSchema;
}
```

Политики, которые принимают `params`, объявляют `PolicyParamsSchema` с типами и значениями по умолчанию для каждого параметра. Оценщик политик внедряет разрешенные значения в `ctx.params` перед вызовом `fn`. Функции политик читают `ctx.params` без проверок на null, потому что значения по умолчанию всегда применяются первыми.

Сопоставление шаблонов внутри политик использует разобранные токены команд (argv), а не прямое сопоставление строк. Это предотвращает обход через инъекции операторов оболочки (например, шаблон для `sudo systemctl status *` не может быть обойден путем добавления `; rm -rf /` к команде).

***

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

`src/hooks/custom-hooks-registry.ts` реализует реестр на основе `globalThis`:

```typescript theme={null}
const REGISTRY_KEY = "__failproofai_custom_hooks__";

export const customPolicies = {
  add(hook: CustomHook): void { ... }
};

export function getCustomHooks(): CustomHook[] { ... }
export function clearCustomHooks(): void { ... }  // used in tests
```

`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`:

```json theme={null}
{
  "timestamp": "2026-04-06T12:34:56.789Z",
  "sessionId": "abc123",
  "eventType": "PreToolUse",
  "toolName": "Bash",
  "policyName": "block-sudo",
  "decision": "deny",
  "reason": "sudo command blocked by failproofai",
  "durationMs": 12
}
```

Одна строка на политику, которая приняла решение не-разрешение. Решения о разрешении не регистрируются (чтобы файл был меньше).

***

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

Панель — это приложение **Next.js 16**, использующее App Router с React Server Components и Server Actions.

```text theme={null}
app/
  layout.tsx                  ← Root layout (theme, telemetry, nav)
  projects/page.tsx           ← Server component: list all Claude projects
  project/[name]/page.tsx     ← Server component: list sessions in a project
  project/[name]/session/
    [sessionId]/page.tsx      ← Server component: render session viewer
  policies/page.tsx           ← Client component: policy management + activity log
  actions/
    get-hooks-config.ts       ← Read config + policy list
    update-hooks-config.ts    ← Toggle policy on/off
    update-policy-params.ts   ← Update policy parameters
    get-hook-activity.ts      ← Paginate/search activity log
    install-hooks-web.ts      ← Install/remove hooks from the browser
  api/
    download/[project]/[session]/route.ts   ← Per-CLI session export (JSONL or JSON)
```

**Поток данных:**

* Компоненты страниц вызывают `lib/projects.ts` и `lib/log-entries.ts` для прямого чтения данных проекта/сеанса из файловой системы (нет API слоя для чтения).
* Страница Политик использует Server Actions для всех мутаций (переключение, обновление параметров, установка/удаление).
* Средство просмотра сеансов анализирует формат JSONL-транскрипта Claude и отображает временную шкалу сообщений и вызовов инструментов.

**Ключевые решения по проектированию:**

* Нет базы данных — все постоянное состояние находится в простых файлах (`~/.failproofai/`, `~/.claude/projects/`).
* Server Actions для мутаций — не требуется REST API для операций CRUD.
* React Server Components для страниц чтения — быстрая первоначальная загрузка, нет клиентского пакета для выборки данных.
* Клиентские компоненты только где нужна интерактивность (переключатели политик, поиск активности, средство просмотра логов).

***

## Расположение файлов

```text theme={null}
failproofai/
├── bin/
│   └── failproofai.mjs           # CLI router (hook / dashboard / install / etc.)
├── src/hooks/
│   ├── handler.ts                # Hook event pipeline
│   ├── builtin-policies.ts       # 39 policy definitions
│   ├── policy-evaluator.ts       # Policy execution engine
│   ├── policy-registry.ts        # Policy registration and lookup
│   ├── policy-types.ts           # TypeScript interfaces
│   ├── hooks-config.ts           # Multi-scope config loading
│   ├── custom-hooks-registry.ts  # globalThis-backed hook registry
│   ├── custom-hooks-loader.ts    # ESM loader for user JS hooks
│   ├── manager.ts                # install / remove / list operations
│   ├── install-prompt.ts         # Interactive policy selection prompt
│   ├── hook-logger.ts            # Logging to hook.log
│   ├── hook-activity-store.ts    # Persist activity to hook-activity.jsonl
│   └── llm-client.ts             # LLM API client (for AI-powered policies)
├── app/                          # Next.js dashboard (pages + server actions)
├── lib/                          # Shared utilities
│   ├── projects.ts               # Enumerate Claude projects from filesystem
│   ├── log-entries.ts            # Parse Claude transcript JSONL format
│   ├── paths.ts                  # Resolve system paths
│   └── ...
├── components/                   # Shared React UI components
├── contexts/                     # React context providers (theme, auto-refresh, telemetry)
├── examples/                     # Example custom hook files
└── __tests__/                    # Unit and E2E tests
```
