---
read_when:
    - Вы создаете plugin, которому нужны хуки before_tool_call, before_agent_reply, хуки сообщений или хуки жизненного цикла
    - Вам нужно блокировать, переписывать или требовать одобрения для вызовов инструментов от Plugin
    - Вы выбираете между внутренними хуками и хуками Plugin
summary: 'Plugin hooks: перехватывайте события жизненного цикла агента, инструмента, сообщения, сессии и Gateway'
title: Plugin hooks
x-i18n:
    generated_at: "2026-06-28T23:17:41Z"
    model: gpt-5.5
    postprocess_version: locale-links-v1
    provider: openai
    source_hash: 6c2db0963c85d15fd391fb575f981992ffd6d77c098bd78cac08be390caea931
    source_path: plugins/hooks.md
    workflow: 16
---

Хуки Plugin — это точки расширения внутри процесса для Plugin'ов OpenClaw. Используйте их,
когда Plugin должен проверять или изменять запуски агентов, вызовы инструментов, поток сообщений,
жизненный цикл сессий, маршрутизацию субагентов, установки или запуск Gateway.

Вместо этого используйте [внутренние хуки](/ru/automation/hooks), когда нужен небольшой
устанавливаемый оператором скрипт `HOOK.md` для командных событий и событий Gateway, таких как
`/new`, `/reset`, `/stop`, `agent:bootstrap` или `gateway:startup`.

## Быстрый старт

Регистрируйте типизированные хуки Plugin через `api.on(...)` из точки входа Plugin:

```typescript
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";

export default definePluginEntry({
  id: "tool-preflight",
  name: "Tool Preflight",
  register(api) {
    api.on(
      "before_tool_call",
      async (event) => {
        if (event.toolName !== "web_search") {
          return;
        }

        return {
          requireApproval: {
            title: "Run web search",
            description: `Allow search query: ${String(event.params.query ?? "")}`,
            severity: "info",
            timeoutMs: 60_000,
            timeoutBehavior: "deny",
          },
        };
      },
      { priority: 50 },
    );
  },
});
```

Обработчики хуков выполняются последовательно в порядке убывания `priority`. Хуки с одинаковым приоритетом
сохраняют порядок регистрации.

`api.on(name, handler, opts?)` принимает:

- `priority` — порядок обработчиков (большее значение выполняется раньше).
- `timeoutMs` — необязательный бюджет на хук. Когда он задан, раннер хуков прерывает этот
  обработчик после истечения бюджета и продолжает со следующим, вместо того чтобы
  позволять медленной настройке или извлечению из памяти расходовать настроенный вызывающей стороной тайм-аут модели.
  Не указывайте его, чтобы использовать стандартный тайм-аут наблюдения/решения, который
  раннер хуков применяет обобщенно.

Операторы также могут задавать бюджеты хуков без правки кода Plugin:

```json
{
  "plugins": {
    "entries": {
      "my-plugin": {
        "hooks": {
          "timeoutMs": 30000,
          "timeouts": {
            "before_prompt_build": 90000,
            "agent_end": 60000
          }
        }
      }
    }
  }
}
```

`hooks.timeouts.<hookName>` переопределяет `hooks.timeoutMs`, который переопределяет
значение `api.on(..., { timeoutMs })`, заданное автором Plugin. Каждое настроенное значение должно
быть положительным целым числом не больше 600000 миллисекунд. Предпочитайте переопределения
для отдельных хуков, когда известны медленные хуки, чтобы один Plugin не получал увеличенный бюджет
везде.

Каждый хук получает `event.context.pluginConfig` — разрешенную конфигурацию для
Plugin, зарегистрировавшего этот обработчик. Используйте ее для решений хуков, которым нужны
текущие параметры Plugin; OpenClaw внедряет ее отдельно для каждого обработчика, не изменяя
общий объект события, видимый другим Plugin'ам.

## Каталог хуков

Хуки сгруппированы по поверхности, которую они расширяют. Имена, выделенные **жирным**, принимают
результат-решение (блокировка, отмена, переопределение или запрос подтверждения); все остальные
предназначены только для наблюдения.

**Ход агента**

- `before_model_resolve` — переопределить провайдера или модель до загрузки сообщений сессии
- `agent_turn_prepare` — обработать поставленные в очередь вставки хода Plugin и добавить контекст того же хода перед хуками промпта
- `before_prompt_build` — добавить динамический контекст или текст системного промпта перед вызовом модели
- `before_agent_start` — объединенная фаза только для совместимости; предпочитайте два хука выше
- **`before_agent_run`** — проверить финальный промпт и сообщения сессии перед отправкой в модель и при необходимости заблокировать запуск
- **`before_agent_reply`** — досрочно завершить ход модели синтетическим ответом или молчанием
- **`before_agent_finalize`** — проверить естественный финальный ответ и запросить еще один проход модели
- `agent_end` — наблюдать финальные сообщения, состояние успеха и длительность запуска
- `heartbeat_prompt_contribution` — добавить контекст только для Heartbeat для фонового монитора и Plugin'ов жизненного цикла

**Наблюдение за разговором**

- `model_call_started` / `model_call_ended` — наблюдать очищенные метаданные вызова провайдера/модели, тайминг, результат и ограниченные хэши идентификатора запроса без содержимого промпта или ответа
- `llm_input` — наблюдать вход провайдера (системный промпт, промпт, историю)
- `llm_output` — наблюдать выход провайдера, использование и разрешенный `contextTokenBudget`, когда он доступен

**Инструменты**

- **`before_tool_call`** — переписать параметры инструмента, заблокировать выполнение или запросить подтверждение
- `after_tool_call` — наблюдать результаты инструмента, ошибки и длительность
- `resolve_exec_env` — добавить переменные окружения, принадлежащие Plugin, в `exec`
- **`tool_result_persist`** — переписать сообщение ассистента, созданное из результата инструмента
- **`before_message_write`** — проверить или заблокировать выполняющуюся запись сообщения (редко)

**Сообщения и доставка**

- **`inbound_claim`** — забрать входящее сообщение до маршрутизации агенту (синтетические ответы)
- `message_received` — наблюдать входящее содержимое, отправителя, поток и метаданные
- **`message_sending`** — переписать исходящее содержимое или отменить доставку
- **`reply_payload_sending`** — изменить или отменить нормализованную полезную нагрузку ответа перед доставкой
- `message_sent` — наблюдать успех или сбой исходящей доставки
- **`before_dispatch`** — проверить или переписать исходящую отправку перед передачей каналу
- **`reply_dispatch`** — участвовать в финальном конвейере отправки ответа

**Сессии и Compaction**

- `session_start` / `session_end` — отслеживать границы жизненного цикла сессии. `reason` события имеет одно из значений: `new`, `reset`, `idle`, `daily`, `compaction`, `deleted`, `shutdown`, `restart` или `unknown`. Значения `shutdown` и `restart` срабатывают из финализатора завершения Gateway, когда процесс остановлен или перезапущен, пока сессии еще активны, чтобы нижестоящие Plugin'ы (например, хранилища памяти или расшифровок) могли финализировать призрачные строки, которые иначе остались бы в открытом состоянии между перезапусками. Финализатор ограничен по времени, поэтому медленный Plugin не может заблокировать SIGTERM/SIGINT.
- `before_compaction` / `after_compaction` — наблюдать или аннотировать циклы Compaction
- `before_reset` — наблюдать события сброса сессии (`/reset`, программные сбросы)

**Субагенты**

- `subagent_spawned` / `subagent_ended` — наблюдать запуск и завершение субагента.
- `subagent_delivery_target` — хук совместимости для доставки завершения, когда базовая привязка сессии ядра не может спроецировать маршрут.
- `subagent_spawning` — устаревший хук совместимости. Теперь ядро подготавливает привязки субагентов `thread: true` через адаптеры привязки сессий каналов до срабатывания `subagent_spawned`.
- `subagent_spawned` включает `resolvedModel` и `resolvedProvider`, когда OpenClaw разрешил нативную модель дочерней сессии перед запуском.
- `subagent_ended` несет `targetSessionKey` (идентичность — это соответствует `subagent_spawned.childSessionKey`), `targetKind` (`"subagent"` или `"acp"`), `reason`, необязательный `outcome` (`"ok"`, `"error"`, `"timeout"`, `"killed"`, `"reset"` или `"deleted"`), необязательные `error`, `runId`, `endedAt`, `accountId` и `sendFarewell`. Он **не** включает `agentId` или `childSessionKey`; используйте `targetSessionKey`, чтобы сопоставить его с соответствующим событием `subagent_spawned`.

**Жизненный цикл**

- `gateway_start` / `gateway_stop` — запускать или останавливать сервисы, принадлежащие Plugin, вместе с Gateway
- `deactivate` — устаревший псевдоним совместимости для `gateway_stop`; используйте `gateway_stop` в новых Plugin'ах
- `cron_changed` — наблюдать изменения жизненного цикла Cron, принадлежащего Gateway (добавлен, обновлен, удален, запущен, завершен, запланирован)
- **`before_install`** — проверить подготовленные материалы установки Skills или Plugin из загруженного
  рантайма Plugin

## Отладка хуков рантайма

Используйте `before_model_resolve`, когда Plugin должен переключить провайдера или модель
для хода агента. Он выполняется до разрешения модели; `llm_output` выполняется только после того,
как попытка модели создает выход ассистента.

Чтобы подтвердить эффективную модель сессии, проверьте регистрации рантайма, затем
используйте `openclaw sessions` или поверхности сессий/статуса Gateway. При отладке
полезных нагрузок провайдера запускайте Gateway с `--raw-stream` и
`--raw-stream-path <path>`; эти флаги записывают сырые события потока модели в jsonl
файл.

## Политика вызовов инструментов

`before_tool_call` получает:

- `event.toolName`
- `event.params`
- необязательные `event.toolKind` и `event.toolInputKind` — авторитетные со стороны хоста
  дискриминаторы для инструментов, которые намеренно имеют общие имена; например, внешние
  вызовы `exec` в режиме кода используют `toolKind: "code_mode_exec"` и
  включают `toolInputKind: "javascript" | "typescript"`, когда язык входных данных
  известен
- необязательный `event.derivedPaths`, содержащий best-effort подсказки целевых путей,
  выведенные хостом, для известных оболочек инструментов, таких как `apply_patch`; при наличии
  эти пути могут быть неполными или могут завышать то, чего инструмент
  фактически коснется (например, при некорректных или частичных входных данных)
- необязательный `event.runId`
- необязательный `event.toolCallId`
- поля контекста, такие как `ctx.agentId`, `ctx.sessionKey`, `ctx.sessionId`,
  `ctx.runId`, `ctx.jobId` (задается для запусков, управляемых Cron), `ctx.toolKind`,
  `ctx.toolInputKind` и диагностический `ctx.trace`

Он может вернуть:

```typescript
type BeforeToolCallResult = {
  params?: Record<string, unknown>;
  block?: boolean;
  blockReason?: string;
  requireApproval?: {
    title: string;
    description: string;
    severity?: "info" | "warning" | "critical";
    timeoutMs?: number;
    timeoutBehavior?: "allow" | "deny";
    allowedDecisions?: Array<"allow-once" | "allow-always" | "deny">;
    pluginId?: string;
    onResolution?: (
      decision: "allow-once" | "allow-always" | "deny" | "timeout" | "cancelled",
    ) => Promise<void> | void;
  };
};
```

Поведение защит хуков для типизированных хуков жизненного цикла:

- `block: true` является терминальным и пропускает обработчики с более низким приоритетом.
- `block: false` трактуется как отсутствие решения.
- `params` переписывает параметры инструмента для выполнения.
- `requireApproval` приостанавливает запуск агента и запрашивает пользователя через подтверждения Plugin.
  Команда `/approve` может подтверждать как exec, так и подтверждения Plugin.
  В нативных ретрансляциях `PreToolUse` режима отчета app-server Codex это откладывается
  до соответствующего запроса подтверждения app-server; см. [рантайм Codex harness](/ru/plugins/codex-harness-runtime#hook-boundaries).
- `block: true` с более низким приоритетом все еще может заблокировать после того, как хук с более высоким приоритетом
  запросил подтверждение.
- `onResolution` получает разрешенное решение подтверждения — `allow-once`,
  `allow-always`, `deny`, `timeout` или `cancelled`.

См. [запросы разрешений Plugin](/ru/plugins/plugin-permission-requests), чтобы узнать о
маршрутизации подтверждений, поведении решений и о том, когда использовать `requireApproval` вместо
необязательных инструментов или подтверждений exec.

Plugin'ы, которым нужна политика уровня хоста, могут регистрировать доверенные политики инструментов через
`api.registerTrustedToolPolicy(...)`. Они выполняются до обычных хуков
`before_tool_call` и до обычных решений хуков. Встроенные доверенные
политики выполняются первыми; доверенные политики установленных Plugin'ов выполняются далее в порядке загрузки
Plugin; обычные хуки `before_tool_call` выполняются после них. Встроенные Plugin'ы сохраняют
существующий путь доверенных политик. Установленные Plugin'ы должны быть явно включены
и объявлять каждый идентификатор политики в `contracts.trustedToolPolicies`; необъявленные идентификаторы
отклоняются до регистрации. Идентификаторы политик ограничены областью регистрирующего
Plugin, поэтому разные Plugin'ы могут повторно использовать один и тот же локальный идентификатор. Используйте этот уровень только
для доверенных хостом ограничителей, таких как политика рабочей области, контроль бюджета или
безопасность зарезервированных рабочих процессов.

### Хук окружения Exec

`resolve_exec_env` позволяет Plugin'ам добавлять переменные окружения в вызовы инструмента `exec`
после построения базового окружения exec и до запуска команды. Он получает:

- `event.sessionKey`
- `event.toolName`, сейчас всегда `"exec"`
- `event.host`, одно из `"gateway"`, `"sandbox"` или `"node"`
- поля контекста, такие как `ctx.agentId`, `ctx.sessionKey`,
  `ctx.messageProvider` и `ctx.channelId`

Верните `Record<string, string>`, чтобы объединить его с окружением exec. Обработчики
выполняются в порядке приоритета, а результаты более поздних хуков переопределяют результаты более ранних хуков для
одного и того же ключа.

Вывод hook фильтруется через политику ключей среды выполнения host перед
объединением. Недопустимые ключи, `PATH` и опасные ключи переопределения host,
такие как `LD_*`, `DYLD_*`, `NODE_OPTIONS`, переменные прокси и переменные
переопределения TLS, отбрасываются. Отфильтрованная среда Plugin включается в
метаданные подтверждения/аудита Gateway и пересылается в запросы выполнения
node-host.

### Сохранение результатов инструментов

Результаты инструментов могут включать структурированные `details` для
рендеринга UI, диагностики, маршрутизации медиа или метаданных, принадлежащих
Plugin. Рассматривайте `details` как метаданные runtime, а не как содержимое
промпта:

- OpenClaw удаляет `toolResult.details` перед повторным воспроизведением у
  провайдера и входом Compaction, чтобы метаданные не стали контекстом модели.
- Сохраненные записи сессий хранят только ограниченные `details`. Слишком
  большие details заменяются компактной сводкой и `persistedDetailsTruncated:
  true`.
- `tool_result_persist` и `before_message_write` выполняются до финального
  ограничения сохранения. Hooks все равно должны держать возвращаемые `details`
  небольшими и не размещать текст, важный для промпта, только в `details`;
  видимый модели вывод инструмента помещайте в `content`.

## Hooks промпта и модели

Используйте hooks, специфичные для фазы, в новых plugins:

- `before_model_resolve`: получает только текущий промпт и метаданные вложений.
  Возвращайте `providerOverride` или `modelOverride`.
- `agent_turn_prepare`: получает текущий промпт, подготовленные сообщения
  сессии и все поставленные в очередь для этой сессии внедрения, которые
  должны быть обработаны ровно один раз и уже извлечены. Возвращайте
  `prependContext` или `appendContext`.
- `before_prompt_build`: получает текущий промпт и сообщения сессии.
  Возвращайте `prependContext`, `appendContext`, `systemPrompt`,
  `prependSystemContext` или `appendSystemContext`.
- `heartbeat_prompt_contribution`: выполняется только для ходов Heartbeat и
  возвращает `prependContext` или `appendContext`. Он предназначен для фоновых
  мониторов, которым нужно суммировать текущее состояние, не изменяя ходы,
  инициированные пользователем.

`before_agent_start` остается для совместимости. Предпочитайте явные hooks выше,
чтобы ваш plugin не зависел от устаревшей объединенной фазы.

`before_agent_run` выполняется после построения промпта и до любого входа
модели, включая загрузку изображений, локальных для промпта, и наблюдение
`llm_input`. Он получает текущий пользовательский ввод как `prompt`, а также
загруженную историю сессии в `messages` и активный системный промпт. Возвращайте
`{ outcome: "block", reason, message? }`, чтобы остановить запуск до того, как
модель сможет прочитать промпт. `reason` является внутренним; `message` — это
замена, видимая пользователю. Поддерживаются только outcomes `pass` и `block`;
неподдерживаемые формы решений завершаются закрытым отказом.

Когда запуск блокируется, OpenClaw сохраняет только текст замены в
`message.content` плюс нечувствительные метаданные блокировки, такие как id
блокирующего plugin и timestamp. Исходный пользовательский текст не сохраняется
в transcript или будущем контексте. Внутренние причины блокировки считаются
чувствительными и исключаются из payloads transcript, history, broadcast, log и
diagnostics. Наблюдаемость должна использовать очищенные поля, такие как id
блокировщика, outcome, timestamp или безопасную категорию.

`before_agent_start` и `agent_end` включают `event.runId`, когда OpenClaw может
идентифицировать активный запуск. То же значение также доступно в `ctx.runId`.
Запуски, управляемые Cron, также раскрывают `ctx.jobId` (id исходного задания
Cron), чтобы hooks plugin могли привязывать метрики, побочные эффекты или
состояние к конкретному запланированному заданию.

Для запусков, исходящих из канала, `ctx.channel` и `ctx.messageProvider`
идентифицируют поверхность провайдера, например `discord` или `telegram`, а
`ctx.channelId` является идентификатором целевой беседы, когда OpenClaw может
вывести его из ключа сессии или метаданных доставки.

Когда доступна идентичность отправителя, контексты hooks агента также включают:

- `ctx.senderId` — ограниченный каналом ID отправителя (например, Feishu
  `open_id`, ID пользователя Discord). Заполняется, когда запуск исходит из
  пользовательского сообщения с известными метаданными отправителя.
- `ctx.chatId` — транспортно-нативный идентификатор беседы (например, Feishu
  `chat_id`, Telegram `chat_id`). Заполняется, когда исходный канал
  предоставляет нативный ID беседы.
- `ctx.channelContext.sender.id` — тот же ID отправителя, что и `ctx.senderId`,
  под объектом, принадлежащим каналу, который plugins могут расширять
  специфичными для канала полями.
- `ctx.channelContext.chat.id` — тот же ID беседы, что и `ctx.chatId`, под
  объектом, принадлежащим каналу, который plugins могут расширять специфичными
  для канала полями.

Core определяет только вложенные поля `id`. Channel plugins, которые передают
более богатые метаданные отправителя или чата через inbound helper, могут
дополнять `PluginHookChannelSenderContext` или `PluginHookChannelChatContext` из
`openclaw/plugin-sdk/channel-inbound`:

```ts
declare module "openclaw/plugin-sdk/channel-inbound" {
  interface PluginHookChannelSenderContext {
    unionId?: string;
    userId?: string;
  }
}
```

Channel plugins передают эти поля через inbound SDK helper:

```ts
buildChannelInboundEventContext({
  // ...
  channelContext: {
    sender: { id: senderOpenId, unionId, userId },
    chat: { id: chatId },
  },
});
```

Эти поля необязательны и отсутствуют для запусков, инициированных системой
(heartbeat, cron, exec-event).

`ctx.senderExternalId` остается устаревшим полем совместимости с исходным кодом
для старых plugins. Core его не заполняет; новые специфичные для канала
идентичности отправителей должны находиться под `ctx.channelContext.sender`
через module augmentation.

`agent_end` — это hook наблюдения. Gateway и пути persistent harness запускают
его fire-and-forget после хода, тогда как краткоживущие one-shot пути CLI ждут
promise hook перед очисткой процесса, чтобы доверенные plugins могли сбросить
терминальную наблюдаемость или захватить состояние. Runner hook применяет
тайм-аут 30 секунд, чтобы зависший plugin или embedding endpoint не мог оставить
promise hook в ожидании навсегда. Тайм-аут логируется, и OpenClaw продолжает
работу; он не отменяет сетевую работу, принадлежащую plugin, если сам plugin не
использует собственный abort signal.

Используйте `model_call_started` и `model_call_ended` для телеметрии вызовов
провайдера, которая не должна получать raw prompts, history, responses, headers,
request bodies или provider request IDs. Эти hooks включают стабильные
метаданные, такие как `runId`, `callId`, `provider`, `model`, необязательные
`api`/`transport`, терминальные `durationMs`/`outcome` и
`upstreamRequestIdHash`, когда OpenClaw может вывести ограниченный hash request
id провайдера. Когда runtime разрешил метаданные context-window, событие hook и
контекст также включают `contextTokenBudget`, эффективный token budget после
ограничений модели/config/agent, а также `contextWindowSource` и
`contextWindowReferenceTokens`, когда был применен более низкий cap.

`before_agent_finalize` выполняется только когда harness собирается принять
естественный финальный ответ ассистента. Это не путь отмены `/stop`, и он не
выполняется, когда пользователь прерывает ход. Возвращайте `{ action: "revise",
reason }`, чтобы запросить у harness еще один проход модели перед финализацией,
`{ action: "finalize", reason? }`, чтобы принудительно финализировать, или
опустите результат, чтобы продолжить. Нативные hooks Codex `Stop` ретранслируются
в этот hook как решения OpenClaw `before_agent_finalize`.

При возврате `action: "revise"` plugins могут включать метаданные `retry`, чтобы
дополнительный проход модели был ограниченным и безопасным для повторного
воспроизведения:

```typescript
type BeforeAgentFinalizeRetry = {
  instruction: string;
  idempotencyKey?: string;
  maxAttempts?: number;
};
```

`instruction` добавляется к причине ревизии, отправляемой в harness.
`idempotencyKey` позволяет host считать retries для одного и того же запроса
plugin среди эквивалентных решений finalize, а `maxAttempts` ограничивает,
сколько дополнительных проходов host разрешит перед продолжением с естественным
финальным ответом.

Не входящие в комплект plugins, которым нужны raw conversation hooks
(`before_model_resolve`, `before_agent_reply`, `llm_input`, `llm_output`,
`before_agent_finalize`, `agent_end` или `before_agent_run`), должны задать:

```json
{
  "plugins": {
    "entries": {
      "my-plugin": {
        "hooks": {
          "allowConversationAccess": true
        }
      }
    }
  }
}
```

Hooks, изменяющие промпт, и долговечные внедрения следующего хода можно
отключить для каждого plugin с помощью
`plugins.entries.<id>.hooks.allowPromptInjection=false`.

### Расширения сессии и внедрения следующего хода

Workflow plugins могут сохранять небольшое JSON-совместимое состояние сессии с
помощью `api.registerSessionExtension(...)` и обновлять его через метод Gateway
`sessions.pluginPatch`. Строки сессии проецируют зарегистрированное состояние
расширения через `pluginExtensions`, позволяя Control UI и другим клиентам
отображать статус, принадлежащий plugin, без знания внутреннего устройства
plugin.

Используйте `api.enqueueNextTurnInjection(...)`, когда plugin нужен долговечный
контекст, который должен попасть в следующий ход модели ровно один раз.
OpenClaw извлекает поставленные в очередь внедрения перед prompt hooks,
отбрасывает истекшие внедрения и дедуплицирует по `idempotencyKey` для каждого
plugin. Это правильный seam для возобновлений подтверждений, сводок политик,
дельт фонового мониторинга и продолжений команд, которые должны быть видимы
модели на следующем ходе, но не должны становиться постоянным текстом
системного промпта.

Семантика очистки является частью контракта. Cleanup расширений сессии и
callbacks cleanup жизненного цикла runtime получают `reset`, `delete`, `disable`
или `restart`. Host удаляет принадлежащее plugin постоянное состояние расширения
сессии и ожидающие внедрения следующего хода для reset/delete/disable; restart
сохраняет долговечное состояние сессии, а callbacks cleanup позволяют plugins
освобождать scheduler jobs, run context и другие out-of-band ресурсы старого
поколения runtime.

## Hooks сообщений

Используйте message hooks для маршрутизации на уровне канала и политики
доставки:

- `message_received`: наблюдает входящий контент, отправителя, `threadId`,
  `messageId`, `senderId`, необязательную корреляцию run/session и метаданные.
- `message_sending`: переписывает `content` или возвращает `{ cancel: true }`.
- `reply_payload_sending`: переписывает нормализованные объекты `ReplyPayload`
  (включая `presentation`, `delivery`, media refs и text) или возвращает `{
  cancel: true }`.
- `message_sent`: наблюдает финальный успех или сбой.

Для audio-only TTS replies `content` может содержать скрытую spoken transcript,
даже когда payload канала не имеет видимого текста/caption. Переписывание этого
`content` обновляет только transcript, видимый hook; он не рендерится как media
caption.

События `reply_payload_sending` могут включать `usageState`, best-effort live
snapshot model/usage/context для текущего хода. Durable delivery, recovered
replay и replies без точной корреляции run его опускают.

Контексты message hook раскрывают стабильные поля корреляции, когда они
доступны: `ctx.sessionKey`, `ctx.runId`, `ctx.messageId`, `ctx.senderId`,
`ctx.trace`, `ctx.traceId`, `ctx.spanId`, `ctx.parentSpanId` и `ctx.callDepth`.
Inbound и контексты `before_dispatch` также раскрывают метаданные reply, когда
канал имеет отфильтрованные по видимости данные цитируемого сообщения:
`replyToId`, `replyToIdFull`, `replyToBody`, `replyToSender` и
`replyToIsQuote`. Предпочитайте эти первоклассные поля чтению legacy metadata.

Предпочитайте типизированные поля `threadId` и `replyToId` перед использованием
специфичных для канала метаданных.

Правила принятия решений:

- `message_sending` с `cancel: true` является терминальным.
- `message_sending` с `cancel: false` трактуется как отсутствие решения.
- Переписанное `content` продолжает передаваться хукам с более низким приоритетом, если последующий хук
  не отменит доставку.
- `reply_payload_sending` выполняется после нормализации полезной нагрузки и перед доставкой в канал,
  включая ответы, направляемые обратно в исходный канал. Обработчики
  выполняются последовательно, и каждый обработчик видит последнюю полезную нагрузку, созданную
  обработчиками с более высоким приоритетом.
- Полезные нагрузки `reply_payload_sending` не раскрывают маркеры доверия времени выполнения, такие как
  `trustedLocalMedia`; плагины могут изменять форму полезной нагрузки, но не могут предоставлять доверие
  к локальным медиа.
- `message_sending` может возвращать `cancelReason` и ограниченные `metadata` вместе с
  отменой. Новые API жизненного цикла сообщений раскрывают это как результат подавленной доставки
  с причиной `cancelled_by_message_sending_hook`; устаревшая прямая
  доставка для совместимости продолжает возвращать пустой массив результатов.
- `message_sent` предназначен только для наблюдения. Сбои обработчиков логируются и не
  изменяют результат доставки.

## Хуки установки

Используйте `security.installPolicy` для решений оператора о разрешении/блокировке. Эта
политика выполняется из конфигурации OpenClaw, охватывает пути установки и обновления CLI
и при включении, но недоступности, завершает выполнение с отказом при сбое.

`before_install` — это хук жизненного цикла среды выполнения плагинов. Он выполняется после
`security.installPolicy` только в процессе OpenClaw, где хуки плагинов уже
загружены, например в потоках установки на основе Gateway. Он полезен для
наблюдений, предупреждений и проверок совместимости, принадлежащих плагинам, но не является
основной корпоративной или хостовой границей безопасности для установок. Поле `builtinScan`
остается в полезной нагрузке события для совместимости, но OpenClaw больше не
выполняет встроенную блокировку опасного кода во время установки, поэтому это пустой результат
`ok`. Верните дополнительные находки или `{ block: true, blockReason }`, чтобы остановить
установку в этом процессе.

`block: true` является терминальным. `block: false` трактуется как отсутствие решения.
Сбои обработчиков блокируют установку с отказом при сбое.

## Жизненный цикл Gateway

Используйте `gateway_start` для сервисов плагинов, которым требуется состояние, принадлежащее Gateway. 
Контекст предоставляет `ctx.config`, `ctx.workspaceDir` и `ctx.getCron?.()` для
проверки и обновления Cron. Используйте `gateway_stop` для очистки долгоживущих
ресурсов.

Не полагайтесь на внутренний хук `gateway:startup` для сервисов времени выполнения,
принадлежащих плагинам.

`cron_changed` срабатывает для событий жизненного цикла Cron, принадлежащих Gateway, с типизированной
полезной нагрузкой события, охватывающей причины `added`, `updated`, `removed`, `started`, `finished`
и `scheduled`. Событие содержит снимок `PluginHookGatewayCronJob`
(включая `state.nextRunAtMs`, `state.lastRunStatus` и
`state.lastError`, если они присутствуют), а также `PluginHookGatewayCronDeliveryStatus`
со значением `not-requested` | `delivered` | `not-delivered` | `unknown`. События удаления
по-прежнему содержат снимок удаленной задачи, чтобы внешние планировщики могли
сверить состояние. Используйте `ctx.getCron?.()` и `ctx.config` из контекста
времени выполнения при синхронизации внешних планировщиков пробуждения и оставляйте OpenClaw
источником истины для проверок наступления срока и выполнения.

## Предстоящие устаревания

Некоторые поверхности, смежные с хуками, устарели, но все еще поддерживаются. Выполните миграцию
до следующего мажорного выпуска:

- **Текстовые конверты каналов** в обработчиках `inbound_claim` и `message_received`.
  Читайте `BodyForAgent` и структурированные блоки пользовательского контекста
  вместо разбора плоского текста конверта. См.
  [Текстовые конверты каналов → BodyForAgent](/ru/plugins/sdk-migration#active-deprecations).
- **`before_agent_start`** остается для совместимости. Новым плагинам следует использовать
  `before_model_resolve` и `before_prompt_build` вместо объединенной
  фазы.
- **`subagent_spawning`** остается для совместимости со старыми плагинами, но
  новым плагинам не следует возвращать из него маршрутизацию потоков. Ядро подготавливает
  привязки подагентов `thread: true` через адаптеры привязки сессий каналов
  до срабатывания `subagent_spawned`.
- **`deactivate`** остается устаревшим совместимым псевдонимом очистки до
  после 2026-08-16. Новым плагинам следует использовать `gateway_stop`.
- **`onResolution` в `before_tool_call`** теперь использует типизированное
  объединение `PluginApprovalResolution` (`allow-once` / `allow-always` / `deny` /
  `timeout` / `cancelled`) вместо свободного `string`.

Полный список — регистрация возможностей памяти, профиль thinking провайдера,
внешние провайдеры аутентификации, типы обнаружения провайдеров, средства доступа к среде выполнения
задач и переименование `command-auth` → `command-status` — см. в
[Миграция Plugin SDK → Активные устаревания](/ru/plugins/sdk-migration#active-deprecations).

## Связанные материалы

- [Миграция Plugin SDK](/ru/plugins/sdk-migration) - активные устаревания и график удаления
- [Создание плагинов](/ru/plugins/building-plugins)
- [Обзор Plugin SDK](/ru/plugins/sdk-overview)
- [Точки входа Plugin](/ru/plugins/sdk-entrypoints)
- [Внутренние хуки](/ru/automation/hooks)
- [Внутренняя архитектура Plugin](/ru/plugins/architecture-internals)
