Plugin maintainer reference
Presentazione dei messaggi
La presentazione dei messaggi è il contratto condiviso di OpenClaw per interfacce chat in uscita avanzate. Consente ad agenti, comandi CLI, flussi di approvazione e plugin di descrivere una volta l'intento del messaggio, mentre ciascun plugin di canale renderizza la migliore forma nativa possibile.
Usa la presentazione per un'interfaccia messaggio portabile:
- sezioni di testo
- piccolo testo di contesto/piè di pagina
- divisori
- pulsanti
- menu di selezione
- titolo e tono della scheda
Non aggiungere nuovi campi nativi del provider come Discord components, Slack
blocks, Telegram buttons, Teams card o Feishu card allo strumento di
messaggio condiviso. Questi sono output del renderer di proprietà del plugin di canale.
Contratto
Gli autori di Plugin importano il contratto pubblico da:
MessagePresentation, ReplyPayloadDelivery,} from "openclaw/plugin-sdk/interactive-runtime";Forma:
type MessagePresentation = { title?: string; tone?: "neutral" | "info" | "success" | "warning" | "danger"; 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;}; type ReplyPayloadDelivery = { pin?: | boolean | { enabled: boolean; notify?: boolean; required?: boolean; };};Semantica dei pulsanti:
valueè un valore di azione dell'applicazione reinstradato attraverso il percorso di interazione esistente del canale quando il canale supporta controlli cliccabili.urlè un pulsante link. Può esistere senzavalue.labelè obbligatorio ed è usato anche nel fallback testuale.styleè consultivo. I renderer devono mappare gli stili non supportati a un valore predefinito sicuro, non far fallire l'invio.
Semantica della selezione:
options[].valueè il valore dell'applicazione selezionato.placeholderè consultivo e può essere ignorato dai canali senza supporto nativo per le selezioni.- Se un canale non supporta le selezioni, il testo di fallback elenca le etichette.
Esempi di produttori
Scheda semplice:
{ "title": "Deploy approval", "tone": "warning", "blocks": [ { "type": "text", "text": "Canary is ready to promote." }, { "type": "context", "text": "Build 1234, staging passed." }, { "type": "buttons", "buttons": [ { "label": "Approve", "value": "deploy:approve", "style": "success" }, { "label": "Decline", "value": "deploy:decline", "style": "danger" } ] } ]}Pulsante link solo URL:
{ "blocks": [ { "type": "text", "text": "Release notes are ready." }, { "type": "buttons", "buttons": [{ "label": "Open notes", "url": "https://example.com/release" }] } ]}Menu di selezione:
{ "title": "Choose environment", "blocks": [ { "type": "select", "placeholder": "Environment", "options": [ { "label": "Canary", "value": "env:canary" }, { "label": "Production", "value": "env:prod" } ] } ]}Invio CLI:
openclaw message send --channel slack \ --target channel:C123 \ --message "Deploy approval" \ --presentation '{"title":"Deploy approval","tone":"warning","blocks":[{"type":"text","text":"Canary is ready."},{"type":"buttons","buttons":[{"label":"Approve","value":"deploy:approve","style":"success"},{"label":"Decline","value":"deploy:decline","style":"danger"}]}]}'Consegna fissata:
openclaw message send --channel telegram \ --target -1001234567890 \ --message "Topic opened" \ --pinConsegna fissata con JSON esplicito:
{ "pin": { "enabled": true, "notify": true, "required": false }}Contratto del renderer
I plugin di canale dichiarano il supporto di rendering sul loro adapter in uscita:
const adapter: ChannelOutboundAdapter = { deliveryMode: "direct", presentationCapabilities: { supported: true, buttons: true, selects: true, context: true, divider: true, }, deliveryCapabilities: { pin: true, }, renderPresentation({ payload, presentation, ctx }) { return renderNativePayload(payload, presentation, ctx); }, async pinDeliveredMessage({ target, messageId, pin }) { await pinNativeMessage(target, messageId, { notify: pin.notify === true }); },};I campi di capacità sono booleani intenzionalmente semplici. Descrivono ciò che il renderer può rendere interattivo, non ogni limite della piattaforma nativa. I renderer restano responsabili dei limiti specifici della piattaforma, come numero massimo di pulsanti, numero di blocchi e dimensione della scheda.
Flusso di rendering del core
Quando un ReplyPayload o un'azione messaggio include presentation, il core:
- Normalizza il payload di presentazione.
- Risolve l'adapter in uscita del canale di destinazione.
- Legge
presentationCapabilities. - Chiama
renderPresentationquando l'adapter può renderizzare il payload. - Ripiega su testo conservativo quando l'adapter è assente o non può renderizzare.
- Invia il payload risultante attraverso il normale percorso di consegna del canale.
- Applica metadati di consegna come
delivery.pindopo il primo messaggio inviato con successo.
Il core possiede il comportamento di fallback così i produttori possono restare indipendenti dal canale. I plugin di canale possiedono rendering nativo e gestione delle interazioni.
Regole di degradazione
La presentazione deve essere sicura da inviare su canali limitati.
Il testo di fallback include:
titlecome prima riga- blocchi
textcome paragrafi normali - blocchi
contextcome righe di contesto compatte - blocchi
dividercome separatore visivo - etichette dei pulsanti, inclusi gli URL per i pulsanti link
- etichette delle opzioni di selezione
I controlli nativi non supportati devono degradare invece di far fallire l'intero invio. Esempi:
- Telegram con pulsanti inline disabilitati invia il fallback testuale.
- Un canale senza supporto per le selezioni elenca le opzioni di selezione come testo.
- Un pulsante solo URL diventa un pulsante link nativo oppure una riga URL di fallback.
- I fallimenti opzionali di fissaggio non fanno fallire il messaggio consegnato.
L'eccezione principale è delivery.pin.required: true; se il fissaggio è richiesto come
obbligatorio e il canale non può fissare il messaggio inviato, la consegna segnala un fallimento.
Mappatura dei provider
Renderer in bundle attuali:
| Canale | Destinazione di rendering nativa | Note |
|---|---|---|
| Discord | Componenti e contenitori di componenti | Preserva channelData.discord.components legacy per i produttori di payload nativi del provider esistenti, ma i nuovi invii condivisi devono usare presentation. |
| Slack | Block Kit | Preserva channelData.slack.blocks legacy per i produttori di payload nativi del provider esistenti, ma i nuovi invii condivisi devono usare presentation. |
| Telegram | Testo più tastiere inline | Pulsanti/selezioni richiedono capacità di pulsanti inline per la superficie di destinazione; altrimenti viene usato il fallback testuale. |
| Mattermost | Testo più props interattive | Gli altri blocchi degradano a testo. |
| Microsoft Teams | Adaptive Cards | Il testo message semplice è incluso con la scheda quando entrambi sono forniti. |
| Feishu | Schede interattive | L'intestazione della scheda può usare title; il corpo evita di duplicare quel titolo. |
| Canali semplici | Fallback testuale | I canali senza renderer ricevono comunque output leggibile. |
La compatibilità con payload nativi del provider è una facilitazione di transizione per i produttori di risposte esistenti. Non è un motivo per aggiungere nuovi campi nativi condivisi.
Presentazione vs InteractiveReply
InteractiveReply è il sottoinsieme interno precedente usato dagli helper di approvazione e interazione.
Supporta:
- testo
- pulsanti
- selezioni
MessagePresentation è il contratto canonico di invio condiviso. Aggiunge:
- titolo
- tono
- contesto
- divisore
- pulsanti solo URL
- metadati di consegna generici tramite
ReplyPayload.delivery
Usa gli helper da openclaw/plugin-sdk/interactive-runtime quando colleghi codice
precedente:
interactiveReplyToPresentation, normalizeMessagePresentation, presentationToInteractiveControlsReply, presentationToInteractiveReply, renderMessagePresentationFallbackText,} from "openclaw/plugin-sdk/interactive-runtime";Il nuovo codice deve accettare o produrre direttamente MessagePresentation.
presentationToInteractiveReply(...) preserva il testo di presentazione visibile
mappando titolo, testo, contesto, pulsanti e selezioni nella forma precedente
InteractiveReply. I renderer di componenti che già disegnano nativamente blocchi titolo, testo,
contesto e divisore devono usare invece
presentationToInteractiveControlsReply(...), quindi aggiungere solo i controlli
pulsante e selezione.
renderMessagePresentationFallbackText(...) restituisce una stringa vuota per i
blocchi di presentazione che non hanno fallback testuale, come una presentazione con solo divisore.
I trasporti che richiedono un corpo di invio non vuoto possono passare
emptyFallback per optare per un corpo minimo senza cambiare il contratto di fallback
predefinito.
Fissaggio della consegna
Il fissaggio è comportamento di consegna, non presentazione. Usa delivery.pin invece dei
campi nativi del provider come channelData.telegram.pin.
Semantica:
pin: truefissa il primo messaggio consegnato con successo.pin.notifyè predefinito afalse.pin.requiredè predefinito afalse.- I fallimenti opzionali di fissaggio degradano e lasciano intatto il messaggio inviato.
- I fallimenti di fissaggio obbligatori fanno fallire la consegna.
- I messaggi suddivisi in blocchi fissano il primo blocco consegnato, non il blocco finale.
Le azioni messaggio manuali pin, unpin e pins esistono ancora per i messaggi
esistenti in cui il provider supporta quelle operazioni.
Checklist per autori di Plugin
- Dichiara
presentationdadescribeMessageTool(...)quando il canale può renderizzare o degradare in sicurezza la presentazione semantica. - Aggiungi
presentationCapabilitiesall'adapter in uscita di runtime. - Implementa
renderPresentationnel codice di runtime, non nel codice di configurazione del Plugin del piano di controllo. - Tieni le librerie UI native fuori dai percorsi caldi di configurazione/catalogo.
- Preserva i limiti della piattaforma nel renderer e nei test.
- Aggiungi test di fallback per pulsanti non supportati, selezioni, pulsanti URL, duplicazione
titolo/testo e invii misti
messagepiùpresentation. - Aggiungi supporto al fissaggio della consegna tramite
deliveryCapabilities.pinepinDeliveredMessagesolo quando il provider può fissare l'id del messaggio inviato. - Non esporre nuovi campi scheda/blocco/componente/pulsante nativi del provider attraverso lo schema di azione messaggio condiviso.