Get started
Plan de refactorisation de la présentation des canaux
Statut
Implémenté pour les surfaces de l’agent partagé, de la CLI, des capacités de plugin et de livraison sortante :
ReplyPayload.presentationtransporte l’interface de message sémantique.ReplyPayload.delivery.pintransporte les demandes d’épinglage des messages envoyés.- Les actions de message partagées exposent
presentation,deliveryetpinau lieu decomponents,blocks,buttonsoucardnatifs du fournisseur. - Le cœur effectue le rendu ou la dégradation automatique de la présentation via les capacités sortantes déclarées par le plugin.
- Les moteurs de rendu Discord, Slack, Telegram, Mattermost, MS Teams et Feishu consomment le contrat générique.
- Le code du plan de contrôle du canal Discord n’importe plus de conteneurs d’interface adossés à Carbon.
La documentation canonique se trouve désormais dans Présentation des messages. Conservez ce plan comme contexte historique d’implémentation ; mettez à jour le guide canonique pour les changements de contrat, de moteur de rendu ou de comportement de repli.
Problème
L’interface des canaux est actuellement répartie entre plusieurs surfaces incompatibles :
- Le cœur possède un hook de rendu intercontextuel de forme Discord via
buildCrossContextComponents. - Le fichier Discord
channel.tspeut importer l’interface Carbon native viaDiscordUiContainer, ce qui introduit des dépendances d’interface à l’exécution dans le plan de contrôle du plugin de canal. - L’agent et la CLI exposent des échappatoires de charge utile native telles que les
componentsDiscord, lesblocksSlack, lesbuttonsTelegram ou Mattermost, et lescardTeams ou Feishu. ReplyPayload.channelDatatransporte à la fois des indications de transport et des enveloppes d’interface natives.- Le modèle générique
interactiveexiste, mais il est plus étroit que les mises en page plus riches déjà utilisées par Discord, Slack, Teams, Feishu, LINE, Telegram et Mattermost.
Cela rend le cœur conscient des formes d’interface natives, affaiblit la paresse d’exécution des plugins et donne aux agents trop de façons spécifiques aux fournisseurs d’exprimer la même intention de message.
Objectifs
- Le cœur décide de la meilleure présentation sémantique pour un message à partir des capacités déclarées.
- Les extensions déclarent des capacités et rendent la présentation sémantique en charges utiles de transport natives.
- L’interface Web Control reste séparée de l’interface native de chat.
- Les charges utiles de canal natives ne sont pas exposées via la surface de message partagée de l’agent ou de la CLI.
- Les fonctionnalités de présentation non prises en charge se dégradent automatiquement vers la meilleure représentation textuelle.
- Les comportements de livraison tels que l’épinglage d’un message envoyé sont des métadonnées de livraison génériques, pas de la présentation.
Non-objectifs
- Aucun shim de rétrocompatibilité pour
buildCrossContextComponents. - Aucune échappatoire native publique pour
components,blocks,buttonsoucard. - Aucun import par le cœur de bibliothèques d’interface natives de canal.
- Aucun seam de SDK spécifique aux fournisseurs pour les canaux groupés.
Modèle cible
Ajouter un champ presentation appartenant au cœur à 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 devient un sous-ensemble de presentation pendant la migration :
- Le bloc texte
interactivecorrespond àpresentation.blocks[].type = "text". - Le bloc de boutons
interactivecorrespond àpresentation.blocks[].type = "buttons". - Le bloc de sélection
interactivecorrespond àpresentation.blocks[].type = "select".
Les schémas externes de l’agent et de la CLI utilisent désormais presentation ; interactive reste un ancien assistant interne d’analyse et de rendu pour les producteurs de réponses existants.
Métadonnées de livraison
Ajouter un champ delivery appartenant au cœur pour les comportements d’envoi qui ne sont pas de l’interface.
type ReplyPayloadDelivery = {
pin?:
| boolean
| {
enabled: boolean;
notify?: boolean;
required?: boolean;
};
};
Sémantique :
delivery.pin = truesignifie épingler le premier message livré avec succès.notifyvautfalsepar défaut.requiredvautfalsepar défaut ; les canaux non pris en charge ou les échecs d’épinglage se dégradent automatiquement en poursuivant la livraison.- Les actions de message manuelles
pin,unpinetlist-pinsrestent disponibles pour les messages existants.
La liaison actuelle de sujet Telegram ACP doit passer de channelData.telegram.pin = true à delivery.pin = true.
Contrat de capacité d’exécution
Ajouter des hooks de rendu de présentation et de livraison à l’adaptateur sortant d’exécution, pas au plugin de canal du plan de contrôle.
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>;
};
Comportement du cœur :
- Résoudre le canal cible et l’adaptateur d’exécution.
- Demander les capacités de présentation.
- Dégrader les blocs non pris en charge avant le rendu.
- Appeler
renderPresentation. - Si aucun moteur de rendu n’existe, convertir la présentation en repli textuel.
- Après un envoi réussi, appeler
pinDeliveredMessagelorsquedelivery.pinest demandé et pris en charge.
Correspondance des canaux
Discord :
- Rendre
presentationen composants v2 et conteneurs Carbon dans des modules uniquement d’exécution. - Conserver les assistants de couleur d’accent dans des modules légers.
- Supprimer les imports
DiscordUiContainerdu code du plan de contrôle du plugin de canal.
Slack :
- Rendre
presentationen Block Kit. - Supprimer l’entrée
blocksde l’agent et de la CLI.
Telegram :
- Rendre le texte, le contexte et les séparateurs sous forme de texte.
- Rendre les actions et la sélection comme claviers en ligne lorsque c’est configuré et autorisé pour la surface cible.
- Utiliser le repli textuel lorsque les boutons en ligne sont désactivés.
- Déplacer l’épinglage de sujet ACP vers
delivery.pin.
Mattermost :
- Rendre les actions comme boutons interactifs lorsque c’est configuré.
- Rendre les autres blocs comme repli textuel.
MS Teams :
- Rendre
presentationen Adaptive Cards. - Conserver les actions manuelles d’épinglage, de désépinglage et de liste des épingles.
- Implémenter éventuellement
pinDeliveredMessagesi la prise en charge Graph est fiable pour la conversation cible.
Feishu :
- Rendre
presentationen cartes interactives. - Conserver les actions manuelles d’épinglage, de désépinglage et de liste des épingles.
- Implémenter éventuellement
pinDeliveredMessagepour l’épinglage des messages envoyés si le comportement de l’API est fiable.
LINE :
- Rendre
presentationen messages Flex ou modèles lorsque c’est possible. - Revenir au texte pour les blocs non pris en charge.
- Supprimer les charges utiles d’interface LINE de
channelData.
Canaux simples ou limités :
- Convertir la présentation en texte avec un formatage conservateur.
Étapes de refactorisation
- Réappliquer le correctif de version Discord qui sépare
ui-colors.tsde l’interface adossée à Carbon et supprimeDiscordUiContainerdeextensions/discord/src/channel.ts. - Ajouter
presentationetdeliveryàReplyPayload, à la normalisation des charges utiles sortantes, aux résumés de livraison et aux charges utiles de hooks. - Ajouter le schéma
MessagePresentationet des assistants d’analyse dans un sous-chemin SDK/exécution étroit. - Remplacer les capacités de message
buttons,cards,componentsetblockspar des capacités de présentation sémantique. - Ajouter des hooks d’adaptateur sortant d’exécution pour le rendu de présentation et l’épinglage de livraison.
- Remplacer la construction de composants intercontextuels par
buildCrossContextPresentation. - Supprimer
src/infra/outbound/channel-adapters.tset retirerbuildCrossContextComponentsdes types de plugin de canal. - Modifier
maybeApplyCrossContextMarkerpour attacherpresentationau lieu de paramètres natifs. - Mettre à jour les chemins d’envoi de répartition des plugins pour ne consommer que la présentation sémantique et les métadonnées de livraison.
- Supprimer les paramètres de charge utile native de l’agent et de la CLI :
components,blocks,buttonsetcard. - Supprimer les assistants SDK qui créent des schémas d’outils de message natifs, en les remplaçant par des assistants de schéma de présentation.
- Supprimer les enveloppes d’interface/natives de
channelData; ne conserver que les métadonnées de transport jusqu’à l’examen de chaque champ restant. - Migrer les moteurs de rendu Discord, Slack, Telegram, Mattermost, MS Teams, Feishu et LINE.
- Mettre à jour la documentation de la CLI de message, des pages de canaux, du SDK de plugin et du guide de recettes des capacités.
- Exécuter le profilage du fanout d’import pour Discord et les points d’entrée de canaux concernés.
Les étapes 1 à 11 et 13 à 14 sont implémentées dans cette refactorisation pour l’agent partagé, la CLI, les capacités de plugin et les contrats d’adaptateur sortant. L’étape 12 reste une passe de nettoyage interne plus profonde pour les enveloppes de transport channelData privées des fournisseurs. L’étape 15 reste une validation de suivi si nous voulons des nombres quantifiés de fanout d’import au-delà du gate de types/tests.
Tests
Ajouter ou mettre à jour :
- Tests de normalisation de présentation.
- Tests de dégradation automatique de présentation pour les blocs non pris en charge.
- Tests de marqueur intercontextuel pour les chemins de répartition de plugins et de livraison cœur.
- Tests de matrice de rendu des canaux pour Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE et le repli textuel.
- Tests de schéma d’outil de message prouvant que les champs natifs ont disparu.
- Tests de CLI prouvant que les flags natifs ont disparu.
- Régression de paresse d’import du point d’entrée Discord couvrant Carbon.
- Tests d’épinglage de livraison couvrant Telegram et le repli générique.
Questions ouvertes
delivery.pindoit-il être implémenté pour Discord, Slack, MS Teams et Feishu dès la première passe, ou seulement Telegram d’abord ?deliverydoit-il finir par absorber des champs existants tels quereplyToId,replyToCurrent,silentetaudioAsVoice, ou rester concentré sur les comportements post-envoi ?- La présentation doit-elle prendre directement en charge les images ou les références de fichiers, ou les médias doivent-ils rester séparés de la mise en page d’interface pour l’instant ?