Перейти до основного вмісту

Внутрішні механізми Plugin

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

Публічна модель можливостей

Можливості — це публічна модель власних Plugin у межах OpenClaw. Кожен власний Plugin OpenClaw реєструється для одного або кількох типів можливостей:
МожливістьМетод реєстраціїПриклади Plugin
Текстовий інференсapi.registerProvider(...)openai, anthropic
Backend інференсу CLIapi.registerCliBackend(...)openai, anthropic
Мовленняapi.registerSpeechProvider(...)elevenlabs, microsoft
Транскрибування в реальному часіapi.registerRealtimeTranscriptionProvider(...)openai
Голос у реальному часіapi.registerRealtimeVoiceProvider(...)openai
Розуміння медіаapi.registerMediaUnderstandingProvider(...)openai, google
Генерація зображеньapi.registerImageGenerationProvider(...)openai, google, fal, minimax
Генерація музикиapi.registerMusicGenerationProvider(...)google, minimax
Генерація відеоapi.registerVideoGenerationProvider(...)qwen
Отримання вебданихapi.registerWebFetchProvider(...)firecrawl
Вебпошукapi.registerWebSearchProvider(...)google
Канал / обмін повідомленнямиapi.registerChannel(...)msteams, matrix
Plugin, який не реєструє жодної можливості, але надає хуки, інструменти або сервіси, є застарілим plugin лише з хуками. Цей шаблон і далі повністю підтримується.

Позиція щодо зовнішньої сумісності

Модель можливостей уже реалізована в core і сьогодні використовується вбудованими/власними Plugin, але сумісність із зовнішніми Plugin усе ще потребує вищої планки, ніж «це експортується, отже це незмінний контракт». Поточні рекомендації:
  • наявні зовнішні Plugin: зберігайте працездатність інтеграцій на основі хуків; вважайте це базовим рівнем сумісності
  • нові вбудовані/власні Plugin: надавайте перевагу явній реєстрації можливостей замість vendor-specific звернень углиб системи або нових реалізацій лише з хуками
  • зовнішні Plugin, що переходять на реєстрацію можливостей: це дозволено, але вважайте допоміжні поверхні для конкретних можливостей такими, що еволюціонують, якщо в документації контракт явно не позначено як стабільний
Практичне правило:
  • API реєстрації можливостей — це цільовий напрямок
  • застарілі хуки залишаються найбезпечнішим шляхом без ризику поломок для зовнішніх Plugin під час переходу
  • не всі експортовані допоміжні підшляхи однакові; надавайте перевагу вузькому задокументованому контракту, а не випадковим допоміжним експортам

Форми Plugin

OpenClaw класифікує кожен завантажений Plugin за формою на основі його фактичної поведінки реєстрації, а не лише статичних метаданих:
  • plain-capability — реєструє рівно один тип можливості (наприклад, Plugin лише провайдера, як-от mistral)
  • hybrid-capability — реєструє кілька типів можливостей (наприклад, openai володіє текстовим інференсом, мовленням, розумінням медіа та генерацією зображень)
  • hook-only — реєструє лише хуки (типізовані або власні), без можливостей, інструментів, команд чи сервісів
  • non-capability — реєструє інструменти, команди, сервіси або маршрути, але не можливості
Використовуйте openclaw plugins inspect <id>, щоб побачити форму Plugin і розподіл можливостей. Докладніше дивіться в довіднику CLI.

Застарілі хуки

Хук before_agent_start залишається підтримуваним шляхом сумісності для Plugin лише з хуками. Від нього все ще залежать реальні застарілі Plugin. Напрямок:
  • зберігати його працездатність
  • документувати його як застарілий
  • для роботи з перевизначенням моделі/провайдера надавати перевагу before_model_resolve
  • для модифікації prompt надавати перевагу before_prompt_build
  • вилучати лише після зменшення реального використання і підтвердження безпечності міграції покриттям фікстур

Сигнали сумісності

Коли ви запускаєте openclaw doctor або openclaw plugins inspect <id>, ви можете побачити одну з таких міток:
СигналЗначення
config validКонфігурація коректно розбирається, а Plugin успішно визначаються
compatibility advisoryPlugin використовує підтримуваний, але старіший шаблон (наприклад, hook-only)
legacy warningPlugin використовує before_agent_start, який застарів
hard errorКонфігурація недійсна або Plugin не вдалося завантажити
Ні hook-only, ні before_agent_start сьогодні не зламають ваш Plugin — hook-only є рекомендаційним сигналом, а before_agent_start лише спричиняє попередження. Ці сигнали також відображаються в openclaw status --all і openclaw plugins doctor.

Огляд архітектури

Система Plugin в OpenClaw має чотири шари:
  1. Маніфест + виявлення OpenClaw знаходить кандидатів у Plugin у налаштованих шляхах, коренях робочих просторів, глобальних коренях розширень і вбудованих розширеннях. Під час виявлення спочатку зчитуються власні маніфести openclaw.plugin.json разом із підтримуваними маніфестами пакетів.
  2. Увімкнення + валідація Core вирішує, чи є знайдений Plugin увімкненим, вимкненим, заблокованим або вибраним для ексклюзивного слота, наприклад пам’яті.
  3. Завантаження часу виконання Власні Plugin OpenClaw завантажуються в межах процесу через jiti і реєструють можливості в центральному реєстрі. Сумісні пакети нормалізуються в записи реєстру без імпорту коду часу виконання.
  4. Використання поверхонь Решта OpenClaw читає реєстр, щоб надавати інструменти, канали, налаштування провайдерів, хуки, HTTP-маршрути, команди CLI та сервіси.
Зокрема для CLI Plugin, виявлення кореневих команд поділено на дві фази:
  • метадані під час розбору надходять із registerCli(..., { descriptors: [...] })
  • фактичний модуль CLI Plugin може залишатися лінивим і реєструватися лише під час першого виклику
Це дозволяє зберігати код CLI, який належить Plugin, усередині самого Plugin, водночас даючи OpenClaw можливість резервувати імена кореневих команд ще до розбору. Важлива межа проєктування:
  • виявлення + валідація конфігурації мають працювати на основі метаданих маніфесту/схеми без виконання коду Plugin
  • власна поведінка часу виконання надходить із шляху register(api) модуля Plugin
Такий поділ дозволяє OpenClaw валідувати конфігурацію, пояснювати відсутні або вимкнені Plugin і будувати підказки для UI/схеми ще до повної активації часу виконання.

Plugin каналів і спільний інструмент повідомлень

Plugin каналів не потрібно реєструвати окремий інструмент send/edit/react для звичайних дій чату. OpenClaw зберігає один спільний інструмент message у core, а Plugin каналів володіють специфічним для каналу виявленням і виконанням за ним. Поточна межа така:
  • core володіє хостом спільного інструмента message, підключенням до prompt, веденням сесій/гілок і диспетчеризацією виконання
  • Plugin каналів володіють виявленням дій у межах області, виявленням можливостей і будь-якими фрагментами схеми, специфічними для каналу
  • Plugin каналів володіють граматикою розмови сесії, специфічною для провайдера, наприклад тим, як ідентифікатори розмов кодують ідентифікатори гілок або успадковуються від батьківських розмов
  • Plugin каналів виконують фінальну дію через свій адаптер дій
Для Plugin каналів поверхня SDK — це ChannelMessageActionAdapter.describeMessageTool(...). Цей уніфікований виклик виявлення дозволяє Plugin повертати видимі дії, можливості та внески в схему разом, щоб ці частини не розходилися. Коли параметр інструмента повідомлень, специфічний для каналу, містить джерело медіа, наприклад локальний шлях або віддалений URL медіа, Plugin також має повертати mediaSourceParams з describeMessageTool(...). Core використовує цей явний список, щоб застосовувати нормалізацію шляхів sandbox і підказки щодо вихідного доступу до медіа без жорсткого кодування імен параметрів, які належать Plugin. Там слід надавати перевагу maps з областю дії на рівні дій, а не одному плоскому списку для всього каналу, щоб параметр медіа лише для профілю не нормалізувався для не пов’язаних дій, таких як send. Core передає область часу виконання в цей крок виявлення. Важливі поля містять:
  • accountId
  • currentChannelId
  • currentThreadTs
  • currentMessageId
  • sessionKey
  • sessionId
  • agentId
  • довірений вхідний requesterSenderId
Це важливо для Plugin, чутливих до контексту. Канал може приховувати або відкривати дії повідомлень залежно від активного облікового запису, поточної кімнати/гілки/повідомлення або довіреної ідентичності запитувача без жорсткого кодування специфічних для каналу гілок у core-інструменті message. Саме тому зміни маршрутизації embedded-runner усе ще належать до роботи Plugin: runner відповідає за передавання поточної ідентичності чату/сесії в межу виявлення Plugin, щоб спільний інструмент message відкривав правильну поверхню, яка належить каналу, для поточного ходу. Для допоміжних засобів виконання, які належать каналу, вбудовані Plugin мають зберігати runtime виконання у власних модулях розширень. Core більше не володіє runtime дій повідомлень Discord, Slack, Telegram або WhatsApp у src/agents/tools. Ми не публікуємо окремі підшляхи plugin-sdk/*-action-runtime, а вбудовані Plugin мають імпортувати свій локальний runtime-код напряму з модулів, що належать їхнім розширенням. Така сама межа застосовується і до SDK-швів, названих на честь провайдерів, загалом: core не має імпортувати зручні barrels, специфічні для каналів Slack, Discord, Signal, WhatsApp або подібних розширень. Якщо core потрібна певна поведінка, слід або використати власний barrel api.ts / runtime-api.ts вбудованого Plugin, або підняти цю потребу до вузької узагальненої можливості в спільному SDK. Зокрема для опитувань є два шляхи виконання:
  • outbound.sendPoll — спільна базова реалізація для каналів, що відповідають загальній моделі опитувань
  • actions.handleAction("poll") — бажаний шлях для специфічної для каналу семантики опитувань або додаткових параметрів опитування
Тепер Core відкладає спільний розбір опитувань до моменту, коли диспетчеризація опитування Plugin відхиляє дію, тож обробники опитувань, що належать Plugin, можуть приймати специфічні для каналу поля опитувань, не блокуючись спочатку загальним парсером опитувань. Повну послідовність запуску дивіться в розділі Конвеєр завантаження.

Модель володіння можливостями

OpenClaw розглядає власний Plugin як межу володіння для компанії або функції, а не як набір не пов’язаних інтеграцій. Це означає:
  • Plugin компанії зазвичай має володіти всіма поверхнями OpenClaw, що належать цій компанії
  • Plugin функції зазвичай має володіти повною поверхнею функції, яку він додає
  • канали мають споживати спільні можливості core замість того, щоб довільно повторно реалізовувати поведінку провайдерів
Приклади:
  • вбудований Plugin openai володіє поведінкою провайдера моделей OpenAI і поведінкою OpenAI для мовлення + голосу в реальному часі + розуміння медіа + генерації зображень
  • вбудований Plugin elevenlabs володіє поведінкою мовлення ElevenLabs
  • вбудований Plugin microsoft володіє поведінкою мовлення Microsoft
  • вбудований Plugin google володіє поведінкою провайдера моделей Google, а також поведінкою Google для розуміння медіа + генерації зображень + вебпошуку
  • вбудований Plugin firecrawl володіє поведінкою Firecrawl для отримання вебданих
  • вбудовані Plugin minimax, mistral, moonshot і zai володіють своїми backends розуміння медіа
  • вбудований Plugin qwen володіє поведінкою текстового провайдера Qwen, а також поведінкою для розуміння медіа і генерації відео
  • Plugin voice-call є Plugin функції: він володіє транспортом викликів, інструментами, CLI, маршрутами та мостом медіапотоку Twilio, але споживає спільні можливості мовлення, а також транскрибування в реальному часі й голосу в реальному часі замість прямого імпорту vendor Plugin
Бажаний кінцевий стан:
  • OpenAI живе в одному Plugin, навіть якщо він охоплює текстові моделі, мовлення, зображення та майбутнє відео
  • інший vendor може зробити те саме для власної області поверхонь
  • канали не повинні зважати на те, який vendor Plugin володіє провайдером; вони споживають спільний контракт можливостей, який надає core
Ось ключова відмінність:
  • plugin = межа володіння
  • capability = контракт core, який можуть реалізовувати або споживати кілька Plugin
Тому якщо OpenClaw додає нову область, наприклад відео, перше запитання не таке: «який провайдер має жорстко закодувати обробку відео?» Перше запитання таке: «який контракт можливостей відео має бути в core?» Щойно такий контракт з’являється, vendor Plugin можуть реєструватися для нього, а Plugin каналів/функцій можуть його споживати. Якщо можливість ще не існує, правильний крок зазвичай такий:
  1. визначити відсутню можливість у core
  2. відкрити її через API/runtime Plugin у типізований спосіб
  3. під’єднати канали/функції до цієї можливості
  4. дозволити vendor Plugin реєструвати реалізації
Це зберігає явне володіння й водночас уникає поведінки core, яка залежить від одного vendor або одноразового шляху коду, специфічного для Plugin.

Шарування можливостей

Користуйтеся цією ментальною моделлю, коли вирішуєте, де має бути код:
  • шар можливостей core: спільна оркестрація, політика, fallback, правила злиття конфігурації, семантика доставки та типізовані контракти
  • шар vendor Plugin: API, автентифікація, каталоги моделей, синтез мовлення, генерація зображень, майбутні backends відео, endpoints використання, специфічні для vendor
  • шар Plugin каналу/функції: інтеграція Slack/Discord/voice-call тощо, яка споживає можливості core і подає їх на певну поверхню
Наприклад, TTS має таку форму:
  • core володіє політикою TTS під час відповіді, порядком fallback, налаштуваннями та доставкою в канал
  • openai, elevenlabs і microsoft володіють реалізаціями синтезу
  • voice-call споживає допоміжний runtime засіб TTS для телефонії
Тому самому шаблону слід надавати перевагу й для майбутніх можливостей.

Приклад Plugin компанії з кількома можливостями

Plugin компанії має виглядати цілісно ззовні. Якщо OpenClaw має спільні контракти для моделей, мовлення, транскрибування в реальному часі, голосу в реальному часі, розуміння медіа, генерації зображень, генерації відео, отримання вебданих і вебпошуку, vendor може володіти всіма своїми поверхнями в одному місці:
import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/plugin-entry";
import {
  describeImageWithModel,
  transcribeOpenAiCompatibleAudio,
} from "openclaw/plugin-sdk/media-understanding";

const plugin: OpenClawPluginDefinition = {
  id: "exampleai",
  name: "ExampleAI",
  register(api) {
    api.registerProvider({
      id: "exampleai",
      // auth/model catalog/runtime hooks
    });

    api.registerSpeechProvider({
      id: "exampleai",
      // vendor speech config — implement the SpeechProviderPlugin interface directly
    });

    api.registerMediaUnderstandingProvider({
      id: "exampleai",
      capabilities: ["image", "audio", "video"],
      async describeImage(req) {
        return describeImageWithModel({
          provider: "exampleai",
          model: req.model,
          input: req.input,
        });
      },
      async transcribeAudio(req) {
        return transcribeOpenAiCompatibleAudio({
          provider: "exampleai",
          model: req.model,
          input: req.input,
        });
      },
    });

    api.registerWebSearchProvider(
      createPluginBackedWebSearchProvider({
        id: "exampleai-search",
        // credential + fetch logic
      }),
    );
  },
};

export default plugin;
Важливі не точні назви допоміжних засобів. Важлива форма:
  • один Plugin володіє поверхнею vendor
  • core, як і раніше, володіє контрактами можливостей
  • Plugin каналів і функцій споживають допоміжні засоби api.runtime.*, а не код vendor
  • тести контрактів можуть перевіряти, що Plugin зареєстрував можливості, якими він заявляє, що володіє

Приклад можливості: розуміння відео

OpenClaw уже розглядає розуміння зображень/аудіо/відео як одну спільну можливість. Тут застосовується та сама модель володіння:
  1. core визначає контракт розуміння медіа
  2. vendor Plugin реєструють describeImage, transcribeAudio і describeVideo, де це застосовно
  3. Plugin каналів і функцій споживають спільну поведінку core замість прямого підключення до коду vendor
Це дозволяє не вшивати в core відеоприпущення одного провайдера. Plugin володіє поверхнею vendor; core володіє контрактом можливості й поведінкою fallback. Генерація відео вже використовує ту саму послідовність: core володіє типізованим контрактом можливості та допоміжним runtime-засобом, а vendor Plugin реєструють реалізації api.registerVideoGenerationProvider(...) для цього контракту. Потрібен конкретний контрольний список розгортання? Дивіться Capability Cookbook.

Контракти та примусове забезпечення

Поверхня API Plugin навмисно типізована й централізована в OpenClawPluginApi. Цей контракт визначає підтримувані точки реєстрації та допоміжні runtime-засоби, на які може покладатися Plugin. Чому це важливо:
  • автори Plugin отримують єдиний стабільний внутрішній стандарт
  • core може відхиляти дубльоване володіння, наприклад коли два Plugin реєструють один і той самий id провайдера
  • під час запуску можна показувати діагностику з конкретними порадами для неправильної реєстрації
  • тести контрактів можуть забезпечувати володіння вбудованих Plugin і запобігати тихому дрейфу
Є два шари забезпечення:
  1. забезпечення під час runtime-реєстрації Реєстр Plugin перевіряє реєстрації під час завантаження Plugin. Наприклад: дубльовані id провайдерів, дубльовані id провайдерів мовлення та неправильно сформовані реєстрації призводять до діагностики Plugin, а не до невизначеної поведінки.
  2. тести контрактів Вбудовані Plugin фіксуються в реєстрах контрактів під час виконання тестів, щоб OpenClaw міг явно перевіряти володіння. Сьогодні це використовується для провайдерів моделей, провайдерів мовлення, провайдерів вебпошуку та володіння реєстрацією вбудованих Plugin.
Практичний ефект такий: OpenClaw заздалегідь знає, який Plugin якою поверхнею володіє. Це дає змогу core і каналам безшовно компонуватися, оскільки володіння задеклароване, типізоване та придатне до тестування, а не неявне.

Що має входити до контракту

Хороші контракти Plugin:
  • типізовані
  • невеликі
  • специфічні для можливості
  • належать core
  • придатні для повторного використання кількома Plugin
  • можуть споживатися каналами/функціями без знання про vendor
Погані контракти Plugin:
  • політика, специфічна для vendor, прихована в core
  • одноразові обхідні шляхи Plugin, які оминають реєстр
  • код каналу, що напряму звертається до реалізації vendor
  • ad hoc runtime-об’єкти, які не є частиною OpenClawPluginApi або api.runtime
Якщо є сумніви, підніміть рівень абстракції: спочатку визначте можливість, а потім дозвольте Plugin підключитися до неї.

Модель виконання

Власні Plugin OpenClaw виконуються в межах процесу разом із Gateway. Вони не ізольовані sandbox-середовищем. Завантажений власний Plugin має ту саму межу довіри на рівні процесу, що й код core. Наслідки:
  • власний Plugin може реєструвати інструменти, мережеві обробники, хуки та сервіси
  • помилка власного Plugin може аварійно завершити роботу gateway або дестабілізувати його
  • зловмисний власний Plugin еквівалентний довільному виконанню коду всередині процесу OpenClaw
Сумісні пакети за замовчуванням безпечніші, оскільки OpenClaw наразі розглядає їх як пакети метаданих/вмісту. У поточних релізах це переважно означає вбудовані Skills. Для невбудованих Plugin використовуйте allowlists і явні шляхи встановлення/завантаження. Розглядайте Plugin робочого простору як код для часу розробки, а не як типові production-налаштування. Для назв пакетів вбудованого робочого простору зберігайте прив’язку id Plugin до npm-імені: @openclaw/<id> за замовчуванням або схвалений типізований суфікс, наприклад -provider, -plugin, -speech, -sandbox або -media-understanding, якщо пакет навмисно відкриває вужчу роль Plugin. Важлива примітка щодо довіри:
  • plugins.allow довіряє id Plugin, а не походженню джерела.
  • Plugin робочого простору з тим самим id, що й у вбудованого Plugin, навмисно затіняє вбудовану копію, коли такий Plugin робочого простору ввімкнено/додано до allowlist.
  • Це нормально й корисно для локальної розробки, тестування патчів і hotfix-ів.

Межа експортів

OpenClaw експортує можливості, а не зручні реалізації. Зберігайте публічною реєстрацію можливостей. Прибирайте експорти допоміжних засобів, які не є контрактами:
  • підшляхи допоміжних засобів, специфічні для вбудованих Plugin
  • підшляхи runtime-проводки, не призначені як публічний API
  • зручні допоміжні засоби, специфічні для vendor
  • допоміжні засоби setup/onboarding, що є деталями реалізації
Деякі підшляхи допоміжних засобів вбудованих Plugin і далі залишаються в згенерованій карті експортів SDK задля сумісності та супроводу вбудованих Plugin. Поточні приклади містять plugin-sdk/feishu, plugin-sdk/feishu-setup, plugin-sdk/zalo, plugin-sdk/zalo-setup і кілька швів plugin-sdk/matrix*. Розглядайте їх як зарезервовані експорти деталей реалізації, а не як рекомендований шаблон SDK для нових сторонніх Plugin.

Конвеєр завантаження

Під час запуску OpenClaw приблизно виконує таке:
  1. виявляє корені кандидатів для Plugin
  2. зчитує власні маніфести або маніфести сумісних пакетів і метадані пакетів
  3. відхиляє небезпечних кандидатів
  4. нормалізує конфігурацію Plugin (plugins.enabled, allow, deny, entries, slots, load.paths)
  5. вирішує стан увімкнення для кожного кандидата
  6. завантажує увімкнені власні модулі через jiti
  7. викликає власні хуки register(api) (або activate(api) — застарілий псевдонім) і збирає реєстрації в реєстр Plugin
  8. відкриває реєстр для поверхонь команд/runtime
activate — це застарілий псевдонім для register — завантажувач визначає, що саме є (def.register ?? def.activate), і викликає це в тій самій точці. Усі вбудовані Plugin використовують register; для нових Plugin надавайте перевагу register.
Перевірки безпеки відбуваються до виконання runtime. Кандидати блокуються, коли точка входу виходить за межі кореня Plugin, шлях є world-writable або володіння шляхом виглядає підозріло для невбудованих Plugin.

Поведінка manifest-first

Маніфест — це джерело істини для control plane. OpenClaw використовує його, щоб:
  • ідентифікувати Plugin
  • виявляти задекларовані канали/Skills/схему конфігурації або можливості пакета
  • валідувати plugins.entries.<id>.config
  • доповнювати мітки/placeholder-значення Control UI
  • показувати метадані встановлення/каталогу
  • зберігати дешеві дескриптори активації та setup без завантаження runtime Plugin
Для власних Plugin runtime-модуль є частиною data plane. Він реєструє фактичну поведінку, таку як хуки, інструменти, команди або потоки провайдера. Необов’язкові блоки маніфесту activation і setup залишаються в control plane. Це лише дескриптори метаданих для планування активації та виявлення setup; вони не замінюють runtime-реєстрацію, register(...) або setupEntry. Перші активні споживачі активації тепер використовують підказки маніфесту щодо команд, каналів і провайдерів, щоб звузити завантаження Plugin до ширшої матеріалізації реєстру:
  • завантаження CLI звужується до Plugin, які володіють запитаною основною командою
  • визначення setup/Plugin каналу звужується до Plugin, які володіють запитаним id каналу
  • явне визначення setup/runtime провайдера звужується до Plugin, які володіють запитаним id провайдера
Виявлення setup тепер надає перевагу id, що належать дескрипторам, таким як setup.providers і setup.cliBackends, щоб звузити коло кандидатів Plugin, перш ніж повертатися до setup-api для Plugin, яким усе ще потрібні runtime-хуки на етапі setup. Якщо більше ніж один виявлений Plugin заявляє про один і той самий нормалізований id провайдера setup або backend CLI, пошук setup відмовляється від неоднозначного власника замість того, щоб покладатися на порядок виявлення.

Що кешує завантажувач

OpenClaw зберігає короткоживучі кеші в межах процесу для:
  • результатів виявлення
  • даних реєстру маніфестів
  • завантажених реєстрів Plugin
Ці кеші зменшують пікове навантаження під час запуску та повторних викликів команд. Їх безпечно розглядати як короткоживучі кеші продуктивності, а не як збереження стану. Примітка щодо продуктивності:
  • Установіть OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1 або OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1, щоб вимкнути ці кеші.
  • Налаштовуйте вікна кешу через OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS і OPENCLAW_PLUGIN_MANIFEST_CACHE_MS.

Модель реєстру

Завантажені Plugin не змінюють безпосередньо довільні глобальні об’єкти core. Вони реєструються в центральному реєстрі Plugin. Реєстр відстежує:
  • записи Plugin (ідентичність, джерело, походження, статус, діагностика)
  • інструменти
  • застарілі хуки й типізовані хуки
  • канали
  • провайдери
  • обробники Gateway RPC
  • HTTP-маршрути
  • реєстратори CLI
  • фонові сервіси
  • команди, що належать Plugin
Потім можливості core читають з цього реєстру замість того, щоб напряму взаємодіяти з модулями Plugin. Це зберігає односторонність завантаження:
  • модуль Plugin -> реєстрація в реєстрі
  • runtime core -> споживання реєстру
Це розділення важливе для підтримуваності. Воно означає, що більшості поверхонь core потрібна лише одна точка інтеграції: «читати реєстр», а не «створювати спеціальний випадок для кожного модуля Plugin».

Зворотні виклики прив’язування розмови

Plugin, які прив’язують розмову, можуть реагувати, коли схвалення завершено. Використовуйте api.onConversationBindingResolved(...), щоб отримати зворотний виклик після того, як запит на прив’язування буде схвалено або відхилено:
export default {
  id: "my-plugin",
  register(api) {
    api.onConversationBindingResolved(async (event) => {
      if (event.status === "approved") {
        // A binding now exists for this plugin + conversation.
        console.log(event.binding?.conversationId);
        return;
      }

      // The request was denied; clear any local pending state.
      console.log(event.request.conversation.conversationId);
    });
  },
};
Поля в корисному навантаженні зворотного виклику:
  • status: "approved" або "denied"
  • decision: "allow-once", "allow-always" або "deny"
  • binding: узгоджене прив’язування для схвалених запитів
  • request: початковий зведений запит, підказка від’єднання, id відправника та метадані розмови
Цей зворотний виклик є лише сповіщенням. Він не змінює того, кому дозволено прив’язувати розмову, і виконується після завершення обробки схвалення в core.

Runtime-хуки провайдера

Plugin провайдерів тепер мають два шари:
  • метадані маніфесту: providerAuthEnvVars для дешевого пошуку env-auth провайдера до завантаження runtime, providerAuthAliases для варіантів провайдера, які спільно використовують auth, channelEnvVars для дешевого пошуку env/setup каналу до завантаження runtime, а також providerAuthChoices для дешевих міток onboarding/auth-choice і метаданих прапорців CLI до завантаження runtime
  • хуки часу конфігурації: catalog / застарілий discovery плюс applyConfigDefaults
  • runtime-хуки: normalizeModelId, normalizeTransport, normalizeConfig, applyNativeStreamingUsageCompat, resolveConfigApiKey, resolveSyntheticAuth, resolveExternalAuthProfiles, shouldDeferSyntheticProfileAuth, resolveDynamicModel, prepareDynamicModel, normalizeResolvedModel, contributeResolvedModelCompat, capabilities, normalizeToolSchemas, inspectToolSchemas, resolveReasoningOutputMode, prepareExtraParams, createStreamFn, wrapStreamFn, resolveTransportTurnState, resolveWebSocketSessionPolicy, formatApiKey, refreshOAuth, buildAuthDoctorHint, matchesContextOverflowError, classifyFailoverReason, isCacheTtlEligible, buildMissingAuthMessage, suppressBuiltInModel, augmentModelCatalog, isBinaryThinking, supportsXHighThinking, resolveDefaultThinkingLevel, isModernModelRef, prepareRuntimeAuth, resolveUsageAuth, fetchUsageSnapshot, createEmbeddingProvider, buildReplayPolicy, sanitizeReplayHistory, validateReplayTurns, onModelSelected
OpenClaw, як і раніше, володіє загальним циклом агента, failover, обробкою транскриптів і політикою інструментів. Ці хуки є поверхнею розширення для поведінки, специфічної для провайдера, без потреби в повністю власному транспорті інференсу. Використовуйте маніфест providerAuthEnvVars, коли провайдер має облікові дані на основі env, які мають бачити узагальнені шляхи auth/status/model-picker без завантаження runtime Plugin. Використовуйте маніфест providerAuthAliases, коли один id провайдера має повторно використовувати env vars, auth profiles, auth на основі конфігурації та варіант onboarding choice для API-ключа іншого id провайдера. Використовуйте маніфест providerAuthChoices, коли поверхні CLI для onboarding/auth-choice мають знати choice id провайдера, мітки груп і просту auth-проводку з одним прапорцем без завантаження runtime провайдера. Зберігайте runtime envVars провайдера для операторських підказок, таких як мітки onboarding або setup vars для OAuth client-id/client-secret. Використовуйте маніфест channelEnvVars, коли канал має auth або setup на основі env, які мають бачити узагальнений shell-env fallback, перевірки config/status або підказки setup без завантаження runtime каналу.

Порядок хуків і використання

Для Plugin моделей/провайдерів OpenClaw викликає хуки приблизно в такому порядку. Стовпець «Коли використовувати» — це короткий посібник для ухвалення рішень.
#ХукЩо він робитьКоли використовувати
1catalogПублікує конфігурацію провайдера в models.providers під час генерації models.jsonПровайдер володіє каталогом або базовими значеннями base URL
2applyConfigDefaultsЗастосовує глобальні значення конфігурації за замовчуванням, які належать провайдеру, під час матеріалізації конфігураціїЗначення за замовчуванням залежать від режиму auth, env або семантики сімейства моделей провайдера
(вбудований пошук моделі)OpenClaw спочатку пробує звичайний шлях через реєстр/каталог(це не хук Plugin)
3normalizeModelIdНормалізує застарілі або preview-псевдоніми id моделей перед пошукомПровайдер володіє очищенням псевдонімів до канонічного визначення моделі
4normalizeTransportНормалізує api / baseUrl сімейства провайдера перед узагальненим складанням моделіПровайдер володіє очищенням транспорту для власних id провайдера в межах того самого сімейства транспорту
5normalizeConfigНормалізує models.providers.<id> до визначення runtime/провайдераПровайдеру потрібне очищення конфігурації, яке має жити разом із Plugin; допоміжні засоби вбудованого сімейства Google також страхують підтримувані записи конфігурації Google
6applyNativeStreamingUsageCompatЗастосовує compat-переписування native streaming usage до конфігурації провайдерівПровайдеру потрібні виправлення метаданих native streaming usage, що залежать від endpoint
7resolveConfigApiKeyВизначає auth на основі env-marker для конфігураційних провайдерів до завантаження runtime authПровайдер має власне визначення API-ключа через env-marker; amazon-bedrock також має тут вбудований resolver AWS env-marker
8resolveSyntheticAuthВиводить локальний/self-hosted або auth на основі конфігурації без збереження відкритого текстуПровайдер може працювати із synthetic/local маркером облікових даних
9resolveExternalAuthProfilesНакладає зовнішні профілі auth, що належать провайдеру; типове значення persistenceruntime-only для облікових даних, що належать CLI/appПровайдер повторно використовує зовнішні облікові дані auth без збереження скопійованих refresh token
10shouldDeferSyntheticProfileAuthПонижує пріоритет збережених synthetic placeholder-профілів порівняно з auth на основі env/configПровайдер зберігає synthetic placeholder-профілі, які не повинні мати вищий пріоритет
11resolveDynamicModelСинхронний fallback для id моделей, що належать провайдеру, але ще відсутні в локальному реєстріПровайдер приймає довільні id моделей від upstream
12prepareDynamicModelАсинхронний прогрів, після чого resolveDynamicModel запускається зновуПровайдеру потрібні мережеві метадані до визначення невідомих id
13normalizeResolvedModelОстаточне переписування перед тим, як embedded runner використає визначену модельПровайдеру потрібні переписування транспорту, але при цьому він усе ще використовує транспорт core
14contributeResolvedModelCompatДодає compat-прапорці для моделей vendor за іншим сумісним транспортомПровайдер розпізнає власні моделі на proxy-транспортах, не перебираючи на себе провайдера
15capabilitiesМетадані транскрипту/інструментів, що належать провайдеру, і використовуються спільною логікою coreПровайдеру потрібні особливості транскрипту/сімейства провайдера
16normalizeToolSchemasНормалізує схеми інструментів до того, як їх побачить embedded runnerПровайдеру потрібне очищення схем для сімейства транспорту
17inspectToolSchemasПоказує діагностику схем, що належить провайдеру, після нормалізаціїПровайдер хоче мати попередження щодо ключових слів без навчання core правилам, специфічним для провайдера
18resolveReasoningOutputModeВибирає native чи tagged-контракт виводу reasoningПровайдеру потрібен tagged reasoning/final output замість native-полів
19prepareExtraParamsНормалізація параметрів запиту до застосування узагальнених обгорток параметрів потокуПровайдеру потрібні параметри запиту за замовчуванням або очищення параметрів для конкретного провайдера
20createStreamFnПовністю замінює звичайний шлях потоку власним транспортомПровайдеру потрібен власний wire protocol, а не просто обгортка
21wrapStreamFnОбгортка потоку після застосування узагальнених обгортокПровайдеру потрібні обгортки сумісності заголовків/тіла/моделі запиту без власного транспорту
22resolveTransportTurnStateПрикріплює native-заголовки транспорту або метадані для кожного ходуПровайдер хоче, щоб узагальнені транспорти надсилали native-ідентичність ходу провайдера
23resolveWebSocketSessionPolicyПрикріплює native-заголовки WebSocket або політику cooling-down сесіїПровайдер хоче, щоб узагальнені WS-транспорти налаштовували заголовки сесії або політику fallback
24formatApiKeyФорматер профілю auth: збережений профіль стає runtime-рядком apiKeyПровайдер зберігає додаткові метадані auth і потребує власної форми runtime-токена
25refreshOAuthПеревизначення оновлення OAuth для власних endpoint оновлення або політики помилки оновленняПровайдер не відповідає спільним refreshers pi-ai
26buildAuthDoctorHintПідказка виправлення, що додається, коли оновлення OAuth не вдаєтьсяПровайдеру потрібна власна порада з відновлення auth після помилки оновлення
27matchesContextOverflowErrorMatcher переповнення контекстного вікна, що належить провайдеруПровайдер має сирі помилки переповнення, які узагальнені евристики пропустили б
28classifyFailoverReasonКласифікація причини failover, що належить провайдеруПровайдер може зіставляти сирі API/транспортні помилки з rate-limit, overload тощо
29isCacheTtlEligibleПолітика prompt-cache для proxy/backhaul-провайдерівПровайдеру потрібне керування TTL кешу, специфічне для proxy
30buildMissingAuthMessageЗамінник узагальненого повідомлення відновлення за відсутності authПровайдеру потрібна власна підказка відновлення за відсутності auth
31suppressBuiltInModelПриховування застарілих upstream-моделей плюс необов’язкова підказка про помилку для користувачаПровайдеру потрібно приховати застарілі upstream-рядки або замінити їх підказкою від vendor
32augmentModelCatalogSynthetic/остаточні рядки каталогу, додані після виявленняПровайдеру потрібні synthetic-рядки прямої сумісності в models list і вибирачах
33isBinaryThinkingПеремикач reasoning увімк./вимк. для провайдерів із binary-thinkingПровайдер відкриває лише binary thinking on/off
34supportsXHighThinkingПідтримка reasoning xhigh для вибраних моделейПровайдер хоче xhigh лише для підмножини моделей
35resolveDefaultThinkingLevelТиповий рівень /think для конкретного сімейства моделейПровайдер володіє типовою політикою /think для сімейства моделей
36isModernModelRefMatcher сучасної моделі для фільтрів live profile і вибору smokeПровайдер володіє зіставленням preferred-model для live/smoke
37prepareRuntimeAuthОбмінює налаштовані облікові дані на фактичний runtime-токен/ключ безпосередньо перед інференсомПровайдеру потрібен обмін токена або короткоживучі облікові дані запиту
38resolveUsageAuthВизначає облікові дані usage/billing для /usage і пов’язаних поверхонь статусуПровайдеру потрібен власний парсер токенів usage/quota або інші облікові дані для usage
39fetchUsageSnapshotОтримує та нормалізує знімки usage/quota, специфічні для провайдера, після визначення authПровайдеру потрібен endpoint usage, специфічний для провайдера, або парсер корисного навантаження
40createEmbeddingProviderСтворює адаптер embedding, що належить провайдеру, для пам’яті/пошукуПоведінка memory embedding має належати Plugin провайдера
41buildReplayPolicyПовертає політику replay, яка керує обробкою транскриптів для провайдераПровайдеру потрібна власна політика транскриптів (наприклад, видалення блоків thinking)
42sanitizeReplayHistoryПереписує історію replay після узагальненого очищення транскриптуПровайдеру потрібні власні переписування replay, специфічні для провайдера, понад спільні допоміжні засоби Compaction
43validateReplayTurnsОстаточна валідація або переформатування ходів replay перед embedded runnerТранспорту провайдера потрібна суворіша валідація ходів після узагальненої санітизації
44onModelSelectedЗапускає побічні ефекти після вибору моделі, що належать провайдеруПровайдеру потрібна телеметрія або стан, що належить провайдеру, коли модель стає активною
normalizeModelId, normalizeTransport і normalizeConfig спочатку перевіряють відповідний Plugin провайдера, а потім переходять до інших Plugin провайдерів, здатних обробляти хуки, доки один із них справді не змінить id моделі або transport/config. Це зберігає працездатність shim-рішень alias/compat для провайдерів без вимоги, щоб викликач знав, який саме вбудований Plugin володіє переписуванням. Якщо жоден хук провайдера не переписує підтримуваний запис конфігурації сімейства Google, нормалізатор конфігурації вбудованого Google усе одно застосовує це сумісне очищення. Якщо провайдеру потрібен повністю власний wire protocol або власний виконавець запитів, це вже інший клас розширення. Ці хуки призначені для поведінки провайдера, яка все ще виконується в межах звичайного циклу інференсу OpenClaw.

Приклад провайдера

api.registerProvider({
  id: "example-proxy",
  label: "Example Proxy",
  auth: [],
  catalog: {
    order: "simple",
    run: async (ctx) => {
      const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey;
      if (!apiKey) {
        return null;
      }
      return {
        provider: {
          baseUrl: "https://proxy.example.com/v1",
          apiKey,
          api: "openai-completions",
          models: [{ id: "auto", name: "Auto" }],
        },
      };
    },
  },
  resolveDynamicModel: (ctx) => ({
    id: ctx.modelId,
    name: ctx.modelId,
    provider: "example-proxy",
    api: "openai-completions",
    baseUrl: "https://proxy.example.com/v1",
    reasoning: false,
    input: ["text"],
    cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
    contextWindow: 128000,
    maxTokens: 8192,
  }),
  prepareRuntimeAuth: async (ctx) => {
    const exchanged = await exchangeToken(ctx.apiKey);
    return {
      apiKey: exchanged.token,
      baseUrl: exchanged.baseUrl,
      expiresAt: exchanged.expiresAt,
    };
  },
  resolveUsageAuth: async (ctx) => {
    const auth = await ctx.resolveOAuthToken();
    return auth ? { token: auth.token } : null;
  },
  fetchUsageSnapshot: async (ctx) => {
    return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn);
  },
});

Вбудовані приклади

  • Anthropic використовує resolveDynamicModel, capabilities, buildAuthDoctorHint, resolveUsageAuth, fetchUsageSnapshot, isCacheTtlEligible, resolveDefaultThinkingLevel, applyConfigDefaults, isModernModelRef і wrapStreamFn, оскільки він володіє прямою сумісністю з Claude 4.6, підказками сімейства провайдера, вказівками з відновлення auth, інтеграцією з endpoint usage, придатністю prompt-cache, значеннями конфігурації за замовчуванням, що враховують auth, типовою/адаптивною політикою thinking для Claude і формуванням потоку, специфічним для Anthropic, для beta headers, /fast / serviceTier та context1m.
  • Допоміжні засоби потоку Anthropic, специфічні для Claude, поки що залишаються у власному публічному шві api.ts / contract-api.ts вбудованого Plugin. Ця поверхня пакета експортує wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier і низькорівневі засоби побудови обгорток Anthropic замість розширення узагальненого SDK навколо правил beta headers одного провайдера.
  • OpenAI використовує resolveDynamicModel, normalizeResolvedModel і capabilities, а також buildMissingAuthMessage, suppressBuiltInModel, augmentModelCatalog, supportsXHighThinking і isModernModelRef, оскільки він володіє прямою сумісністю з GPT-5.4, прямою нормалізацією OpenAI openai-completions -> openai-responses, підказками auth з урахуванням Codex, приглушенням Spark, synthetic-рядками списку OpenAI та політикою thinking / live-model для GPT-5; сімейство потоків openai-responses-defaults володіє спільними native-обгортками OpenAI Responses для attribution headers, /fast/serviceTier, багатослівності тексту, native вебпошуку Codex, формуванням корисного навантаження reasoning-compat і керуванням контекстом Responses.
  • OpenRouter використовує catalog разом із resolveDynamicModel і prepareDynamicModel, оскільки провайдер є наскрізним і може відкривати нові id моделей раніше, ніж оновиться статичний каталог OpenClaw; він також використовує capabilities, wrapStreamFn і isCacheTtlEligible, щоб заголовки запитів, метадані маршрутизації, патчі reasoning і політика prompt-cache, специфічні для провайдера, не потрапляли в core. Його політика replay надходить із сімейства passthrough-gemini, тоді як сімейство потоків openrouter-thinking володіє ін’єкцією reasoning через proxy і пропусками для непідтримуваних моделей / auto.
  • GitHub Copilot використовує catalog, auth, resolveDynamicModel і capabilities, а також prepareRuntimeAuth і fetchUsageSnapshot, оскільки йому потрібні вхід через пристрій, що належить провайдеру, поведінка fallback моделей, особливості транскриптів Claude, обмін GitHub token -> Copilot token і endpoint usage, що належить провайдеру.
  • OpenAI Codex використовує catalog, resolveDynamicModel, normalizeResolvedModel, refreshOAuth і augmentModelCatalog, а також prepareExtraParams, resolveUsageAuth і fetchUsageSnapshot, оскільки він усе ще працює на транспортах core OpenAI, але володіє нормалізацією свого transport/base URL, політикою fallback для оновлення OAuth, типовим вибором транспорту, synthetic-рядками каталогу Codex та інтеграцією з endpoint usage ChatGPT; він використовує те саме сімейство потоків openai-responses-defaults, що й прямий OpenAI.
  • Google AI Studio та Gemini CLI OAuth використовують resolveDynamicModel, buildReplayPolicy, sanitizeReplayHistory, resolveReasoningOutputMode, wrapStreamFn і isModernModelRef, оскільки сімейство replay google-gemini володіє fallback прямої сумісності для Gemini 3.1, native-валідацією replay Gemini, санітизацією bootstrap replay, режимом tagged reasoning-output і зіставленням сучасних моделей, тоді як сімейство потоків google-thinking володіє нормалізацією payload thinking Gemini; Gemini CLI OAuth також використовує formatApiKey, resolveUsageAuth і fetchUsageSnapshot для форматування токена, розбору токена і проводки endpoint quota.
  • Anthropic Vertex використовує buildReplayPolicy через сімейство replay anthropic-by-model, щоб очищення replay, специфічне для Claude, залишалося в межах id Claude, а не кожного транспорту anthropic-messages.
  • Amazon Bedrock використовує buildReplayPolicy, matchesContextOverflowError, classifyFailoverReason і resolveDefaultThinkingLevel, оскільки він володіє класифікацією помилок throttle/not-ready/context-overflow, специфічною для Bedrock, для трафіку Anthropic-on-Bedrock; його політика replay і далі використовує той самий guard anthropic-by-model, призначений лише для Claude.
  • OpenRouter, Kilocode, Opencode і Opencode Go використовують buildReplayPolicy через сімейство replay passthrough-gemini, оскільки вони проксіюють моделі Gemini через транспорти, сумісні з OpenAI, і потребують санітизації thought-signature Gemini без native-валідації replay Gemini або переписувань bootstrap.
  • MiniMax використовує buildReplayPolicy через сімейство replay hybrid-anthropic-openai, оскільки один провайдер володіє як семантикою повідомлень Anthropic, так і OpenAI-сумісною семантикою; він зберігає відкидання блоків thinking лише для Claude на боці Anthropic, водночас перевизначаючи режим виводу reasoning назад на native, а сімейство потоків minimax-fast-mode володіє переписуванням моделей fast-mode на спільному шляху потоку.
  • Moonshot використовує catalog разом із wrapStreamFn, оскільки він і далі використовує спільний транспорт OpenAI, але потребує нормалізації payload thinking, що належить провайдеру; сімейство потоків moonshot-thinking відображає config і стан /think у його native payload binary thinking.
  • Kilocode використовує catalog, capabilities, wrapStreamFn і isCacheTtlEligible, оскільки йому потрібні заголовки запитів, що належать провайдеру, нормалізація payload reasoning, підказки транскриптів Gemini та керування TTL кешу Anthropic; сімейство потоків kilocode-thinking утримує ін’єкцію thinking Kilo на спільному шляху proxy-потоку, водночас пропускаючи kilo/auto та інші proxy id моделей, які не підтримують явні payload reasoning.
  • Z.AI використовує resolveDynamicModel, prepareExtraParams, wrapStreamFn, isCacheTtlEligible, isBinaryThinking, isModernModelRef, resolveUsageAuth і fetchUsageSnapshot, оскільки він володіє fallback для GLM-5, значеннями tool_stream за замовчуванням, UX binary thinking, зіставленням сучасних моделей і як auth для usage, так і отриманням quota; сімейство потоків tool-stream-default-on утримує обгортку tool_stream, увімкнену за замовчуванням, поза вручну написаним glue-кодом для кожного провайдера.
  • xAI використовує normalizeResolvedModel, normalizeTransport, contributeResolvedModelCompat, prepareExtraParams, wrapStreamFn, resolveSyntheticAuth, resolveDynamicModel і isModernModelRef, оскільки він володіє нормалізацією native-транспорту xAI Responses, переписуванням псевдонімів fast-mode для Grok, типовим tool_stream, очищенням strict-tool / reasoning-payload, повторним використанням fallback auth для інструментів, що належать Plugin, прямим визначенням моделей Grok і compat-патчами, що належать провайдеру, такими як профіль schema інструментів xAI, непідтримувані ключові слова schema, native web_search і декодування аргументів виклику інструментів із HTML-сутностями.
  • Mistral, OpenCode Zen і OpenCode Go використовують лише capabilities, щоб не виносити особливості транскриптів/інструментів у core.
  • Вбудовані провайдери лише з каталогом, такі як byteplus, cloudflare-ai-gateway, huggingface, kimi-coding, nvidia, qianfan, synthetic, together, venice, vercel-ai-gateway і volcengine, використовують лише catalog.
  • Qwen використовує catalog для свого текстового провайдера разом зі спільними реєстраціями розуміння медіа й генерації відео для своїх мультимодальних поверхонь.
  • MiniMax і Xiaomi використовують catalog разом із usage-хуками, оскільки їхня поведінка /usage належить Plugin, хоча сам інференс і далі виконується через спільні транспорти.

Допоміжні засоби runtime

Plugin можуть отримувати доступ до вибраних допоміжних засобів core через api.runtime. Для TTS:
const clip = await api.runtime.tts.textToSpeech({
  text: "Hello from OpenClaw",
  cfg: api.config,
});

const result = await api.runtime.tts.textToSpeechTelephony({
  text: "Hello from OpenClaw",
  cfg: api.config,
});

const voices = await api.runtime.tts.listVoices({
  provider: "elevenlabs",
  cfg: api.config,
});
Примітки:
  • textToSpeech повертає звичайне корисне навантаження виводу TTS із core для поверхонь файлів/голосових нотаток.
  • Використовує конфігурацію core messages.tts і вибір провайдера.
  • Повертає PCM-аудіобуфер + sample rate. Plugin мають виконати повторну дискретизацію/кодування для провайдерів.
  • listVoices є необов’язковим для кожного провайдера. Використовуйте його для вибирачів голосу або потоків setup, що належать vendor.
  • Списки голосів можуть містити багатші метадані, такі як locale, gender і теги personality для вибирачів, обізнаних про провайдера.
  • OpenAI і ElevenLabs сьогодні підтримують телефонію. Microsoft — ні.
Plugin також можуть реєструвати провайдери мовлення через api.registerSpeechProvider(...).
api.registerSpeechProvider({
  id: "acme-speech",
  label: "Acme Speech",
  isConfigured: ({ config }) => Boolean(config.messages?.tts),
  synthesize: async (req) => {
    return {
      audioBuffer: Buffer.from([]),
      outputFormat: "mp3",
      fileExtension: ".mp3",
      voiceCompatible: false,
    };
  },
});
Примітки:
  • Зберігайте політику TTS, fallback і доставку відповіді в core.
  • Використовуйте провайдери мовлення для поведінки синтезу, що належить vendor.
  • Застарілий вхід Microsoft edge нормалізується до id провайдера microsoft.
  • Бажана модель володіння є орієнтованою на компанію: один vendor Plugin може володіти текстовими, мовленнєвими, графічними та майбутніми медіапровайдерами, коли OpenClaw додає відповідні контракти можливостей.
Для розуміння зображень/аудіо/відео Plugin реєструють один типізований провайдер розуміння медіа, а не узагальнений набір ключ/значення:
api.registerMediaUnderstandingProvider({
  id: "google",
  capabilities: ["image", "audio", "video"],
  describeImage: async (req) => ({ text: "..." }),
  transcribeAudio: async (req) => ({ text: "..." }),
  describeVideo: async (req) => ({ text: "..." }),
});
Примітки:
  • Зберігайте оркестрацію, fallback, конфігурацію та підключення каналів у core.
  • Зберігайте поведінку vendor у Plugin провайдера.
  • Адитивне розширення має залишатися типізованим: нові необов’язкові методи, нові необов’язкові поля результату, нові необов’язкові можливості.
  • Генерація відео вже дотримується того самого шаблону:
    • core володіє контрактом можливості та допоміжним runtime-засобом
    • vendor Plugin реєструють api.registerVideoGenerationProvider(...)
    • Plugin функцій/каналів споживають api.runtime.videoGeneration.*
Для допоміжних runtime-засобів розуміння медіа Plugin можуть викликати:
const image = await api.runtime.mediaUnderstanding.describeImageFile({
  filePath: "/tmp/inbound-photo.jpg",
  cfg: api.config,
  agentDir: "/tmp/agent",
});

const video = await api.runtime.mediaUnderstanding.describeVideoFile({
  filePath: "/tmp/inbound-video.mp4",
  cfg: api.config,
});
Для транскрибування аудіо Plugin можуть використовувати або runtime розуміння медіа, або старіший псевдонім STT:
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({
  filePath: "/tmp/inbound-audio.ogg",
  cfg: api.config,
  // Optional when MIME cannot be inferred reliably:
  mime: "audio/ogg",
});
Примітки:
  • api.runtime.mediaUnderstanding.* — це бажана спільна поверхня для розуміння зображень/аудіо/відео.
  • Використовує конфігурацію аудіо розуміння медіа з core (tools.media.audio) і порядок fallback провайдерів.
  • Повертає { text: undefined }, коли вихід транскрибування не створюється (наприклад, у разі пропущеного/непідтримуваного вводу).
  • api.runtime.stt.transcribeAudioFile(...) залишається псевдонімом для сумісності.
Plugin також можуть запускати фонові виконання subagent через api.runtime.subagent:
const result = await api.runtime.subagent.run({
  sessionKey: "agent:main:subagent:search-helper",
  message: "Expand this query into focused follow-up searches.",
  provider: "openai",
  model: "gpt-4.1-mini",
  deliver: false,
});
Примітки:
  • provider і model — це необов’язкові перевизначення для окремого запуску, а не постійні зміни сесії.
  • OpenClaw враховує ці поля перевизначення лише для довірених викликачів.
  • Для запусків fallback, що належать Plugin, оператори мають явно дозволити це через plugins.entries.<id>.subagent.allowModelOverride: true.
  • Використовуйте plugins.entries.<id>.subagent.allowedModels, щоб обмежити довірені Plugin конкретними канонічними цілями provider/model, або "*", щоб явно дозволити будь-яку ціль.
  • Виконання subagent для недовірених Plugin усе одно працюють, але запити на перевизначення відхиляються, а не тихо переходять до fallback.
Для вебпошуку Plugin можуть споживати спільний допоміжний runtime-засіб замість того, щоб звертатися до підключення інструментів агента:
const providers = api.runtime.webSearch.listProviders({
  config: api.config,
});

const result = await api.runtime.webSearch.search({
  config: api.config,
  args: {
    query: "OpenClaw plugin runtime helpers",
    count: 5,
  },
});
Plugin також можуть реєструвати провайдери вебпошуку через api.registerWebSearchProvider(...). Примітки:
  • Зберігайте вибір провайдера, визначення облікових даних і спільну семантику запитів у core.
  • Використовуйте провайдери вебпошуку для транспортів пошуку, специфічних для vendor.
  • api.runtime.webSearch.* — це бажана спільна поверхня для Plugin функцій/каналів, яким потрібна поведінка пошуку без залежності від обгортки інструментів агента.

api.runtime.imageGeneration

const result = await api.runtime.imageGeneration.generate({
  config: api.config,
  args: { prompt: "A friendly lobster mascot", size: "1024x1024" },
});

const providers = api.runtime.imageGeneration.listProviders({
  config: api.config,
});
  • generate(...): генерує зображення, використовуючи налаштований ланцюжок провайдерів генерації зображень.
  • listProviders(...): перелічує доступні провайдери генерації зображень та їхні можливості.

HTTP-маршрути Gateway

Plugin можуть відкривати HTTP-endpoint-и через api.registerHttpRoute(...).
api.registerHttpRoute({
  path: "/acme/webhook",
  auth: "plugin",
  match: "exact",
  handler: async (_req, res) => {
    res.statusCode = 200;
    res.end("ok");
    return true;
  },
});
Поля маршруту:
  • path: шлях маршруту в HTTP-сервері gateway.
  • auth: обов’язкове поле. Використовуйте "gateway", щоб вимагати звичайну auth Gateway, або "plugin" для auth/webhook verification, якими керує Plugin.
  • match: необов’язкове поле. "exact" (типово) або "prefix".
  • replaceExisting: необов’язкове поле. Дозволяє тому самому Plugin замінити власну наявну реєстрацію маршруту.
  • handler: повертає true, якщо маршрут обробив запит.
Примітки:
  • api.registerHttpHandler(...) вилучено, і він спричинить помилку завантаження Plugin. Натомість використовуйте api.registerHttpRoute(...).
  • Маршрути Plugin мають явно оголошувати auth.
  • Конфлікти точного path + match відхиляються, якщо не вказано replaceExisting: true, і один Plugin не може замінити маршрут іншого Plugin.
  • Маршрути, що перекриваються та мають різні рівні auth, відхиляються. Зберігайте ланцюги проходження exact/prefix лише в межах одного рівня auth.
  • Маршрути auth: "plugin" не отримують автоматично runtime-scopes оператора. Вони призначені для webhook-ів/перевірки підписів, якими керує Plugin, а не для привілейованих допоміжних викликів Gateway.
  • Маршрути auth: "gateway" працюють у межах runtime-scope запиту Gateway, але цей scope навмисно консервативний:
    • bearer auth зі спільним секретом (gateway.auth.mode = "token" / "password") утримує runtime-scopes маршрутів Plugin зафіксованими на operator.write, навіть якщо викликач надсилає x-openclaw-scopes
    • довірені HTTP-режими з ідентичністю (наприклад, trusted-proxy або gateway.auth.mode = "none" у приватному ingress) враховують x-openclaw-scopes лише тоді, коли цей заголовок явно присутній
    • якщо x-openclaw-scopes відсутній у таких запитах до маршрутів Plugin з ідентичністю, runtime-scope переходить до operator.write
  • Практичне правило: не вважайте маршрут Plugin із gateway-auth неявною admin-поверхнею. Якщо вашому маршруту потрібна поведінка лише для admin, вимагайте режим auth із носієм ідентичності та задокументуйте явний контракт заголовка x-openclaw-scopes.

Шляхи імпорту Plugin SDK

Під час створення Plugin використовуйте підшляхи SDK замість монолітного імпорту openclaw/plugin-sdk:
  • openclaw/plugin-sdk/plugin-entry для примітивів реєстрації Plugin.
  • openclaw/plugin-sdk/core для узагальненого спільного контракту, орієнтованого на Plugin.
  • openclaw/plugin-sdk/config-schema для експорту кореневої схеми Zod openclaw.json (OpenClawSchema).
  • Стабільні примітиви каналів, такі як openclaw/plugin-sdk/channel-setup, openclaw/plugin-sdk/setup-runtime, openclaw/plugin-sdk/setup-adapter-runtime, openclaw/plugin-sdk/setup-tools, openclaw/plugin-sdk/channel-pairing, openclaw/plugin-sdk/channel-contract, openclaw/plugin-sdk/channel-feedback, openclaw/plugin-sdk/channel-inbound, openclaw/plugin-sdk/channel-lifecycle, openclaw/plugin-sdk/channel-reply-pipeline, openclaw/plugin-sdk/command-auth, openclaw/plugin-sdk/secret-input і openclaw/plugin-sdk/webhook-ingress для спільного підключення setup/auth/reply/webhook. channel-inbound — це спільне місце для debounce, зіставлення згадок, допоміжних засобів політики вхідних згадок, форматування envelope і допоміжних засобів контексту вхідного envelope. channel-setup — це вузький шов setup для необов’язкового встановлення. setup-runtime — це безпечна для runtime поверхня setup, яка використовується setupEntry / відкладеним запуском, зокрема безпечними для імпорту адаптерами патчів setup. setup-adapter-runtime — це env-aware шов адаптера setup облікового запису. setup-tools — це малий шов допоміжних засобів CLI/архівів/документації (formatCliCommand, detectBinary, extractArchive, resolveBrewExecutable, formatDocsLink, CONFIG_DIR).
  • Доменні підшляхи, такі як openclaw/plugin-sdk/channel-config-helpers, openclaw/plugin-sdk/allow-from, openclaw/plugin-sdk/channel-config-schema, openclaw/plugin-sdk/telegram-command-config, openclaw/plugin-sdk/channel-policy, openclaw/plugin-sdk/approval-gateway-runtime, openclaw/plugin-sdk/approval-handler-adapter-runtime, openclaw/plugin-sdk/approval-handler-runtime, openclaw/plugin-sdk/approval-runtime, openclaw/plugin-sdk/config-runtime, openclaw/plugin-sdk/infra-runtime, openclaw/plugin-sdk/agent-runtime, openclaw/plugin-sdk/lazy-runtime, openclaw/plugin-sdk/reply-history, openclaw/plugin-sdk/routing, openclaw/plugin-sdk/status-helpers, openclaw/plugin-sdk/text-runtime, openclaw/plugin-sdk/runtime-store і openclaw/plugin-sdk/directory-runtime для спільних допоміжних засобів runtime/config. telegram-command-config — це вузький публічний шов для нормалізації/валідації власних команд Telegram і залишається доступним навіть якщо поверхня контракту вбудованого Telegram тимчасово недоступна. text-runtime — це спільний шов тексту/markdown/логування, зокрема видалення видимого для асистента тексту, допоміжні засоби рендерингу/розбиття markdown, засоби редагування, засоби directive-tag і утиліти безпечного тексту.
  • Специфічним для схвалення швам каналу слід надавати перевагу одному контракту approvalCapability у Plugin. Тоді core зчитує auth схвалення, доставку, рендеринг, native-routing і ліниву поведінку native-handler через цю одну можливість замість змішування поведінки схвалення з не пов’язаними полями Plugin.
  • openclaw/plugin-sdk/channel-runtime застарів і залишається лише як shim сумісності для старіших Plugin. Новий код має імпортувати натомість вужчі узагальнені примітиви, а код репозиторію не повинен додавати нові імпорти цього shim.
  • Внутрішні механізми вбудованих розширень залишаються приватними. Зовнішні Plugin повинні використовувати лише підшляхи openclaw/plugin-sdk/*. Код core/test OpenClaw може використовувати публічні точки входу репозиторію в корені пакета Plugin, такі як index.js, api.js, runtime-api.js, setup-entry.js і вузькоспрямовані файли, такі як login-qr-api.js. Ніколи не імпортуйте src/* пакета Plugin з core або з іншого розширення.
  • Поділ точок входу репозиторію: <plugin-package-root>/api.js — це barrel допоміжних засобів/типів, <plugin-package-root>/runtime-api.js — це barrel лише для runtime, <plugin-package-root>/index.js — це точка входу вбудованого Plugin, а <plugin-package-root>/setup-entry.js — це точка входу Plugin для setup.
  • Поточні приклади вбудованих провайдерів:
    • Anthropic використовує api.js / contract-api.js для допоміжних засобів потоку Claude, таких як wrapAnthropicProviderStream, засоби для beta-header і розбір service_tier.
    • OpenAI використовує api.js для builder-ів провайдера, допоміжних засобів типових моделей і builder-ів провайдера realtime.
    • OpenRouter використовує api.js для свого builder-а провайдера разом із допоміжними засобами onboarding/config, тоді як register.runtime.js усе ще може повторно експортувати узагальнені допоміжні засоби plugin-sdk/provider-stream для локального використання в репозиторії.
  • Публічні точки входу, завантажені через facade, надають перевагу активному знімку конфігурації runtime, коли він існує, а потім переходять до визначеного файла конфігурації на диску, коли OpenClaw ще не обслуговує знімок runtime.
  • Узагальнені спільні примітиви залишаються бажаним публічним контрактом SDK. Невеликий зарезервований набір брендових допоміжних швів вбудованих каналів для сумісності все ще існує. Розглядайте їх як шви для супроводу вбудованих рішень/сумісності, а не як нові цілі імпорту для сторонніх рішень; нові міжканальні контракти, як і раніше, мають з’являтися у вузьких підшляхах plugin-sdk/* або в локальних barrel api.js / runtime-api.js самого Plugin.
Примітка щодо сумісності:
  • Уникайте кореневого barrel openclaw/plugin-sdk у новому коді.
  • Спочатку надавайте перевагу вузьким стабільним примітивам. Новіші підшляхи setup/pairing/reply/feedback/contract/inbound/threading/command/secret-input/webhook/infra/ allowlist/status/message-tool є цільовим контрактом для нової роботи з вбудованими та зовнішніми Plugin. Розбір/зіставлення цілей має належати openclaw/plugin-sdk/channel-targets. Обмеження дій повідомлень і допоміжні засоби message-id для реакцій мають належати openclaw/plugin-sdk/channel-actions.
  • Barrels допоміжних засобів, специфічні для вбудованих розширень, за замовчуванням не є стабільними. Якщо допоміжний засіб потрібен лише вбудованому розширенню, тримайте його за локальним швом api.js або runtime-api.js цього розширення замість того, щоб просувати його в openclaw/plugin-sdk/<extension>.
  • Нові спільні шви допоміжних засобів мають бути узагальненими, а не брендовими для каналу. Спільний розбір цілей має належати openclaw/plugin-sdk/channel-targets; внутрішні механізми, специфічні для каналу, залишаються за локальним швом api.js або runtime-api.js Plugin-власника.
  • Підшляхи, специфічні для можливостей, такі як image-generation, media-understanding і speech, існують, бо вбудовані/власні Plugin уже використовують їх сьогодні. Їхня наявність сама по собі не означає, що кожен експортований допоміжний засіб є довгостроковим незмінним зовнішнім контрактом.

Схеми інструмента повідомлень

Plugin мають володіти внесками до схеми describeMessageTool(...), специфічними для каналу. Зберігайте поля, специфічні для провайдера, у Plugin, а не в спільному core. Для спільних переносних фрагментів схем повторно використовуйте узагальнені допоміжні засоби, експортовані через openclaw/plugin-sdk/channel-actions:
  • createMessageToolButtonsSchema() для payload у стилі сітки кнопок
  • createMessageToolCardSchema() для структурованих payload карток
Якщо форма схеми має сенс лише для одного провайдера, визначайте її у власному коді цього Plugin замість просування в спільний SDK.

Визначення цілей каналу

Plugin каналів мають володіти семантикою цілей, специфічною для каналу. Зберігайте спільний хост вихідних повідомлень узагальненим і використовуйте поверхню адаптера повідомлень для правил провайдера:
  • messaging.inferTargetChatType({ to }) визначає, чи нормалізовану ціль слід трактувати як direct, group або channel до пошуку в директорії.
  • messaging.targetResolver.looksLikeId(raw, normalized) повідомляє core, чи слід вводу одразу перейти до визначення як id-подібної цілі, а не до пошуку в директорії.
  • messaging.targetResolver.resolveTarget(...) — це fallback Plugin, коли core потребує остаточного визначення, що належить провайдеру, після нормалізації або після промаху в директорії.
  • messaging.resolveOutboundSessionRoute(...) володіє побудовою маршруту сесії, специфічного для провайдера, після того як ціль уже визначена.
Рекомендований поділ:
  • Використовуйте inferTargetChatType для категорійних рішень, які мають відбуватися до пошуку peer/group.
  • Використовуйте looksLikeId для перевірок «трактувати це як явний/native id цілі».
  • Використовуйте resolveTarget для fallback-нормалізації, специфічної для провайдера, а не для широкого пошуку в директорії.
  • Зберігайте native id провайдера, такі як id чату, id гілки, JID, handle та id кімнати, всередині значень target або параметрів, специфічних для провайдера, а не в узагальнених полях SDK.

Директорії на основі конфігурації

Plugin, які виводять записи директорії з конфігурації, мають зберігати цю логіку в Plugin і повторно використовувати спільні допоміжні засоби з openclaw/plugin-sdk/directory-runtime. Використовуйте це, коли каналу потрібні peer/group на основі конфігурації, наприклад:
  • peer DM на основі allowlist
  • налаштовані maps каналів/group
  • fallback статичної директорії в межах облікового запису
Спільні допоміжні засоби в directory-runtime обробляють лише узагальнені операції:
  • фільтрацію запитів
  • застосування ліміту
  • допоміжні засоби deduping/normalization
  • побудову ChannelDirectoryEntry[]
Інспекція облікових записів і нормалізація id, специфічні для каналу, мають залишатися в реалізації Plugin.

Каталоги провайдерів

Plugin провайдерів можуть визначати каталоги моделей для інференсу за допомогою registerProvider({ catalog: { run(...) { ... } } }). catalog.run(...) повертає ту саму форму, яку OpenClaw записує в models.providers:
  • { provider } для одного запису провайдера
  • { providers } для кількох записів провайдерів
Використовуйте catalog, коли Plugin володіє id моделей, базовими значеннями URL або метаданими моделей, захищеними auth, специфічними для провайдера. catalog.order керує тим, коли каталог Plugin зливається відносно вбудованих неявних провайдерів OpenClaw:
  • simple: прості провайдери з API-ключем або на основі env
  • profile: провайдери, які з’являються, коли існують профілі auth
  • paired: провайдери, які синтезують кілька пов’язаних записів провайдерів
  • late: останній прохід, після інших неявних провайдерів
Пізніші провайдери перемагають у разі колізії ключів, тому Plugin можуть навмисно перевизначити вбудований запис провайдера з тим самим id провайдера. Сумісність:
  • discovery усе ще працює як застарілий псевдонім
  • якщо зареєстровано і catalog, і discovery, OpenClaw використовує catalog

Інспекція каналу лише для читання

Якщо ваш Plugin реєструє канал, надавайте перевагу реалізації plugin.config.inspectAccount(cfg, accountId) разом із resolveAccount(...). Чому:
  • resolveAccount(...) — це шлях runtime. Йому дозволено припускати, що облікові дані повністю матеріалізовані, і швидко завершуватися з помилкою, якщо потрібних секретів бракує.
  • Шляхи команд лише для читання, такі як openclaw status, openclaw status --all, openclaw channels status, openclaw channels resolve, а також потоки doctor/config repair не повинні вимагати матеріалізації runtime-облікових даних лише для опису конфігурації.
Рекомендована поведінка inspectAccount(...):
  • Повертайте лише описовий стан облікового запису.
  • Зберігайте enabled і configured.
  • Додавайте поля джерела/стану облікових даних, коли це доречно, наприклад:
    • tokenSource, tokenStatus
    • botTokenSource, botTokenStatus
    • appTokenSource, appTokenStatus
    • signingSecretSource, signingSecretStatus
  • Вам не потрібно повертати сирі значення токенів лише для повідомлення про доступність у режимі лише читання. Для команд у стилі status достатньо повертати tokenStatus: "available" (і відповідне поле джерела).
  • Використовуйте configured_unavailable, коли облікові дані налаштовані через SecretRef, але недоступні в поточному шляху команди.
Це дозволяє командам лише для читання повідомляти «налаштовано, але недоступно в цьому шляху команди» замість аварійного завершення або хибного повідомлення, що обліковий запис не налаштований.

Пакети-pack

Каталог Plugin може містити package.json з openclaw.extensions:
{
  "name": "my-pack",
  "openclaw": {
    "extensions": ["./src/safety.ts", "./src/tools.ts"],
    "setupEntry": "./src/setup-entry.ts"
  }
}
Кожен запис стає Plugin. Якщо pack перелічує кілька розширень, id Plugin стає name/<fileBase>. Якщо ваш Plugin імпортує npm-залежності, встановіть їх у цьому каталозі, щоб node_modules був доступний (npm install / pnpm install). Захисне обмеження безпеки: кожен запис openclaw.extensions має залишатися в межах каталогу Plugin після визначення symlink. Записи, які виходять за межі каталогу пакета, відхиляються. Примітка щодо безпеки: openclaw plugins install встановлює залежності Plugin через npm install --omit=dev --ignore-scripts (без lifecycle scripts і без dev-залежностей під час runtime). Зберігайте дерева залежностей Plugin «чистими JS/TS» і уникайте пакетів, яким потрібні збірки через postinstall. Необов’язково: openclaw.setupEntry може вказувати на легкий модуль лише для setup. Коли OpenClaw потребує поверхонь setup для вимкненого Plugin каналу або коли Plugin каналу ввімкнений, але ще не налаштований, він завантажує setupEntry замість повної точки входу Plugin. Це зберігає запуск і setup легшими, коли основна точка входу Plugin також підключає інструменти, хуки чи інший код лише для runtime. Необов’язково: openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen може перевести Plugin каналу на той самий шлях setupEntry під час фази запуску gateway до початку прослуховування, навіть якщо канал уже налаштований. Використовуйте це лише тоді, коли setupEntry повністю покриває поверхню запуску, яка має існувати до того, як gateway почне прослуховувати. На практиці це означає, що точка входу setup має реєструвати кожну можливість, що належить каналу, від якої залежить запуск, наприклад:
  • саму реєстрацію каналу
  • будь-які HTTP-маршрути, які мають бути доступні до того, як gateway почне прослуховувати
  • будь-які методи Gateway, інструменти або сервіси, які мають існувати в тому самому вікні
Якщо ваша повна точка входу все ще володіє будь-якою потрібною можливістю запуску, не вмикайте цей прапорець. Залиште Plugin на типовій поведінці й дозвольте OpenClaw завантажити повну точку входу під час запуску. Вбудовані канали також можуть публікувати допоміжні засоби поверхні контракту лише для setup, які core може використовувати до завантаження повного runtime каналу. Поточна поверхня просування setup така:
  • singleAccountKeysToMove
  • namedAccountPromotionKeys
  • resolveSingleAccountPromotionTarget(...)
Core використовує цю поверхню, коли йому потрібно просунути застарілу конфігурацію каналу з одним обліковим записом у channels.<id>.accounts.* без завантаження повної точки входу Plugin. Matrix — поточний вбудований приклад: він переміщує лише ключі auth/bootstrap у іменований просунутий обліковий запис, коли іменовані облікові записи вже існують, і може зберегти налаштований неканонічний ключ типового облікового запису замість того, щоб завжди створювати accounts.default. Ці адаптери патчів setup зберігають ліниве виявлення поверхні контракту вбудованих рішень. Час імпорту залишається малим; поверхня просування завантажується лише під час першого використання замість повторного входу в запуск вбудованого каналу під час імпорту модуля. Коли ці поверхні запуску містять методи Gateway RPC, зберігайте їх на префіксі, специфічному для Plugin. Простори імен admin core (config.*, exec.approvals.*, wizard.*, update.*) залишаються зарезервованими й завжди визначаються як operator.admin, навіть якщо Plugin запитує вужчий scope. Приклад:
{
  "name": "@scope/my-channel",
  "openclaw": {
    "extensions": ["./index.ts"],
    "setupEntry": "./setup-entry.ts",
    "startup": {
      "deferConfiguredChannelFullLoadUntilAfterListen": true
    }
  }
}

Метадані каталогу каналів

Plugin каналів можуть оголошувати метадані setup/discovery через openclaw.channel і підказки встановлення через openclaw.install. Це дозволяє не тримати дані каталогу в core. Приклад:
{
  "name": "@openclaw/nextcloud-talk",
  "openclaw": {
    "extensions": ["./index.ts"],
    "channel": {
      "id": "nextcloud-talk",
      "label": "Nextcloud Talk",
      "selectionLabel": "Nextcloud Talk (self-hosted)",
      "docsPath": "/channels/nextcloud-talk",
      "docsLabel": "nextcloud-talk",
      "blurb": "Self-hosted chat via Nextcloud Talk webhook bots.",
      "order": 65,
      "aliases": ["nc-talk", "nc"]
    },
    "install": {
      "npmSpec": "@openclaw/nextcloud-talk",
      "localPath": "<bundled-plugin-local-path>",
      "defaultChoice": "npm"
    }
  }
}
Корисні поля openclaw.channel, окрім мінімального прикладу:
  • detailLabel: вторинна мітка для багатших поверхонь каталогу/статусу
  • docsLabel: перевизначення тексту посилання для посилання на документацію
  • preferOver: id Plugin/каналів із нижчим пріоритетом, які цей запис каталогу має перевершувати
  • selectionDocsPrefix, selectionDocsOmitLabel, selectionExtras: керування текстом поверхні вибору
  • markdownCapable: позначає канал як сумісний із markdown для рішень форматування вихідних повідомлень
  • exposure.configured: приховує канал із поверхонь списку налаштованих каналів, якщо встановлено false
  • exposure.setup: приховує канал з інтерактивних вибирачів setup/configure, якщо встановлено false
  • exposure.docs: позначає канал як внутрішній/приватний для поверхонь навігації документації
  • showConfigured / showInSetup: застарілі псевдоніми, які все ще приймаються для сумісності; надавайте перевагу exposure
  • quickstartAllowFrom: додає канал до стандартного потоку quickstart allowFrom
  • forceAccountBinding: вимагає явного прив’язування облікового запису, навіть якщо існує лише один обліковий запис
  • preferSessionLookupForAnnounceTarget: надає перевагу пошуку сесії під час визначення announce target
OpenClaw також може зливати зовнішні каталоги каналів (наприклад, експорт реєстру MPM). Розмістіть JSON-файл в одному з таких місць:
  • ~/.openclaw/mpm/plugins.json
  • ~/.openclaw/mpm/catalog.json
  • ~/.openclaw/plugins/catalog.json
Або вкажіть OPENCLAW_PLUGIN_CATALOG_PATHS (або OPENCLAW_MPM_CATALOG_PATHS) на один чи кілька JSON-файлів (розділених комою/крапкою з комою/роздільником PATH). Кожен файл має містити { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }. Парсер також приймає "packages" або "plugins" як застарілі псевдоніми для ключа "entries".

Plugin рушія контексту

Plugin рушія контексту володіють оркестрацією контексту сесії для ingest, assembly і Compaction. Реєструйте їх зі свого Plugin через api.registerContextEngine(id, factory), а потім вибирайте активний рушій через plugins.slots.contextEngine. Використовуйте це, коли вашому Plugin потрібно замінити або розширити типовий конвеєр контексту, а не просто додати пошук у пам’яті чи хуки.
import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";

export default function (api) {
  api.registerContextEngine("lossless-claw", () => ({
    info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true },
    async ingest() {
      return { ingested: true };
    },
    async assemble({ messages, availableTools, citationsMode }) {
      return {
        messages,
        estimatedTokens: 0,
        systemPromptAddition: buildMemorySystemPromptAddition({
          availableTools: availableTools ?? new Set(),
          citationsMode,
        }),
      };
    },
    async compact() {
      return { ok: true, compacted: false };
    },
  }));
}
Якщо ваш рушій не володіє алгоритмом Compaction, залишайте compact() реалізованим і явно делегуйте його:
import {
  buildMemorySystemPromptAddition,
  delegateCompactionToRuntime,
} from "openclaw/plugin-sdk/core";

export default function (api) {
  api.registerContextEngine("my-memory-engine", () => ({
    info: {
      id: "my-memory-engine",
      name: "My Memory Engine",
      ownsCompaction: false,
    },
    async ingest() {
      return { ingested: true };
    },
    async assemble({ messages, availableTools, citationsMode }) {
      return {
        messages,
        estimatedTokens: 0,
        systemPromptAddition: buildMemorySystemPromptAddition({
          availableTools: availableTools ?? new Set(),
          citationsMode,
        }),
      };
    },
    async compact(params) {
      return await delegateCompactionToRuntime(params);
    },
  }));
}

Додавання нової можливості

Коли Plugin потребує поведінки, яка не вписується в поточний API, не обходьте систему Plugin через приватне звернення всередину. Додайте відсутню можливість. Рекомендована послідовність:
  1. визначте контракт core Вирішіть, якою спільною поведінкою має володіти core: політика, fallback, злиття конфігурації, життєвий цикл, семантика для каналів і форма допоміжного runtime-засобу.
  2. додайте типізовані поверхні реєстрації/runtime для Plugin Розширте OpenClawPluginApi і/або api.runtime найменшою корисною типізованою поверхнею можливості.
  3. під’єднайте споживачів core + каналів/функцій Канали та Plugin функцій мають споживати нову можливість через core, а не імпортуючи реалізацію vendor напряму.
  4. зареєструйте реалізації vendor Потім vendor Plugin реєструють свої backends для цієї можливості.
  5. додайте покриття контрактів Додайте тести, щоб володіння і форма реєстрації з часом залишалися явними.
Саме так OpenClaw зберігає чітку архітектурну позицію, не стаючи жорстко прив’язаним до світогляду одного провайдера. Дивіться Capability Cookbook для конкретного контрольного списку файлів і готового прикладу.

Контрольний список можливості

Коли ви додаєте нову можливість, реалізація зазвичай має одночасно торкатися таких поверхонь:
  • типи контракту core в src/<capability>/types.ts
  • runner/допоміжний runtime-засіб core в src/<capability>/runtime.ts
  • поверхня реєстрації API Plugin в src/plugins/types.ts
  • підключення реєстру Plugin в src/plugins/registry.ts
  • відкриття runtime Plugin у src/plugins/runtime/*, коли Plugin функцій/каналів мають це споживати
  • допоміжні засоби capture/test у src/test-utils/plugin-registration.ts
  • перевірки володіння/контрактів у src/plugins/contracts/registry.ts
  • документація для операторів/Plugin у docs/
Якщо однієї з цих поверхонь бракує, це зазвичай ознака того, що можливість ще не повністю інтегрована.

Шаблон можливості

Мінімальний шаблон:
// core contract
export type VideoGenerationProviderPlugin = {
  id: string;
  label: string;
  generateVideo: (req: VideoGenerationRequest) => Promise<VideoGenerationResult>;
};

// plugin API
api.registerVideoGenerationProvider({
  id: "openai",
  label: "OpenAI",
  async generateVideo(req) {
    return await generateOpenAiVideo(req);
  },
});

// shared runtime helper for feature/channel plugins
const clip = await api.runtime.videoGeneration.generate({
  prompt: "Show the robot walking through the lab.",
  cfg,
});
Шаблон тесту контракту:
expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]);
Це зберігає правило простим:
  • core володіє контрактом можливості + оркестрацією
  • vendor Plugin володіють реалізаціями vendor
  • Plugin функцій/каналів споживають допоміжні runtime-засоби
  • тести контрактів зберігають явність володіння