components、Slack blocks、Telegram buttons、Teams card 或 Feishu card。这些是由渠道插件拥有的渲染器输出。
契约
插件作者从以下位置导入公开契约:action.type: "command"通过核心的命令路径运行原生斜杠命令。将它用于内置命令按钮和菜单。action.type: "callback"通过渠道的交互路径携带不透明插件数据。渠道插件不得将回调数据重新解释为斜杠命令。value是旧版不透明回调值。新控件应使用action,这样渠道插件就能映射命令和回调,而无需从文本猜测。url是链接按钮。它可以在没有value的情况下存在。webApp描述渠道原生 Web App 按钮。Telegram 会将它渲染为web_app,并且只在私聊中支持它。为了兼容性,宽松 JSON 负载中仍接受web_app,但 TypeScript 生成方应使用webApp。label是必需的,也会用于文本回退。style是建议性的。渲染器应将不支持的样式映射为安全默认值,而不是让发送失败。priority是可选的。当渠道声明操作限制且必须丢弃控件时,核心会优先保留较高优先级的按钮,并在相同优先级的按钮之间保留原始顺序。当所有控件都能放下时,会保留作者定义的顺序。disabled是可选的。渠道必须通过supportsDisabled显式选择支持;否则核心会将禁用控件降级为非交互式回退文本。禁用按钮在回退文本中始终只渲染标签,即使它携带command操作也是如此。reusable是可选的。支持可复用原生回调的渠道可在一次成功交互后继续保留该操作可用。将它用于可重复或幂等的操作,例如刷新、检查或查看更多详情;对于普通的一次性审批和破坏性操作,保持未设置。
options[].action与按钮action具有相同的命令/回调含义。options[].value是旧版选中的应用值。placeholder是建议性的,可能会被没有原生选择支持的渠道忽略。- 如果渠道不支持选择,回退文本会列出标签。
生成方示例
简单卡片:渲染器契约
渠道插件在其出站适配器上声明渲染支持:limits 描述核心在调用渲染器之前可以适配的通用信封:
核心渲染流程
当ReplyPayload 或消息操作包含 presentation 时,核心会:
- 规范化呈现负载。
- 解析目标渠道的出站适配器。
- 读取
presentationCapabilities。 - 在适配器声明时应用通用能力限制,例如操作数量、标签长度和选择选项数量。
- 当适配器能够渲染负载时调用
renderPresentation。 - 当适配器不存在或无法渲染时回退到保守文本。
- 通过正常渠道投递路径发送生成的负载。
- 在第一条消息成功发送后应用投递元数据,例如
delivery.pin。
降级规则
呈现必须能安全发送到能力受限的渠道。 回退文本包括:title作为第一行text块作为普通段落context块作为紧凑上下文行divider块作为视觉分隔符- 按钮标签,包括链接按钮的 URL
- 选择选项标签
按钮值回退可见性
当渠道无法渲染交互式控件时,按钮和选择值会回退为纯文本。回退行为在保持可用性的同时,也会让不透明回调数据保持私密:command类型的操作渲染为label: \command“,这样用户可以复制命令并在渠道输入中手动运行。callback类型的操作和旧版value字段仅渲染为标签。不透明回调值不会暴露在回退文本中。url/webApp按钮会在按钮标签旁边渲染 URL 文本,因为 URL 面向用户。- 选择选项仅渲染为标签。底层选项值不会暴露在回退文本中。
- 禁用内联按钮的 Telegram 会发送文本回退。
- 没有选择支持的渠道会将选择选项列为文本。
- 仅 URL 的按钮会变为原生链接按钮或回退 URL 行。
- 可选的置顶失败不会让已投递消息失败。
delivery.pin.required: true;如果置顶被请求为必需,而渠道无法置顶已发送消息,投递会报告失败。
提供商映射
当前内置渲染器:| 渠道 | 原生渲染目标 | 说明 |
|---|---|---|
| Discord | 组件和组件容器 | 为现有提供商原生 payload 生成器保留旧版 channelData.discord.components,但新的共享发送应使用 presentation。 |
| Feishu | 交互式卡片 | 卡片标题可以使用 title;正文避免重复该标题。 |
| Matrix | 文本回退加结构化事件字段 | 按钮/选择器会声明为受支持,但目前每个块都会渲染为 renderMessagePresentationFallbackText 输出,并放在 com.openclaw.presentation 事件字段中,而不是原生交互式小部件。 |
| Mattermost | 文本加交互式 props | 不支持选择器和分隔线;这些块会降级为文本。 |
| Microsoft Teams | Adaptive Cards | 如果同时提供普通 message 文本和卡片,文本会随卡片一起包含。不支持选择器、样式和禁用状态。 |
| Slack | Block Kit | 为现有提供商原生 payload 生成器保留旧版 channelData.slack.blocks,但新的共享发送应使用 presentation。 |
| Telegram | 文本加内联键盘 | 按钮/选择器要求目标表面具备内联按钮能力;否则会使用文本回退。 |
| 普通渠道 | 文本回退 | 没有渲染器的渠道仍会获得可读输出。 |
Presentation 与 InteractiveReply
InteractiveReply 是审批和交互辅助函数使用的较旧内部子集。它支持:
- 文本
- 按钮
- 选择器
MessagePresentation 是规范的共享发送契约。它新增:
- 标题
- 语气
- 上下文
- 分隔线
- 仅 URL 按钮
- 通过
ReplyPayload.delivery提供通用投递元数据
openclaw/plugin-sdk/interactive-runtime 中的辅助函数:
OC_I18N_900011
新代码应直接接受或生成 MessagePresentation。现有 interactive payload 是 presentation 的已弃用子集;运行时仍支持较旧的生成器。
值得了解的未弃用辅助函数:
normalizeMessagePresentation(raw)/hasMessagePresentationBlocks(value)会验证并强制转换无类型 payload(例如来自 CLI--presentation标志的 JSON) 为MessagePresentation。isMessagePresentationInteractiveBlock(block)会将块收窄为buttons|select联合类型。resolveMessagePresentationActionValue(action)/resolveMessagePresentationControlValue(control)会读取action上的有效 command/callback 值,并且resolveMessagePresentationControlValue会回退到旧版value字段。
InteractiveReply* 类型和转换辅助函数在 SDK 中标记为
@deprecated:
InteractiveReply,InteractiveReplyBlock,InteractiveReplyButton,InteractiveReplyOption,InteractiveReplySelectBlock, 和InteractiveReplyTextBlocknormalizeInteractiveReply(...)hasInteractiveReplyBlocks(...)interactiveReplyToPresentation(...)presentationToInteractiveReply(...)presentationToInteractiveControlsReply(...)resolveInteractiveTextFallback(...)reduceInteractiveReply(...)
presentationToInteractiveReply(...) 和
presentationToInteractiveControlsReply(...) 仍可作为旧版渠道实现的渲染器桥接使用。
新的生成器代码不应调用它们;发送 presentation,并让核心/渠道适配处理渲染。
审批辅助函数也有 presentation 优先的替代项:
- 使用
buildApprovalPresentationFromActionDescriptors(...),而不是buildApprovalInteractiveReplyFromActionDescriptors(...) - 使用
buildApprovalPresentation(...),而不是buildApprovalInteractiveReply(...) - 使用
buildExecApprovalPresentation(...),而不是buildExecApprovalInteractiveReply(...)
renderMessagePresentationFallbackText(...) 会返回空字符串。需要非空发送正文的传输协议可以传入
emptyFallback,以选择使用最小正文,而不改变默认回退契约。
投递置顶
置顶是投递行为,不是呈现。使用delivery.pin,而不是
channelData.telegram.pin 等提供商原生字段。
语义:
pin: true会置顶第一条成功投递的消息。pin.notify默认为false。pin.required默认为false。- 可选置顶失败会降级,并保留已发送消息不变。
- 必需置顶失败会导致投递失败。
- 分块消息会置顶第一个已投递分块,而不是最后一个分块。
pin、unpin 和 pins 消息操作仍然存在。
插件作者检查清单
- 当渠道可以渲染语义化 presentation 或安全降级时,从
describeMessageTool(...)声明presentation。 - 将
presentationCapabilities添加到运行时出站适配器。 - 在运行时代码中实现
renderPresentation,而不是在控制平面插件设置代码中实现。 - 不要把原生 UI 库放入热设置/catalog 路径。
- 已知时,在
presentationCapabilities.limits上声明通用能力限制。 - 在渲染器和测试中保留最终平台限制。
- 为不受支持的按钮、选择器、URL 按钮、标题/文本重复,以及混合
message加presentation发送添加回退测试。 - 仅在提供商可以置顶已发送消息 ID 时,通过
deliveryCapabilities.pin和pinDeliveredMessage添加投递置顶支持。 - 不要通过共享消息操作 schema 暴露新的提供商原生卡片/块/组件/按钮字段。