Get started
チャネルプレゼンテーションのリファクタリング計画
ステータス
共有エージェント、CLI、Plugin ケイパビリティ、送信配信の各サーフェスに実装済みです。
ReplyPayload.presentationはセマンティックなメッセージ UI を保持します。ReplyPayload.delivery.pinは送信済みメッセージのピン留め要求を保持します。- 共有メッセージアクションは、プロバイダー固有の
components、blocks、buttons、cardの代わりに、presentation、delivery、pinを公開します。 - コアは、Plugin が宣言した送信ケイパビリティを通じてプレゼンテーションをレンダリングするか、自動的にフォールバックします。
- Discord、Slack、Telegram、Mattermost、MS Teams、Feishu のレンダラーは汎用コントラクトを使用します。
- Discord チャンネルのコントロールプレーンコードは、Carbon ベースの UI コンテナーをインポートしなくなりました。
正規ドキュメントは現在、メッセージプレゼンテーションにあります。 この計画は実装時の履歴コンテキストとして保持し、コントラクト、レンダラー、フォールバック動作を変更する場合は正規ガイドを更新してください。
問題
チャンネル UI は現在、互換性のない複数のサーフェスに分散しています。
- コアは、
buildCrossContextComponentsを通じて Discord 形式のコンテキスト横断レンダラーフックを所有しています。 - Discord の
channel.tsは、DiscordUiContainerを通じてネイティブの Carbon UI をインポートできるため、ランタイム UI の依存関係がチャンネル Plugin のコントロールプレーンに持ち込まれます。 - エージェントと CLI は、Discord の
components、Slack のblocks、Telegram または Mattermost のbuttons、Teams または Feishu のcardなど、ネイティブペイロードへの迂回手段を公開しています。 ReplyPayload.channelDataは、トランスポートヒントとネイティブ UI エンベロープの両方を保持しています。- 汎用の
interactiveモデルは存在しますが、Discord、Slack、Teams、Feishu、LINE、Telegram、Mattermost ですでに使用されている、より豊富なレイアウトよりも限定的です。
このため、コアがネイティブ UI の形式を認識することになり、Plugin ランタイムの遅延読み込みが損なわれ、同じメッセージ意図を表現するためのプロバイダー固有の方法がエージェントに多く与えられすぎています。
目標
- コアは、宣言されたケイパビリティからメッセージに最適なセマンティックプレゼンテーションを決定します。
- 拡張機能はケイパビリティを宣言し、セマンティックプレゼンテーションをネイティブのトランスポートペイロードにレンダリングします。
- Web Control UI はチャットのネイティブ UI から分離したままにします。
- ネイティブチャンネルペイロードは、共有エージェントまたは CLI のメッセージサーフェスを通じて公開しません。
- サポートされていないプレゼンテーション機能は、最適なテキスト表現へ自動的にフォールバックします。
- 送信済みメッセージのピン留めなどの配信動作は、プレゼンテーションではなく汎用の配信メタデータとします。
対象外
buildCrossContextComponentsの後方互換性シムは追加しません。components、blocks、buttons、cardの公開ネイティブ迂回手段は追加しません。- コアからチャンネル固有のネイティブ UI ライブラリをインポートしません。
- バンドル済みチャンネル向けのプロバイダー固有 SDK シームは追加しません。
目標モデル
コアが所有する presentation フィールドを 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 を presentation のサブセットとします。
interactiveのテキストブロックはpresentation.blocks[].type = "text"にマッピングします。interactiveのボタンブロックはpresentation.blocks[].type = "buttons"にマッピングします。interactiveの選択ブロックはpresentation.blocks[].type = "select"にマッピングします。
外部エージェントと CLI のスキーマは現在 presentation を使用し、interactive は既存の返信生成側向けの内部レガシーパーサー兼レンダリングヘルパーとして残します。
公開される生成側 API では interactive を非推奨として扱います。新しいコードが presentation を出力する一方で、既存の承認ヘルパーと古い Plugin が引き続き動作できるよう、ランタイムサポートは維持します。
配信メタデータ
UI ではない送信動作のために、コアが所有する delivery フィールドを追加します。
type ReplyPayloadDelivery = { pin?: | boolean | { enabled: boolean; notify?: boolean; required?: boolean; };};セマンティクス:
delivery.pin = trueは、最初に正常配信されたメッセージをピン留めすることを意味します。notifyのデフォルトはfalseです。requiredのデフォルトはfalseです。サポートされていないチャンネルやピン留めの失敗時には、配信を継続して自動的にフォールバックします。- 既存メッセージ向けの手動
pin、unpin、list-pinsメッセージアクションは維持します。
現在の Telegram ACP トピックバインディングは、channelData.telegram.pin = true から delivery.pin = true に移行する必要があります。
ランタイムケイパビリティコントラクト
プレゼンテーションと配信のレンダリングフックは、コントロールプレーンのチャンネル Plugin ではなく、ランタイム送信アダプターに追加します。
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>;};コアの動作:
- 対象チャンネルとランタイムアダプターを解決します。
- プレゼンテーションケイパビリティを照会します。
- レンダリング前に、サポートされていないブロックをフォールバックし、汎用ケイパビリティ制限を適用します。
renderPresentationを呼び出します。- レンダラーが存在しない場合は、プレゼンテーションをテキストフォールバックに変換します。
- 送信成功後、
delivery.pinが要求され、かつサポートされている場合はpinDeliveredMessageを呼び出します。
チャンネルマッピング
Discord:
presentationをコンポーネント v2 と Carbon コンテナーへ、ランタイム専用モジュール内でレンダリングします。- アクセントカラーヘルパーは軽量モジュール内に維持します。
- チャンネル Plugin のコントロールプレーンコードから
DiscordUiContainerのインポートを削除します。
Slack:
presentationを Block Kit にレンダリングします。- エージェントと CLI の
blocks入力を削除します。
Telegram:
- テキスト、コンテキスト、区切りをテキストとしてレンダリングします。
- 対象サーフェスで設定および許可されている場合、アクションと選択をインラインキーボードとしてレンダリングします。
- インラインボタンが無効な場合は、テキストフォールバックを使用します。
- ACP トピックのピン留めを
delivery.pinに移行します。
Mattermost:
- 設定されている場合、アクションをインタラクティブボタンとしてレンダリングします。
- その他のブロックはテキストフォールバックとしてレンダリングします。
MS Teams:
presentationを Adaptive Cards にレンダリングします。- 手動のピン留め、ピン留め解除、ピン留め一覧アクションを維持します。
- 対象の会話で Graph のサポートが信頼できる場合は、必要に応じて
pinDeliveredMessageを実装します。
Feishu:
presentationをインタラクティブカードにレンダリングします。- 手動のピン留め、ピン留め解除、ピン留め一覧アクションを維持します。
- 送信済みメッセージのピン留めに関する API 動作が信頼できる場合は、必要に応じて
pinDeliveredMessageを実装します。
LINE:
- 可能な場合は、
presentationを Flex またはテンプレートメッセージにレンダリングします。 - サポートされていないブロックはテキストにフォールバックします。
- LINE UI ペイロードを
channelDataから削除します。
プレーンまたは機能が限定されたチャンネル:
- 控えめな書式を使用してプレゼンテーションをテキストに変換します。
リファクタリング手順
ui-colors.tsを Carbon ベースの UI から分離し、extensions/discord/src/channel.tsからDiscordUiContainerを削除する Discord リリース修正を再適用します。ReplyPayload、送信ペイロードの正規化、配信サマリー、フックペイロードにpresentationとdeliveryを追加します。- 限定された SDK/ランタイムのサブパスに
MessagePresentationスキーマとパーサーヘルパーを追加します。 - メッセージケイパビリティの
buttons、cards、components、blocksをセマンティックプレゼンテーションケイパビリティに置き換えます。 - プレゼンテーションのレンダリングと配信後のピン留めのためのランタイム送信アダプターフックを追加します。
- コンテキスト横断コンポーネントの構築を
buildCrossContextPresentationに置き換えます。 src/infra/outbound/channel-adapters.tsを削除し、チャンネル Plugin の型からbuildCrossContextComponentsを削除します。maybeApplyCrossContextMarkerを変更し、ネイティブパラメーターの代わりにpresentationを添付するようにします。- Plugin ディスパッチの送信パスを更新し、セマンティックプレゼンテーションと配信メタデータのみを使用するようにします。
- エージェントと CLI のネイティブペイロードパラメーター
components、blocks、buttons、cardを削除します。 - ネイティブのメッセージツールスキーマを作成する SDK ヘルパーを削除し、プレゼンテーションスキーマヘルパーに置き換えます。
channelDataから UI/ネイティブエンベロープを削除します。残りの各フィールドを確認するまでは、トランスポートメタデータのみを維持します。- Discord、Slack、Telegram、Mattermost、MS Teams、Feishu、LINE のレンダラーを移行します。
- メッセージ CLI、チャンネルページ、Plugin SDK、ケイパビリティのクックブックに関するドキュメントを更新します。
- Discord と影響を受けるチャンネルエントリーポイントについて、インポートのファンアウトプロファイリングを実行します。
このリファクタリングでは、共有エージェント、CLI、Plugin ケイパビリティ、送信アダプターのコントラクトについて、手順 1~11 および 13~14 が実装されています。手順 12 は、プロバイダー固有の非公開 channelData トランスポートエンベロープに対する、より深い内部クリーンアップとして残っています。手順 15 は、型/テストゲートを超える定量的なインポートファンアウト値が必要な場合の後続検証として残っています。
テスト
追加または更新する項目:
- プレゼンテーション正規化テスト。
- サポートされていないブロックに対するプレゼンテーションの自動フォールバックテスト。
- Plugin ディスパッチとコア配信パスのコンテキスト横断マーカーテスト。
- Discord、Slack、Telegram、Mattermost、MS Teams、Feishu、LINE、テキストフォールバックのチャンネルレンダリングマトリクステスト。
- ネイティブフィールドが削除されたことを証明するメッセージツールスキーマテスト。
- ネイティブフラグが削除されたことを証明する CLI テスト。
- Carbon を対象とする Discord エントリーポイントのインポート遅延読み込み回帰テスト。
- Telegram と汎用フォールバックを対象とする配信ピン留めテスト。
未解決の質問
delivery.pinは最初の段階で Discord、Slack、MS Teams、Feishu に実装すべきですか。それとも最初は Telegram のみに実装すべきですか。deliveryは最終的にreplyToId、replyToCurrent、silent、audioAsVoiceなどの既存フィールドも取り込むべきですか。それとも送信後の動作に限定すべきですか。- プレゼンテーションは画像やファイル参照を直接サポートすべきですか。それとも現時点では、メディアを UI レイアウトから分離したままにすべきですか。