- Блокировать опасные операции перед их выполнением, такие как деструктивные команды shell или несанкционированный доступ к файлам
- Логировать и аудировать вызовы инструментов для соответствия требованиям, отладки или аналитики
- Преобразовывать входные и выходные данные для санитизации данных, внедрения учетных данных или перенаправления путей файлов
- Требовать одобрение человека для чувствительных действий, таких как запись в базу данных или вызовы API
- Отслеживать жизненный цикл сеанса для управления состоянием, очистки ресурсов или отправки уведомлений
Как работают хуки
1
Срабатывает событие
Во время выполнения агента что-то происходит, и SDK генерирует событие: инструмент вот-вот будет вызван (
PreToolUse), инструмент вернул результат (PostToolUse), субагент запустился или остановился, агент простаивает или выполнение завершилось. См. полный список событий.2
SDK собирает зарегистрированные хуки
SDK проверяет наличие хуков, зарегистрированных для этого типа события. Сюда входят callback-хуки, которые вы передаете в
options.hooks, и хуки shell-команд из файлов настроек, если включена соответствующая запись settingSources или setting_sources, что по умолчанию так и есть для параметров query().3
Фильтрация запускаемых хуков с помощью matcher
Если у хука есть паттерн
matcher (например, "Write|Edit"), SDK проверяет его на соответствие цели события (например, имени инструмента). Хуки без matcher запускаются для каждого события этого типа.4
Выполняются функции обратного вызова
Функция обратного вызова каждого подходящего хука получает информацию о том, что происходит: имя инструмента, его аргументы, ID сессии и другие детали, специфичные для события.
5
Ваш callback возвращает решение
После выполнения любых операций (логирование, вызовы API, валидация) ваш callback возвращает объект вывода, который сообщает агенту, что делать: разрешить операцию, заблокировать ее, изменить входные данные или внедрить контекст в диалог.
PreToolUse (шаг 1) с matcher "Write|Edit" (шаг 3), поэтому callback срабатывает для инструментов Write и Edit. При срабатывании callback получает входные данные инструмента (шаг 4), проверяет, указывает ли путь файла на файл .env, и возвращает permissionDecision: "deny" для блокировки операции (шаг 5):
file_path заканчивается на /.env, в любом регистре букв. В Windows file_path использует обратные косые черты, например C:\project\.env, и этот хук разрешает вызов Write или Edit для C:\project\.env.
Доступные hooks
SDK предоставляет hooks для различных этапов выполнения агента. Некоторые hooks доступны в обоих SDK, в то время как другие доступны только в TypeScript.Настройка хуков
Чтобы настроить хук, передайте его в полеhooks ваших параметров агента (ClaudeAgentOptions в Python, объект options в TypeScript). Этот фрагмент предполагает, что вы уже определили callback хука, например protect_env_files в Python или protectEnvFiles в TypeScript из примера выше:
hooks — это словарь (Python) или объект (TypeScript), где:
- Ключи: имена событий хуков, такие как
'PreToolUse','PostToolUse'и'Stop' - Значения: массивы matchers, каждый содержащий необязательный паттерн фильтра и ваши функции обратного вызова
Matchers
Используйте matchers для фильтрации, когда срабатывают ваши callbacks. Полеmatcher сопоставляется с разными значениями в зависимости от типа события хука. Например, хуки на основе инструментов сопоставляются с именем инструмента, в то время как хуки Notification сопоставляются с типом уведомления.
SDK matchers следуют тем же правилам, что и matchers в файлах настроек. Этот раздел документирует пути оценки точной строки и регулярного выражения, требования к версиям и значения matcher для каждого типа события.
Используйте паттерн
matcher для нацеливания на конкретные инструменты, когда это возможно. Matcher с 'Bash' запускается только для команд Bash, в то время как опущение паттерна запускает ваши callbacks для каждого возникновения события.
Функции обратного вызова
Входные данные
Каждый callback хука получает три аргумента:- Входные данные: типизированный объект, содержащий детали события. Каждый тип хука имеет свою форму входных данных. Например,
PreToolUseHookInputвключаетtool_nameиtool_input, в то время какNotificationHookInputвключаетmessage. См. полные определения типов в справочниках TypeScript и Python SDK.- Все входные данные хуков содержат
session_id,cwdиhook_event_name. agent_idиagent_typeзаполняются, когда хук срабатывает внутри субагента. В TypeScript они находятся на базовом входе хука и доступны для всех типов хуков. В Python они являются необязательными полями наPreToolUse,PostToolUse,PostToolUseFailureиPermissionRequest, и обязательными полями наSubagentStartиSubagentStop.
- Все входные данные хуков содержат
- ID использования инструмента (
str | None/string | undefined): коррелирует событияPreToolUseиPostToolUseдля одного и того же вызова инструмента. - Контекст: в TypeScript содержит свойство
signal(AbortSignal) для отмены. В Python этот аргумент зарезервирован для будущего использования.
Выходные данные
Ваш callback возвращает объект с двумя категориями полей:- Поля верхнего уровня принимаются для каждого события:
systemMessageпоказывает сообщение пользователю, иcontinue(continue_в Python) определяет, продолжает ли агент работать после этого хука. Некоторые события отбрасывают их или доставляют их в другое место. Раздел каждого события на странице хуков говорит, где они попадают. hookSpecificOutputконтролирует текущую операцию. Поля, которые вы устанавливаете внутри, зависят от типа события хука:- Для хуков
PreToolUseздесь вы устанавливаетеpermissionDecision("allow","deny","ask"или"defer"),permissionDecisionReasonиupdatedInput. Если вы вернете"defer", ход завершается сообщением с результатом, у которогоstop_reasonравен"tool_deferred", чтобы вы могли возобновить вызов позже. - Для хуков
PostToolUseвы можете установитьadditionalContextдля добавления информации к результату инструмента. Чтобы заменить выходные данные инструмента перед тем, как Claude их увидит, установитеupdatedToolOutput, который работает для любого инструмента в обоих SDK. Более старое полеupdatedMCPToolOutputзаменяет только выходные данные MCP инструмента. - В TypeScript SDK callback
PostToolUseможет также возвращатьclassifierContext, краткую заметку о результате вызова инструмента для классификатора разрешений авторежима. Поскольку ваш callback работает в собственном процессе вашего приложения, классификатор может учитывать заявление пользователя, которое вы передаете в заметке, как намерение пользователя. Поле требует TypeScript Agent SDK версии 0.3.236 или позже. В разделе Аннотирование результата для классификатора авторежима описаны ограничение по длине, правило только синхронного выполнения и то, что не следует помещать в заметку.
- Для хуков
{} для разрешения операции без изменений. SDK callback-хуки используют тот же формат вывода JSON, что и хуки shell-команд Claude Code, который документирует каждое поле и опцию, специфичную для события. Для определений типов SDK см. справочники TypeScript и Python SDK.
Когда несколько хуков
PreToolUse возвращают разные решения, deny имеет приоритет над defer, который имеет приоритет над ask, который имеет приоритет над allow.Асинхронный вывод
По умолчанию агент ждет, пока ваш хук вернется, прежде чем продолжить. Если ваш хук выполняет побочный эффект, такой как логирование или отправка webhook, и ему не нужно влиять на поведение агента, вы можете вернуть асинхронный вывод вместо этого. Это говорит агенту продолжить немедленно без ожидания завершения хука. В этом фрагментеsend_to_logging_service в Python и sendToLoggingService в TypeScript служат заменой для любой функции логирования, которую вы определяете:
Асинхронные выходы не могут блокировать, изменять или внедрять контекст в операцию, так как агент уже продолжил. Используйте их только для побочных эффектов, таких как логирование, метрики или уведомления.
Примеры
Несколько примеров в этом разделе показывают только функцию callback. Чтобы запустить один из них, зарегистрируйте callback под соответствующим событием в полеhooks ваших параметров, как показано в Настройка hooks.
Изменение входных данных инструмента
Этот пример перехватывает вызовы инструмента Write и переписывает аргументfile_path, добавляя к нему префикс /sandbox. Callback возвращает updatedInput с измененным путем и permissionDecision: 'allow' для автоматического одобрения переписанной операции:
Используйте
updatedInput с permissionDecision: 'allow' для автоматического одобрения измененного входа или permissionDecision: 'ask' для отображения его пользователю. Если вы опустите permissionDecision, измененный вход все равно применяется и проходит через обычную оценку разрешений. С 'defer' updatedInput игнорируется. Всегда возвращайте новый объект вместо мутирования оригинального tool_input../sandbox или /tmp/sandbox (macOS не позволяет создавать корневой каталог /sandbox), затем попросите агента написать файл: результат инструмента Write в потоке сообщений указывает путь с вашим префиксом sandbox вместо того, который запросил Claude.
Добавление контекста и блокировка инструмента
Этот пример отклоняет вызовы инструментов, у которыхfile_path начинается с /etc, и объясняет причину как модели, так и пользователю:
permissionDecision: 'deny'останавливает вызов инструмента.permissionDecisionReasonсообщает модели причину.systemMessageпоказывает пользователю, что произошло.
PreToolUse с matcher Write|Edit и попросите агента создать файл в /etc: результат инструмента Write в потоке сообщений содержит Writing to /etc is not allowed, и файл не создается.
Автоматическое одобрение конкретных инструментов
По умолчанию агент может запросить разрешение перед использованием определенных инструментов. Этот пример автоматически одобряет инструменты файловой системы только для чтения (Read, Glob, Grep), возвращаяpermissionDecision: 'allow', позволяя им запускаться без подтверждения пользователя, за исключением чтения из сетевых путей, и оставляя все остальные инструменты подлежащими обычным проверкам разрешений:
Регистрация нескольких hooks
Когда событие срабатывает, соответствующие хуки выполняются параллельно. Для решений о разрешениях применяется наиболее ограничивающий результат.deny одного хука блокирует вызов инструмента. Поскольку порядок завершения недетерминирован, пишите каждый хук так, чтобы он действовал независимо, а не полагался на то, что другой хук уже выполнился.
Пример ниже регистрирует три независимые проверки, каждую с опущенным matcher. Имена хуков в нем, такие как audit_logger в Python или auditLogger в TypeScript, служат заменой для callbacks, которые вы определяете:
Фильтрация с помощью multi-tool matchers
Используйте multi-tool matchers для совместного использования одного callback для связанных инструментов. Этот пример регистрирует три matcher с разными областями, и каждый hook, который он называет, служит заменой для callback, который вы определяете:- Точный список с разделением вертикальной чертой (
Write|Edit|NotebookEdit) запускаетfile_security_hookдля инструментов Write, Edit и NotebookEdit. - Regex (
^mcp__) срабатываетmcp_audit_hookдля любого MCP инструмента, имя которого начинается сmcp__. - Опущенный matcher запускает
global_loggerбез фильтрации по имени инструмента.
Отслеживание активности подагента
Используйте hooksSubagentStop для мониторинга, когда подагенты завершают свою работу. См. полный тип входных данных в справочниках TypeScript и Python SDK. Этот пример логирует сводку каждый раз, когда подагент завершается:
[SUBAGENT] Completed: с ID подагента и путем к транскрипту.
Выполнение HTTP запросов из hooks
Hooks могут выполнять асинхронные операции, такие как HTTP запросы. Ловите ошибки внутри вашего hook вместо того, чтобы позволить им распространяться. Этот пример отправляет webhook после завершения каждого инструмента, логируя, какой инструмент запустился и когда. Hook ловит ошибки из неудачного webhook:Перенаправление уведомлений в Slack
Используйте hooksNotification для получения системных уведомлений от агента и перенаправления их во внешние сервисы. В сеансах SDK Claude Code запускает этот hook для следующих типов уведомлений:
permission_promptодин раз, когда запрос разрешения ждал около шести секунд на вашем callbackcanUseTool. Требуется TypeScript Agent SDK v0.3.233 или позже, или Python Agent SDK v0.2.139 или позжеelicitation_completeиelicitation_responseдля потоков запроса пользователя
idle_prompt, auth_success и elicitation_dialog, из интерактивного пользовательского интерфейса, который сеансы SDK не запускают.
Каждое уведомление включает поле message с описанием, понятным человеку, и опционально title.
Этот пример перенаправляет каждое уведомление в канал Slack. Требуется URL входящего webhook Slack, который вы создаете, добавляя приложение в ваше рабочее пространство Slack и включая входящие webhooks:
Notification, hook отправляет message уведомления с префиксом Agent status: в канал, на который указывает ваш webhook.
Исправление распространенных проблем
Хук не срабатывает
- Проверьте, что имя события хука правильное и чувствительно к регистру (
PreToolUse, а неpreToolUse) - Проверьте, что ваш паттерн matcher точно совпадает с именем инструмента
- Убедитесь, что хук находится под правильным типом события в
options.hooks - Для хуков, не связанных с инструментами, которые поддерживают matcher, таких как
NotificationиSubagentStop, matcher сопоставляется с другими полями, аStopполностью игнорирует matcher (см. паттерны matcher) - Хуки могут не срабатывать, когда агент достигает лимита
max_turns, потому что сессия заканчивается до того, как хуки смогут выполниться
Matcher не фильтрует как ожидается
Matcher сопоставляется только с именами инструментов, а не с путями файлов или другими аргументами. Для фильтрации по пути файла проверьтеtool_input.file_path внутри вашего хука:
Таймаут хука
Claude Code запускает каждый callback с таймаутом, который вы устанавливаете в секундах с помощью поляtimeout в его HookMatcher. Если вы его не устанавливаете, Claude Code использует значение по умолчанию для события: 600 секунд для большинства событий, 30 секунд для UserPromptSubmit, PreModelSwitch и PostModelSwitch и 10 секунд для MessageDisplay. Claude Code запускает callbacks SessionEnd во время завершения работы в рамках более короткого бюджета таймаута SessionEnd, по умолчанию 1,5 секунды.
Когда callback превышает свой таймаут, Claude Code отменяет его и отбрасывает его выходные данные, и сессия продолжается, а не зависает. Что происходит дальше, зависит от события:
PreToolUse: Claude Code блокирует вызов инструмента. Вместо него Claude получает результат инструмента, указывающий, что хук не ответил до истечения таймаута, и ход продолжается. До версии 2.1.210 Claude Code сообщал Claude о таймауте как об отклонении пользователем, из-за чего автоматические сессии останавливались и ждали ввода.PostToolUseиPostToolUseFailure: Claude Code сохраняет результат инструмента, и ход продолжается.UserPromptSubmitиUserPromptExpansion: Claude Code блокирует промпт с сообщением, указывающим хук и таймаут, и сессия продолжается. До версии 2.1.208 Claude Code завершал запрос сerror_during_execution, когда у callback на этих событиях истекал таймаут.StopиSubagentStop: callback с истекшим таймаутом считается не вернувшим решения. Агент или субагент останавливается так, как если бы этот callback это разрешил, а решение ваших других хуков на этом событии по-прежнему применяется. До Claude Code версии 2.1.273 callbackStopилиSubagentStopс истекшим таймаутом считался неудачным запуском хука, и Claude Code отбрасывал решения ваших других хуков на этом событии.SessionStart: callback с истекшим таймаутом считается не вернувшим выходных данных, и сессия продолжается с выходными данными ваших других хуковSessionStart.PreModelSwitch: Claude Code блокирует переключение модели. Хук, который не отвечает, не одобрил переключение.- Другие события, такие как
Notification,PreCompactиPostModelSwitch: Claude Code записывает сбой в лог и продолжает работу.
Stop или SessionStart впервые истекает таймаут в основной сессии, Claude Code также добавляет в поток сообщений SDKInformationalMessage о том, что приложение, управляющее сессией, не ответило. Последующие таймауты не повторяют это сообщение, пока ваше приложение остается неотвечающим.
Если вы прерываете запрос во время ожидания callback, Claude Code отменяет ожидающий вызов инструмента. До версии 2.1.208 вызов инструмента мог все же выполниться, если вы прерывали запрос во время ожидания callback PreToolUse.
Если вашему callback нужно больше времени, установите более высокий timeout в его HookMatcher. В TypeScript используйте AbortSignal из третьего аргумента callback для корректной обработки отмены при срабатывании таймаута.
Инструмент заблокирован неожиданно
- Проверьте все хуки
PreToolUseна возвращениеpermissionDecision: 'deny' - Добавьте логирование в ваши хуки, чтобы увидеть, какие
permissionDecisionReasonони возвращают - Проверьте, что паттерны matcher не слишком широкие: пустой matcher соответствует всем инструментам
Измененные входные данные не применяются
-
Убедитесь, что
updatedInputнаходится внутриhookSpecificOutput, а не на верхнем уровне: -
Не объединяйте
updatedInputсpermissionDecision: 'defer', так как это отбрасывает измененные входные данные. ОпускатьpermissionDecisionдопустимо: измененные входные данные все равно применяются через обычную оценку разрешений. Вы также можете вернуть'allow'для автоматического одобрения измененных входных данных или'ask', чтобы показать их пользователю для подтверждения -
Включите
hookEventNameвhookSpecificOutput, чтобы указать, к какому типу хука относится вывод
Хуки сессии недоступны в Python
SessionStart и SessionEnd можно зарегистрировать как callback-хуки SDK в TypeScript, но они недоступны в Python SDK, потому что его тип HookEvent их не включает. В Python они доступны только как хуки shell-команд, определенные в файлах настроек, таких как .claude/settings.json. То, какие файлы настроек загружает ваше приложение SDK, зависит от setting_sources или settingSources. Если вы задаете этот параметр, включите источник, который содержит хуки:
client.receive_response() в качестве триггера.
Запросы разрешений субагентов множатся
При порождении нескольких субагентов каждый из них может запрашивать разрешения отдельно для своих собственных вызовов инструментов. Чтобы избежать повторных запросов, используйте хукиPreToolUse для автоматического одобрения конкретных инструментов или настройте правила разрешений, которые субагенты наследуют от родительского диалога.
Рекурсивные циклы хуков с субагентами
ХукUserPromptSubmit, который порождает субагентов, может создать бесконечные циклы, если эти субагенты вызывают срабатывание того же хука. Чтобы предотвратить это:
- Используйте общую переменную или состояние сессии для отслеживания того, находитесь ли вы уже внутри субагента
- Ограничьте хуки, чтобы они запускались только для сессии агента верхнего уровня
systemMessage не появляется в выводе
ПолеsystemMessage показывает сообщение пользователю, а не модели. В Claude Code версии 2.1.227 или новее systemMessage хука может появиться в потоке сообщений как SDKInformationalMessage. Появится ли оно, зависит от события. В разделе каждого события на странице хуков описано, как выводится результат. Чтобы вместо этого передать контекст модели, верните additionalContext.
До версии 2.1.227 SDK выводил выходные данные хука в поток сообщений только для хуков SessionStart и Setup. Для любого другого события выходные данные появлялись только в событиях жизненного цикла, которые добавляет includeHookEvents (include_hook_events в Python). В описании этого параметра указано, какие события жизненного цикла порождает каждое событие хука.
Если вам нужно надежно передавать решения хуков в ваше приложение, логируйте их отдельно или используйте выделенный канал вывода.
Связанные ресурсы
- Справочник hooks Claude Code: полные схемы входных/выходных данных JSON, документация событий и паттерны matcher
- Руководство hooks Claude Code: примеры hooks команд shell и пошаговые инструкции
- Справочник TypeScript SDK: типы hooks, определения входных/выходных данных и параметры конфигурации
- Справочник Python SDK: типы hooks, определения входных/выходных данных и параметры конфигурации
- Разрешения: контролируйте, что может делать ваш агент
- Пользовательские инструменты: создавайте инструменты для расширения возможностей агента