---
read_when:
    - Вы создаёте новый плагин канала обмена сообщениями
    - Вы хотите подключить OpenClaw к платформе обмена сообщениями
    - Необходимо разобраться в интерфейсе адаптера ChannelPlugin
sidebarTitle: Channel Plugins
summary: Пошаговое руководство по созданию плагина канала обмена сообщениями для OpenClaw
title: Создание плагинов каналов
x-i18n:
    generated_at: "2026-07-16T17:17:59Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 32
    provider: openai
    source_hash: 2c6398dd0b4789b9f4aaf7ad2d1786a7e6388cb8fbb74e8ecaecae7ac0a5eb90
    source_path: plugins/sdk-channel-plugins.md
    workflow: 16
---

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

<Info>
  Впервые работаете с плагинами OpenClaw? Сначала прочитайте [Начало работы](/ru/plugins/building-plugins),
  чтобы узнать о структуре пакета и настройке манифеста.
</Info>

## За что отвечает ваш плагин

Плагины каналов не реализуют инструменты отправки, редактирования и реакций; ядро предоставляет один
общий инструмент `message`. Ваш плагин отвечает за следующее:

- **Конфигурация** — определение учётной записи и мастер настройки
- **Безопасность** — политика личных сообщений и списки разрешённых отправителей
- **Сопряжение** — процесс одобрения личных сообщений
- **Грамматика сессий** — сопоставление идентификаторов бесед, специфичных для провайдера, с базовыми
  чатами, идентификаторами веток и резервными родительскими контекстами
- **Исходящие сообщения** — отправка текста, медиафайлов и опросов на платформу
- **Ветвление** — распределение ответов по веткам
- **Индикатор набора для Heartbeat** — необязательные сигналы набора текста или занятости для целей доставки
  Heartbeat

Ядро отвечает за общий инструмент сообщений, подключение к промптам, внешнюю структуру ключа сессии,
универсальный учёт `:thread:` и диспетчеризацию.

## Адаптер сообщений

Предоставьте адаптер `message` с `defineChannelMessageAdapter` из
`openclaw/plugin-sdk/channel-outbound`. Объявляйте только устойчивые возможности окончательной отправки,
которые действительно поддерживает ваш нативный транспорт, и подкрепляйте их контрактным
тестом, подтверждающим нативный побочный эффект и возвращаемую квитанцию. Для отправки текста и медиафайлов
используйте те же транспортные функции, что и устаревший адаптер `outbound`. Полное описание
контракта API, матрицы возможностей, правил квитанций, завершения интерактивного предпросмотра,
политики подтверждения получения, тестов и таблицы миграции см. в разделе
[API исходящих сообщений канала](/ru/plugins/sdk-channel-outbound).

Если существующий адаптер `outbound` уже содержит нужные методы отправки и
метаданные возможностей, создайте адаптер `message` с помощью
`createChannelMessageAdapterFromOutbound(...)`, а не пишите ещё один
мост вручную. Методы отправки адаптера возвращают значения `MessageReceipt`. Для устаревших идентификаторов создавайте
их с помощью `listMessageReceiptPlatformIds(...)` или
`resolveMessageReceiptPrimaryId(...)`, а не сохраняйте параллельные поля `messageIds`.

Точно объявляйте возможности интерактивного режима и финализатора — ядро использует их, чтобы определить,
что может делать канал, а расхождение между объявленным и фактическим поведением приводит к ошибке
контрактного теста:

| Поверхность                           | Значения                                                                                         |
| ------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `message.live.capabilities`           | `draftPreview`, `previewFinalization`, `progressUpdates`, `nativeStreaming`, `quietFinalization` |
| `message.live.finalizer.capabilities` | `finalEdit`, `normalFallback`, `discardPending`, `previewReceipt`, `retainOnAmbiguousFailure`    |

Каналам, которые на месте преобразуют черновик предпросмотра в окончательное сообщение, следует направлять логику среды выполнения
через `defineFinalizableLivePreviewAdapter(...)` вместе с
`deliverWithFinalizableLivePreviewAdapter(...)` и подкреплять объявленные
возможности тестами `verifyChannelMessageLiveCapabilityAdapterProofs(...)`
и `verifyChannelMessageLiveFinalizerProofs(...)`, чтобы нативное поведение предпросмотра,
хода выполнения, редактирования, резервного варианта или сохранения, очистки и квитанций не могло незаметно
измениться.

Обработчики входящих сообщений, которые откладывают подтверждения платформы, должны объявлять
`message.receive.defaultAckPolicy` и `supportedAckPolicies`, а не скрывать
время подтверждения в локальном состоянии монитора. Проверяйте каждую объявленную политику с помощью
`verifyChannelMessageReceiveAckPolicyAdapterProofs(...)`.

Устаревшие вспомогательные функции ответов, такие как `dispatchInboundReplyWithBase` и
`recordInboundSessionAndDispatchReply`, остаются доступными для совместимых
диспетчеров. Не используйте их в новом коде канала; вместо этого начните с адаптера `message`,
квитанций и вспомогательных средств жизненного цикла получения и отправки в
`openclaw/plugin-sdk/channel-outbound`.

### Приём входящих сообщений (экспериментальная возможность)

Каналы, переводящие авторизацию входящих сообщений на новую систему, могут использовать экспериментальный
подпуть `openclaw/plugin-sdk/channel-ingress-runtime` из путей получения среды выполнения.
Он принимает факты платформы, необработанные списки разрешений, дескрипторы маршрутов, сведения о командах
и конфигурацию групп доступа, а затем возвращает проекции отправителя, маршрута, команды и активации,
а также упорядоченный граф приёма, при этом поиск на платформе и побочные эффекты остаются в плагине.
Сохраняйте нормализацию идентичности плагина в дескрипторе, передаваемом распознавателю; не сериализуйте
необработанные значения совпадений из разрешённого состояния или решения. Описание структуры API,
границ ответственности и требований к тестам см. в разделе
[API приёма сообщений канала](/ru/plugins/sdk-channel-ingress).

### Индикаторы набора текста

Если канал поддерживает индикаторы набора текста вне ответов на входящие сообщения, предоставьте
`heartbeat.sendTyping(...)` в плагине канала. Ядро вызывает его с
определённой целью доставки Heartbeat до запуска модели Heartbeat и
использует общий жизненный цикл поддержания и очистки индикатора набора текста. Добавьте
`heartbeat.clearTyping(...)`, если платформе требуется явный сигнал остановки.

### Параметры источников медиафайлов

Если канал добавляет параметры инструмента сообщений, содержащие источники медиафайлов, предоставьте
имена этих параметров через `plugin.actions.describeMessageTool(...).mediaSourceParams`.
Ядро использует этот явный список для нормализации путей песочницы и политики
доступа к исходящим медиафайлам, поэтому плагинам не нужны особые случаи в общем ядре для
специфичных для провайдера параметров аватаров, вложений или обложек.

Предпочтительно использовать карту с ключами действий, например `{ "set-profile": ["avatarUrl", "avatarPath"] }`,
чтобы несвязанные действия не наследовали аргументы медиафайлов другого действия. Плоский массив
по-прежнему подходит для параметров, намеренно общих для всех предоставляемых действий.

Каналы, которым необходимо предоставить временный общедоступный URL для получения медиафайла
на стороне платформы, могут использовать `createHostedOutboundMediaStore(...)` из
`openclaw/plugin-sdk/outbound-media` с хранилищами состояния плагина. Разбор
маршрута платформы и проверка токена должны оставаться в плагине канала; общий вспомогательный компонент
отвечает только за загрузку медиафайлов, метаданные срока действия, строки фрагментов и очистку.

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

Если каналу требуется специфичное для провайдера формирование `message(action="send")`,
предпочтительно использовать `actions.prepareSendPayload(...)`. Помещайте нативные карточки, блоки, встраиваемые элементы и
другие устойчивые данные в `payload.channelData.<channel>`, а отправку через
адаптер исходящих сообщений или сообщений оставьте ядру. Используйте `actions.handleAction(...)` для отправки
только как резервный вариант совместимости для полезных нагрузок, которые невозможно сериализовать и
отправить повторно.

### Грамматика бесед сессии

Если платформа хранит дополнительную область действия внутри идентификаторов бесед, оставьте их разбор
в плагине с помощью `messaging.resolveSessionConversation(...)`. Это
каноническая точка расширения для сопоставления `rawId` с базовым идентификатором беседы, необязательным
идентификатором ветки, явным `baseConversationId` и любыми
`parentConversationCandidates`. При возврате `parentConversationCandidates`
располагайте их от наиболее узкого родительского контекста к наиболее широкому или базовому контексту беседы.

`messaging.resolveParentConversationCandidates(...)` — устаревший
резервный механизм совместимости для плагинов, которым нужны только родительские резервные контексты поверх
универсального или необработанного идентификатора. Если существуют обе точки расширения, ядро сначала использует
`resolveSessionConversation(...).parentConversationCandidates` и обращается к
`resolveParentConversationCandidates(...)`, только если каноническая
точка расширения их не предоставляет.

Встроенные плагины, которым нужен тот же разбор до запуска реестра каналов,
могут предоставить файл верхнего уровня `session-key-api.ts` с соответствующим
экспортом `resolveSessionConversation(...)` (см. плагины Feishu и Telegram).
Ядро использует эту безопасную для начальной загрузки поверхность, только когда реестр плагинов среды выполнения
ещё недоступен.

Используйте `openclaw/plugin-sdk/channel-route`, когда коду плагина нужно нормализовать
поля, подобные маршрутам, сравнить дочернюю ветку с её родительским маршрутом или создать
стабильный ключ дедупликации из `{ channel, to, accountId, threadId }`. Вспомогательная функция
нормализует числовые идентификаторы веток так же, как ядро, поэтому используйте её вместо ситуативных
сравнений `String(threadId)`. Плагины со специфичной для провайдера грамматикой целей
должны предоставлять `messaging.resolveOutboundSessionRoute(...)`, чтобы ядро получало
нативные для провайдера идентификаторы сессии и ветки без адаптеров разбора.

### Поддержка привязки бесед в рамках учётной записи

Установите `conversationBindings.supportsCurrentConversationBinding`, если канал
поддерживает универсальные привязки текущих бесед. `createChatChannelPlugin(...)`
по умолчанию устанавливает эту статическую возможность в `true`.

Если поддержка зависит от настроенной учётной записи, также реализуйте
`conversationBindings.isCurrentConversationBindingSupported({ accountId })`.
Ядро вычисляет результат этой синхронной точки расширения только после включения статической возможности.
Возврат `false` делает универсальные операции проверки возможности текущей беседы,
привязки, поиска, перечисления, обновления времени обращения и отмены привязки недоступными для этой учётной записи.
Если точка расширения отсутствует, статическая возможность применяется ко всем учётным записям.

Определяйте ответ по уже загруженной конфигурации учётной записи или состоянию среды выполнения. Эта
точка расширения управляет только универсальными привязками текущих бесед; она не заменяет
настроенные правила привязки или принадлежащую плагину маршрутизацию сессий. Контрактные тесты
должны охватывать как минимум одну поддерживаемую и одну неподдерживаемую учётную запись с использованием
контракта `ChannelPlugin["conversationBindings"]`, экспортируемого из
`openclaw/plugin-sdk/channel-core`.

## Одобрения и возможности каналов

Большинству плагинов каналов не нужен специальный код для одобрений. Ядро отвечает за
`/approve` в том же чате, общие полезные нагрузки кнопок одобрения и универсальную резервную доставку.
`ChannelPlugin.approvals` удалён; вместо него размещайте факты доставки, нативного поведения, отображения и авторизации
одобрений в одном объекте `approvalCapability`. `plugin.auth` предназначен только для входа и выхода —
ядро больше не считывает из этого объекта точки расширения авторизации одобрений.

Используйте `approvalCapability.delivery` только для нативной маршрутизации одобрений или подавления
резервного варианта, а `approvalCapability.render` — только когда каналу действительно нужны
собственные полезные нагрузки одобрений вместо общего средства отображения.

### Авторизация одобрений

- `approvalCapability.authorizeActorAction` и
  `approvalCapability.getActionAvailabilityState` являются канонической
  точкой расширения для авторизации одобрений.
- Используйте `getActionAvailabilityState` для доступности авторизации одобрений в том же чате.
  Сохраняйте доступность настроенных одобряющих лиц для `/approve`, даже когда нативная доставка
  отключена; вместо этого используйте состояние нативной инициирующей поверхности для рекомендаций по доставке и настройке.
- Если канал предоставляет нативные одобрения выполнения, используйте
  `approvalCapability.getExecInitiatingSurfaceState` для
  состояния инициирующей поверхности или нативного клиента, если оно отличается от авторизации
  одобрений в том же чате. Ядро использует эту специфичную для выполнения точку расширения, чтобы различать `enabled` и
  `disabled`, определять, поддерживает ли инициирующий канал нативные одобрения выполнения,
  и включать канал в рекомендации по резервному использованию нативного клиента.
  `createApproverRestrictedNativeApprovalCapability(...)` заполняет это значение для
  типичного случая.
- Если канал может вывести стабильные идентичности личных сообщений, подобные владельцам, из существующей конфигурации,
  используйте `createResolvedApproverActionAuthAdapter` из
  `openclaw/plugin-sdk/approval-runtime`, чтобы ограничить `/approve` в том же чате,
  не добавляя в ядро специальную логику одобрений.
- Если пользовательская авторизация одобрений намеренно разрешает только резервный вариант в том же чате, верните
  `markImplicitSameChatApprovalAuthorization({ authorized: true })` из
  `openclaw/plugin-sdk/approval-auth-runtime`; в противном случае ядро считает
  результат явной авторизацией одобряющего лица.
- Если принадлежащий каналу нативный обратный вызов разрешает одобрения напрямую, используйте
  `isImplicitSameChatApprovalAuthorization(...)` перед разрешением, чтобы неявный
  резервный вариант всё равно проходил через обычную авторизацию участников канала.

### Жизненный цикл полезной нагрузки и рекомендации по настройке

- Используйте `outbound.shouldSuppressLocalPayloadPrompt` или
  `outbound.beforeDeliverPayload` для специфичного для канала поведения жизненного цикла полезной нагрузки,
  например скрытия дублирующихся локальных запросов одобрения или отправки индикаторов
  набора текста перед доставкой.
- Используйте `approvalCapability.describeExecApprovalSetup`, когда канал должен
  объяснить в ответе для отключённого пути, какие именно параметры конфигурации необходимы для включения
  нативных одобрений выполнения. Точка расширения получает `{ channel, channelLabel, accountId }`;
  каналы с именованными учётными записями должны отображать пути в рамках учётной записи, например
  `channels.<channel>.accounts.<id>.execApprovals.*`, вместо значений
  верхнего уровня по умолчанию.
- Используйте `approvalCapability.describePluginApprovalSetup`, когда рекомендации при сбое одобрения
  плагина можно безопасно показывать при отсутствии маршрута и истечении времени ожидания
  одобрения плагина. `createApproverRestrictedNativeApprovalCapability(...)` не
  выводит это из `describeExecApprovalSetup`; передавайте тот же вспомогательный компонент явно,
  только если одобрения плагина и выполнения действительно используют одну и ту же нативную настройку.

### Нативная доставка одобрений

Если каналу нужна нативная доставка одобрений, сосредоточьте код канала на
нормализации целей и фактах транспорта или представления. Используйте
`createChannelExecApprovalProfile`, `createChannelNativeOriginTargetResolver`,
`createChannelApproverDmTargetResolver` и
`createApproverRestrictedNativeApprovalCapability` из
`openclaw/plugin-sdk/approval-runtime`. Разместите специфичные для канала факты за
`approvalCapability.nativeRuntime`, предпочтительно через
`createChannelApprovalNativeRuntimeAdapter(...)` или
`createLazyChannelApprovalNativeRuntimeAdapter(...)`, чтобы ядро могло собрать
обработчик и отвечать за фильтрацию запросов, маршрутизацию, дедупликацию, истечение срока действия, подписку
Gateway и уведомления о маршрутизации в другое место.

`nativeRuntime` разделён на несколько меньших точек расширения:

- `availability` — настроена ли учётная запись и следует ли обрабатывать запрос
- `presentation` — преобразование общей модели представления подтверждения в
  ожидающие, разрешённые или просроченные нативные полезные нагрузки либо итоговые действия
- `transport` — подготовка целей, а также отправка, обновление и удаление нативных
  сообщений подтверждения
- `interactions` — необязательные хуки привязки, отвязки и очистки действий для нативных кнопок
  или реакций, а также необязательный хук `cancelDelivered`. Реализуйте
  `cancelDelivered`, когда `deliverPending` регистрирует внутрипроцессное или постоянное
  состояние (например, хранилище целей реакций), чтобы это состояние можно было освободить, если
  остановка обработчика отменяет доставку до выполнения `bindPending` или если
  `bindPending` не возвращает дескриптор
- `observe` — необязательные хуки диагностики доставки

Другие вспомогательные средства подтверждения:

- Используйте `createNativeApprovalChannelRouteGates` из
  `openclaw/plugin-sdk/approval-native-runtime`, когда канал поддерживает как нативную доставку
  из исходного сеанса, так и явные цели пересылки подтверждений. Это вспомогательное
  средство централизует выбор конфигурации подтверждений, обработку `mode`, фильтры
  агентов и сеансов, привязку учётных записей, сопоставление целей сеанса и сопоставление списков целей,
  при этом вызывающий код по-прежнему отвечает за идентификатор канала, режим пересылки по умолчанию,
  поиск учётной записи, проверку включения транспорта, нормализацию целей и разрешение цели
  из источника хода. Не используйте его для создания принадлежащих ядру настроек политики канала
  по умолчанию; явно передавайте документированный режим канала по умолчанию.
- `createChannelNativeOriginTargetResolver` по умолчанию использует общий механизм
  сопоставления маршрутов каналов для целей `{ to, accountId, threadId }`. Передавайте
  `targetsMatch` только тогда, когда канал имеет специфичные для провайдера правила эквивалентности,
  например сопоставление префиксов временных меток Slack. Передавайте `normalizeTargetForMatch`, когда
  каналу требуется привести идентификаторы провайдера к каноническому виду до запуска стандартного механизма
  сопоставления маршрутов или пользовательского обратного вызова `targetsMatch`, сохраняя при этом
  исходную цель для доставки. Используйте `normalizeTarget` только тогда, когда к каноническому виду
  следует привести саму разрешённую цель доставки.
- Если каналу нужны принадлежащие среде выполнения объекты, например клиент, токен, приложение Bolt
  или приёмник Webhook, зарегистрируйте их через
  `openclaw/plugin-sdk/channel-runtime-context`. Универсальный реестр контекста среды выполнения
  позволяет ядру запускать обработчики на основе возможностей из состояния запуска канала
  без добавления специализированных для подтверждений обёрток.
- Используйте низкоуровневые `createChannelApprovalHandler` или
  `createChannelNativeApprovalRuntime` только тогда, когда интерфейс на основе возможностей
  пока недостаточно выразителен.
- Каналы нативных подтверждений должны направлять и `accountId`, и `approvalKind`
  через эти вспомогательные средства. `accountId` ограничивает политику подтверждений для нескольких учётных записей
  нужной учётной записью бота, а `approvalKind` делает поведение подтверждений выполнения и плагинов
  доступным каналу без жёстко заданных ветвей в ядре.
- Уведомления о перенаправлении подтверждений также принадлежат ядру. Плагины каналов не должны отправлять
  собственные последующие сообщения «подтверждение отправлено в личные сообщения / другой канал» из
  `createChannelNativeApprovalRuntime`; вместо этого предоставляйте точную маршрутизацию от источника
  к личным сообщениям подтверждающего через общие вспомогательные средства возможностей подтверждения и позволяйте
  ядру агрегировать фактические доставки перед публикацией уведомления обратно в
  чат-инициатор.
- Сохраняйте тип идентификатора доставленного подтверждения на всём пути. Нативные клиенты не должны
  угадывать или переписывать маршрутизацию подтверждений выполнения и плагинов на основе локального
  состояния канала.
- Передавайте этот явный `approvalKind` в `resolveApprovalOverGateway`. При этом используется
  каноническая служба `approval.resolve` и возвращается зарегистрированный победитель, если
  другая поверхность отвечает первой. Старый явный вход `resolveMethod`
  сохраняется для элементов управления на основе команд; новые нативные действия не должны использовать его или
  выводить тип из идентификатора.
- Разные типы подтверждений могут намеренно предоставлять разные нативные
  поверхности. Текущие встроенные примеры: Matrix сохраняет одинаковую нативную маршрутизацию
  в личные сообщения и каналы и интерфейс реакций для подтверждений выполнения и плагинов, при этом
  позволяя аутентификации различаться в зависимости от типа подтверждения; Slack сохраняет нативную маршрутизацию подтверждений
  доступной как для идентификаторов выполнения, так и для идентификаторов плагинов.
- `createApproverRestrictedNativeApprovalAdapter` по-прежнему существует как
  обёртка совместимости, но в новом коде следует предпочитать конструктор возможностей
  и предоставлять `approvalCapability` в плагине.

### Более узкие подпути среды выполнения подтверждений

Для часто вызываемых точек входа каналов предпочитайте следующие более узкие подпути более широкому
экспорту `approval-runtime`, когда нужна только одна часть этого семейства:

- `openclaw/plugin-sdk/approval-auth-runtime`
- `openclaw/plugin-sdk/approval-client-runtime`
- `openclaw/plugin-sdk/approval-delivery-runtime`
- `openclaw/plugin-sdk/approval-gateway-runtime`
- `openclaw/plugin-sdk/approval-reference-runtime`
- `openclaw/plugin-sdk/approval-handler-adapter-runtime`
- `openclaw/plugin-sdk/approval-handler-runtime`
- `openclaw/plugin-sdk/approval-native-runtime`
- `openclaw/plugin-sdk/approval-reply-runtime`
- `openclaw/plugin-sdk/channel-runtime-context`

Аналогично предпочитайте `openclaw/plugin-sdk/reply-runtime`,
`openclaw/plugin-sdk/reply-dispatch-runtime`,
`openclaw/plugin-sdk/reply-reference` и
`openclaw/plugin-sdk/reply-chunking` более широким объединяющим поверхностям, когда
они не нужны все сразу.

### Подпути настройки

- `openclaw/plugin-sdk/setup-runtime` охватывает безопасные для среды выполнения вспомогательные средства настройки:
  `createSetupTranslator`, безопасные для импорта адаптеры исправлений настройки
  (`createPatchedAccountSetupAdapter`, `createEnvPatchedAccountSetupAdapter`,
  `createSetupInputPresenceValidator`), вывод примечаний поиска,
  `promptResolvedAllowFrom`, `splitSetupEntries` и делегированные
  конструкторы прокси настройки.
- `openclaw/plugin-sdk/channel-setup` охватывает конструкторы настройки
  необязательной установки и несколько безопасных для настройки примитивов: `createOptionalChannelSetupSurface`,
  `createOptionalChannelSetupAdapter`, `createOptionalChannelSetupWizard`,
  `DEFAULT_ACCOUNT_ID`, `createTopLevelChannelDmPolicy`,
  `setSetupChannelEnabled` и `splitSetupEntries`.
- Используйте более широкий интерфейс `openclaw/plugin-sdk/setup` только тогда, когда вам также нужны
  более тяжёлые общие вспомогательные средства настройки и конфигурации, такие как
  `moveSingleAccountChannelSectionToDefaultAccount(...)`.

Если ваш канал хочет лишь сообщать «сначала установите этот плагин» на поверхностях
настройки, предпочитайте `createOptionalChannelSetupSurface(...)`. Созданные
адаптер и мастер работают с отказом по умолчанию при записи конфигурации и завершении настройки и повторно используют
одно и то же сообщение о необходимости установки при проверке, завершении и копировании
ссылки на документацию.

Если ваш канал поддерживает настройку или аутентификацию через переменные среды и универсальные процессы запуска и конфигурации
должны знать имена этих переменных среды до загрузки среды выполнения, объявите их в
манифесте плагина с помощью `channelEnvVars`. Сохраняйте `envVars` среды выполнения канала или локальные
константы только для текста, предназначенного для операторов.

Если ваш канал может появляться в `status`, `channels list`, `channels status` или
проверках SecretRef до запуска среды выполнения плагина, добавьте `openclaw.setupEntry` в
`package.json`. Эту точку входа должно быть безопасно импортировать в доступных только для чтения путях
команд; она должна возвращать метаданные канала, безопасный для настройки адаптер конфигурации,
адаптер состояния и метаданные цели секретов канала, необходимые для этих
сводок. Не запускайте клиенты, прослушиватели или транспортные среды выполнения из
точки входа настройки.

Сохраняйте узким и путь импорта основной точки входа канала. Механизм обнаружения может вычислять
точку входа и модуль плагина канала для регистрации возможностей без
активации канала. Файлы наподобие `channel-plugin-api.ts` должны экспортировать
объект плагина канала, не импортируя мастеры настройки, транспортные
клиенты, прослушиватели сокетов, средства запуска подпроцессов или модули запуска служб.
Размещайте эти компоненты среды выполнения в модулях, загружаемых из `registerFull(...)`, установщиках
среды выполнения или ленивых адаптерах возможностей.

### Другие узкие подпути каналов

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

- `openclaw/plugin-sdk/account-core`, `openclaw/plugin-sdk/account-id`,
  `openclaw/plugin-sdk/account-resolution` и
  `openclaw/plugin-sdk/account-helpers` для конфигурации нескольких учётных записей и
  отката к учётной записи по умолчанию
- `openclaw/plugin-sdk/inbound-envelope` и
  `openclaw/plugin-sdk/channel-inbound` для входящего маршрута и конверта, а также
  связывания регистрации и диспетчеризации
- `openclaw/plugin-sdk/channel-targets` для вспомогательных средств разбора целей
- `openclaw/plugin-sdk/outbound-media` для загрузки медиа и
  `openclaw/plugin-sdk/channel-outbound` для делегатов исходящей идентификации и отправки,
  а также планирования полезной нагрузки
- `buildThreadAwareOutboundSessionRoute(...)` из
  `openclaw/plugin-sdk/channel-core`, когда исходящий маршрут должен сохранять
  явный `replyToId`/`threadId` или восстанавливать текущий сеанс `:thread:`
  после того, как базовый ключ сеанса всё ещё совпал. Плагины провайдеров могут
  переопределять приоритет, поведение суффиксов и нормализацию идентификатора ветки, когда
  их платформа имеет нативную семантику доставки в ветки.
- `openclaw/plugin-sdk/thread-bindings-runtime` для жизненного цикла привязки веток
  и регистрации адаптеров
- `openclaw/plugin-sdk/agent-media-payload` только тогда, когда всё ещё требуется устаревшая
  структура полей полезной нагрузки агента или медиа
- `openclaw/plugin-sdk/telegram-command-config` (устарело: ни один встроенный
  плагин не использует это в рабочей среде) для нормализации пользовательских команд Telegram,
  проверки дубликатов и конфликтов и контракта конфигурации команд
  со стабильным откатом; для нового кода плагинов предпочитайте локальную обработку конфигурации команд в плагине

Каналам только с аутентификацией обычно достаточно стандартного пути: ядро обрабатывает
подтверждения, а плагин лишь предоставляет возможности исходящей отправки и аутентификации. Каналы
нативных подтверждений, такие как Matrix, Slack, Telegram и пользовательские чат-транспорты,
должны использовать общие нативные вспомогательные средства вместо реализации собственного жизненного цикла
подтверждений.

## Политика входящих упоминаний

Разделяйте обработку входящих упоминаний на два уровня:

- сбор свидетельств, принадлежащий плагину
- оценка общей политики

Используйте `openclaw/plugin-sdk/channel-mention-gating` для принятия решений по политике упоминаний.
Используйте `openclaw/plugin-sdk/channel-inbound` только тогда, когда нужен более широкий
экспорт вспомогательных средств входящей обработки.

Для локальной логики плагина подходят:

- обнаружение ответа боту
- обнаружение цитирования бота
- проверки участия в ветке
- исключение служебных и системных сообщений
- нативные для платформы кэши, необходимые для подтверждения участия бота

Для общего вспомогательного средства подходят:

- `requireMention`
- результат явного упоминания
- список разрешённых неявных упоминаний
- обход для команд
- итоговое решение о пропуске

Предпочтительный процесс:

1. Вычислите локальные факты об упоминании.
2. Передайте эти факты в `resolveInboundMentionDecision({ facts, policy })`.
3. Используйте `decision.effectiveWasMentioned`, `decision.shouldBypassMention` и
   `decision.shouldSkip` во входном фильтре.

```typescript
import {
  implicitMentionKindWhen,
  matchesMentionWithExplicit,
  resolveInboundMentionDecision,
} from "openclaw/plugin-sdk/channel-inbound";

const wasMentioned = matchesMentionWithExplicit({
  text,
  mentionRegexes,
  explicit: {
    hasAnyMention,
    isExplicitlyMentioned,
    canResolveExplicit,
  },
});

const facts = {
  canDetectMention: true,
  wasMentioned,
  hasAnyMention,
  implicitMentionKinds: [
    ...implicitMentionKindWhen("reply_to_bot", isReplyToBot),
    ...implicitMentionKindWhen("quoted_bot", isQuoteOfBot),
  ],
};

const decision = resolveInboundMentionDecision({
  facts,
  policy: {
    isGroup,
    requireMention,
    allowedImplicitMentionKinds: requireExplicitMention ? [] : ["reply_to_bot", "quoted_bot"],
    allowTextCommands,
    hasControlCommand,
    commandAuthorized,
  },
});

if (decision.shouldSkip) return;
```

`matchesMentionWithExplicit(...)` возвращает логическое значение. `hasAnyMention`,
`isExplicitlyMentioned` и `canResolveExplicit` поступают из собственных
нативных метаданных упоминаний канала (сущностей сообщения, признаков ответа боту и аналогичных данных);
передавайте значения `false`/`undefined`, когда ваша платформа не может их определить.

`api.runtime.channel.mentions` предоставляет те же общие вспомогательные средства упоминаний для
встроенных плагинов каналов, которые уже зависят от внедрения среды выполнения:
`buildMentionRegexes`, `matchesMentionPatterns`, `matchesMentionWithExplicit`,
`implicitMentionKindWhen`, `resolveInboundMentionDecision`.

Если нужны только `implicitMentionKindWhen` и `resolveInboundMentionDecision`,
импортируйте их из `openclaw/plugin-sdk/channel-mention-gating`, чтобы не загружать
несвязанные вспомогательные средства входящей среды выполнения.

## Пошаговое руководство

<Steps>
  <a id="step-1-package-and-manifest"></a>
  <Step title="Пакет и манифест">
    Создайте стандартные файлы плагина. Поле `channels` в
    `openclaw.plugin.json` (а не поле `kind`) указывает, что манифест
    владеет каналом. Полное описание метаданных пакета см. в разделе
    [Настройка и конфигурация плагина](/ru/plugins/sdk-setup#openclaw-channel):

    <CodeGroup>
    ```json package.json
    {
      "name": "@myorg/openclaw-acme-chat",
      "version": "1.0.0",
      "type": "module",
      "openclaw": {
        "extensions": ["./index.ts"],
        "setupEntry": "./setup-entry.ts",
        "channel": {
          "id": "acme-chat",
          "label": "Acme Chat",
          "blurb": "Подключить OpenClaw к Acme Chat."
        }
      }
    }
    ```

    ```json openclaw.plugin.json
    {
      "id": "acme-chat",
      "channels": ["acme-chat"],
      "name": "Acme Chat",
      "description": "Плагин канала Acme Chat",
      "configSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {}
      },
      "channelConfigs": {
        "acme-chat": {
          "schema": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "token": { "type": "string" },
              "allowFrom": {
                "type": "array",
                "items": { "type": "string" }
              }
            }
          },
          "uiHints": {
            "token": {
              "label": "Токен бота",
              "sensitive": true
            }
          }
        }
      }
    }
    ```
    </CodeGroup>

    `configSchema` проверяет `plugins.entries.acme-chat.config`. Используйте его для
    настроек, принадлежащих плагину и не относящихся к конфигурации учётной записи канала.
    `channelConfigs.acme-chat.schema` проверяет `channels.acme-chat` и служит
    источником холодного пути для схемы конфигурации, настройки и интерфейса до
    загрузки среды выполнения плагина. Полный справочник полей верхнего уровня см. в разделе
    [Манифест плагина](/ru/plugins/manifest).

  </Step>

  <Step title="Создание объекта плагина канала">
    Интерфейс `ChannelPlugin` содержит множество необязательных поверхностей адаптеров. Начните
    с минимума — `id`, `config` и `setup` — и добавляйте адаптеры
    по мере необходимости.

    Создайте `src/channel.ts`:

    ```typescript src/channel.ts
    import {
      createChatChannelPlugin,
      createChannelPluginBase,
    } from "openclaw/plugin-sdk/channel-core";
    import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core";
    import { acmeChatApi } from "./client.js"; // клиент API вашей платформы

    type ResolvedAccount = {
      accountId: string | null;
      token: string;
      allowFrom: string[];
      dmPolicy: string | undefined;
    };

    function resolveAccount(
      cfg: OpenClawConfig,
      accountId?: string | null,
    ): ResolvedAccount {
      const section = (cfg.channels as Record<string, any>)?.["acme-chat"];
      const token = section?.token;
      if (!token) throw new Error("acme-chat: требуется токен");
      return {
        accountId: accountId ?? null,
        token,
        allowFrom: section?.allowFrom ?? [],
        dmPolicy: section?.dmSecurity,
      };
    }

    export const acmeChatPlugin = createChatChannelPlugin<ResolvedAccount>({
      base: createChannelPluginBase({
        id: "acme-chat",
        // Разрешение и проверка учётной записи относятся к `config`, а не к `setup`.
        // `setup` отвечает за запись при первоначальной настройке (applyAccountConfig, validateInput).
        config: {
          listAccountIds: () => ["default"],
          resolveAccount,
          inspectAccount(cfg, accountId) {
            const section =
              (cfg.channels as Record<string, any>)?.["acme-chat"];
            return {
              enabled: Boolean(section?.token),
              configured: Boolean(section?.token),
              tokenStatus: section?.token ? "available" : "missing",
            };
          },
        },
        setup: {
          applyAccountConfig: ({ cfg, input }) => ({
            ...cfg,
            channels: {
              ...cfg.channels,
              "acme-chat": { ...(cfg.channels as any)?.["acme-chat"], ...input },
            },
          }),
        },
      }),

      // Безопасность личных сообщений: кто может отправлять сообщения боту
      security: {
        dm: {
          channelKey: "acme-chat",
          resolvePolicy: (account) => account.dmPolicy,
          resolveAllowFrom: (account) => account.allowFrom,
          defaultPolicy: "allowlist",
        },
      },

      // Сопряжение: процесс подтверждения новых контактов в личных сообщениях
      pairing: {
        text: {
          idLabel: "Имя пользователя Acme Chat",
          message: "Отправьте этот код для подтверждения своей личности:",
          notify: async ({ target, code }) => {
            await acmeChatApi.sendDm(target, `Код сопряжения: ${code}`);
          },
        },
      },

      // Ветки обсуждения: способ доставки ответов
      threading: { topLevelReplyToMode: "reply" },

      // Исходящие сообщения: отправка сообщений на платформу
      outbound: {
        attachedResults: {
          channel: "acme-chat",
          sendText: async (params) => {
            const result = await acmeChatApi.sendMessage(
              params.to,
              params.text,
            );
            return { messageId: result.id };
          },
        },
        base: {
          sendMedia: async (params) => {
            await acmeChatApi.sendFile(params.to, params.filePath);
          },
        },
      },
    });
    ```

    Для каналов, которые принимают как канонические ключи личных сообщений верхнего уровня, так и устаревшие вложенные ключи, используйте вспомогательные функции из `plugin-sdk/channel-config-helpers`: `resolveChannelDmAccess`, `resolveChannelDmPolicy`, `resolveChannelDmAllowFrom` и `normalizeChannelDmPolicy` обеспечивают приоритет локальных значений учётной записи над унаследованными корневыми значениями. Используйте тот же преобразователь вместе с исправлением через doctor посредством `normalizeLegacyDmAliases`, чтобы среда выполнения и миграция читали один и тот же контракт.

    <Accordion title="Что createChatChannelPlugin делает за вас">
      Вместо ручной реализации низкоуровневых интерфейсов адаптеров вы передаёте
      декларативные параметры, а конструктор объединяет их:

      | Параметр | Что он подключает |
      | --- | --- |
      | `security.dm` | Ограниченный областью преобразователь безопасности личных сообщений из полей конфигурации |
      | `pairing.text` | Текстовый процесс сопряжения в личных сообщениях с обменом кодом |
      | `threading` | Преобразователь режима ответа (фиксированный, привязанный к учётной записи или пользовательский) |
      | `outbound.attachedResults` | Функции отправки, возвращающие метаданные результата (идентификаторы сообщений); требуется соседний идентификатор `channel`, чтобы ядро могло добавить канал в возвращаемый результат доставки |

      Если требуется полный контроль, вместо декларативных параметров также можно
      передавать необработанные объекты адаптеров.

      Необработанные адаптеры исходящих сообщений могут определять функцию `chunker(text, limit, ctx)`.
      Необязательный параметр `ctx.formatting` содержит решения по форматированию во время доставки,
      например `maxLinesPerMessage`; применяйте его перед отправкой, чтобы ветвление ответов
      и границы фрагментов однократно определялись общей системой доставки исходящих сообщений.
      Контексты отправки также включают `replyToIdSource` (`implicit` или `explicit`),
      когда определена нативная цель ответа, благодаря чему вспомогательные функции полезной нагрузки могут сохранять
      явные теги ответа, не занимая неявный одноразовый слот ответа.
    </Accordion>

  </Step>

  <Step title="Подключение точки входа">
    Создайте `index.ts`:

    ```typescript index.ts
    import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
    import { acmeChatPlugin } from "./src/channel.js";

    export default defineChannelPluginEntry({
      id: "acme-chat",
      name: "Acme Chat",
      description: "Плагин канала Acme Chat",
      plugin: acmeChatPlugin,
      registerCliMetadata(api) {
        api.registerCli(
          ({ program }) => {
            program
              .command("acme-chat")
              .description("Управление Acme Chat");
          },
          {
            descriptors: [
              {
                name: "acme-chat",
                description: "Управление Acme Chat",
                hasSubcommands: false,
              },
            ],
          },
        );
      },
      registerFull(api) {
        api.registerGatewayMethod(/* ... */);
      },
    });
    ```

    Размещайте принадлежащие каналу дескрипторы CLI в `registerCliMetadata(...)`, чтобы OpenClaw
    мог отображать их в корневой справке без активации полной среды выполнения канала,
    а при обычной полной загрузке те же дескрипторы использовались для фактической
    регистрации команд. Оставьте `registerFull(...)` для задач, выполняемых только во время работы.
    `defineChannelPluginEntry` автоматически обрабатывает разделение режимов регистрации.
    Если `registerFull(...)` регистрирует RPC-методы Gateway, используйте
    префикс, относящийся к плагину. Пространства имён администрирования ядра (`config.*`,
    `exec.approvals.*`, `wizard.*`, `update.*`) остаются зарезервированными и всегда
    разрешаются в `operator.admin`. Все параметры см. в разделе
    [Точки входа](/ru/plugins/sdk-entrypoints#definechannelpluginentry).

  </Step>

  <Step title="Добавление точки входа настройки">
    Создайте `setup-entry.ts` для облегчённой загрузки во время первоначальной настройки:

    ```typescript setup-entry.ts
    import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
    import { acmeChatPlugin } from "./src/channel.js";

    export default defineSetupPluginEntry(acmeChatPlugin);
    ```

    OpenClaw загружает эту точку вместо полной, когда канал отключён
    или не настроен. Это предотвращает загрузку тяжёлого кода среды выполнения во время настройки.
    Подробности см. в разделе [Настройка и конфигурация](/ru/plugins/sdk-setup#setup-entry).

    Встроенные каналы рабочей области, которые выносят безопасные для настройки экспорты в дополнительные
    модули, могут использовать `defineBundledChannelSetupEntry(...)` из
    `openclaw/plugin-sdk/channel-entry-contract`, если им также требуется
    явный установщик среды выполнения на этапе настройки.

  </Step>

  <Step title="Обработка входящих сообщений">
    Плагин должен получать сообщения с платформы и пересылать их в
    OpenClaw. Обычно используется Webhook, который проверяет запрос и
    передаёт его обработчику входящих сообщений канала:

    ```typescript
    registerFull(api) {
      api.registerHttpRoute({
        path: "/acme-chat/webhook",
        auth: "plugin", // аутентификация под управлением плагина (проверяйте подписи самостоятельно)
        handler: async (req, res) => {
          const event = parseWebhookPayload(req);

          // Ваш обработчик входящих сообщений передаёт сообщение в OpenClaw.
          // Точное подключение зависит от SDK вашей платформы —
          // реальный пример см. в пакете встроенного плагина Microsoft Teams или Google Chat.
          await handleAcmeChatInbound(api, event);

          res.statusCode = 200;
          res.end("ok");
          return true;
        },
      });
    }
    ```

    <Note>
      Обработка входящих сообщений зависит от конкретного канала. Каждый плагин канала владеет
      собственным конвейером входящих сообщений. Реальные шаблоны см. во встроенных плагинах каналов
      (например, в пакете плагина Microsoft Teams или Google Chat).
    </Note>

  </Step>

<a id="step-6-test"></a>
<Step title="Тестирование">
Размещайте тесты рядом с кодом в `src/channel.test.ts`:

    ```typescript src/channel.test.ts
    import { describe, it, expect } from "vitest";
    import { acmeChatPlugin } from "./channel.js";

    describe("плагин acme-chat", () => {
      it("определяет учётную запись из конфигурации", () => {
        const cfg = {
          channels: {
            "acme-chat": { token: "test-token", allowFrom: ["user1"] },
          },
        } as any;
        const account = acmeChatPlugin.config.resolveAccount(cfg, undefined);
        expect(account.token).toBe("test-token");
      });

      it("проверяет учётную запись без материализации секретов", () => {
        const cfg = {
          channels: { "acme-chat": { token: "test-token" } },
        } as any;
        const result = acmeChatPlugin.config.inspectAccount!(cfg, undefined);
        expect(result.configured).toBe(true);
        expect(result.tokenStatus).toBe("available");
      });

      it("сообщает об отсутствующей конфигурации", () => {
        const cfg = { channels: {} } as any;
        const result = acmeChatPlugin.config.inspectAccount!(cfg, undefined);
        expect(result.configured).toBe(false);
      });
    });
    ```

    ```bash
    pnpm test <bundled-plugin-root>/acme-chat/
    ```

    Общие вспомогательные средства для тестирования описаны в разделе [Тестирование](/ru/plugins/sdk-testing).

</Step>
</Steps>

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

```text
<bundled-plugin-root>/acme-chat/
├── package.json              # метаданные openclaw.channel
├── openclaw.plugin.json      # манифест со схемой конфигурации
├── index.ts                  # defineChannelPluginEntry
├── setup-entry.ts            # defineSetupPluginEntry
├── api.ts                    # публичные экспорты (необязательно)
├── runtime-api.ts            # внутренние экспорты среды выполнения (необязательно)
└── src/
    ├── channel.ts            # ChannelPlugin через createChatChannelPlugin
    ├── channel.test.ts       # тесты
    ├── client.ts             # клиент API платформы
    └── runtime.ts            # хранилище среды выполнения (при необходимости)
```

## Расширенные темы

<CardGroup cols={2}>
  <Card title="Варианты ветвления" icon="git-branch" href="/ru/plugins/sdk-entrypoints#registration-mode">
    Фиксированный, ограниченный учётной записью или пользовательский режим ответов
  </Card>
  <Card title="Интеграция инструмента сообщений" icon="puzzle" href="/ru/plugins/architecture#channel-plugins-and-the-shared-message-tool">
    describeMessageTool и обнаружение действий
  </Card>
  <Card title="Определение цели" icon="crosshair" href="/ru/plugins/architecture-internals#channel-target-resolution">
    inferTargetChatType, looksLikeId, reservedLiterals, resolveTarget
  </Card>
  <Card title="Вспомогательные средства среды выполнения" icon="settings" href="/ru/plugins/sdk-runtime">
    TTS, STT, мультимедиа, подагент через api.runtime
  </Card>
  <Card title="API входящих событий канала" icon="bolt" href="/ru/plugins/sdk-channel-inbound">
    Общий жизненный цикл входящего события: приём, определение, запись, диспетчеризация, завершение
  </Card>
</CardGroup>

<Note>
Некоторые встроенные вспомогательные точки интеграции всё ещё существуют для
сопровождения встроенных плагинов и обеспечения совместимости. Они не являются
рекомендуемым шаблоном для новых плагинов каналов; предпочитайте универсальные
подпути channel/setup/reply/runtime из общей поверхности SDK, если только вы
не сопровождаете непосредственно соответствующее семейство встроенных плагинов.
</Note>

## Дальнейшие шаги

- [Плагины провайдеров](/ru/plugins/sdk-provider-plugins) — если ваш плагин также предоставляет модели
- [Обзор SDK](/ru/plugins/sdk-overview) — полный справочник по импорту подпутей
- [Тестирование SDK](/ru/plugins/sdk-testing) — утилиты тестирования и контрактные тесты
- [Манифест плагина](/ru/plugins/manifest) — полная схема манифеста

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

- [Настройка Plugin SDK](/ru/plugins/sdk-setup)
- [Создание плагинов](/ru/plugins/building-plugins)
- [Плагины среды выполнения агентов](/ru/plugins/sdk-agent-harness)
