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, Slackblocks, Telegram или Mattermostbuttons, а также Teams или Feishucard. ReplyPayload.channelDataпередаёт как подсказки для транспорта, так и нативные оболочки интерфейса.- Общая модель
interactiveуже существует, но она уже, чем более функциональные макеты, которые уже используются в Discord, Slack, Teams, Feishu, LINE, Telegram и Mattermost.
Из-за этого ядро осведомлено о нативных структурах интерфейса, ослабляется отложенная загрузка среды выполнения плагинов, а агенты получают слишком много специфичных для провайдера способов выразить одно и то же назначение сообщения.
Цели
- Ядро выбирает оптимальное семантическое представление сообщения на основе заявленных возможностей.
- Расширения заявляют возможности и преобразуют семантическое представление в нативные полезные нагрузки транспорта.
- Веб-интерфейс управления остаётся отделённым от нативного интерфейса чатов.
- Нативные полезные нагрузки каналов не предоставляются через общую поверхность сообщений агента или CLI.
- Неподдерживаемые возможности представления автоматически упрощаются до оптимального текстового представления.
- Поведение доставки, например закрепление отправленного сообщения, является общими метаданными доставки, а не представлением.
Не является целью
- Не добавлять прослойку обратной совместимости для
buildCrossContextComponents. - Не предоставлять общедоступные обходные механизмы для нативных данных
components,blocks,buttonsилиcard. - Не импортировать библиотеки нативного интерфейса каналов в ядро.
- Не добавлять специфичные для провайдера поверхности 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 считается устаревшим. Поддержка
в среде выполнения сохраняется, чтобы существующие вспомогательные средства подтверждения и старые плагины продолжали
работать, пока новый код создаёт presentation.
Метаданные доставки
Добавьте принадлежащее ядру поле delivery для поведения отправки, не относящегося к интерфейсу.
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.
Контракт возможностей среды выполнения
Добавьте хуки рендеринга представления и доставки в исходящий адаптер среды выполнения, а не в плагин канала плоскости управления.
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.
Обычные каналы или каналы с ограниченными возможностями:
- Преобразовывать представление в текст с консервативным форматированием.
Этапы рефакторинга
- Повторно применить исправление выпуска Discord, которое отделяет
ui-colors.tsот интерфейса на основе Carbon и удаляетDiscordUiContainerизextensions/discord/src/channel.ts. - Добавить
presentationиdeliveryвReplyPayload, нормализацию исходящей полезной нагрузки, сводки доставки и полезные нагрузки хуков. - Добавить схему
MessagePresentationи вспомогательные средства синтаксического анализа в узкий подпуть SDK/среды выполнения. - Заменить возможности сообщений
buttons,cards,componentsиblocksвозможностями семантического представления. - Добавить хуки исходящего адаптера среды выполнения для рендеринга представления и закрепления при доставке.
- Заменить межконтекстное создание компонентов на
buildCrossContextPresentation. - Удалить
src/infra/outbound/channel-adapters.tsи убратьbuildCrossContextComponentsиз типов плагина канала. - Изменить
maybeApplyCrossContextMarker, чтобы он прикреплялpresentationвместо нативных параметров. - Обновить пути отправки диспетчеризации плагинов, чтобы они использовали только семантическое представление и метаданные доставки.
- Удалить из агента и CLI параметры нативной полезной нагрузки:
components,blocks,buttonsиcard. - Удалить вспомогательные средства SDK, создающие нативные схемы инструментов сообщений, заменив их вспомогательными средствами схем представления.
- Удалить интерфейсные и нативные оболочки из
channelData; оставить только метаданные транспорта до проверки каждого оставшегося поля. - Мигрировать рендереры Discord, Slack, Telegram, Mattermost, MS Teams, Feishu и LINE.
- Обновить документацию по CLI сообщений, страницам каналов, SDK плагинов и справочнику возможностей.
- Выполнить профилирование разветвления импортов для 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, или оставить его ориентированным на действия после отправки? - Должно ли представление напрямую поддерживать изображения или ссылки на файлы либо пока следует отделить медиаданные от компоновки интерфейса?