Plugins
메시지 표시
메시지 프레젠테이션은 풍부한 발신 채팅 UI를 위한 OpenClaw의 공유 계약입니다. 이를 통해 에이전트, CLI 명령, 승인 흐름, Plugin이 메시지 의도를 한 번만 설명하면, 각 채널 Plugin은 가능한 최적의 네이티브 형태로 렌더링할 수 있습니다.
이식 가능한 메시지 UI에는 프레젠테이션을 사용하세요.
- 텍스트 섹션
- 작은 컨텍스트/푸터 텍스트
- 구분선
- 버튼
- 선택 메뉴
- 카드 제목 및 톤
Discord components, Slack
blocks, Telegram buttons, Teams card, Feishu card 같은 새 제공자 네이티브 필드를 공유
메시지 도구에 추가하지 마세요. 이러한 항목은 채널 Plugin이 소유하는 렌더러 출력입니다.
계약
Plugin 작성자는 공개 계약을 다음에서 가져옵니다.
MessagePresentation,
ReplyPayloadDelivery,
} from "openclaw/plugin-sdk/interactive-runtime";
형태:
type MessagePresentation = {
title?: string;
tone?: "neutral" | "info" | "success" | "warning" | "danger";
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;
};
type ReplyPayloadDelivery = {
pin?:
| boolean
| {
enabled: boolean;
notify?: boolean;
required?: boolean;
};
};
버튼 의미:
value는 채널이 클릭 가능한 컨트롤을 지원할 때 채널의 기존 상호작용 경로를 통해 다시 라우팅되는 애플리케이션 동작 값입니다.url은 링크 버튼입니다.value없이도 존재할 수 있습니다.label은 필수이며 텍스트 대체 표시에도 사용됩니다.style은 권고 사항입니다. 렌더러는 지원되지 않는 스타일을 안전한 기본값으로 매핑해야 하며, 전송을 실패시켜서는 안 됩니다.
선택 의미:
options[].value는 선택된 애플리케이션 값입니다.placeholder는 권고 사항이며 네이티브 선택 지원이 없는 채널에서는 무시될 수 있습니다.- 채널이 선택을 지원하지 않으면, 대체 텍스트가 레이블을 나열합니다.
생산자 예시
간단한 카드:
{
"title": "Deploy approval",
"tone": "warning",
"blocks": [
{ "type": "text", "text": "Canary is ready to promote." },
{ "type": "context", "text": "Build 1234, staging passed." },
{
"type": "buttons",
"buttons": [
{ "label": "Approve", "value": "deploy:approve", "style": "success" },
{ "label": "Decline", "value": "deploy:decline", "style": "danger" }
]
}
]
}
URL 전용 링크 버튼:
{
"blocks": [
{ "type": "text", "text": "Release notes are ready." },
{
"type": "buttons",
"buttons": [{ "label": "Open notes", "url": "https://example.com/release" }]
}
]
}
선택 메뉴:
{
"title": "Choose environment",
"blocks": [
{
"type": "select",
"placeholder": "Environment",
"options": [
{ "label": "Canary", "value": "env:canary" },
{ "label": "Production", "value": "env:prod" }
]
}
]
}
CLI 전송:
openclaw message send --channel slack \
--target channel:C123 \
--message "Deploy approval" \
--presentation '{"title":"Deploy approval","tone":"warning","blocks":[{"type":"text","text":"Canary is ready."},{"type":"buttons","buttons":[{"label":"Approve","value":"deploy:approve","style":"success"},{"label":"Decline","value":"deploy:decline","style":"danger"}]}]}'
고정 전달:
openclaw message send --channel telegram \
--target -1001234567890 \
--message "Topic opened" \
--pin
명시적 JSON을 사용한 고정 전달:
{
"pin": {
"enabled": true,
"notify": true,
"required": false
}
}
렌더러 계약
채널 Plugin은 발신 어댑터에 렌더링 지원을 선언합니다.
const adapter: ChannelOutboundAdapter = {
deliveryMode: "direct",
presentationCapabilities: {
supported: true,
buttons: true,
selects: true,
context: true,
divider: true,
},
deliveryCapabilities: {
pin: true,
},
renderPresentation({ payload, presentation, ctx }) {
return renderNativePayload(payload, presentation, ctx);
},
async pinDeliveredMessage({ target, messageId, pin }) {
await pinNativeMessage(target, messageId, { notify: pin.notify === true });
},
};
기능 필드는 의도적으로 단순한 불리언입니다. 이는 렌더러가 상호작용 가능하게 만들 수 있는 것을 설명하며, 모든 네이티브 플랫폼 제한을 설명하지는 않습니다. 최대 버튼 수, 블록 수, 카드 크기 같은 플랫폼별 제한은 여전히 렌더러가 소유합니다.
Core 렌더링 흐름
ReplyPayload 또는 메시지 동작에 presentation이 포함되면, core는 다음을 수행합니다.
- 프레젠테이션 페이로드를 정규화합니다.
- 대상 채널의 발신 어댑터를 확인합니다.
presentationCapabilities를 읽습니다.- 어댑터가 페이로드를 렌더링할 수 있으면
renderPresentation을 호출합니다. - 어댑터가 없거나 렌더링할 수 없으면 보수적인 텍스트로 대체합니다.
- 결과 페이로드를 일반 채널 전달 경로를 통해 전송합니다.
- 첫 번째 성공적으로 전송된 메시지 이후
delivery.pin같은 전달 메타데이터를 적용합니다.
Core는 대체 동작을 소유하므로 생산자는 채널에 구애받지 않을 수 있습니다. 채널 Plugin은 네이티브 렌더링과 상호작용 처리를 소유합니다.
성능 저하 규칙
프레젠테이션은 제한된 채널에서도 안전하게 전송될 수 있어야 합니다.
대체 텍스트에는 다음이 포함됩니다.
- 첫 줄로
title - 일반 단락으로
text블록 - 간결한 컨텍스트 줄로
context블록 - 시각적 구분선으로
divider블록 - 링크 버튼의 URL을 포함한 버튼 레이블
- 선택 옵션 레이블
지원되지 않는 네이티브 컨트롤은 전체 전송을 실패시키기보다 성능 저하되어야 합니다. 예:
- 인라인 버튼이 비활성화된 Telegram은 텍스트 대체 표시를 전송합니다.
- 선택 지원이 없는 채널은 선택 옵션을 텍스트로 나열합니다.
- URL 전용 버튼은 네이티브 링크 버튼 또는 대체 URL 줄이 됩니다.
- 선택적 고정 실패는 전달된 메시지를 실패시키지 않습니다.
주요 예외는 delivery.pin.required: true입니다. 고정이
필수로 요청되었고 채널이 전송된 메시지를 고정할 수 없으면 전달은 실패를 보고합니다.
제공자 매핑
현재 번들 렌더러:
| 채널 | 네이티브 렌더링 대상 | 참고 |
|---|---|---|
| Discord | 구성 요소 및 구성 요소 컨테이너 | 기존 제공자 네이티브 페이로드 생산자를 위해 레거시 channelData.discord.components를 보존하지만, 새 공유 전송은 presentation을 사용해야 합니다. |
| Slack | Block Kit | 기존 제공자 네이티브 페이로드 생산자를 위해 레거시 channelData.slack.blocks를 보존하지만, 새 공유 전송은 presentation을 사용해야 합니다. |
| Telegram | 텍스트와 인라인 키보드 | 버튼/선택에는 대상 표면의 인라인 버튼 기능이 필요합니다. 그렇지 않으면 텍스트 대체 표시가 사용됩니다. |
| Mattermost | 텍스트와 상호작용 props | 다른 블록은 텍스트로 성능 저하됩니다. |
| Microsoft Teams | Adaptive Cards | 둘 다 제공되면 일반 message 텍스트가 카드와 함께 포함됩니다. |
| Feishu | 상호작용 카드 | 카드 헤더는 title을 사용할 수 있으며, 본문은 해당 제목을 중복하지 않습니다. |
| 일반 채널 | 텍스트 대체 표시 | 렌더러가 없는 채널도 읽을 수 있는 출력을 받습니다. |
제공자 네이티브 페이로드 호환성은 기존 응답 생산자를 위한 전환 편의 기능입니다. 이는 새 공유 네이티브 필드를 추가할 이유가 아닙니다.
Presentation과 InteractiveReply
InteractiveReply는 승인 및 상호작용
도우미에서 사용하는 이전 내부 하위 집합입니다. 다음을 지원합니다.
- 텍스트
- 버튼
- 선택
MessagePresentation은 표준 공유 전송 계약입니다. 다음을 추가합니다.
- 제목
- 톤
- 컨텍스트
- 구분선
- URL 전용 버튼
ReplyPayload.delivery를 통한 일반 전달 메타데이터
이전 코드를 연결할 때는 openclaw/plugin-sdk/interactive-runtime의 도우미를 사용하세요.
interactiveReplyToPresentation,
normalizeMessagePresentation,
presentationToInteractiveReply,
renderMessagePresentationFallbackText,
} from "openclaw/plugin-sdk/interactive-runtime";
새 코드는 MessagePresentation을 직접 수락하거나 생성해야 합니다.
전달 고정
고정은 프레젠테이션이 아니라 전달 동작입니다. channelData.telegram.pin 같은
제공자 네이티브 필드 대신 delivery.pin을 사용하세요.
의미:
pin: true는 첫 번째 성공적으로 전달된 메시지를 고정합니다.pin.notify의 기본값은false입니다.pin.required의 기본값은false입니다.- 선택적 고정 실패는 성능 저하되어 전송된 메시지를 그대로 둡니다.
- 필수 고정 실패는 전달을 실패시킵니다.
- 청크된 메시지는 마지막 청크가 아니라 첫 번째 전달된 청크를 고정합니다.
수동 pin, unpin, pins 메시지 동작은 제공자가 해당 작업을 지원하는
기존 메시지에 대해 여전히 존재합니다.
Plugin 작성자 체크리스트
- 채널이 의미론적 프레젠테이션을 렌더링하거나 안전하게 성능 저하시킬 수 있으면
describeMessageTool(...)에서presentation을 선언하세요. - 런타임 발신 어댑터에
presentationCapabilities를 추가하세요. - 제어 플레인 Plugin 설정 코드가 아니라 런타임 코드에서
renderPresentation을 구현하세요. - 네이티브 UI 라이브러리를 핫 설정/카탈로그 경로에서 제외하세요.
- 렌더러와 테스트에서 플랫폼 제한을 보존하세요.
- 지원되지 않는 버튼, 선택, URL 버튼, 제목/텍스트
중복, 그리고
message와presentation을 함께 전송하는 경우에 대한 대체 테스트를 추가하세요. - 제공자가 전송된 메시지 ID를 고정할 수 있을 때만
deliveryCapabilities.pin및pinDeliveredMessage를 통해 전달 고정 지원을 추가하세요. - 공유 메시지 동작 스키마를 통해 새 제공자 네이티브 카드/블록/구성 요소/버튼 필드를 노출하지 마세요.