Loading...
    • Руководство разработчика
    • Справочник API
    • MCP
    • Ресурсы
    • Примечания к выпуску
    Search...
    ⌘K
    Первые шаги
    Введение в ClaudeБыстрый старт
    Модели и цены
    Обзор моделейВыбор моделиЧто нового в Claude 4.5Миграция на Claude 4.5Устаревшие моделиЦены
    Разработка с Claude
    Обзор функцийИспользование Messages APIКонтекстные окнаЛучшие практики промптинга
    Возможности
    Кеширование промптовРедактирование контекстаРасширенное мышлениеУсилиеПотоковая передача сообщенийПакетная обработкаЦитированияМногоязычная поддержкаПодсчет токеновEmbeddingsЗрениеПоддержка PDFFiles APIРезультаты поискаСтруктурированные выходные данные
    Инструменты
    ОбзорКак реализовать использование инструментовПотоковая передача инструментов с детализациейИнструмент BashИнструмент выполнения кодаПрограммное вызывание инструментовИнструмент управления компьютеромИнструмент текстового редактораИнструмент веб-выборкиИнструмент веб-поискаИнструмент памятиИнструмент поиска инструментов
    Agent Skills
    ОбзорБыстрый стартЛучшие практикиИспользование Skills с API
    Agent SDK
    ОбзорБыстрый стартTypeScript SDKTypeScript V2 (предпросмотр)Python SDKРуководство по миграции
    Потоковый вводОбработка разрешенийУправление выполнением с помощью хуковУправление сеансамиСтруктурированные выходные данные в SDKРазмещение Agent SDKБезопасное развертывание AI-агентовИзменение системных промптовMCP в SDKПользовательские инструментыПодагенты в SDKКосые команды в SDKAgent Skills в SDKОтслеживание затрат и использованияСписки задачПлагины в SDK
    MCP в API
    MCP коннекторУдаленные MCP серверы
    Claude на платформах третьих сторон
    Amazon BedrockMicrosoft FoundryVertex AI
    Инженерия промптов
    ОбзорГенератор промптовИспользование шаблонов промптовУлучшитель промптовБудьте ясны и прямолинейныИспользуйте примеры (многошаговый промптинг)Дайте Claude подумать (CoT)Используйте XML тегиДайте Claude роль (системные промпты)Предзаполните ответ ClaudeЦепочка сложных промптовСоветы по длинному контекстуСоветы по расширенному мышлению
    Тестирование и оценка
    Определение критериев успехаРазработка тестовых случаевИспользование инструмента оценкиСнижение задержки
    Усиление защиты
    Снижение галлюцинацийПовышение согласованности выходных данныхСмягчение взломовПотоковые отказыСнижение утечки промптовДержите Claude в образе
    Администрирование и мониторинг
    Обзор Admin APIAPI использования и затратClaude Code Analytics API
    Console
    Log in
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Partners

    • Amazon Bedrock
    • Google Cloud's Vertex AI

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Help and security

    • Availability
    • Status
    • Support
    • Discord

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy
    Руководства

    Перехват и управление поведением агента с помощью hooks

    Перехватывайте и настраивайте поведение агента в ключевых точках выполнения с помощью hooks
    • Доступные hooks
    • Распространенные случаи использования
    • Настройка hooks
    • Сопоставители
    • Входные данные функции обратного вызова
    • Входные данные
    • Выходные данные обратного вызова
    • Обработка продвинутых сценариев
    • Цепочка нескольких hooks
    • Сопоставители, специфичные для инструмента, с регулярными выражениями
    • Отслеживание активности подагента
    • Асинхронные операции в hooks
    • Отправка уведомлений (только TypeScript)
    • Исправление распространенных проблем
    • Hook не срабатывает
    • Сопоставитель не фильтрует как ожидается
    • Тайм-аут hook
    • Инструмент заблокирован неожиданно
    • Измененный входной сигнал не применяется
    • Hooks сеанса недоступны
    • Запросы разрешений подагента умножаются
    • Рекурсивные циклы hook с подагентами
    • systemMessage не отображается в выходных данных
    • Узнать больше

    Hooks позволяют вам перехватывать выполнение агента в ключевых точках для добавления валидации, логирования, элементов управления безопасностью или пользовательской логики. С помощью hooks вы можете:

    • Блокировать опасные операции перед их выполнением, такие как деструктивные команды shell или несанкционированный доступ к файлам
    • Логировать и аудировать каждый вызов инструмента для соответствия, отладки или аналитики
    • Преобразовывать входные и выходные данные для санитизации данных, внедрения учетных данных или перенаправления путей файлов
    • Требовать одобрение человека для чувствительных действий, таких как запись в базу данных или вызовы API
    • Отслеживать жизненный цикл сеанса для управления состоянием, очистки ресурсов или отправки уведомлений

    Hook состоит из двух частей:

    1. Функция обратного вызова: логика, которая выполняется при срабатывании hook
    2. Конфигурация hook: указывает SDK, какое событие перехватывать (например, PreToolUse) и какие инструменты сопоставлять

    Следующий пример блокирует агента от изменения файлов .env. Сначала определите обратный вызов, который проверяет путь файла, затем передайте его в query() для запуска перед любым вызовом инструмента Write или Edit:

    Это hook PreToolUse. Он выполняется перед выполнением инструмента и может блокировать или разрешать операции на основе вашей логики. Остальная часть этого руководства охватывает все доступные hooks, их параметры конфигурации и шаблоны для распространенных случаев использования.

    Доступные hooks

    SDK предоставляет hooks для различных этапов выполнения агента. Некоторые hooks доступны в обоих SDK, в то время как другие доступны только в TypeScript, потому что Python SDK их не поддерживает.

    Hook EventPython SDKTypeScript SDKЧто его запускаетПример использования
    PreToolUseДаДаЗапрос вызова инструмента (может блокировать или изменять)Блокировать опасные команды shell
    PostToolUseДаДаРезультат выполнения инструментаЛогировать все изменения файлов в журнал аудита
    PostToolUseFailureНетДаОшибка выполнения инструментаОбработать или логировать ошибки инструмента
    UserPromptSubmitДаДаОтправка пользовательского запросаВнедрить дополнительный контекст в запросы

    Распространенные случаи использования

    Hooks достаточно гибкие для обработки многих различных сценариев. Вот некоторые из наиболее распространенных шаблонов, организованные по категориям.

    Настройка hooks

    Чтобы настроить hook для вашего агента, передайте hook в параметр options.hooks при вызове query():

    async for message in query(
        prompt="Your prompt",
        options=ClaudeAgentOptions(
            hooks={
                'PreToolUse': [HookMatcher(matcher='Bash', hooks=[my_callback])]
            }
        )
    ):
        print(message)

    Опция hooks — это словарь (Python) или объект (TypeScript), где:

    • Ключи — это имена событий hook (например, 'PreToolUse', 'PostToolUse', 'Stop')
    • Значения — это массивы сопоставителей, каждый содержащий необязательный шаблон фильтра и ваши функции обратного вызова

    Ваши функции обратного вызова hook получают входные данные о событии и возвращают ответ, чтобы агент знал, разрешить, блокировать или изменить операцию.

    Сопоставители

    Используйте сопоставители для фильтрации, какие инструменты запускают ваши обратные вызовы:

    ОпцияТипПо умолчаниюОписание
    matcherstringundefinedШаблон регулярного выражения для сопоставления имен инструментов. Встроенные инструменты включают Bash, Read, Write, Edit, Glob, Grep, WebFetch, Task и другие. Инструменты MCP используют шаблон mcp__<server>__<action>.
    hooksHookCallback[]

    Используйте шаблон matcher для нацеливания на конкретные инструменты, когда это возможно. Сопоставитель с 'Bash' выполняется только для команд Bash, в то время как опущение шаблона запускает ваши обратные вызовы для каждого вызова инструмента. Обратите внимание, что сопоставители фильтруют только по имени инструмента, а не по путям файлов или другим аргументам — для фильтрации по пути файла проверьте tool_input.file_path внутри вашего обратного вызова.

    Сопоставители применяются только к hooks на основе инструментов (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest). Для hooks жизненного цикла, таких как Stop, SessionStart и Notification, сопоставители игнорируются и hook срабатывает для всех событий этого типа.

    Обнаружение имен инструментов: Проверьте массив tools в начальном системном сообщении при запуске вашего сеанса или добавьте hook без сопоставителя для логирования всех вызовов инструментов.

    Именование инструментов MCP: Инструменты MCP всегда начинаются с mcp__, за которым следует имя сервера и действие: mcp__<server>__<action>. Например, если вы настроите сервер с именем playwright, его инструменты будут названы mcp__playwright__browser_screenshot, mcp__playwright__browser_click и т. д. Имя сервера берется из ключа, который вы используете в конфигурации mcpServers.

    Этот пример использует сопоставитель для запуска hook только для инструментов, изменяющих файлы, при срабатывании события PreToolUse:

    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(matcher='Write|Edit', hooks=[validate_file_path])
            ]
        }
    )

    Входные данные функции обратного вызова

    Каждый hook обратного вызова получает три аргумента:

    1. Входные данные (dict / HookInput): Детали события. См. входные данные для полей
    2. ID использования инструмента (str | None / string | null): Коррелировать события PreToolUse и PostToolUse
    3. Контекст (HookContext): В TypeScript содержит свойство signal (AbortSignal) для отмены. Передайте это асинхронным операциям, таким как fetch(), чтобы они автоматически отменялись, если hook истечет. В Python этот аргумент зарезервирован для будущего использования.

    Входные данные

    Первый аргумент вашего hook обратного вызова содержит информацию о событии. Имена полей идентичны в SDK (оба используют snake_case).

    Общие поля, присутствующие во всех типах hooks:

    ПолеТипОписание
    hook_event_namestringТип hook (PreToolUse, PostToolUse и т. д.)
    session_idstringТекущий идентификатор сеанса
    transcript_pathstringПуть к стенограмме разговора
    cwdstringТекущий рабочий каталог

    Поля, специфичные для hook, варьируются в зависимости от типа hook. Элементы, отмеченные TS, доступны только в TypeScript SDK:

    ПолеТипОписаниеHooks
    tool_namestringИмя вызываемого инструментаPreToolUse, PostToolUse, PostToolUseFailureTS, PermissionRequestTS
    tool_inputobjectАргументы, передаваемые инструментуPreToolUse, PostToolUse, PostToolUseFailureTS, PermissionRequestTS
    tool_responseanyРезультат, возвращенный из выполнения инструментаPostToolUse
    errorstring

    Код ниже определяет hook обратного вызова, который использует tool_name и tool_input для логирования деталей каждого вызова инструмента:

    Выходные данные обратного вызова

    Ваша функция обратного вызова возвращает объект, который сообщает SDK, как продолжить. Верните пустой объект {} для разрешения операции без изменений. Чтобы блокировать, изменять или добавлять контекст к операции, верните объект с полем hookSpecificOutput, содержащим ваше решение.

    Поля верхнего уровня (вне hookSpecificOutput):

    ПолеТипОписание
    continuebooleanДолжен ли агент продолжить после этого hook (по умолчанию: true)
    stopReasonstringСообщение, показываемое при continue равном false
    suppressOutputbooleanСкрыть stdout из стенограммы (по умолчанию: false)
    systemMessagestring

    Поля внутри hookSpecificOutput:

    ПолеТипHooksОписание
    hookEventNamestringВсеОбязательно. Используйте input.hook_event_name для сопоставления текущего события
    permissionDecision'allow' | 'deny' | 'ask'PreToolUseКонтролирует, выполняется ли инструмент
    permissionDecisionReasonstringPreToolUseОбъяснение, показываемое Claude для решения
    updatedInput

    Этот пример блокирует операции записи в каталог /etc при внедрении системного сообщения для напоминания Claude о безопасных практиках работы с файлами:

    Поток принятия решения о разрешении

    Когда применяются несколько hooks или правил разрешений, SDK оценивает их в этом порядке:

    1. Правила Deny проверяются первыми (любое совпадение = немедленный отказ).
    2. Правила Ask проверяются вторыми.
    3. Правила Allow проверяются третьими.
    4. По умолчанию Ask, если ничего не совпадает.

    Если какой-либо hook возвращает deny, операция блокируется — другие hooks, возвращающие allow, не переопределят это.

    Блокировать инструмент

    Верните решение deny для предотвращения выполнения инструмента:

    Изменить входной сигнал инструмента

    Верните обновленный входной сигнал для изменения того, что получает инструмент:

    При использовании updatedInput вы также должны включить permissionDecision. Всегда возвращайте новый объект вместо мутирования исходного tool_input.

    Добавить системное сообщение

    Внедрите контекст в разговор:

    async def add_security_reminder(input_data, tool_use_id, context):
        return {
            'systemMessage': 'Remember to follow security best practices.'
        }

    Автоматически одобрять конкретные инструменты

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

    Поле permissionDecision принимает три значения: 'allow' (автоматическое одобрение), 'deny' (блокировка) или 'ask' (запрос подтверждения).

    Обработка продвинутых сценариев

    Эти шаблоны помогут вам создавать более сложные системы hooks для сложных случаев использования.

    Цепочка нескольких hooks

    Hooks выполняются в порядке их появления в массиве. Сосредоточьте каждый hook на одной ответственности и свяжите несколько hooks для сложной логики. Этот пример запускает все четыре hook для каждого вызова инструмента (сопоставитель не указан):

    Сопоставители, специфичные для инструмента, с регулярными выражениями

    Используйте шаблоны регулярных выражений для сопоставления нескольких инструментов:

    Сопоставители сопоставляют только имена инструментов, а не пути файлов или другие аргументы. Для фильтрации по пути файла проверьте tool_input.file_path внутри вашего hook обратного вызова.

    Отслеживание активности подагента

    Используйте hooks SubagentStop для мониторинга завершения подагента. tool_use_id помогает коррелировать вызовы родительского агента с их подагентами:

    Асинхронные операции в hooks

    Hooks могут выполнять асинхронные операции, такие как HTTP-запросы. Обрабатывайте ошибки корректно, перехватывая исключения вместо их выброса. В TypeScript передайте signal в fetch(), чтобы запрос отменялся, если hook истечет:

    Отправка уведомлений (только TypeScript)

    Используйте hooks Notification для получения обновлений статуса от агента и их перенаправления во внешние сервисы, такие как Slack или панели мониторинга:

    TypeScript
    import { query, HookCallback, NotificationHookInput } from "@anthropic-ai/claude-agent-sdk";
    
    const notificationHandler: HookCallback = async (input, toolUseID, { signal }) => {
      const notification = input as NotificationHookInput;
    
      await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
        method: 'POST',
        body: JSON.stringify({
          text: `Agent status: ${notification.message}`
        }),
        signal
      });
    
      return {};
    };
    
    for await (const message of query({
      prompt: "Analyze this codebase",
      options: {
        hooks: {
          Notification: [{ hooks: [notificationHandler] }]
        }
      }
    })) {
      console.log(message);
    }

    Исправление распространенных проблем

    Этот раздел охватывает распространенные проблемы и способы их решения.

    Hook не срабатывает

    • Проверьте, что имя события hook правильное и чувствительно к регистру (PreToolUse, а не preToolUse)
    • Проверьте, что ваш шаблон сопоставителя точно совпадает с именем инструмента
    • Убедитесь, что hook находится под правильным типом события в options.hooks
    • Для hooks SubagentStop, Stop, SessionStart, SessionEnd и Notification сопоставители игнорируются. Эти hooks срабатывают для всех событий этого типа.
    • Hooks могут не срабатывать, когда агент достигает лимита max_turns, потому что сеанс завершается перед выполнением hooks

    Сопоставитель не фильтрует как ожидается

    Сопоставители сопоставляют только имена инструментов, а не пути файлов или другие аргументы. Для фильтрации по пути файла проверьте tool_input.file_path внутри вашего hook:

    const myHook: HookCallback = async (input, toolUseID, { signal }) => {
      const preInput = input as PreToolUseHookInput;
      const filePath = preInput.tool_input?.file_path as string;
      if (!filePath?.endsWith('.md')) return {};  // Skip non-markdown files
      // Process markdown files...
    };

    Тайм-аут hook

    • Увеличьте значение timeout в конфигурации HookMatcher
    • Используйте AbortSignal из третьего аргумента обратного вызова для корректной обработки отмены в TypeScript

    Инструмент заблокирован неожиданно

    • Проверьте все hooks PreToolUse на возвращение permissionDecision: 'deny'
    • Добавьте логирование в ваши hooks, чтобы увидеть, какие permissionDecisionReason они возвращают
    • Проверьте, что шаблоны сопоставителей не слишком широкие (пустой сопоставитель совпадает со всеми инструментами)

    Измененный входной сигнал не применяется

    • Убедитесь, что updatedInput находится внутри hookSpecificOutput, а не на верхнем уровне:

      return {
        hookSpecificOutput: {
          hookEventName: input.hook_event_name,
          permissionDecision: 'allow',
          updatedInput: { command: 'new command' }
        }
      };
    • Вы также должны вернуть permissionDecision: 'allow' для применения изменения входного сигнала

    Hooks сеанса недоступны

    Hooks SessionStart, SessionEnd и Notification доступны только в TypeScript SDK. Python SDK не поддерживает эти события из-за ограничений установки.

    Запросы разрешений подагента умножаются

    При порождении нескольких подагентов каждый может запросить разрешения отдельно. Подагенты не наследуют автоматически разрешения родительского агента. Чтобы избежать повторяющихся запросов, используйте hooks PreToolUse для автоматического одобрения конкретных инструментов или настройте правила разрешений, которые применяются к сеансам подагентов.

    Рекурсивные циклы hook с подагентами

    Hook UserPromptSubmit, который порождает подагентов, может создать бесконечные циклы, если эти подагенты запускают тот же hook. Чтобы предотвратить это:

    • Проверьте наличие индикатора подагента во входных данных hook перед порождением
    • Используйте поле parent_tool_use_id для обнаружения, находитесь ли вы уже в контексте подагента
    • Ограничьте hooks для запуска только для сеанса агента верхнего уровня

    systemMessage не отображается в выходных данных

    Поле systemMessage добавляет контекст в разговор, который видит модель, но оно может не отображаться во всех режимах вывода SDK. Если вам нужно отобразить решения hook вашему приложению, логируйте их отдельно или используйте выделенный канал вывода.

    Узнать больше

    • Разрешения: контролируйте, что может делать ваш агент
    • Пользовательские инструменты: создавайте инструменты для расширения возможностей агента
    • Справочник TypeScript SDK
    • Справочник Python SDK
    import asyncio
    from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher
    
    # Define a hook callback that receives tool call details
    async def protect_env_files(input_data, tool_use_id, context):
        # Extract the file path from the tool's input arguments
        file_path = input_data['tool_input'].get('file_path', '')
        file_name = file_path.split('/')[-1]
    
        # Block the operation if targeting a .env file
        if file_name == '.env':
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Cannot modify .env files'
                }
            }
    
        # Return empty object to allow the operation
        return {}
    
    async def main():
        async for message in query(
            prompt="Update the database configuration",
            options=ClaudeAgentOptions(
                hooks={
                    # Register the hook for PreToolUse events
                    # The matcher filters to only Write and Edit tool calls
                    'PreToolUse': [HookMatcher(matcher='Write|Edit', hooks=[protect_env_files])]
                }
            )
        ):
            print(message)
    
    asyncio.run(main())
    StopДаДаОстановка выполнения агентаСохранить состояние сеанса перед выходом
    SubagentStartНетДаИнициализация подагентаОтслеживать порождение параллельных задач
    SubagentStopДаДаЗавершение подагентаАгрегировать результаты из параллельных задач
    PreCompactДаДаЗапрос компактирования разговораАрхивировать полную стенограмму перед суммированием
    PermissionRequestНетДаДиалог разрешения будет отображенПользовательская обработка разрешений
    SessionStartНетДаИнициализация сеансаИнициализировать логирование и телеметрию
    SessionEndНетДаЗавершение сеансаОчистить временные ресурсы
    NotificationНетДаСообщения о статусе агентаОтправить обновления статуса агента в Slack или PagerDuty
    -
    Обязательно. Массив функций обратного вызова для выполнения при совпадении шаблона
    timeoutnumber60Тайм-аут в секундах; увеличьте для hooks, которые выполняют внешние вызовы API
    Сообщение об ошибке из ошибки выполнения инструмента
    PostToolUseFailureTS
    is_interruptbooleanБыла ли ошибка вызвана прерываниемPostToolUseFailureTS
    promptstringТекст запроса пользователяUserPromptSubmit
    stop_hook_activebooleanОбрабатывается ли в данный момент stop hookStop, SubagentStop
    agent_idstringУникальный идентификатор подагентаSubagentStartTS, SubagentStopTS
    agent_typestringТип/роль подагентаSubagentStartTS
    agent_transcript_pathstringПуть к стенограмме разговора подагентаSubagentStopTS
    triggerstringЧто вызвало компактирование: manual или autoPreCompact
    custom_instructionsstringПользовательские инструкции, предоставленные для компактированияPreCompact
    permission_suggestionsarrayПредлагаемые обновления разрешений для инструментаPermissionRequestTS
    sourcestringКак был запущен сеанс: startup, resume, clear или compactSessionStartTS
    reasonstringПочему сеанс завершился: clear, logout, prompt_input_exit, bypass_permissions_disabled или otherSessionEndTS
    messagestringСообщение о статусе от агентаNotificationTS
    notification_typestringТип уведомления: permission_prompt, idle_prompt, auth_success или elicitation_dialogNotificationTS
    titlestringНеобязательный заголовок, установленный агентомNotificationTS
    async def log_tool_calls(input_data, tool_use_id, context):
        if input_data['hook_event_name'] == 'PreToolUse':
            print(f"Tool: {input_data['tool_name']}")
            print(f"Input: {input_data['tool_input']}")
        return {}
    Сообщение, внедренное в разговор для Claude
    object
    PreToolUse
    Измененный входной сигнал инструмента (требует permissionDecision: 'allow')
    additionalContextstringPostToolUse, UserPromptSubmit, SessionStartTS, SubagentStartTSКонтекст, добавленный в разговор
    async def block_etc_writes(input_data, tool_use_id, context):
        file_path = input_data['tool_input'].get('file_path', '')
    
        if file_path.startswith('/etc'):
            return {
                # Top-level field: inject guidance into the conversation
                'systemMessage': 'Remember: system directories like /etc are protected.',
                # hookSpecificOutput: block the operation
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Writing to /etc is not allowed'
                }
            }
        return {}
    async def block_dangerous_commands(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        command = input_data['tool_input'].get('command', '')
    
        if 'rm -rf /' in command:
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Dangerous command blocked: rm -rf /'
                }
            }
        return {}
    async def redirect_to_sandbox(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        if input_data['tool_name'] == 'Write':
            original_path = input_data['tool_input'].get('file_path', '')
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'allow',
                    'updatedInput': {
                        **input_data['tool_input'],
                        'file_path': f'/sandbox{original_path}'
                    }
                }
            }
        return {}
    async def auto_approve_read_only(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        read_only_tools = ['Read', 'Glob', 'Grep', 'LS']
        if input_data['tool_name'] in read_only_tools:
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'allow',
                    'permissionDecisionReason': 'Read-only tool auto-approved'
                }
            }
        return {}
    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(hooks=[rate_limiter]),        # First: check rate limits
                HookMatcher(hooks=[authorization_check]), # Second: verify permissions
                HookMatcher(hooks=[input_sanitizer]),     # Third: sanitize inputs
                HookMatcher(hooks=[audit_logger])         # Last: log the action
            ]
        }
    )
    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                # Match file modification tools
                HookMatcher(matcher='Write|Edit|Delete', hooks=[file_security_hook]),
    
                # Match all MCP tools
                HookMatcher(matcher='^mcp__', hooks=[mcp_audit_hook]),
    
                # Match everything (no matcher)
                HookMatcher(hooks=[global_logger])
            ]
        }
    )
    async def subagent_tracker(input_data, tool_use_id, context):
        if input_data['hook_event_name'] == 'SubagentStop':
            print(f"[SUBAGENT] Completed")
            print(f"  Tool use ID: {tool_use_id}")
            print(f"  Stop hook active: {input_data.get('stop_hook_active')}")
        return {}
    
    options = ClaudeAgentOptions(
        hooks={
            'SubagentStop': [HookMatcher(hooks=[subagent_tracker])]
        }
    )
    import aiohttp
    from datetime import datetime
    
    async def webhook_notifier(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PostToolUse':
            return {}
    
        try:
            async with aiohttp.ClientSession() as session:
                await session.post(
                    'https://api.example.com/webhook',
                    json={
                        'tool': input_data['tool_name'],
                        'timestamp': datetime.now().isoformat()
                    }
                )
        except Exception as e:
            print(f'Webhook request failed: {e}')
    
        return {}

    Включите hookEventName в hookSpecificOutput для идентификации типа hook для выходных данных