상태
공유 agent, CLI, Plugin capability, outbound delivery 표면에 구현되었습니다.ReplyPayload.presentation은 의미론적 메시지 UI를 전달합니다.ReplyPayload.delivery.pin은 전송된 메시지 고정 요청을 전달합니다.- 공유 메시지 액션은 provider 네이티브
components,blocks,buttons, 또는card대신presentation,delivery,pin을 노출합니다. - Core는 Plugin이 선언한 outbound capability를 통해 presentation을 렌더링하거나 자동으로 degrade합니다.
- Discord, Slack, Telegram, Mattermost, MS Teams, Feishu 렌더러는 generic contract를 사용합니다.
- Discord channel control-plane 코드는 더 이상 Carbon 기반 UI 컨테이너를 import하지 않습니다.
문제
Channel UI는 현재 서로 호환되지 않는 여러 표면으로 나뉘어 있습니다.- Core는
buildCrossContextComponents를 통해 Discord 형태의 cross-context renderer hook을 소유합니다. - Discord
channel.ts는DiscordUiContainer를 통해 네이티브 Carbon UI를 import할 수 있으며, 이로 인해 runtime UI dependency가 channel Plugin control plane으로 들어옵니다. - agent와 CLI는 Discord
components, Slackblocks, Telegram 또는 Mattermostbuttons, Teams 또는 Feishucard같은 네이티브 payload escape hatch를 노출합니다. ReplyPayload.channelData는 transport hint와 네이티브 UI envelope를 모두 전달합니다.- generic
interactive모델은 존재하지만, Discord, Slack, Teams, Feishu, LINE, Telegram, Mattermost에서 이미 사용하는 더 풍부한 layout보다 범위가 좁습니다.
목표
- Core는 선언된 capability를 기반으로 메시지에 가장 적합한 의미론적 presentation을 결정합니다.
- Extension은 capability를 선언하고 의미론적 presentation을 네이티브 transport payload로 렌더링합니다.
- Web Control UI는 chat 네이티브 UI와 분리된 상태로 유지됩니다.
- 네이티브 channel payload는 공유 agent 또는 CLI 메시지 표면을 통해 노출되지 않습니다.
- 지원되지 않는 presentation 기능은 최선의 텍스트 표현으로 자동 degrade됩니다.
- 전송된 메시지 고정 같은 delivery 동작은 presentation이 아니라 generic delivery metadata입니다.
비목표
buildCrossContextComponents에 대한 이전 버전 호환성 shim은 없습니다.components,blocks,buttons, 또는card에 대한 public 네이티브 escape hatch는 없습니다.- channel-native UI library를 core에서 import하지 않습니다.
- 번들 channel을 위한 provider별 SDK seam은 없습니다.
대상 모델
Core가 소유하는presentation 필드를 ReplyPayload에 추가합니다.
interactive는 migration 중 presentation의 subset이 됩니다.
interactivetext block은presentation.blocks[].type = "text"에 매핑됩니다.interactivebuttons block은presentation.blocks[].type = "buttons"에 매핑됩니다.interactiveselect block은presentation.blocks[].type = "select"에 매핑됩니다.
presentation을 사용합니다. interactive는 기존 reply producer를 위한 내부 legacy parser/rendering helper로 남습니다.
public producer-facing API는 interactive를 deprecated로 취급합니다. 기존 approval helper와 오래된 Plugin이 계속 작동하는 동안 새 코드는 presentation을 emit할 수 있도록 runtime 지원은 유지됩니다.
Delivery metadata
UI가 아닌 send 동작을 위한 core 소유delivery 필드를 추가합니다.
delivery.pin = true는 성공적으로 전달된 첫 번째 메시지를 고정한다는 뜻입니다.notify기본값은false입니다.required기본값은false입니다. 지원되지 않는 channel 또는 pinning 실패는 delivery를 계속 진행하는 방식으로 자동 degrade됩니다.- 수동
pin,unpin,list-pins메시지 액션은 기존 메시지에 대해 유지됩니다.
channelData.telegram.pin = true에서 delivery.pin = true로 이동해야 합니다.
Runtime capability contract
control-plane channel Plugin이 아니라 runtime outbound adapter에 presentation 및 delivery render hook을 추가합니다.- 대상 channel과 runtime adapter를 resolve합니다.
- presentation capability를 요청합니다.
- 렌더링 전에 지원되지 않는 block을 degrade하고 generic capability limit을 적용합니다.
renderPresentation을 호출합니다.- renderer가 없으면 presentation을 text fallback으로 변환합니다.
- send 성공 후
delivery.pin이 요청되고 지원되는 경우pinDeliveredMessage를 호출합니다.
Channel mapping
Discord:- runtime-only module에서
presentation을 components v2와 Carbon container로 렌더링합니다. - accent color helper는 light module에 유지합니다.
- channel Plugin control-plane 코드에서
DiscordUiContainerimport를 제거합니다.
presentation을 Block Kit으로 렌더링합니다.- agent와 CLI
blocksinput을 제거합니다.
- text, context, divider를 text로 렌더링합니다.
- target surface에 대해 구성되고 허용된 경우 action과 select를 inline keyboard로 렌더링합니다.
- inline button이 비활성화된 경우 text fallback을 사용합니다.
- ACP topic pinning을
delivery.pin으로 이동합니다.
- 구성된 경우 action을 interactive button으로 렌더링합니다.
- 다른 block은 text fallback으로 렌더링합니다.
presentation을 Adaptive Card로 렌더링합니다.- 수동 pin/unpin/list-pins action을 유지합니다.
- 대상 conversation에 대해 Graph 지원이 신뢰할 수 있다면 선택적으로
pinDeliveredMessage를 구현합니다.
presentation을 interactive card로 렌더링합니다.- 수동 pin/unpin/list-pins action을 유지합니다.
- API 동작이 신뢰할 수 있다면 sent-message pinning을 위해 선택적으로
pinDeliveredMessage를 구현합니다.
- 가능한 경우
presentation을 Flex 또는 template message로 렌더링합니다. - 지원되지 않는 block은 text로 fallback합니다.
channelData에서 LINE UI payload를 제거합니다.
- 보수적인 formatting으로 presentation을 text로 변환합니다.
Refactor 단계
ui-colors.ts를 Carbon 기반 UI에서 분리하고extensions/discord/src/channel.ts에서DiscordUiContainer를 제거하는 Discord release fix를 다시 적용합니다.ReplyPayload, outbound payload normalization, delivery summary, hook payload에presentation과delivery를 추가합니다.- 좁은 SDK/runtime subpath에
MessagePresentationschema와 parser helper를 추가합니다. - 메시지 capability
buttons,cards,components,blocks를 의미론적 presentation capability로 교체합니다. - presentation render와 delivery pinning을 위한 runtime outbound adapter hook을 추가합니다.
- cross-context component construction을
buildCrossContextPresentation으로 교체합니다. src/infra/outbound/channel-adapters.ts를 삭제하고 channel Plugin type에서buildCrossContextComponents를 제거합니다.maybeApplyCrossContextMarker를 변경해 네이티브 param 대신presentation을 attach하도록 합니다.- plugin-dispatch send path가 의미론적 presentation과 delivery metadata만 사용하도록 업데이트합니다.
- agent와 CLI 네이티브 payload param인
components,blocks,buttons,card를 제거합니다. - 네이티브 message-tool schema를 생성하는 SDK helper를 제거하고 presentation schema helper로 대체합니다.
channelData에서 UI/native envelope를 제거합니다. 남은 각 field가 review될 때까지 transport metadata만 유지합니다.- Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE renderer를 migrate합니다.
- message CLI, channel page, Plugin SDK, capability cookbook 문서를 업데이트합니다.
- Discord 및 영향을 받는 channel entrypoint에 대해 import fanout profiling을 실행합니다.
channelData transport envelope에 대한 더 깊은 내부 cleanup pass로 남아 있습니다. 15단계는 type/test gate를 넘어 정량화된 import-fanout 수치를 원할 경우 follow-up validation으로 남아 있습니다.
테스트
추가 또는 업데이트:- Presentation normalization test.
- 지원되지 않는 block에 대한 presentation auto-degrade test.
- Plugin dispatch 및 core delivery path에 대한 cross-context marker test.
- Discord, Slack, Telegram, Mattermost, MS Teams, Feishu, LINE, text fallback에 대한 channel render matrix test.
- 네이티브 field가 제거되었음을 증명하는 message tool schema test.
- 네이티브 flag가 제거되었음을 증명하는 CLI test.
- Carbon을 다루는 Discord entrypoint import-laziness regression.
- Telegram과 generic fallback을 다루는 delivery pin test.
미해결 질문
delivery.pin을 첫 번째 pass에서 Discord, Slack, MS Teams, Feishu에 구현해야 하나요, 아니면 먼저 Telegram에만 구현해야 하나요?delivery가 결국replyToId,replyToCurrent,silent,audioAsVoice같은 기존 field를 흡수해야 하나요, 아니면 post-send 동작에 집중해야 하나요?- presentation이 image 또는 file reference를 직접 지원해야 하나요, 아니면 당분간 media를 UI layout과 분리된 상태로 유지해야 하나요?