---
read_when:
    - Рефакторинг интерфейса сообщений каналов, интерактивных полезных нагрузок или нативных средств отображения каналов
    - Изменение возможностей инструмента сообщений, подсказок по доставке или маркеров между контекстами
    - Отладка разветвления импорта Discord Carbon или ленивой загрузки среды выполнения плагина канала
summary: Отделить семантическое представление сообщений от средств отрисовки нативного интерфейса каналов.
title: План рефакторинга представления каналов
x-i18n:
    generated_at: "2026-07-13T18:23:47Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 24
    provider: openai
    source_hash: 6b0f0c4f64e0c503209ac0a5b763b1b5483bf8d55a28ceacffbbcd1337d4371e
    source_path: plan/ui-channels.md
    workflow: 16
---

## Статус

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

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

Актуальная документация теперь находится в разделе [Представление сообщений](/ru/plugins/message-presentation).
Сохраните этот план как исторический контекст реализации; при изменении контракта,
рендерера или поведения резервного варианта обновляйте актуальное руководство.

## Проблема

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

- Ядро предоставляет ориентированный на 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`, или оставить его ориентированным на действия после отправки?
- Должно ли представление напрямую поддерживать изображения или ссылки на файлы либо пока следует отделить медиаданные от компоновки интерфейса?

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

- [Обзор каналов](/ru/channels)
- [Представление сообщений](/ru/plugins/message-presentation)
