Get started

План рефакторинга представления каналов

Статус

Реализовано для общего агента, CLI, возможностей плагинов и исходящей доставки:

  • ReplyPayload.presentation передаёт семантический интерфейс сообщения.
  • ReplyPayload.delivery.pin передаёт запросы на закрепление отправленного сообщения.
  • Общие действия с сообщениями предоставляют presentation, delivery и pin вместо специфичных для провайдера components, blocks, buttons или card.
  • Ядро выполняет рендеринг представления или автоматически упрощает его с учётом заявленных плагином возможностей исходящей доставки.
  • Рендереры Discord, Slack, Telegram, Mattermost, MS Teams и Feishu используют общий контракт.
  • Код плоскости управления каналом Discord больше не импортирует контейнеры интерфейса на основе Carbon.

Актуальная документация теперь находится в разделе Представление сообщений. Сохраните этот план как исторический контекст реализации; при изменении контракта, рендерера или поведения резервного варианта обновляйте актуальное руководство.

Проблема

В настоящее время интерфейс каналов разделён между несколькими несовместимыми поверхностями:

  • Ядро предоставляет ориентированный на Discord хук межконтекстного рендерера через buildCrossContextComponents.
  • Discord channel.ts может импортировать нативный интерфейс Carbon через DiscordUiContainer, что добавляет зависимости интерфейса среды выполнения в плоскость управления плагином канала.
  • Агент и CLI предоставляют обходные механизмы для нативных полезных нагрузок, например Discord components, Slack blocks, Telegram или Mattermost buttons, а также Teams или Feishu card.
  • ReplyPayload.channelData передаёт как подсказки для транспорта, так и нативные оболочки интерфейса.
  • Общая модель interactive уже существует, но она уже, чем более функциональные макеты, которые уже используются в Discord, Slack, Teams, Feishu, LINE, Telegram и Mattermost.

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

Цели

  • Ядро выбирает оптимальное семантическое представление сообщения на основе заявленных возможностей.
  • Расширения заявляют возможности и преобразуют семантическое представление в нативные полезные нагрузки транспорта.
  • Веб-интерфейс управления остаётся отделённым от нативного интерфейса чатов.
  • Нативные полезные нагрузки каналов не предоставляются через общую поверхность сообщений агента или CLI.
  • Неподдерживаемые возможности представления автоматически упрощаются до оптимального текстового представления.
  • Поведение доставки, например закрепление отправленного сообщения, является общими метаданными доставки, а не представлением.

Не является целью

  • Не добавлять прослойку обратной совместимости для buildCrossContextComponents.
  • Не предоставлять общедоступные обходные механизмы для нативных данных components, blocks, buttons или card.
  • Не импортировать библиотеки нативного интерфейса каналов в ядро.
  • Не добавлять специфичные для провайдера поверхности SDK для встроенных каналов.

Целевая модель

Добавьте принадлежащее ядру поле presentation в ReplyPayload.

ts
type MessagePresentationTone = "neutral" | "info" | "success" | "warning" | "danger"; type MessagePresentation = {  tone?: MessagePresentationTone;  title?: string;  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;};

Во время миграции interactive становится подмножеством presentation:

  • Текстовый блок interactive сопоставляется с presentation.blocks[].type = "text".
  • Блок кнопок interactive сопоставляется с presentation.blocks[].type = "buttons".
  • Блок выбора interactive сопоставляется с presentation.blocks[].type = "select".

Внешние схемы агента и CLI теперь используют presentation; interactive остаётся внутренним устаревшим вспомогательным средством синтаксического анализа и рендеринга для существующих производителей ответов. В общедоступном API для производителей interactive считается устаревшим. Поддержка в среде выполнения сохраняется, чтобы существующие вспомогательные средства подтверждения и старые плагины продолжали работать, пока новый код создаёт presentation.

Метаданные доставки

Добавьте принадлежащее ядру поле delivery для поведения отправки, не относящегося к интерфейсу.

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

Семантика:

  • delivery.pin = true означает закрепление первого успешно доставленного сообщения.
  • Значение notify по умолчанию — false.
  • Значение required по умолчанию — false; для неподдерживаемых каналов или при сбое закрепления выполняется автоматическое упрощение с продолжением доставки.
  • Ручные действия с сообщениями pin, unpin и list-pins сохраняются для существующих сообщений.

Текущую привязку темы ACP в Telegram следует перенести из channelData.telegram.pin = true в delivery.pin = true.

Контракт возможностей среды выполнения

Добавьте хуки рендеринга представления и доставки в исходящий адаптер среды выполнения, а не в плагин канала плоскости управления.

ts
type ChannelPresentationCapabilities = {  supported: boolean;  buttons?: boolean;  selects?: boolean;  context?: boolean;  divider?: boolean;  tones?: MessagePresentationTone[];  limits?: {    actions?: {      maxActions?: number;      maxActionsPerRow?: number;      maxRows?: number;      maxLabelLength?: number;      maxValueBytes?: number;      supportsStyles?: boolean;      supportsDisabled?: boolean;      supportsLayoutHints?: boolean;    };    selects?: {      maxOptions?: number;      maxLabelLength?: number;      maxValueBytes?: number;    };    text?: {      maxLength?: number;      encoding?: "characters" | "utf8-bytes" | "utf16-units";      markdownDialect?: "plain" | "markdown" | "html" | "slack-mrkdwn" | "discord-markdown";      supportsEdit?: boolean;    };  };}; type ChannelDeliveryCapabilities = {  pinSentMessage?: boolean;}; type ChannelOutboundAdapter = {  presentationCapabilities?: ChannelPresentationCapabilities;   renderPresentation?: (params: {    payload: ReplyPayload;    presentation: MessagePresentation;    ctx: ChannelOutboundSendContext;  }) => ReplyPayload | null;   deliveryCapabilities?: ChannelDeliveryCapabilities;   pinDeliveredMessage?: (params: {    cfg: OpenClawConfig;    accountId?: string | null;    to: string;    threadId?: string | number | null;    messageId: string;    notify: boolean;  }) => Promise<void>;};

Поведение ядра:

  • Определить целевой канал и адаптер среды выполнения.
  • Запросить возможности представления.
  • Упростить неподдерживаемые блоки и применить общие ограничения возможностей перед рендерингом.
  • Вызвать renderPresentation.
  • Если рендерер отсутствует, преобразовать представление в резервный текстовый вариант.
  • После успешной отправки вызвать pinDeliveredMessage, если запрошено и поддерживается delivery.pin.

Сопоставление каналов

Discord:

  • Преобразовать presentation в компоненты v2 и контейнеры Carbon в модулях, предназначенных только для среды выполнения.
  • Оставить вспомогательные средства для акцентных цветов в лёгких модулях.
  • Удалить импорты DiscordUiContainer из кода плоскости управления плагином канала.

Slack:

  • Преобразовать presentation в Block Kit.
  • Удалить входной параметр blocks из агента и CLI.

Telegram:

  • Преобразовывать текст, контекст и разделители в текст.
  • Преобразовывать действия и выбор во встроенные клавиатуры, если они настроены и разрешены для целевой поверхности.
  • Использовать резервный текстовый вариант, если встроенные кнопки отключены.
  • Перенести закрепление темы ACP в delivery.pin.

Mattermost:

  • Преобразовывать действия в интерактивные кнопки, если это настроено.
  • Преобразовывать остальные блоки в резервный текстовый вариант.

MS Teams:

  • Преобразовать presentation в Adaptive Cards.
  • Сохранить ручные действия закрепления, открепления и вывода списка закреплений.
  • При необходимости реализовать pinDeliveredMessage, если поддержка Graph надёжна для целевого разговора.

Feishu:

  • Преобразовать presentation в интерактивные карточки.
  • Сохранить ручные действия закрепления, открепления и вывода списка закреплений.
  • При необходимости реализовать pinDeliveredMessage для закрепления отправленных сообщений, если поведение API надёжно.

LINE:

  • По возможности преобразовать presentation в сообщения Flex или шаблонные сообщения.
  • Использовать резервный текстовый вариант для неподдерживаемых блоков.
  • Удалить полезные нагрузки интерфейса LINE из channelData.

Обычные каналы или каналы с ограниченными возможностями:

  • Преобразовывать представление в текст с консервативным форматированием.

Этапы рефакторинга

  1. Повторно применить исправление выпуска Discord, которое отделяет ui-colors.ts от интерфейса на основе Carbon и удаляет DiscordUiContainer из extensions/discord/src/channel.ts.
  2. Добавить presentation и delivery в ReplyPayload, нормализацию исходящей полезной нагрузки, сводки доставки и полезные нагрузки хуков.
  3. Добавить схему MessagePresentation и вспомогательные средства синтаксического анализа в узкий подпуть SDK/среды выполнения.
  4. Заменить возможности сообщений buttons, cards, components и blocks возможностями семантического представления.
  5. Добавить хуки исходящего адаптера среды выполнения для рендеринга представления и закрепления при доставке.
  6. Заменить межконтекстное создание компонентов на buildCrossContextPresentation.
  7. Удалить src/infra/outbound/channel-adapters.ts и убрать buildCrossContextComponents из типов плагина канала.
  8. Изменить maybeApplyCrossContextMarker, чтобы он прикреплял presentation вместо нативных параметров.
  9. Обновить пути отправки диспетчеризации плагинов, чтобы они использовали только семантическое представление и метаданные доставки.
  10. Удалить из агента и CLI параметры нативной полезной нагрузки: components, blocks, buttons и card.
  11. Удалить вспомогательные средства SDK, создающие нативные схемы инструментов сообщений, заменив их вспомогательными средствами схем представления.
  12. Удалить интерфейсные и нативные оболочки из channelData; оставить только метаданные транспорта до проверки каждого оставшегося поля.
  13. Мигрировать рендереры Discord, Slack, Telegram, Mattermost, MS Teams, Feishu и LINE.
  14. Обновить документацию по CLI сообщений, страницам каналов, SDK плагинов и справочнику возможностей.
  15. Выполнить профилирование разветвления импортов для Discord и затронутых точек входа каналов.

Этапы 1-11 и 13-14 реализованы в этом рефакторинге для контрактов общего агента, CLI, возможностей плагинов и исходящего адаптера. Этап 12 остаётся более глубокой внутренней очисткой специфичных для провайдера транспортных оболочек channelData. Этап 15 остаётся последующей проверкой на случай, если помимо проверки типов и тестов потребуются количественные показатели разветвления импортов.

Тесты

Добавить или обновить:

  • Тесты нормализации представления.
  • Тесты автоматического упрощения представления для неподдерживаемых блоков.
  • Тесты межконтекстных маркеров для диспетчеризации плагинов и путей доставки ядра.
  • Тесты матрицы рендеринга каналов для Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE и резервного текстового варианта.
  • Тесты схемы инструмента сообщений, подтверждающие отсутствие нативных полей.
  • Тесты CLI, подтверждающие отсутствие нативных флагов.
  • Регрессионный тест отложенной загрузки импортов точки входа Discord, охватывающий Carbon.
  • Тесты закрепления при доставке, охватывающие Telegram и общий резервный вариант.

Открытые вопросы

  • Следует ли в первой версии реализовать delivery.pin для Discord, Slack, MS Teams и Feishu или сначала только для Telegram?
  • Следует ли со временем включить в delivery существующие поля, такие как replyToId, replyToCurrent, silent и audioAsVoice, или оставить его ориентированным на действия после отправки?
  • Должно ли представление напрямую поддерживать изображения или ссылки на файлы либо пока следует отделить медиаданные от компоновки интерфейса?

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

Was this useful?
On this page

On this page