Channel Presentation Refactor Plan
Status
Implemented for the shared agent, CLI, plugin capability, and outbound delivery surfaces:ReplyPayload.presentationcarries semantic message UI.ReplyPayload.delivery.pincarries sent-message pin requests.- Shared message actions expose
presentation,delivery, andpininstead of provider-nativecomponents,blocks,buttons, orcard. - Core renders or auto-degrades presentation through plugin-declared outbound capabilities.
- Discord, Slack, Telegram, Mattermost, MS Teams, and Feishu renderers consume the generic contract.
- Discord channel control-plane code no longer imports Carbon-backed UI containers.
Problem
Channel UI is currently split across several incompatible surfaces:- Core owns a Discord-shaped cross-context renderer hook through
buildCrossContextComponents. - Discord
channel.tscan import native Carbon UI throughDiscordUiContainer, which pulls runtime UI dependencies into the channel plugin control plane. - The agent and CLI expose native payload escape hatches such as Discord
components, Slackblocks, Telegram or Mattermostbuttons, and Teams or Feishucard. ReplyPayload.channelDatacarries both transport hints and native UI envelopes.- The generic
interactivemodel exists, but it is narrower than the richer layouts already used by Discord, Slack, Teams, Feishu, LINE, Telegram, and Mattermost.
Goals
- Core decides the best semantic presentation for a message from declared capabilities.
- Extensions declare capabilities and render semantic presentation into native transport payloads.
- Web Control UI remains separate from chat native UI.
- Native channel payloads are not exposed through the shared agent or CLI message surface.
- Unsupported presentation features auto-degrade to the best text representation.
- Delivery behavior such as pinning a sent message is generic delivery metadata, not presentation.
Non Goals
- No backwards compatibility shim for
buildCrossContextComponents. - No public native escape hatches for
components,blocks,buttons, orcard. - No core imports of channel-native UI libraries.
- No provider-specific SDK seams for bundled channels.
Target Model
Add a core-ownedpresentation field to ReplyPayload.
interactive becomes a subset of presentation during migration:
interactivetext block maps topresentation.blocks[].type = "text".interactivebuttons block maps topresentation.blocks[].type = "buttons".interactiveselect block maps topresentation.blocks[].type = "select".
presentation; interactive remains an internal legacy parser/rendering helper for existing reply producers.
Delivery Metadata
Add a core-owneddelivery field for send behavior that is not UI.
delivery.pin = truemeans pin the first successfully delivered message.notifydefaults tofalse.requireddefaults tofalse; unsupported channels or failed pinning auto-degrade by continuing delivery.- Manual
pin,unpin, andlist-pinsmessage actions remain for existing messages.
channelData.telegram.pin = true to delivery.pin = true.
Runtime Capability Contract
Add presentation and delivery render hooks to the runtime outbound adapter, not the control-plane channel plugin.- Resolve target channel and runtime adapter.
- Ask for presentation capabilities.
- Degrade unsupported blocks before rendering.
- Call
renderPresentation. - If no renderer exists, convert presentation to text fallback.
- After successful send, call
pinDeliveredMessagewhendelivery.pinis requested and supported.
Channel Mapping
Discord:- Render
presentationto components v2 and Carbon containers in runtime-only modules. - Keep accent color helpers in light modules.
- Remove
DiscordUiContainerimports from channel plugin control-plane code.
- Render
presentationto Block Kit. - Remove agent and CLI
blocksinput.
- Render text, context, and dividers as text.
- Render actions and select as inline keyboards when configured and allowed for the target surface.
- Use text fallback when inline buttons are disabled.
- Move ACP topic pinning to
delivery.pin.
- Render actions as interactive buttons where configured.
- Render other blocks as text fallback.
- Render
presentationto Adaptive Cards. - Keep manual pin/unpin/list-pins actions.
- Optionally implement
pinDeliveredMessageif Graph support is reliable for the target conversation.
- Render
presentationto interactive cards. - Keep manual pin/unpin/list-pins actions.
- Optionally implement
pinDeliveredMessagefor sent-message pinning if API behavior is reliable.
- Render
presentationto Flex or template messages where possible. - Fall back to text for unsupported blocks.
- Remove LINE UI payloads from
channelData.
- Convert presentation to text with conservative formatting.
Refactor Steps
- Reapply the Discord release fix that splits
ui-colors.tsfrom Carbon-backed UI and removesDiscordUiContainerfromextensions/discord/src/channel.ts. - Add
presentationanddeliverytoReplyPayload, outbound payload normalization, delivery summaries, and hook payloads. - Add
MessagePresentationschema and parser helpers in a narrow SDK/runtime subpath. - Replace message capabilities
buttons,cards,components, andblockswith semantic presentation capabilities. - Add runtime outbound adapter hooks for presentation render and delivery pinning.
- Replace cross-context component construction with
buildCrossContextPresentation. - Delete
src/infra/outbound/channel-adapters.tsand removebuildCrossContextComponentsfrom channel plugin types. - Change
maybeApplyCrossContextMarkerto attachpresentationinstead of native params. - Update plugin-dispatch send paths to consume only semantic presentation and delivery metadata.
- Remove agent and CLI native payload params:
components,blocks,buttons, andcard. - Remove SDK helpers that create native message-tool schemas, replacing them with presentation schema helpers.
- Remove UI/native envelopes from
channelData; keep only transport metadata until each remaining field is reviewed. - Migrate Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, and LINE renderers.
- Update docs for message CLI, channel pages, plugin SDK, and capability cookbook.
- Run import fanout profiling for Discord and affected channel entrypoints.
channelData transport envelopes. Step 15 remains follow-up validation if we want quantified import-fanout numbers beyond the type/test gate.
Tests
Add or update:- Presentation normalization tests.
- Presentation auto-degrade tests for unsupported blocks.
- Cross-context marker tests for plugin dispatch and core delivery paths.
- Channel render matrix tests for Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE, and text fallback.
- Message tool schema tests proving native fields are gone.
- CLI tests proving native flags are gone.
- Discord entrypoint import-laziness regression covering Carbon.
- Delivery pin tests covering Telegram and generic fallback.
Open Questions
- Should
delivery.pinbe implemented for Discord, Slack, MS Teams, and Feishu in the first pass, or only Telegram first? - Should
deliveryeventually absorb existing fields such asreplyToId,replyToCurrent,silent, andaudioAsVoice, or stay focused on post-send behaviors? - Should presentation support images or file references directly, or should media remain separate from UI layout for now?