Get started
Plano de refatoração da apresentação de canais
Status
Implementado para as superfícies de agente compartilhado, CLI, capacidade de plugin e entrega de saída:
ReplyPayload.presentationcarrega a UI semântica da mensagem.ReplyPayload.delivery.pincarrega solicitações de fixação de mensagem enviada.- Ações de mensagem compartilhadas expõem
presentation,deliveryepinem vez decomponents,blocks,buttonsoucardnativos do provedor. - O core renderiza ou degrada automaticamente a apresentação por meio das capacidades de saída declaradas pelo plugin.
- Renderizadores de Discord, Slack, Telegram, Mattermost, MS Teams e Feishu consomem o contrato genérico.
- O código do plano de controle do canal Discord não importa mais contêineres de UI baseados em Carbon.
A documentação canônica agora está em Apresentação de Mensagens. Mantenha este plano como contexto histórico de implementação; atualize o guia canônico para alterações de contrato, renderizador ou comportamento de fallback.
Problema
A UI de canal atualmente está dividida entre várias superfícies incompatíveis:
- O core possui um hook de renderizador entre contextos com formato de Discord por meio de
buildCrossContextComponents. - O
channel.tsdo Discord pode importar a UI Carbon nativa por meio deDiscordUiContainer, o que traz dependências de UI em runtime para o plano de controle do plugin de canal. - O agente e a CLI expõem escapes de payload nativo, como
componentsdo Discord,blocksdo Slack,buttonsdo Telegram ou Mattermost ecarddo Teams ou Feishu. ReplyPayload.channelDatacarrega tanto dicas de transporte quanto envelopes de UI nativos.- O modelo genérico
interactiveexiste, mas é mais limitado que os layouts mais ricos já usados por Discord, Slack, Teams, Feishu, LINE, Telegram e Mattermost.
Isso faz o core conhecer formatos de UI nativos, enfraquece a preguiça de runtime dos plugins e dá aos agentes muitas formas específicas de provedor para expressar a mesma intenção de mensagem.
Objetivos
- O core decide a melhor apresentação semântica para uma mensagem a partir das capacidades declaradas.
- Extensões declaram capacidades e renderizam apresentação semântica em payloads de transporte nativos.
- A UI Web Control permanece separada da UI nativa de chat.
- Payloads de canal nativos não são expostos pela superfície de mensagem compartilhada do agente ou da CLI.
- Recursos de apresentação sem suporte degradam automaticamente para a melhor representação em texto.
- Comportamentos de entrega, como fixar uma mensagem enviada, são metadados genéricos de entrega, não apresentação.
Não objetivos
- Nenhum shim de compatibilidade retroativa para
buildCrossContextComponents. - Nenhum escape nativo público para
components,blocks,buttonsoucard. - Nenhuma importação pelo core de bibliotecas de UI nativas de canal.
- Nenhum seam de SDK específico de provedor para canais integrados.
Modelo-alvo
Adicione um campo presentation, pertencente ao core, a 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 se torna um subconjunto de presentation durante a migração:
- O bloco de texto de
interactiveé mapeado parapresentation.blocks[].type = "text". - O bloco de botões de
interactiveé mapeado parapresentation.blocks[].type = "buttons". - O bloco de seleção de
interactiveé mapeado parapresentation.blocks[].type = "select".
Os esquemas externos do agente e da CLI agora usam presentation; interactive permanece como um auxiliar interno legado de análise/renderização para produtores de resposta existentes.
Metadados de entrega
Adicione um campo delivery, pertencente ao core, para comportamento de envio que não seja UI.
type ReplyPayloadDelivery = {
pin?:
| boolean
| {
enabled: boolean;
notify?: boolean;
required?: boolean;
};
};
Semântica:
delivery.pin = truesignifica fixar a primeira mensagem entregue com sucesso.notifytem padrãofalse.requiredtem padrãofalse; canais sem suporte ou falhas de fixação degradam automaticamente, continuando a entrega.- Ações manuais de mensagem
pin,unpinelist-pinspermanecem para mensagens existentes.
A vinculação atual de tópico ACP do Telegram deve migrar de channelData.telegram.pin = true para delivery.pin = true.
Contrato de capacidade de runtime
Adicione hooks de renderização de apresentação e entrega ao adaptador de saída em runtime, não ao plugin de canal do plano de controle.
type ChannelPresentationCapabilities = {
supported: boolean;
buttons?: boolean;
selects?: boolean;
context?: boolean;
divider?: boolean;
tones?: MessagePresentationTone[];
};
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>;
};
Comportamento do core:
- Resolver o canal de destino e o adaptador de runtime.
- Consultar as capacidades de apresentação.
- Degradar blocos sem suporte antes da renderização.
- Chamar
renderPresentation. - Se não houver renderizador, converter a apresentação em fallback de texto.
- Após o envio bem-sucedido, chamar
pinDeliveredMessagequandodelivery.pinfor solicitado e tiver suporte.
Mapeamento de canais
Discord:
- Renderizar
presentationpara components v2 e contêineres Carbon em módulos somente de runtime. - Manter auxiliares de cor de destaque em módulos leves.
- Remover importações de
DiscordUiContainerdo código do plano de controle do plugin de canal.
Slack:
- Renderizar
presentationpara Block Kit. - Remover a entrada
blocksdo agente e da CLI.
Telegram:
- Renderizar texto, contexto e divisores como texto.
- Renderizar ações e seleção como teclados inline quando configurado e permitido para a superfície de destino.
- Usar fallback de texto quando botões inline estiverem desativados.
- Migrar a fixação de tópico ACP para
delivery.pin.
Mattermost:
- Renderizar ações como botões interativos quando configurado.
- Renderizar outros blocos como fallback de texto.
MS Teams:
- Renderizar
presentationpara Adaptive Cards. - Manter ações manuais de fixar/desafixar/listar fixações.
- Opcionalmente implementar
pinDeliveredMessagese o suporte do Graph for confiável para a conversa de destino.
Feishu:
- Renderizar
presentationpara cartões interativos. - Manter ações manuais de fixar/desafixar/listar fixações.
- Opcionalmente implementar
pinDeliveredMessagepara fixação de mensagens enviadas se o comportamento da API for confiável.
LINE:
- Renderizar
presentationpara mensagens Flex ou de modelo quando possível. - Fazer fallback para texto em blocos sem suporte.
- Remover payloads de UI do LINE de
channelData.
Canais simples ou limitados:
- Converter apresentação em texto com formatação conservadora.
Etapas de refatoração
- Reaplicar a correção de lançamento do Discord que separa
ui-colors.tsda UI baseada em Carbon e removeDiscordUiContainerdeextensions/discord/src/channel.ts. - Adicionar
presentationedeliveryaReplyPayload, normalização de payload de saída, resumos de entrega e payloads de hook. - Adicionar esquema
MessagePresentatione auxiliares de parser em um subcaminho estreito de SDK/runtime. - Substituir capacidades de mensagem
buttons,cards,componentseblockspor capacidades semânticas de apresentação. - Adicionar hooks de adaptador de saída em runtime para renderização de apresentação e fixação de entrega.
- Substituir a construção de componentes entre contextos por
buildCrossContextPresentation. - Excluir
src/infra/outbound/channel-adapters.tse removerbuildCrossContextComponentsdos tipos de plugin de canal. - Alterar
maybeApplyCrossContextMarkerpara anexarpresentationem vez de parâmetros nativos. - Atualizar caminhos de envio de plugin-dispatch para consumir apenas apresentação semântica e metadados de entrega.
- Remover parâmetros de payload nativo do agente e da CLI:
components,blocks,buttonsecard. - Remover auxiliares de SDK que criam esquemas nativos de ferramentas de mensagem, substituindo-os por auxiliares de esquema de apresentação.
- Remover envelopes de UI/nativos de
channelData; manter apenas metadados de transporte até que cada campo restante seja revisado. - Migrar renderizadores de Discord, Slack, Telegram, Mattermost, MS Teams, Feishu e LINE.
- Atualizar documentação para CLI de mensagens, páginas de canal, SDK de Plugin e cookbook de capacidades.
- Executar perfilamento de fanout de importações para Discord e pontos de entrada de canal afetados.
As etapas 1-11 e 13-14 estão implementadas nesta refatoração para os contratos do agente compartilhado, CLI, capacidade de plugin e adaptador de saída. A etapa 12 permanece como uma rodada mais profunda de limpeza interna para envelopes de transporte channelData privados de provedor. A etapa 15 permanece como validação de acompanhamento se quisermos números quantificados de fanout de importação além do gate de tipos/testes.
Testes
Adicionar ou atualizar:
- Testes de normalização de apresentação.
- Testes de degradação automática de apresentação para blocos sem suporte.
- Testes de marcador entre contextos para plugin dispatch e caminhos de entrega do core.
- Testes de matriz de renderização de canais para Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE e fallback de texto.
- Testes de esquema de ferramenta de mensagem comprovando que campos nativos foram removidos.
- Testes de CLI comprovando que flags nativas foram removidas.
- Regressão de preguiça de importação do ponto de entrada do Discord cobrindo Carbon.
- Testes de fixação de entrega cobrindo Telegram e fallback genérico.
Perguntas em aberto
delivery.pindeve ser implementado para Discord, Slack, MS Teams e Feishu na primeira rodada, ou apenas Telegram primeiro?deliverydeve eventualmente absorver campos existentes comoreplyToId,replyToCurrent,silenteaudioAsVoice, ou permanecer focado em comportamentos pós-envio?- A apresentação deve oferecer suporte direto a imagens ou referências de arquivo, ou mídia deve permanecer separada do layout de UI por enquanto?