Get started
Plan de refactorización de la presentación de canales
Estado
Implementado para las superficies del agente compartido, la CLI, las capacidades de plugins y la entrega saliente:
ReplyPayload.presentationtransporta la interfaz semántica de los mensajes.ReplyPayload.delivery.pintransporta solicitudes para fijar mensajes enviados.- Las acciones de mensajes compartidas exponen
presentation,deliveryypinen lugar decomponents,blocks,buttonsocardnativos del proveedor. - El núcleo renderiza la presentación o la degrada automáticamente mediante las capacidades salientes declaradas por los plugins.
- Los renderizadores de Discord, Slack, Telegram, Mattermost, MS Teams y Feishu consumen el contrato genérico.
- El código del plano de control del canal de Discord ya no importa contenedores de interfaz basados en Carbon.
La documentación canónica se encuentra ahora en Presentación de mensajes. Conserva este plan como contexto histórico de implementación; actualiza la guía canónica cuando cambien el contrato, el renderizador o el comportamiento de respaldo.
Problema
Actualmente, la interfaz de los canales está dividida entre varias superficies incompatibles:
- El núcleo posee un enlace de renderización entre contextos con la estructura de Discord mediante
buildCrossContextComponents. channel.tsde Discord puede importar la interfaz nativa de Carbon medianteDiscordUiContainer, lo que incorpora dependencias de interfaz en tiempo de ejecución al plano de control del plugin del canal.- El agente y la CLI exponen mecanismos de escape para cargas útiles nativas, como
componentsde Discord,blocksde Slack,buttonsde Telegram o Mattermost ycardde Teams o Feishu. ReplyPayload.channelDatatransporta tanto indicaciones de transporte como envoltorios de interfaz nativa.- El modelo genérico
interactiveexiste, pero es más limitado que los diseños más completos que ya utilizan Discord, Slack, Teams, Feishu, LINE, Telegram y Mattermost.
Esto hace que el núcleo conozca las estructuras de interfaz nativas, debilita la carga diferida del entorno de ejecución de los plugins y ofrece a los agentes demasiadas formas específicas de cada proveedor para expresar la misma intención de mensaje.
Objetivos
- El núcleo decide la mejor presentación semántica para un mensaje a partir de las capacidades declaradas.
- Las extensiones declaran capacidades y renderizan la presentación semántica en cargas útiles de transporte nativas.
- La interfaz web de control permanece separada de la interfaz nativa del chat.
- Las cargas útiles nativas de los canales no se exponen mediante la superficie de mensajes compartida del agente ni de la CLI.
- Las funciones de presentación no compatibles se degradan automáticamente a la mejor representación textual.
- El comportamiento de entrega, como fijar un mensaje enviado, consiste en metadatos genéricos de entrega, no de presentación.
No objetivos
- Ningún adaptador de compatibilidad con versiones anteriores para
buildCrossContextComponents. - Ningún mecanismo de escape nativo público para
components,blocks,buttonsocard. - Ninguna importación en el núcleo de bibliotecas de interfaz nativas de los canales.
- Ningún punto de integración del SDK específico del proveedor para canales incluidos.
Modelo objetivo
Añadir un campo presentation, propiedad del núcleo, 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 convierte en un subconjunto de presentation durante la migración:
- El bloque de texto de
interactivese asigna apresentation.blocks[].type = "text". - El bloque de botones de
interactivese asigna apresentation.blocks[].type = "buttons". - El bloque de selección de
interactivese asigna apresentation.blocks[].type = "select".
Los esquemas externos del agente y la CLI ahora utilizan presentation; interactive permanece como auxiliar interno heredado de análisis y renderización para los productores de respuestas existentes.
La API pública orientada a productores considera interactive obsoleto. La compatibilidad
en tiempo de ejecución se mantiene para que los auxiliares de aprobación existentes y los plugins antiguos sigan
funcionando mientras el código nuevo emite presentation.
Metadatos de entrega
Añadir un campo delivery, propiedad del núcleo, para el comportamiento de envío que no forme parte de la interfaz.
type ReplyPayloadDelivery = { pin?: | boolean | { enabled: boolean; notify?: boolean; required?: boolean; };};Semántica:
delivery.pin = truesignifica fijar el primer mensaje entregado correctamente.- El valor predeterminado de
notifyesfalse. - El valor predeterminado de
requiredesfalse; los canales no compatibles o los errores al fijar se degradan automáticamente y la entrega continúa. - Las acciones manuales de mensajes
pin,unpinylist-pinsse mantienen para los mensajes existentes.
La vinculación actual de temas ACP de Telegram debe pasar de channelData.telegram.pin = true a delivery.pin = true.
Contrato de capacidades del entorno de ejecución
Añadir enlaces de renderización de presentación y entrega al adaptador saliente del entorno de ejecución, no al plugin del canal del plano de control.
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>;};Comportamiento del núcleo:
- Resolver el canal de destino y el adaptador del entorno de ejecución.
- Consultar las capacidades de presentación.
- Degradar los bloques no compatibles y aplicar límites genéricos de capacidad antes de renderizar.
- Llamar a
renderPresentation. - Si no existe ningún renderizador, convertir la presentación en texto de respaldo.
- Tras un envío correcto, llamar a
pinDeliveredMessagecuando se solicitedelivery.piny sea compatible.
Asignación por canal
Discord:
- Renderizar
presentationcomo componentes v2 y contenedores de Carbon en módulos exclusivos del entorno de ejecución. - Mantener los auxiliares de color de énfasis en módulos ligeros.
- Eliminar las importaciones de
DiscordUiContainerdel código del plano de control del plugin del canal.
Slack:
- Renderizar
presentationcomo Block Kit. - Eliminar la entrada
blocksdel agente y la CLI.
Telegram:
- Renderizar el texto, el contexto y los separadores como texto.
- Renderizar las acciones y la selección como teclados en línea cuando estén configurados y permitidos para la superficie de destino.
- Utilizar el texto de respaldo cuando los botones en línea estén desactivados.
- Trasladar la fijación de temas ACP a
delivery.pin.
Mattermost:
- Renderizar las acciones como botones interactivos cuando estén configurados.
- Renderizar los demás bloques como texto de respaldo.
MS Teams:
- Renderizar
presentationcomo Adaptive Cards. - Mantener las acciones manuales
pin/unpin/list-pins. - Implementar opcionalmente
pinDeliveredMessagesi la compatibilidad con Graph es fiable para la conversación de destino.
Feishu:
- Renderizar
presentationcomo tarjetas interactivas. - Mantener las acciones manuales
pin/unpin/list-pins. - Implementar opcionalmente
pinDeliveredMessagepara fijar mensajes enviados si el comportamiento de la API es fiable.
LINE:
- Renderizar
presentationcomo mensajes Flex o de plantilla cuando sea posible. - Recurrir al texto para los bloques no compatibles.
- Eliminar las cargas útiles de interfaz de LINE de
channelData.
Canales simples o limitados:
- Convertir la presentación en texto con un formato conservador.
Pasos de refactorización
- Volver a aplicar la corrección de la versión de Discord que separa
ui-colors.tsde la interfaz basada en Carbon y eliminaDiscordUiContainerdeextensions/discord/src/channel.ts. - Añadir
presentationydeliveryaReplyPayload, la normalización de cargas útiles salientes, los resúmenes de entrega y las cargas útiles de los enlaces. - Añadir el esquema
MessagePresentationy auxiliares de análisis en una subruta específica del SDK o del entorno de ejecución. - Sustituir las capacidades de mensajes
buttons,cards,componentsyblockspor capacidades de presentación semántica. - Añadir enlaces del adaptador saliente del entorno de ejecución para renderizar la presentación y fijar la entrega.
- Sustituir la construcción de componentes entre contextos por
buildCrossContextPresentation. - Eliminar
src/infra/outbound/channel-adapters.tsy quitarbuildCrossContextComponentsde los tipos de plugins de canal. - Cambiar
maybeApplyCrossContextMarkerpara que adjuntepresentationen lugar de parámetros nativos. - Actualizar las rutas de envío de la distribución a plugins para que solo consuman la presentación semántica y los metadatos de entrega.
- Eliminar los parámetros de cargas útiles nativas del agente y la CLI:
components,blocks,buttonsycard. - Eliminar los auxiliares del SDK que crean esquemas nativos de herramientas de mensajes y sustituirlos por auxiliares del esquema de presentación.
- Eliminar los envoltorios de interfaz o nativos de
channelData; conservar solo los metadatos de transporte hasta revisar cada campo restante. - Migrar los renderizadores de Discord, Slack, Telegram, Mattermost, MS Teams, Feishu y LINE.
- Actualizar la documentación de la CLI de mensajes, las páginas de canales, el SDK de plugins y el recetario de capacidades.
- Ejecutar un análisis de la propagación de importaciones para Discord y los puntos de entrada de los canales afectados.
Los pasos 1-11 y 13-14 están implementados en esta refactorización para los contratos del agente compartido, la CLI, las capacidades de plugins y el adaptador saliente. El paso 12 queda pendiente como una limpieza interna más profunda de los envoltorios de transporte channelData privados de los proveedores. El paso 15 queda como validación posterior si se desean cifras cuantificadas de propagación de importaciones más allá de la comprobación de tipos y pruebas.
Pruebas
Añadir o actualizar:
- Pruebas de normalización de la presentación.
- Pruebas de degradación automática de la presentación para bloques no compatibles.
- Pruebas de marcadores entre contextos para las rutas de distribución a plugins y entrega del núcleo.
- Pruebas de la matriz de renderización de canales para Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE y el texto de respaldo.
- Pruebas del esquema de herramientas de mensajes que demuestren que los campos nativos ya no existen.
- Pruebas de la CLI que demuestren que las opciones nativas ya no existen.
- Regresión de carga diferida de importaciones del punto de entrada de Discord que abarque Carbon.
- Pruebas de fijación durante la entrega que abarquen Telegram y el respaldo genérico.
Preguntas abiertas
- ¿Debe implementarse
delivery.pinpara Discord, Slack, MS Teams y Feishu en la primera fase, o solo para Telegram inicialmente? - ¿Debe
deliveryincorporar finalmente campos existentes comoreplyToId,replyToCurrent,silentyaudioAsVoice, o mantenerse centrado en comportamientos posteriores al envío? - ¿Debe la presentación admitir directamente imágenes o referencias a archivos, o deben los contenidos multimedia permanecer separados del diseño de la interfaz por ahora?