Get started
План рефакторинга представления каналов
Статус
Реализовано для общих поверхностей агента, CLI, возможностей Plugin и исходящей доставки:
ReplyPayload.presentationпередает семантический UI сообщения.ReplyPayload.delivery.pinпередает запросы на закрепление отправленного сообщения.- Общие действия сообщений предоставляют
presentation,deliveryиpinвместо нативных для провайдераcomponents,blocks,buttonsилиcard. - Ядро отрисовывает или автоматически упрощает представление через объявленные Plugin возможности исходящей доставки.
- Рендереры Discord, Slack, Telegram, Mattermost, MS Teams и Feishu используют общий контракт.
- Код плоскости управления канала Discord больше не импортирует UI-контейнеры на базе Carbon.
Каноническая документация теперь находится в Представление сообщений. Сохраните этот план как исторический контекст реализации; обновляйте каноническое руководство при изменениях контракта, рендерера или поведения резервного варианта.
Проблема
UI каналов сейчас разделен между несколькими несовместимыми поверхностями:
- Ядро владеет Discord-образным хуком рендерера для межконтекстного использования через
buildCrossContextComponents. - Discord
channel.tsможет импортировать нативный Carbon UI черезDiscordUiContainer, что втягивает зависимости UI времени выполнения в плоскость управления Plugin канала. - Агент и CLI предоставляют нативные обходные пути полезной нагрузки, такие как Discord
components, Slackblocks, Telegram или Mattermostbuttons, а также Teams или Feishucard. ReplyPayload.channelDataпереносит как подсказки транспорта, так и нативные UI-конверты.- Общая модель
interactiveсуществует, но она уже, чем более богатые макеты, уже используемые Discord, Slack, Teams, Feishu, LINE, Telegram и Mattermost.
Из-за этого ядро знает о нативных формах UI, ослабляется ленивость времени выполнения Plugin, а агенты получают слишком много специфичных для провайдера способов выразить один и тот же замысел сообщения.
Цели
- Ядро выбирает лучшее семантическое представление для сообщения на основе объявленных возможностей.
- Расширения объявляют возможности и рендерят семантическое представление в нативные транспортные полезные нагрузки.
- Web Control UI остается отделенным от нативного UI чатов.
- Нативные полезные нагрузки каналов не раскрываются через общую поверхность сообщений агента или CLI.
- Неподдерживаемые функции представления автоматически упрощаются до лучшего текстового представления.
- Поведение доставки, такое как закрепление отправленного сообщения, является общими метаданными доставки, а не представлением.
Нецели
- Нет shim для обратной совместимости для
buildCrossContextComponents. - Нет публичных нативных обходных путей для
components,blocks,buttonsилиcard. - Нет импортов нативных UI-библиотек каналов в ядре.
- Нет специфичных для провайдера стыков SDK для встроенных каналов.
Целевая модель
Добавить принадлежащее ядру поле presentation в ReplyPayload.
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 устаревшим. Поддержка времени выполнения
сохраняется, чтобы существующие помощники подтверждений и старые Plugin продолжали
работать, пока новый код испускает presentation.
Метаданные доставки
Добавить принадлежащее ядру поле delivery для поведения отправки, которое не является UI.
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.
Контракт возможностей времени выполнения
Добавить хуки рендеринга представления и доставки в исходящий адаптер времени выполнения, а не в Plugin канала плоскости управления.
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в components v2 и контейнеры Carbon в модулях только времени выполнения. - Сохранить помощники акцентных цветов в легких модулях.
- Удалить импорты
DiscordUiContainerиз кода Plugin канала плоскости управления.
Slack:
- Рендерить
presentationв Block Kit. - Удалить ввод
blocksдля агента и CLI.
Telegram:
- Рендерить текст, контекст и разделители как текст.
- Рендерить действия и выбор как встроенные клавиатуры, когда это настроено и разрешено для целевой поверхности.
- Использовать текстовый резервный вариант, когда встроенные кнопки отключены.
- Перенести закрепление темы ACP в
delivery.pin.
Mattermost:
- Рендерить действия как интерактивные кнопки, когда это настроено.
- Рендерить остальные блоки как текстовый резервный вариант.
MS Teams:
- Рендерить
presentationв Adaptive Cards. - Сохранить ручные действия pin/unpin/list-pins.
- При необходимости реализовать
pinDeliveredMessage, если поддержка Graph надежна для целевой беседы.
Feishu:
- Рендерить
presentationв интерактивные карточки. - Сохранить ручные действия pin/unpin/list-pins.
- При необходимости реализовать
pinDeliveredMessageдля закрепления отправленного сообщения, если поведение API надежно.
LINE:
- Рендерить
presentationв Flex или шаблонные сообщения, где возможно. - Откатываться к тексту для неподдерживаемых блоков.
- Удалить UI-полезные нагрузки LINE из
channelData.
Простые или ограниченные каналы:
- Преобразовывать представление в текст с консервативным форматированием.
Шаги рефакторинга
- Повторно применить релизное исправление Discord, которое отделяет
ui-colors.tsот UI на базе Carbon и удаляетDiscordUiContainerизextensions/discord/src/channel.ts. - Добавить
presentationиdeliveryвReplyPayload, нормализацию исходящей полезной нагрузки, сводки доставки и полезные нагрузки хуков. - Добавить схему
MessagePresentationи помощники парсера в узком подпути SDK/времени выполнения. - Заменить возможности сообщений
buttons,cards,componentsиblocksсемантическими возможностями представления. - Добавить хуки исходящего адаптера времени выполнения для рендеринга представления и закрепления доставки.
- Заменить построение межконтекстных компонентов на
buildCrossContextPresentation. - Удалить
src/infra/outbound/channel-adapters.tsи удалитьbuildCrossContextComponentsиз типов Plugin канала. - Изменить
maybeApplyCrossContextMarker, чтобы он прикреплялpresentationвместо нативных параметров. - Обновить пути отправки plugin-dispatch, чтобы они использовали только семантическое представление и метаданные доставки.
- Удалить нативные параметры полезной нагрузки агента и CLI:
components,blocks,buttonsиcard. - Удалить помощники SDK, создающие нативные схемы message-tool, заменив их помощниками схемы представления.
- Удалить UI/нативные конверты из
channelData; оставить только транспортные метаданные, пока каждое оставшееся поле не будет проверено. - Мигрировать рендереры Discord, Slack, Telegram, Mattermost, MS Teams, Feishu и LINE.
- Обновить документацию для message CLI, страниц каналов, Plugin SDK и книги рецептов возможностей.
- Запустить профилирование веерного импорта для Discord и затронутых точек входа каналов.
Шаги 1-11 и 13-14 реализованы в этом рефакторинге для контрактов общего агента, CLI, возможностей Plugin и исходящего адаптера. Шаг 12 остается более глубоким проходом внутренней очистки для приватных транспортных конвертов channelData провайдеров. Шаг 15 остается последующей проверкой, если нам нужны количественные числа веерного импорта сверх проверки типов/тестов.
Тесты
Добавить или обновить:
- Тесты нормализации представления.
- Тесты автоматического упрощения представления для неподдерживаемых блоков.
- Тесты межконтекстного маркера для путей plugin dispatch и доставки ядра.
- Матричные тесты рендеринга каналов для Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE и текстового резервного варианта.
- Тесты схемы message tool, доказывающие, что нативные поля удалены.
- Тесты CLI, доказывающие, что нативные флаги удалены.
- Регрессионный тест ленивости импорта точки входа Discord, покрывающий Carbon.
- Тесты закрепления доставки, покрывающие Telegram и общий резервный вариант.
Открытые вопросы
- Следует ли реализовать
delivery.pinдля Discord, Slack, MS Teams и Feishu в первом проходе или сначала только для Telegram? - Должно ли
deliveryсо временем поглотить существующие поля, такие какreplyToId,replyToCurrent,silentиaudioAsVoice, или остаться сфокусированным на поведении после отправки? - Должно ли представление напрямую поддерживать изображения или ссылки на файлы, или пока медиа следует оставить отдельно от макета UI?