Plugins

Представлення повідомлень

Представлення повідомлень — це спільний контракт OpenClaw для розширеного інтерфейсу вихідного чату. Він дає змогу агентам, командам CLI, потокам погодження та плагінам один раз описати намір повідомлення, тоді як кожен плагін каналу рендерить найкращу нативну форму, яку може.

Використовуйте представлення для переносного інтерфейсу повідомлень:

  • текстові секції
  • невеликий контекстний текст/текст нижнього колонтитула
  • роздільники
  • кнопки
  • меню вибору
  • заголовок картки та тон

Не додавайте нові нативні для провайдера поля, як-от Discord components, Slack blocks, Telegram buttons, Teams card або Feishu card, до спільного інструмента повідомлень. Це результати рендерингу, якими володіє плагін каналу.

Контракт

Автори плагінів імпортують публічний контракт із:


  MessagePresentation,
  ReplyPayloadDelivery,
} from "openclaw/plugin-sdk/interactive-runtime";

Форма:

type MessagePresentation = {
  title?: string;
  tone?: "neutral" | "info" | "success" | "warning" | "danger";
  blocks: MessagePresentationBlock[];
};

type MessagePresentationBlock =
  | { type: "text"; text: string }
  | { type: "context"; text: string }
  | { type: "divider" }
  | { type: "buttons"; buttons: MessagePresentationButton[] }
  | { type: "select"; placeholder?: string; options: MessagePresentationOption[] };

type MessagePresentationButton = {
  label: string;
  value?: string;
  url?: string;
  style?: "primary" | "secondary" | "success" | "danger";
};

type MessagePresentationOption = {
  label: string;
  value: string;
};

type ReplyPayloadDelivery = {
  pin?:
    | boolean
    | {
        enabled: boolean;
        notify?: boolean;
        required?: boolean;
      };
};

Семантика кнопок:

  • value — це значення дії застосунку, яке повертається через наявний шлях взаємодії каналу, коли канал підтримує елементи керування, що можна натискати.
  • url — це кнопка-посилання. Вона може існувати без value.
  • label є обов’язковим і також використовується в текстовому резервному варіанті.
  • style є рекомендаційним. Рендерери мають зіставляти непідтримувані стилі з безпечним значенням за замовчуванням, а не завершувати надсилання з помилкою.

Семантика меню вибору:

  • options[].value — це вибране значення застосунку.
  • placeholder є рекомендаційним і може ігноруватися каналами без нативної підтримки меню вибору.
  • Якщо канал не підтримує меню вибору, резервний текст перелічує мітки.

Приклади відправника

Проста картка:

{
  "title": "Deploy approval",
  "tone": "warning",
  "blocks": [
    { "type": "text", "text": "Canary is ready to promote." },
    { "type": "context", "text": "Build 1234, staging passed." },
    {
      "type": "buttons",
      "buttons": [
        { "label": "Approve", "value": "deploy:approve", "style": "success" },
        { "label": "Decline", "value": "deploy:decline", "style": "danger" }
      ]
    }
  ]
}

Кнопка-посилання лише з URL:

{
  "blocks": [
    { "type": "text", "text": "Release notes are ready." },
    {
      "type": "buttons",
      "buttons": [{ "label": "Open notes", "url": "https://example.com/release" }]
    }
  ]
}

Меню вибору:

{
  "title": "Choose environment",
  "blocks": [
    {
      "type": "select",
      "placeholder": "Environment",
      "options": [
        { "label": "Canary", "value": "env:canary" },
        { "label": "Production", "value": "env:prod" }
      ]
    }
  ]
}

Надсилання через CLI:

openclaw message send --channel slack \
  --target channel:C123 \
  --message "Deploy approval" \
  --presentation '{"title":"Deploy approval","tone":"warning","blocks":[{"type":"text","text":"Canary is ready."},{"type":"buttons","buttons":[{"label":"Approve","value":"deploy:approve","style":"success"},{"label":"Decline","value":"deploy:decline","style":"danger"}]}]}'

Закріплена доставка:

openclaw message send --channel telegram \
  --target -1001234567890 \
  --message "Topic opened" \
  --pin

Закріплена доставка з явним JSON:

{
  "pin": {
    "enabled": true,
    "notify": true,
    "required": false
  }
}

Контракт рендерера

Плагіни каналів оголошують підтримку рендерингу у своєму outbound-адаптері:

const adapter: ChannelOutboundAdapter = {
  deliveryMode: "direct",
  presentationCapabilities: {
    supported: true,
    buttons: true,
    selects: true,
    context: true,
    divider: true,
  },
  deliveryCapabilities: {
    pin: true,
  },
  renderPresentation({ payload, presentation, ctx }) {
    return renderNativePayload(payload, presentation, ctx);
  },
  async pinDeliveredMessage({ target, messageId, pin }) {
    await pinNativeMessage(target, messageId, { notify: pin.notify === true });
  },
};

Поля можливостей навмисно зведені до простих булевих значень. Вони описують, які інтерактивні елементи може відобразити рендерер, а не кожне нативне обмеження платформи. Рендерери все одно володіють специфічними для платформи обмеженнями, як-от максимальна кількість кнопок, кількість блоків і розмір картки.

Основний потік рендерингу

Коли ReplyPayload або дія повідомлення містить presentation, ядро:

  1. Нормалізує корисне навантаження представлення.
  2. Визначає outbound-адаптер цільового каналу.
  3. Зчитує presentationCapabilities.
  4. Викликає renderPresentation, коли адаптер може відрендерити корисне навантаження.
  5. Повертається до консервативного тексту, коли адаптер відсутній або не може виконати рендеринг.
  6. Надсилає результативне корисне навантаження через звичайний шлях доставки каналу.
  7. Застосовує метадані доставки, як-от delivery.pin, після першого успішно надісланого повідомлення.

Ядро володіє поведінкою резервного варіанта, щоб відправники могли залишатися незалежними від каналів. Плагіни каналів володіють нативним рендерингом і обробкою взаємодії.

Правила деградації

Представлення має бути безпечним для надсилання в обмежених каналах.

Резервний текст містить:

  • title як перший рядок
  • блоки text як звичайні абзаци
  • блоки context як компактні контекстні рядки
  • блоки divider як візуальний роздільник
  • мітки кнопок, включно з URL для кнопок-посилань
  • мітки варіантів меню вибору

Непідтримувані нативні елементи керування мають деградувати, а не спричиняти збій усього надсилання. Приклади:

  • Telegram із вимкненими вбудованими кнопками надсилає текстовий резервний варіант.
  • Канал без підтримки меню вибору перелічує варіанти вибору як текст.
  • Кнопка лише з URL стає або нативною кнопкою-посиланням, або резервним рядком з URL.
  • Необов’язкові помилки закріплення не спричиняють збою вже доставленого повідомлення.

Головний виняток — delivery.pin.required: true; якщо запитано закріплення як обов’язкове і канал не може закріпити надіслане повідомлення, доставка повідомляє про помилку.

Відображення на провайдерів

Поточні вбудовані рендерери:

Канал Нативна ціль рендерингу Примітки
Discord Components and component containers Зберігає застаріле channelData.discord.components для наявних відправників корисного навантаження, нативного для провайдера, але нові спільні надсилання мають використовувати presentation.
Slack Block Kit Зберігає застаріле channelData.slack.blocks для наявних відправників корисного навантаження, нативного для провайдера, але нові спільні надсилання мають використовувати presentation.
Telegram Текст і вбудовані клавіатури Кнопки/меню вибору потребують можливості вбудованих кнопок для цільової поверхні; інакше використовується текстовий резервний варіант.
Mattermost Текст та інтерактивні props Інші блоки деградують до тексту.
Microsoft Teams Adaptive Cards Звичайний текст message включається разом із карткою, якщо надано обидва.
Feishu Інтерактивні картки Заголовок картки може використовувати title; тіло уникає дублювання цього заголовка.
Plain channels Текстовий резервний варіант Канали без рендерера все одно отримують читабельний вивід.

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

Представлення проти InteractiveReply

InteractiveReply — це старіша внутрішня підмножина, що використовується допоміжними засобами погодження та взаємодії. Вона підтримує:

  • текст
  • кнопки
  • меню вибору

MessagePresentation — це канонічний спільний контракт надсилання. Він додає:

  • заголовок
  • тон
  • контекст
  • роздільник
  • кнопки лише з URL
  • загальні метадані доставки через ReplyPayload.delivery

Використовуйте допоміжні функції з openclaw/plugin-sdk/interactive-runtime під час адаптації старішого коду:


  interactiveReplyToPresentation,
  normalizeMessagePresentation,
  presentationToInteractiveReply,
  renderMessagePresentationFallbackText,
} from "openclaw/plugin-sdk/interactive-runtime";

Новий код має безпосередньо приймати або створювати MessagePresentation.

Закріплення доставки

Закріплення — це поведінка доставки, а не представлення. Використовуйте delivery.pin замість нативних для провайдера полів, як-от channelData.telegram.pin.

Семантика:

  • pin: true закріплює перше успішно доставлене повідомлення.
  • pin.notify за замовчуванням дорівнює false.
  • pin.required за замовчуванням дорівнює false.
  • Необов’язкові помилки закріплення деградують і залишають надіслане повідомлення без змін.
  • Обов’язкові помилки закріплення призводять до збою доставки.
  • Для фрагментованих повідомлень закріплюється перший доставлений фрагмент, а не кінцевий.

Ручні дії повідомлень pin, unpin і pins усе ще існують для наявних повідомлень, де провайдер підтримує ці операції.

Контрольний список автора плагіна

  • Оголосіть presentation у describeMessageTool(...), коли канал може рендерити або безпечно деградувати семантичне представлення.
  • Додайте presentationCapabilities до runtime outbound-адаптера.
  • Реалізуйте renderPresentation у runtime-коді, а не в коді налаштування плагіна площини керування.
  • Не допускайте нативних UI-бібліотек у гарячі шляхи налаштування/каталогу.
  • Зберігайте обмеження платформи в рендерері та тестах.
  • Додайте резервні тести для непідтримуваних кнопок, меню вибору, кнопок з URL, дублювання заголовка/тексту та змішаних надсилань message плюс presentation.
  • Додайте підтримку закріплення доставки через deliveryCapabilities.pin і pinDeliveredMessage лише тоді, коли провайдер може закріпити id надісланого повідомлення.
  • Не відкривайте нові нативні для провайдера поля карток/блоків/компонентів/кнопок через спільну схему дій повідомлення.

Пов’язана документація