Plan de refactorización de la presentación del canal
Implementado para el agente compartido, la CLI, la capacidad del complemento y las superficies de entrega saliente:
ReplyPayload.presentationtransporta la IU de mensajes semántica.ReplyPayload.delivery.pintransporta las solicitudes de fijación de mensajes enviados.- Las acciones de mensajes compartidos exponen
presentation,deliveryypinen lugar decomponents,blocks,buttonsocardnativos del proveedor. - El núcleo renderiza o degrada automáticamente la presentación a través de capacidades de salida declaradas por el complemento.
- 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 IU respaldados por Carbon.
La documentación canónica ahora se encuentra en Message Presentation. Mantenga este plan como contexto de implementación histórico; actualice la guía canónica para cambios en el contrato, el renderizador o el comportamiento de reserva.
Problema
Sección titulada «Problema»La IU del canal actualmente está dividida en varias superficies incompatibles:
- El núcleo posee un gancho de renderizado entre contextos con forma de Discord a través de
buildCrossContextComponents. - El
channel.tsde Discord puede importar la IU nativa de Carbon a través deDiscordUiContainer, lo que introduce dependencias de IU en tiempo de ejecución en el plano de control del complemento del canal. - El agente y la CLI exponen salidas de escape de carga útil nativa, como
componentsde Discord,blocksde Slack,buttonsde Telegram o Mattermost, ycardde Teams o Feishu. ReplyPayload.channelDatatransporta tanto sugerencias de transporte como sobres de IU nativos.- Existe el modelo genérico
interactive, pero es más estrecho que los diseños enriquecidos ya utilizados por Discord, Slack, Teams, Feishu, LINE, Telegram y Mattermost.
Esto hace que el núcleo sea consciente de las formas de la IU nativa, debilita la pereza del tiempo de ejecución del complemento y da a los agentes demasiadas formas específicas del proveedor para expresar la misma intención del mensaje.
Objetivos
Sección titulada «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 de usuario de Control Web permanece separada de la interfaz de usuario nativa del chat.
- Las cargas útiles del canal nativo no se exponen a través del agente compartido ni de la superficie de mensajes de la CLI.
- Las características de presentación no compatibles se degradan automáticamente a la mejor representación de texto.
- El comportamiento de entrega, como fijar un mensaje enviado, es metadatos de entrega genéricos, no presentación.
Objetivos no excluidos
Sección titulada «Objetivos no excluidos»- No hay una capa de compatibilidad hacia atrás para
buildCrossContextComponents. - No hay salidas de escape nativas públicas para
components,blocks,buttonsocard. - No hay importaciones principales de librerías de interfaz de usuario nativas del canal.
- No hay costuras de SDK específicas del proveedor para canales agrupados.
Modelo de destino
Sección titulada «Modelo de destino»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
interactivese asigna apresentation.blocks[].type = "text". - El bloque de botones
interactivese asigna apresentation.blocks[].type = "buttons". - El bloque de selección
interactivese asigna apresentation.blocks[].type = "select".
Los esquemas del agente externo y de la CLI ahora usan presentation; interactive sigue siendo un asistente de análisis/representación (parser/rendering) de legado interno para los productores de respuestas existentes.
La API pública orientada al productor trata interactive como obsoleta. El soporte
en tiempo de ejecución se mantiene para que los asistentes de aprobación existentes y los complementos antiguos continúen
funcionando mientras el código nuevo emite presentation.
Metadatos de entrega
Sección titulada «Metadatos de entrega»Añadir un campo delivery propiedad del núcleo (core-owned) para el comportamiento de envío que no es de UI.
type ReplyPayloadDelivery = { pin?: | boolean | { enabled: boolean; notify?: boolean; required?: boolean; };};Semántica:
delivery.pin = truesignifica fijar el primer mensaje entregado con éxito.notifyse establece de forma predeterminada enfalse.requiredse establece de forma predeterminada enfalse; los canales no compatibles o el fallo al fijar se degradan automáticamente continuando con la entrega.- Las acciones de mensaje manual
pin,unpinylist-pinsse mantienen para los mensajes existentes.
El enlace actual de temas de ACP de Telegram debería moverse de channelData.telegram.pin = true a delivery.pin = true.
Contrato de capacidad en tiempo de ejecución
Sección titulada «Contrato de capacidad en tiempo de ejecución»Añadir hooks de renderizado de presentación y entrega al adaptador de salida en tiempo de ejecución, no al complemento 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 principal:
- Resolver el canal de destino y el adaptador en tiempo de ejecución.
- Solicitar capacidades de presentación.
- Degrada los bloques no compatibles y aplica los límites de capacidad genéricos antes de renderizar.
- Llamar a
renderPresentation. - Si no existe un renderizador, convertir la presentación a texto de respaldo.
- Después de un envío exitoso, llamar a
pinDeliveredMessagecuandodelivery.pinse solicita y es compatible.
Asignación de canales
Sección titulada «Asignación de canales»Discord:
- Renderizar
presentationa componentes v2 y contenedores de Carbon en módulos solo de tiempo de ejecución. - Mantener los auxiliares de color de acento en módulos ligeros.
- Eliminar las importaciones de
DiscordUiContainerdel código del plano de control (control-plane) del complemento del canal.
Slack:
- Renderizar
presentationa Block Kit. - Eliminar la entrada
blocksdel agente y la CLI.
Telegram:
- Renderizar texto, contexto y divisores como texto.
- Renderizar acciones y selecciones como teclados en línea cuando estén configurados y permitidos para la superficie de destino.
- Usar texto de respaldo cuando los botones en línea están deshabilitados.
- Mover la fijación de temas de ACP a
delivery.pin.
Mattermost:
- Renderizar acciones como botones interactivos donde estén configurados.
- Renderizar otros bloques como texto de respaldo.
MS Teams:
- Renderizar
presentationa Adaptive Cards. - Mantener las acciones manuales de fijar/desanclar/listar fijados.
- Implementar opcionalmente
pinDeliveredMessagesi el soporte de Graph es confiable para la conversación de destino.
Feishu:
- Renderizar
presentationa tarjetas interactivas. - Mantener las acciones manuales de fijar/desanclar/listar fijados.
- Opcionalmente, implementa
pinDeliveredMessagepara la fijación de mensajes enviados si el comportamiento de la API es confiable.
LINE:
- Renderiza
presentationcomo mensajes Flex o de plantilla cuando sea posible. - Recurre al texto para bloques no admitidos.
- Elimina los payloads de UI de LINE de
channelData.
Canales simples o limitados:
- Convertir la presentación a texto con formato conservador.
Pasos de refactorización
Sección titulada «Pasos de refactorización»- Vuelve a aplicar la corrección de la versión de Discord que separa
ui-colors.tsde la UI respaldada por Carbon y eliminaDiscordUiContainerdeextensions/discord/src/channel.ts. - Agrega
presentationydeliveryaReplyPayload, la normalización de payloads salientes, los resúmenes de entrega y los payloads de hooks. - Agrega el esquema
MessagePresentationy los auxiliares de análisis en una subruta estrecha del SDK/runtime. - Reemplaza las capacidades de mensaje
buttons,cards,componentsyblockscon capacidades de presentación semántica. - Añadir hooks de adaptador de salida en tiempo de ejecución para el renderizado de presentación y la fijación de entrega.
- Reemplaza la construcción de componentes entre contextos con
buildCrossContextPresentation. - Elimina
src/infra/outbound/channel-adapters.tsy quitabuildCrossContextComponentsde los tipos de complementos de canal. - Cambia
maybeApplyCrossContextMarkerpara adjuntarpresentationen lugar de parámetros nativos. - Actualizar las rutas de envío de despacho de complementos para consumir solo metadatos de presentación y entrega semántica.
- Elimina los parámetros de payload nativos del agente y la CLI:
components,blocks,buttonsycard. - Eliminar ayudantes del SDK que crean esquemas de herramientas de mensaje nativas, reemplazándolos con ayudantes de esquema de presentación.
- Elimina los sobres de UI/nativos de
channelData; mantén solo los metadatos de transporte hasta que se revise cada campo restante. - Migrar los renderizadores de Discord, Slack, Telegram, Mattermost, MS Teams, Feishu y LINE.
- Actualizar la documentación para la CLI de mensajes, páginas de canales, SDK de complementos y libro de recetas de capacidades.
- Ejecutar el perfilado de difusión de importación para Discord y los puntos de entrada de canales afectados.
Los pasos 1-11 y 13-14 están implementados en esta refactorización para los contratos del agente compartido, la CLI, la capacidad del complemento y los adaptadores salientes. El paso 12 sigue siendo una limpieza interna más profunda de los sobres de transporte channelData privados del proveedor. El paso 15 sigue siendo una validación de seguimiento si queremos números cuantificados de importación/difusión más allá de la puerta de tipos/pruebas.
Pruebas
Sección titulada «Pruebas»Añadir o actualizar:
- Pruebas de normalización de presentación.
- Pruebas de autodegradación de presentación para bloques no compatibles.
- Pruebas de marcadores entre contextos para el despacho de complementos y las rutas de entrega principales.
- Pruebas de matriz de renderizado de canales para Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE y alternativa de texto.
- Pruebas de esquema de herramientas de mensaje que demuestran que los campos nativos han desaparecido.
- Pruebas de CLI que demuestran que las marcas nativas han desaparecido.
- Regresión de pereza de importación del punto de entrada de Discord que cubre Carbon.
- Pruebas de fijación de entrega que cubren Telegram y la alternativa genérica.
Preguntas abiertas
Sección titulada «Preguntas abiertas»- ¿Se debe implementar
delivery.pinpara Discord, Slack, MS Teams y Feishu en la primera pasada, o solo Telegram primero? - ¿Debería
deliveryeventualmente absorber campos existentes comoreplyToId,replyToCurrent,silentyaudioAsVoice, o mantenerse enfocado en comportamientos posteriores al envío? - ¿Debe la presentación admitir imágenes o referencias de archivos directamente, o los medios deben permanecer separados del diseño de la interfaz de usuario por ahora?