Get started

Plan refaktoryzacji prezentacji kanałów

Status

Zaimplementowane dla współdzielonego agenta, CLI, możliwości Plugin oraz powierzchni dostarczania wychodzącego:

  • ReplyPayload.presentation przenosi semantyczny interfejs wiadomości.
  • ReplyPayload.delivery.pin przenosi żądania przypięcia wysłanej wiadomości.
  • Współdzielone akcje wiadomości udostępniają presentation, delivery i pin zamiast natywnych dla dostawcy components, blocks, buttons lub card.
  • Rdzeń renderuje albo automatycznie degraduje prezentację przez zadeklarowane przez Plugin możliwości wychodzące.
  • Renderery Discord, Slack, Telegram, Mattermost, MS Teams i Feishu używają ogólnego kontraktu.
  • Kod płaszczyzny sterowania kanału Discord nie importuje już kontenerów UI opartych na Carbon.

Kanoniczna dokumentacja znajduje się teraz w Prezentacji wiadomości. Zachowaj ten plan jako historyczny kontekst implementacji; w przypadku zmian kontraktu, renderera lub zachowania awaryjnego aktualizuj kanoniczny przewodnik.

Problem

Interfejs kanałów jest obecnie podzielony między kilka niezgodnych powierzchni:

  • Rdzeń posiada hak renderera międzykontekstowego o kształcie Discord przez buildCrossContextComponents.
  • Discord channel.ts może importować natywny UI Carbon przez DiscordUiContainer, co wciąga zależności UI czasu wykonania do płaszczyzny sterowania Plugin kanału.
  • Agent i CLI udostępniają natywne obejścia ładunku, takie jak Discord components, Slack blocks, Telegram lub Mattermost buttons oraz Teams lub Feishu card.
  • ReplyPayload.channelData przenosi zarówno wskazówki transportowe, jak i natywne koperty UI.
  • Ogólny model interactive istnieje, ale jest węższy niż bogatsze układy używane już przez Discord, Slack, Teams, Feishu, LINE, Telegram i Mattermost.

To sprawia, że rdzeń zna natywne kształty UI, osłabia leniwe ładowanie czasu wykonania Plugin i daje agentom zbyt wiele specyficznych dla dostawcy sposobów wyrażenia tego samego zamiaru wiadomości.

Cele

  • Rdzeń wybiera najlepszą semantyczną prezentację wiadomości na podstawie zadeklarowanych możliwości.
  • Rozszerzenia deklarują możliwości i renderują semantyczną prezentację do natywnych ładunków transportowych.
  • Web Control UI pozostaje oddzielony od natywnego UI czatu.
  • Natywne ładunki kanału nie są udostępniane przez współdzieloną powierzchnię wiadomości agenta ani CLI.
  • Nieobsługiwane funkcje prezentacji automatycznie degradują do najlepszej reprezentacji tekstowej.
  • Zachowanie dostarczania, takie jak przypięcie wysłanej wiadomości, jest ogólnymi metadanymi dostarczania, a nie prezentacją.

Poza zakresem

  • Brak podkładki zgodności wstecznej dla buildCrossContextComponents.
  • Brak publicznych natywnych obejść dla components, blocks, buttons lub card.
  • Brak importów natywnych bibliotek UI kanałów w rdzeniu.
  • Brak specyficznych dla dostawcy szwów SDK dla dołączonych kanałów.

Model docelowy

Dodaj należące do rdzenia pole presentation do 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 staje się podzbiorem presentation podczas migracji:

  • Blok tekstowy interactive mapuje się na presentation.blocks[].type = "text".
  • Blok przycisków interactive mapuje się na presentation.blocks[].type = "buttons".
  • Blok wyboru interactive mapuje się na presentation.blocks[].type = "select".

Zewnętrzne schematy agenta i CLI używają teraz presentation; interactive pozostaje wewnętrznym starszym pomocnikiem parsowania/renderowania dla istniejących producentów odpowiedzi.

Metadane dostarczania

Dodaj należące do rdzenia pole delivery dla zachowania wysyłania, które nie jest UI.

type ReplyPayloadDelivery = {
  pin?:
    | boolean
    | {
        enabled: boolean;
        notify?: boolean;
        required?: boolean;
      };
};

Semantyka:

  • delivery.pin = true oznacza przypięcie pierwszej pomyślnie dostarczonej wiadomości.
  • notify domyślnie ma wartość false.
  • required domyślnie ma wartość false; nieobsługiwane kanały lub nieudane przypięcie automatycznie degradują przez kontynuowanie dostarczania.
  • Ręczne akcje wiadomości pin, unpin i list-pins pozostają dla istniejących wiadomości.

Bieżące wiązanie tematu ACP Telegram powinno zostać przeniesione z channelData.telegram.pin = true do delivery.pin = true.

Kontrakt możliwości czasu wykonania

Dodaj haki renderowania prezentacji i dostarczania do adaptera wychodzącego czasu wykonania, a nie do Plugin kanału płaszczyzny sterowania.

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>;
};

Zachowanie rdzenia:

  • Rozwiąż docelowy kanał i adapter czasu wykonania.
  • Poproś o możliwości prezentacji.
  • Zdegraduj nieobsługiwane bloki przed renderowaniem.
  • Wywołaj renderPresentation.
  • Jeśli renderer nie istnieje, przekonwertuj prezentację na tekst awaryjny.
  • Po pomyślnym wysłaniu wywołaj pinDeliveredMessage, gdy delivery.pin jest żądane i obsługiwane.

Mapowanie kanałów

Discord:

  • Renderuj presentation do komponentów v2 i kontenerów Carbon w modułach wyłącznie czasu wykonania.
  • Zachowaj pomocniki kolorów akcentu w lekkich modułach.
  • Usuń importy DiscordUiContainer z kodu płaszczyzny sterowania Plugin kanału.

Slack:

  • Renderuj presentation do Block Kit.
  • Usuń wejście agenta i CLI blocks.

Telegram:

  • Renderuj tekst, kontekst i dzielniki jako tekst.
  • Renderuj akcje i wybór jako klawiatury inline, gdy są skonfigurowane i dozwolone dla docelowej powierzchni.
  • Używaj tekstu awaryjnego, gdy przyciski inline są wyłączone.
  • Przenieś przypinanie tematu ACP do delivery.pin.

Mattermost:

  • Renderuj akcje jako interaktywne przyciski, gdy są skonfigurowane.
  • Renderuj inne bloki jako tekst awaryjny.

MS Teams:

  • Renderuj presentation do Adaptive Cards.
  • Zachowaj ręczne akcje pin/unpin/list-pins.
  • Opcjonalnie zaimplementuj pinDeliveredMessage, jeśli obsługa Graph jest niezawodna dla docelowej konwersacji.

Feishu:

  • Renderuj presentation do kart interaktywnych.
  • Zachowaj ręczne akcje pin/unpin/list-pins.
  • Opcjonalnie zaimplementuj pinDeliveredMessage dla przypinania wysłanych wiadomości, jeśli zachowanie API jest niezawodne.

LINE:

  • Renderuj presentation do wiadomości Flex lub szablonowych tam, gdzie to możliwe.
  • Wróć do tekstu dla nieobsługiwanych bloków.
  • Usuń ładunki UI LINE z channelData.

Kanały zwykłe lub ograniczone:

  • Konwertuj prezentację na tekst z zachowawczym formatowaniem.

Kroki refaktoryzacji

  1. Ponownie zastosuj poprawkę wydania Discord, która oddziela ui-colors.ts od UI opartego na Carbon i usuwa DiscordUiContainer z extensions/discord/src/channel.ts.
  2. Dodaj presentation i delivery do ReplyPayload, normalizacji ładunku wychodzącego, podsumowań dostarczania i ładunków haków.
  3. Dodaj schemat MessagePresentation i pomocniki parsera w wąskiej podścieżce SDK/czasu wykonania.
  4. Zastąp możliwości wiadomości buttons, cards, components i blocks semantycznymi możliwościami prezentacji.
  5. Dodaj haki adaptera wychodzącego czasu wykonania dla renderowania prezentacji i przypinania dostarczania.
  6. Zastąp konstrukcję komponentów międzykontekstowych przez buildCrossContextPresentation.
  7. Usuń src/infra/outbound/channel-adapters.ts i usuń buildCrossContextComponents z typów Plugin kanału.
  8. Zmień maybeApplyCrossContextMarker, aby dołączał presentation zamiast natywnych parametrów.
  9. Zaktualizuj ścieżki wysyłania plugin-dispatch, aby używały wyłącznie semantycznej prezentacji i metadanych dostarczania.
  10. Usuń natywne parametry ładunku agenta i CLI: components, blocks, buttons i card.
  11. Usuń pomocniki SDK tworzące natywne schematy narzędzi wiadomości, zastępując je pomocnikami schematu prezentacji.
  12. Usuń koperty UI/natywne z channelData; zachowaj wyłącznie metadane transportowe, dopóki każde pozostałe pole nie zostanie przejrzane.
  13. Zmigruj renderery Discord, Slack, Telegram, Mattermost, MS Teams, Feishu i LINE.
  14. Zaktualizuj dokumentację dla CLI wiadomości, stron kanałów, SDK Plugin i cookbooka możliwości.
  15. Uruchom profilowanie fanoutu importów dla Discord i dotkniętych punktów wejścia kanałów.

Kroki 1-11 i 13-14 są zaimplementowane w tej refaktoryzacji dla kontraktów współdzielonego agenta, CLI, możliwości Plugin i adaptera wychodzącego. Krok 12 pozostaje głębszym wewnętrznym przebiegiem czyszczenia dla prywatnych dla dostawcy kopert transportowych channelData. Krok 15 pozostaje walidacją następczą, jeśli chcemy ilościowych danych o fanoucie importów wykraczających poza bramkę typów/testów.

Testy

Dodaj lub zaktualizuj:

  • Testy normalizacji prezentacji.
  • Testy automatycznej degradacji prezentacji dla nieobsługiwanych bloków.
  • Testy znaczników międzykontekstowych dla ścieżek plugin dispatch i dostarczania rdzenia.
  • Testy macierzy renderowania kanałów dla Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE i tekstu awaryjnego.
  • Testy schematu narzędzia wiadomości dowodzące, że natywne pola zostały usunięte.
  • Testy CLI dowodzące, że natywne flagi zostały usunięte.
  • Regresja leniwości importu punktu wejścia Discord obejmująca Carbon.
  • Testy przypinania dostarczania obejmujące Telegram i ogólny tryb awaryjny.

Otwarte pytania

  • Czy delivery.pin powinno zostać zaimplementowane dla Discord, Slack, MS Teams i Feishu w pierwszym przebiegu, czy najpierw tylko dla Telegram?
  • Czy delivery powinno ostatecznie wchłonąć istniejące pola, takie jak replyToId, replyToCurrent, silent i audioAsVoice, czy pozostać skupione na zachowaniach po wysłaniu?
  • Czy prezentacja powinna obsługiwać bezpośrednio obrazy lub odwołania do plików, czy media powinny na razie pozostać oddzielone od układu UI?

Powiązane