Plan de refactorisation de la présentation des channels
Implémenté pour l’agent partagé, le CLI, les capacités des plugins et les surfaces de livraison sortante :
ReplyPayload.presentationtransporte l’interface utilisateur sémantique des messages.ReplyPayload.delivery.pintransporte les requêtes d’épinglage de messages envoyés.- Les actions de message partagées exposent
presentation,deliveryetpinau lieu descomponents,blocks,buttonsoucardnatives du provider. - Le cœur effectue le rendu ou dégrade automatiquement 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 channel Discord n’importe plus les conteneurs d’interface utilisateur basés sur Carbon.
La documentation canonique se trouve désormais dans Message Presentation. Conservez ce plan comme contexte historique de l’implémentation ; mettez à jour le guide canonique pour les modifications de contrat, de moteur de rendu ou de comportement de repli.
Problème
Section intitulée « Problème »L’interface utilisateur du channel est actuellement répartie sur plusieurs surfaces incompatibles :
- Le cœur possède un hook de moteur de rendu inter-contexte de forme Discord via
buildCrossContextComponents. - Le Discord
channel.tspeut importer l’interface utilisateur Carbon native viaDiscordUiContainer, ce qui introduit des dépendances d’interface utilisateur d’exécution dans le plan de contrôle du plugin de channel. - L’agent et le CLI exposent des échappatoires de charge utile native telles que Discord
components, Slackblocks, Telegram ou Mattermostbuttons, et Teams ou Feishucard. ReplyPayload.channelDatatransporte à la fois des indices de transport et des enveloppes d’interface utilisateur 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 conscient des formes de l’interface utilisateur native, affaiblit la paresse (laziness) de l’exécution du plugin et donne aux agents trop de moyens spécifiques au provider pour exprimer la même intention de message.
Objectifs
Section intitulée « 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 effectuent le rendu de la présentation sémantique en charges utiles de transport natives.
- L’interface utilisateur de contrôle Web reste séparée de l’interface utilisateur native de chat.
- Les charges utiles natives de canal ne sont pas exposées via la surface de messages de l’agent partagé ou du CLI.
- Les fonctionnalités de présentation non prises en charge se dégradent automatiquement vers la meilleure représentation textuelle.
- Le comportement de livraison, tel que l’épinglage d’un message envoyé, est des métadonnées de livraison génériques, et non une présentation.
Non objectifs
Section intitulée « Non objectifs »- Aucune couche de compatibilité ascendante pour
buildCrossContextComponents. - Aucune échappatoire native publique pour
components,blocks,buttonsoucard. - Aucune importation principale de bibliothèques d’interface utilisateur natives de canal.
- Aucune interface SDK spécifique au fournisseur pour les canaux groupés.
Modèle cible
Section intitulée « 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 lors de la migration :
- Le bloc de 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 de l’agent externe et de la CLI utilisent désormais presentation ; interactive reste un assistant d’analyse/de rendu hérité interne pour les producteurs de réponses existants.
L’API publique orientée producteur considère interactive comme obsolète. La prise en charge
du runtime est maintenue pour que les assistants d’approbation existants et les anciens plugins continuent de
fonctionner tandis que le nouveau code émet presentation.
Métadonnées de livraison
Section intitulée « Métadonnées de livraison »Ajoutez un champ delivery appartenant au cœur pour le comportement d’envoi qui n’est pas lié à l’interface utilisateur.
type ReplyPayloadDelivery = { pin?: | boolean | { enabled: boolean; notify?: boolean; required?: boolean; };};Sémantique :
delivery.pin = truesignifie épingler le premier message livré avec succès.notifyestfalsepar défaut.requiredestfalsepar défaut ; les channels non pris en charge ou les échecs d’épinglage se dégradent automatiquement en continuant la livraison.- Les actions de message manuelles
pin,unpinetlist-pinsrestent disponibles pour les messages existants.
La liaison actuelle des sujets ACP Telegram doit passer de channelData.telegram.pin = true à delivery.pin = true.
Contrat de capacité d’exécution
Section intitulée « Contrat de capacité d’exécution »Ajoutez les crochets (hooks) de rendu de présentation et de livraison à l’adaptateur sortant (outbound) de l’exécution (runtime), et non au plugin de channel du plan de contrôle (control-plane).
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>;};Comportement de base :
- Résoudre le channel cible et l’adaptateur d’exécution.
- Demander les capacités de présentation.
- Dégrader les blocs non pris en charge et appliquer les limites de capacités génériques avant le rendu.
- Appelez
renderPresentation. - Si aucun moteur de rendu n’existe, convertir la présentation en repli texte.
- Après un envoi réussi, appelez
pinDeliveredMessagelorsquedelivery.pinest demandé et pris en charge.
Mapping de channel
Section intitulée « Mapping de channel »Discord :
- Rendez
presentationdans les composants v2 et les conteneurs Carbon dans les modules d’exécution uniquement. - Conserver les assistants de couleur d’accentuation dans les modules légers.
- Supprimez les importations
DiscordUiContainerdu code du plan de contrôle du plugin de channel.
Slack :
- Rendez
presentationen Block Kit. - Supprimez l’entrée
blocksde l’agent et de la CLI.
Telegram :
- Rendre le texte, le contexte et les diviseurs sous forme de texte.
- Rendre les actions et les sélections sous forme de claviers en ligne lorsque configuré et autorisé pour la surface cible.
- Utiliser le repli texte lorsque les boutons en ligne sont désactivés.
- Déplacez l’épinglage des sujets ACP vers
delivery.pin.
Mattermost :
- Rendre les actions sous forme de boutons interactifs là où configuré.
- Rendre les autres blocs sous forme de repli texte.
MS Teams :
- Rendez
presentationen Adaptive Cards. - Conserver les actions manuelles d’épinglage/désepinglage/liste-des-épingles.
- Implémentez
pinDeliveredMessageen option si la prise en charge de Graph est fiable pour la conversation cible.
Feishu :
- Rendre
presentationsous forme de cartes interactives. - Conserver les actions manuelles d’épinglage/désepinglage/liste-des-épingles.
- Implémenter facultativement
pinDeliveredMessageAPI pour l’épinglage des messages envoyés si le comportement de l’API est fiable.
LINE :
- Rendre
presentationsous forme de messages Flex ou de modèles lorsque cela est possible. - Revenir au texte pour les blocs non pris en charge.
- Supprimer les payloads d’interface utilisateur LINE de
channelData.
Channels simples ou limités :
- Convertir la présentation en texte avec un formatage conservateur.
Étapes de refactorisation
Section intitulée « Étapes de refactorisation »- Réappliquer le correctif de version Discord qui sépare Discord
ui-colors.tsde l’interface utilisateur basée sur Carbon et supprimeDiscordUiContainerdeextensions/discord/src/channel.ts. - Ajouter
presentationetdeliveryàReplyPayload, à la normalisation des payloads sortants, aux résumés de livraison et aux payloads de hooks. - Ajouter le schéma
MessagePresentationet les assistants d’analyse dans un sous-chemin étroit du SDK/runtime. - Remplacer les capacités de message
buttons,cards,componentsetblockspar des capacités de présentation sémantique. - Ajoutez des hooks d’adaptateur sortant pour le rendu de la présentation et l’épinglage de la livraison.
- Remplacer la construction de composants multi-contextes par
buildCrossContextPresentation. - Supprimer
src/infra/outbound/channel-adapters.tset retirerbuildCrossContextComponentsdes types de plugins de channel. - Modifier
maybeApplyCrossContextMarkerpour attacherpresentationau lieu des paramètres natifs. - Mettez à jour les chemins d’envoi plugin-dispatch pour consommer uniquement la présentation sémantique et les métadonnées de livraison.
- Supprimer les paramètres de payload natifs de l’agent et de la CLI : CLI
components,blocks,buttonsetcard. - Supprimez les assistants SDK qui créent des schémas de message-tool natifs, en les remplaçant par des assistants de schéma de présentation.
- Supprimer les enveloppes d’interface utilisateur/natives de
channelData; ne conserver que les métadonnées de transport jusqu’à ce que chaque champ restant soit révisé. - Migrez les moteurs de rendu Discord, Slack, Telegram, Mattermost, MS Teams, Feishu et LINE.
- Mettez à jour la documentation pour le message CLI, les pages de channel, le plugin SDK et le livre de recettes des capacités.
- Exécutez le profilage de l’étalement d’importation pour Discord et les points d’entrée de channel affectés.
Les étapes 1 à 11 et 13 à 14 sont implémentées dans cette refactorisation pour l’agent partagé, la CLI, la capacité du plugin et les contrats d’adaptateur sortant. L’étape 12 reste une passe de nettoyage interne plus approfondie pour les enveloppes de transport CLIchannelData privées au fournisseur. L’étape 15 reste une validation de suivi si nous souhaitons des nombres quantifiés de diffusion d’importation (import-fanout) au-delà de la porte de type/test.
Ajouter ou mettre à jour :
- Tests de normalisation de la présentation.
- Tests de dégradation automatique de la présentation pour les blocs non pris en charge.
- Tests de marqueurs inter-contextes pour la répartition des plugins et les chemins de livraison principaux.
- Tests de matrice de rendu de canal pour Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE et le repli texte.
- Tests de schéma de l’outil de message prouvant que les champs natifs ont disparu.
- Tests CLI prouvant que les indicateurs natifs ont disparu.
- Régression de la paresse d’importation du point d’entrée Discord couvrant Carbon.
- Tests d’épingle de livraison couvrant Telegram et le repli générique.
Questions ouvertes
Section intitulée « Questions ouvertes »delivery.pindoit-il être implémenté pour Discord, Slack, MS Teams et Feishu lors de la première passe, ou uniquement Telegram dans un premier temps ?deliverydoit-il finalement absorber les champs existants tels quereplyToId,replyToCurrent,silentetaudioAsVoice, ou rester concentré sur les comportements post-envoi ?- La présentation doit-elle prendre en charge directement les images ou les références de fichiers, ou les médias doivent-ils rester séparés de la mise en page de l’interface utilisateur pour l’instant ?