Building plugins

Створення плагінів каналів

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

За що відповідає ваш плагін

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

  • Конфігурацію — визначення облікового запису й майстер налаштування
  • Безпеку — політику особистих повідомлень і списки дозволених користувачів
  • Сполучення — процес схвалення особистих повідомлень
  • Граматику сеансів — відображення ідентифікаторів розмов постачальника на базові чати, ідентифікатори гілок і резервні батьківські варіанти
  • Вихідні повідомлення — надсилання тексту, медіафайлів і опитувань на платформу
  • Гілки — спосіб упорядкування відповідей у гілки
  • Індикатор введення Heartbeat — необов’язкові сигнали введення/зайнятості для цілей доставки Heartbeat

Ядро відповідає за спільний інструмент повідомлень, підключення промптів, зовнішню форму ключа сеансу, загальний облік :thread: і диспетчеризацію.

Адаптер повідомлень

Надайте адаптер message з defineChannelMessageAdapter із openclaw/plugin-sdk/channel-outbound. Оголошуйте лише довготривалі можливості остаточного надсилання, які фактично підтримує ваш нативний транспорт, і підкріпіть їх контрактним тестом, що підтверджує нативний побічний ефект і повернену квитанцію. Спрямуйте надсилання тексту й медіафайлів до тих самих транспортних функцій, які використовує застарілий адаптер outbound. Повний контракт API, матрицю можливостей, правила квитанцій, фіналізацію попереднього перегляду наживо, політику підтвердження отримання, тести й таблицю міграції див. у API вихідних повідомлень каналу.

Якщо ваш наявний адаптер 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 вхідних повідомлень каналу.

Індикатори введення

Якщо ваш канал підтримує індикатори введення поза відповідями на вхідні повідомлення, надайте 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 надає каналу поведінку схвалення для exec і plugin без жорстко закодованих розгалужень у ядрі.
  • Ядро також відповідає за сповіщення про переспрямування схвалень. Плагіни каналів не повинні надсилати власні подальші повідомлення «схвалення надіслано в особисті повідомлення / інший канал» із createChannelNativeApprovalRuntime; натомість надавайте точну маршрутизацію від джерела + до особистих повідомлень особи, яка схвалює, через спільні допоміжні засоби можливостей схвалення й дозвольте ядру агрегувати фактичні доставлення, перш ніж публікувати будь-яке сповіщення назад у чат, що ініціював запит.
  • Зберігайте вид ідентифікатора доставленого схвалення наскрізно. Нативні клієнти не повинні вгадувати або переписувати маршрутизацію схвалень exec і plugin на основі локального стану каналу.
  • Передайте цей явний approvalKind до resolveApprovalOverGateway. Це використовує канонічний сервіс approval.resolve і повертає зареєстрованого переможця, коли інша поверхня відповідає першою. Старіший явний вхід resolveMethod залишається для елементів керування на основі команд; нові нативні дії не повинні використовувати його або визначати вид з ідентифікатора.
  • Різні види схвалень можуть навмисно надавати різні нативні поверхні. Поточні вбудовані приклади: Matrix зберігає однакову нативну маршрутизацію особистих повідомлень/каналів і взаємодію з реакціями для схвалень exec і plugin, водночас дозволяючи автентифікації відрізнятися за видом схвалення; Slack зберігає доступність нативної маршрутизації схвалень як для ідентифікаторів exec, так і для ідентифікаторів plugin.
  • 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
   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, щоб уникнути завантаження непов’язаних допоміжних засобів середовища виконання для вхідних даних.

Покроковий огляд

  • Пакет і маніфест

    Створіть стандартні файли плагіна. Поле channels у openclaw.plugin.json (а не поле kind) позначає маніфест як власника каналу. Повний перелік метаданих пакета див. у розділі Налаштування та конфігурація плагіна:

    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",    "blurb": "Підключіть OpenClaw до чату Acme."  }}}
    openclaw.plugin.json
    {"id": "acme-chat","channels": ["acme-chat"],"name": "Чат Acme","description": "Плагін каналу чату Acme","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      }    }  }}}

    configSchema перевіряє plugins.entries.acme-chat.config. Використовуйте його для налаштувань, що належать плагіну й не є конфігурацією облікового запису каналу. channelConfigs.acme-chat.schema перевіряє channels.acme-chat і є джерелом холодного шляху, яке використовується схемою конфігурації, налаштуванням та інтерфейсом до завантаження середовища виконання плагіна. Повний опис полів верхнього рівня див. у розділі Маніфест плагіна.

  • Створення об’єкта плагіна каналу

    Інтерфейс ChannelPlugin має багато необов’язкових поверхонь адаптера. Почніть із мінімального набору — id, config і setup — та додавайте адаптери за потреби.

    Створіть src/channel.ts:

    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&lt;ResolvedAccount&gt;({  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",      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, щоб середовище виконання та міграція читали той самий контракт.

    Що createChatChannelPlugin робить за вас

    Замість реалізації низькорівневих інтерфейсів адаптерів вручну ви передаєте декларативні параметри, а побудовник їх компонуватиме:

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

    Якщо потрібен повний контроль, замість декларативних параметрів також можна передавати необроблені об’єкти адаптерів.

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

  • Підключення точки входу

    Створіть index.ts:

    index.ts
    import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js"; export default defineChannelPluginEntry({  id: "acme-chat",  name: "Чат Acme",  description: "Плагін каналу чату Acme",  plugin: acmeChatPlugin,  registerCliMetadata(api) {    api.registerCli(      ({ program }) => {        program          .command("acme-chat")          .description("Керування чатом Acme");      },      {        descriptors: [          {            name: "acme-chat",            description: "Керування чатом Acme",            hasSubcommands: false,          },        ],      },    );  },  registerFull(api) {    api.registerGatewayMethod(/* ... */);  },});

    Розміщуйте дескриптори CLI, що належать каналу, у registerCliMetadata(...), щоб OpenClaw міг показувати їх у кореневій довідці без активації повного середовища виконання каналу, тоді як звичайне повне завантаження й надалі використовуватиме ті самі дескриптори для фактичної реєстрації команд. Залиште registerFull(...) для роботи лише під час виконання. defineChannelPluginEntry автоматично обробляє поділ режимів реєстрації. Якщо registerFull(...) реєструє методи RPC Gateway, використовуйте префікс, специфічний для плагіна. Простори імен адміністрування ядра (config.*, exec.approvals.*, wizard.*, update.*) залишаються зарезервованими та завжди визначаються як operator.admin. Усі параметри див. у розділі Точки входу.

  • Додавання точки входу налаштування

    Створіть setup-entry.ts для полегшеного завантаження під час початкового налаштування:

    setup-entry.ts
    import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js"; export default defineSetupPluginEntry(acmeChatPlugin);

    OpenClaw завантажує її замість повної точки входу, коли канал вимкнений або не налаштований. Це дає змогу не завантажувати важкий код середовища виконання під час процесів налаштування. Докладніше див. у розділі Налаштування та конфігурація.

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

  • Оброблення вхідних повідомлень

    Плагін має отримувати повідомлення з платформи й переспрямовувати їх до 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;    },  });}
  • Тестування

    Пишіть тести поруч із кодом у src/channel.test.ts:

    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/

    Спільні допоміжні засоби для тестування описано в розділі Тестування.

  • Структура файлів

    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            # Сховище середовища виконання (за потреби)

    Розширені теми

    Наступні кроки

    Пов’язані матеріали

    Was this useful?
    On this page

    On this page