components, Slack blocks, Telegram buttons, Teams card o Feishu card, a la herramienta de mensajes compartida. Esas son salidas de renderizador propiedad del plugin de canal.
Contrato
Los autores de plugins importan el contrato público desde:action.type: "command"ejecuta un comando de barra nativo a través de la ruta de comandos del núcleo. Usa esto para botones y menús de comandos integrados.action.type: "callback"transporta datos opacos del plugin a través de la ruta de interacción del canal. Los plugins de canal no deben reinterpretar datos de callback como comandos de barra.valuees el valor de callback opaco heredado. Los controles nuevos deberían usaractionpara que los plugins de canal puedan mapear comandos y callbacks sin inferirlo desde el texto.urles un botón de enlace. Puede existir sinvalue.webAppdescribe un botón de aplicación web nativa del canal. Telegram lo renderiza comoweb_appy solo lo admite en chats privados.web_apptodavía se acepta en cargas JSON flexibles por compatibilidad, pero los productores TypeScript deberían usarwebApp.labeles obligatorio y también se usa en el texto de respaldo.stylees orientativo. Los renderizadores deberían mapear estilos no admitidos a un valor predeterminado seguro, no fallar el envío.priorityes opcional. Cuando un canal anuncia límites de acciones y deben descartarse controles, el núcleo conserva primero los botones de mayor prioridad y preserva el orden original entre botones con la misma prioridad. Cuando todos los controles caben, se preserva el orden escrito.disabledes opcional. Los canales deben optar por admitirlo consupportsDisabled; de lo contrario, el núcleo degrada el control deshabilitado a texto de respaldo no interactivo. Un botón deshabilitado siempre se renderiza solo con etiqueta en el texto de respaldo, incluso cuando lleva una accióncommand.reusablees opcional. Los canales que admiten callbacks nativos reutilizables pueden mantener la acción disponible después de una interacción exitosa. Úsalo para acciones repetibles o idempotentes como actualizar, inspeccionar o ver más detalles; déjalo sin establecer para aprobaciones normales de un solo uso y acciones destructivas.
options[].actiontiene el mismo significado de comando/callback queactionde botón.options[].valuees el valor de aplicación seleccionado heredado.placeholderes orientativo y puede ser ignorado por canales sin soporte nativo para selección.- Si un canal no admite selecciones, el texto de respaldo enumera las etiquetas.
Ejemplos de productores
Tarjeta simple:Contrato del renderizador
Los plugins de canal declaran soporte de renderizado en su adaptador saliente:limits opcionales describen el contenedor genérico que el núcleo puede adaptar antes de llamar al renderizador:
Flujo de renderizado del núcleo
Cuando unReplyPayload o una acción de mensaje incluye presentation, el núcleo:
- Normaliza la carga de presentación.
- Resuelve el adaptador saliente del canal de destino.
- Lee
presentationCapabilities. - Aplica límites de capacidad genéricos, como conteo de acciones, longitud de etiquetas y conteo de opciones de selección, cuando el adaptador los anuncia.
- Llama a
renderPresentationcuando el adaptador puede renderizar la carga. - Recurre a texto conservador cuando el adaptador está ausente o no puede renderizar.
- Envía la carga resultante a través de la ruta normal de entrega del canal.
- Aplica metadatos de entrega, como
delivery.pin, después del primer mensaje enviado correctamente.
Reglas de degradación
La presentación debe ser segura para enviar en canales limitados. El texto de respaldo incluye:titlecomo primera línea- bloques
textcomo párrafos normales - bloques
contextcomo líneas de contexto compactas - bloques
dividercomo separador visual - etiquetas de botones, incluidas URL para botones de enlace
- etiquetas de opciones de selección
Visibilidad de respaldo del valor de botón
Cuando un canal no puede renderizar controles interactivos, los valores de botones y selecciones se convierten en texto sin formato como respaldo. El comportamiento de respaldo preserva la usabilidad mientras mantiene privados los datos opacos de callback:- Las acciones tipadas como
commandse renderizan comolabel: \command“ para que los usuarios puedan copiar el comando y ejecutarlo manualmente en la entrada del canal. - Las acciones tipadas como
callbacky los camposvalueheredados se renderizan solo con etiqueta. El valor de callback opaco no se expone en el texto de respaldo. - Los botones
url/webApprenderizan el texto de la URL junto a la etiqueta del botón, ya que la URL es visible para el usuario. - Las opciones de selección se renderizan solo con etiqueta. El valor de opción subyacente no se expone en el texto de respaldo.
- Telegram con botones en línea deshabilitados envía texto de respaldo.
- Un canal sin soporte para selección enumera las opciones de selección como texto.
- Un botón solo URL se convierte en un botón de enlace nativo o en una línea de URL de respaldo.
- Los fallos opcionales al fijar no hacen fallar el mensaje entregado.
delivery.pin.required: true; si se solicita fijar como obligatorio y el canal no puede fijar el mensaje enviado, la entrega informa un fallo.
Mapeo de proveedor
Renderizadores integrados actuales:| Canal | Destino de renderizado nativo | Notas |
|---|---|---|
| Discord | Componentes y contenedores de componentes | Conserva el channelData.discord.components heredado para productores existentes de cargas útiles nativas del proveedor, pero los nuevos envíos compartidos deben usar presentation. |
| Feishu | Tarjetas interactivas | El encabezado de la tarjeta puede usar title; el cuerpo evita duplicar ese título. |
| Matrix | Reserva de texto más campo de evento estructurado | Los botones/selectores se anuncian como compatibles, pero actualmente cada bloque se renderiza como salida de renderMessagePresentationFallbackText transportada en un campo de evento com.openclaw.presentation, no como widgets interactivos nativos. |
| Mattermost | Texto más props interactivas | Los selectores y divisores no son compatibles; esos bloques degradan a texto. |
| Microsoft Teams | Adaptive Cards | El texto plano de message se incluye con la tarjeta cuando se proporcionan ambos. Los selectores, estilos y el estado deshabilitado no son compatibles. |
| Slack | Block Kit | Conserva el channelData.slack.blocks heredado para productores existentes de cargas útiles nativas del proveedor, pero los nuevos envíos compartidos deben usar presentation. |
| Telegram | Texto más teclados en línea | Los botones/selectores requieren capacidad de botón en línea para la superficie de destino; de lo contrario, se usa la reserva de texto. |
| Canales simples | Reserva de texto | Los canales sin renderizador siguen recibiendo una salida legible. |
Presentación frente a InteractiveReply
InteractiveReply es el subconjunto interno anterior que usan los ayudantes de aprobación e interacción. Admite:
- texto
- botones
- selectores
MessagePresentation es el contrato canónico de envío compartido. Agrega:
- título
- tono
- contexto
- divisor
- botones solo de URL
- metadatos genéricos de entrega mediante
ReplyPayload.delivery
openclaw/plugin-sdk/interactive-runtime al conectar código anterior:
OC_I18N_900011
El código nuevo debe aceptar o producir MessagePresentation directamente. Las cargas útiles interactive existentes son un subconjunto obsoleto de presentation; el soporte en tiempo de ejecución permanece para productores anteriores.
Ayudantes no obsoletos que conviene conocer:
normalizeMessagePresentation(raw)/hasMessagePresentationBlocks(value)validan y convierten una carga útil sin tipo (por ejemplo, JSON desde la marca--presentationde la CLI) enMessagePresentation.isMessagePresentationInteractiveBlock(block)reduce un bloque a la uniónbuttons|select.resolveMessagePresentationActionValue(action)/resolveMessagePresentationControlValue(control)leen el valor efectivo de comando/callback de unaaction, recurriendo al campo heredadovaluepararesolveMessagePresentationControlValue.
InteractiveReply* y los ayudantes de conversión están marcados como
@deprecated en el SDK:
InteractiveReply,InteractiveReplyBlock,InteractiveReplyButton,InteractiveReplyOption,InteractiveReplySelectBlockyInteractiveReplyTextBlocknormalizeInteractiveReply(...)hasInteractiveReplyBlocks(...)interactiveReplyToPresentation(...)presentationToInteractiveReply(...)presentationToInteractiveControlsReply(...)resolveInteractiveTextFallback(...)reduceInteractiveReply(...)
presentationToInteractiveReply(...) y
presentationToInteractiveControlsReply(...) siguen disponibles como puentes de renderizador
para implementaciones heredadas de canales. El código productor nuevo no debe llamarlos;
envía presentation y deja que la adaptación de núcleo/canal gestione el renderizado.
Los ayudantes de aprobación también tienen reemplazos que priorizan la presentación:
- usa
buildApprovalPresentationFromActionDescriptors(...)en lugar debuildApprovalInteractiveReplyFromActionDescriptors(...) - usa
buildApprovalPresentation(...)en lugar debuildApprovalInteractiveReply(...) - usa
buildExecApprovalPresentation(...)en lugar debuildExecApprovalInteractiveReply(...)
renderMessagePresentationFallbackText(...) devuelve una cadena vacía para
bloques de presentación que no tienen reserva de texto, como una presentación
solo con divisor. Los transportes que requieren un cuerpo de envío no vacío pueden pasar
emptyFallback para optar por un cuerpo mínimo sin cambiar el contrato predeterminado
de reserva.
Fijación de entrega
La fijación es comportamiento de entrega, no presentación. Usadelivery.pin en lugar de
campos nativos del proveedor como channelData.telegram.pin.
Semántica:
pin: truefija el primer mensaje entregado correctamente.pin.notifytienefalsecomo valor predeterminado.pin.requiredtienefalsecomo valor predeterminado.- Los fallos opcionales de fijación degradan y dejan intacto el mensaje enviado.
- Los fallos obligatorios de fijación hacen fallar la entrega.
- Los mensajes fragmentados fijan el primer fragmento entregado, no el fragmento final.
pin, unpin y pins siguen existiendo para mensajes existentes donde el proveedor admite esas operaciones.
Lista de comprobación para autores de Plugin
- Declara
presentationdesdedescribeMessageTool(...)cuando el canal pueda renderizar o degradar de forma segura la presentación semántica. - Agrega
presentationCapabilitiesal adaptador saliente de tiempo de ejecución. - Implementa
renderPresentationen el código de tiempo de ejecución, no en el código de configuración del Plugin del plano de control. - Mantén las bibliotecas de interfaz nativa fuera de las rutas críticas de configuración/catálogo.
- Declara límites de capacidad genéricos en
presentationCapabilities.limitscuando se conozcan. - Conserva los límites finales de la plataforma en el renderizador y las pruebas.
- Agrega pruebas de reserva para botones no compatibles, selectores, botones de URL, duplicación de título/texto y envíos mixtos de
messagemáspresentation. - Agrega soporte de fijación de entrega mediante
deliveryCapabilities.pinypinDeliveredMessagesolo cuando el proveedor pueda fijar el id del mensaje enviado. - No expongas nuevos campos nativos del proveedor de tarjeta/bloque/componente/botón mediante el esquema compartido de acciones de mensaje.