components、Slack blocks、Telegram buttons、Teams card 或 Feishu card 之类的提供方原生字段。这些是由频道插件拥有的渲染器输出。
契约
插件作者从以下位置导入公开契约:action.type: "command"通过核心的命令路径运行原生斜杠命令。内置命令按钮和菜单应使用此类型。action.type: "callback"通过频道的交互路径传递不透明的插件数据。频道插件不得将回调数据重新解释为斜杠命令。action.type: "approval"标识一个持久化的操作员审批、其明确的exec或plugin类型,以及请求的决定。频道插件会将该动作编码到传输层私有的回调中,并通过审批服务解析;不得解析/approve命令文本,也不得根据 ID 推断类型。action.type: "question"标识一个面向实时运行时编写的ask_user问题的选项。与approval一样,这是一个 OpenClaw 运行时动作;代理和插件不得自行生成问题 ID。Telegram、Discord 和 Slack 会将其映射为传输层私有的原生回调,并通过 Gateway 解析选项。当问题变为已回答、已过期或已取消时,这些频道会编辑已发送的消息、移除其动作,并追加最终状态。WhatsApp、Signal 和 iMessage 会将最多四个单选选项渲染为1️⃣至4️⃣的反应。其他问题形状会降级为标签文本,用户可以用纯文本回复。action.type: "url"打开普通链接。action.type: "web-app"启动频道原生 Web 应用。对于基于 URL 的应用,设置url;对于由 OpenClaw 托管、启动机制由频道负责的 widget,设置widgetId;两者至少需要一个。当两者同时存在时,频道可以优先使用其原生的托管 widget 启动方式,并在该机制不可用时使用 URL。value是旧版不透明回调值。新控件应使用action,这样频道插件就能映射命令和回调,而无需根据文本猜测。url、webApp和web_app仍作为已弃用的边界输入接受。规范化器会保留这些字段,以便渲染器区分已发布的旧版语义和显式类型化动作。新的生产者应使用action。label是必需的,也会用于文本回退。style仅供参考。渲染器应将不支持的样式映射为安全的默认值,而不是使发送失败。priority是可选的。当频道声明了动作数量限制且必须丢弃控件时,核心会优先保留优先级较高的按钮,并在优先级相同的按钮之间保持原始顺序。当所有控件都能容纳时,则保持编写时的顺序。disabled是可选的。频道必须通过supportsDisabled显式选择支持;否则核心会将禁用控件降级为不可交互的回退文本。禁用按钮在回退文本中始终只渲染标签,即使它携带了command动作。reusable是可选的。支持可复用原生回调的频道可以在交互成功后继续保留该动作。可将其用于刷新、检查或查看更多详情等可重复或幂等的动作;普通的一次性审批和破坏性操作应保持未设置。
options[].action仅接受command或callback;审批和链接动作仅适用于按钮。options[].value是旧版选中的应用值。placeholder仅作为建议,可能会被不支持原生选择器的频道忽略。- 如果频道不支持选择器,回退文本会列出标签。
pie要求分段值为正数。bar、area和line使用一个有序的categories数组。每个系列都必须按相同顺序为每个类别提供恰好一个有限值。- 类别标签和系列名称必须唯一。无效或不完整的图表块在规范化期间会被丢弃,而不是悄悄更改数据。
- 原生图表渲染通过
presentationCapabilities.charts进行可选启用。其他频道会以确定性文本接收图表标题、坐标轴、类别、系列和值。这也是可访问性回退。
-
caption是必需的简短标题。headers必须至少包含一个唯一的、非空的列标签。 -
rows必须至少包含一行。每一行必须恰好包含每个标题对应的一个单元格,且每个单元格必须是非空字符串或有限数字。 -
rowHeaderColumnIndex是可选的、从零开始的索引,用于标识其单元格应被原生渲染器作为行标题公开的列。 - 表格规范化是原子的。无效的标题、表头、行宽、单元格或行标题索引会直接丢弃表格块,而不是截断或修复其数据。
-
原生表格渲染通过
presentationCapabilities.tables进行可选启用。其他频道会以确定性线性文本接收标题和每一行,并折叠内部空白:
report discriminator。使用 title、tone、text、context、chart、table 和动作块来组合报告。这样可以让每个块都能独立渲染,并让完整报告拥有同样确定性的文本回退。
生产者示例
简单卡片:渲染器契约
频道插件在其出站适配器上声明渲染支持:richMessages 的账户上,并且仅在 Markdown 路径中渲染原生表格——请在静态对象旁声明可选的
resolvePresentationCapabilities({ cfg, accountId, formatting }) 钩子。Core 会在每次投递时解析一次能力,并且该钩子的优先级高于静态声明;请将静态对象保留为与账户无关的基线。
limits 描述了 Core 在调用渲染器之前可以适配的通用封装限制:
核心渲染流程
在 CLI 和标准消息操作使用的规范出站路径中,核心:- 规范化展示载荷。
- 解析目标频道的出站适配器。
- 读取
presentationCapabilities。 - 当适配器声明支持时,应用通用能力限制,例如操作数量、标签长度以及选择项数量。图表和表格块会变为确定性的文本,除非适配器明确声明分别支持
charts: true或tables: true。 - 当适配器能够渲染载荷时,调用
renderPresentation。 - 当适配器缺失或无法渲染时,回退为保守文本。
- 通过正常的频道投递路径发送最终载荷。
- 在首条成功发送的消息之后,应用投递元数据,例如
delivery.pin。
ReplyPayload 的频道本地回复或预览流程,必须要么进入该规范路径,要么在将载荷下探为纯文本/媒体之前,先生成相同的展示回退。
核心负责回退行为,因此生产者可以保持与频道无关。频道插件负责原生渲染和交互处理。
降级规则
展示必须能够安全地发送到受限频道。 手动编写相同事实的纯文本呈现的生产者,可以在回复载荷中通过presentationTextMode: "fallback" 标记该文本。原生渲染展示数据区块的频道会丢弃这段文本;当每个 table 和 chart 区块都发生降级且不再有交互区块时,系统会原样发送手动编写的文本,而不是使用下方的通用扁平化文本。
回退文本包括:
title作为第一行text区块作为普通段落context区块作为紧凑的上下文行divider区块作为视觉分隔符- 按钮标签,包括链接按钮的 URL
- 选择选项标签
- 图表标题、类型、坐标轴、类别、系列和值
- 表格标题、表头以及每一行的值
按钮值回退可见性
当某个频道无法渲染交互控件时,按钮和值选择会回退为纯文本。此回退行为在保持可用性的同时,会保护不透明的回调数据私密:command-typed actions 渲染为label: `command`,这样用户可以复制该命令,并在频道输入框中手动运行。callback-typed actions 和旧版value字段仅渲染标签。不透明的回调值不会暴露在回退文本中。approval-typed actions 仅渲染标签。审批 ID 和决策属于传输数据,不会通过通用标量辅助函数或回退文本暴露。urlactions、基于 URL 的web-appactions,以及已弃用的url/webApp/web_app输入,会在按钮标签旁渲染 URL 文本,因为 URL 是面向用户的内容。仅支持托管小组件的操作,在不具备原生小组件启动能力的频道中仅渲染标签。- 选择选项仅渲染标签。底层选项值不会暴露在回退文本中。
- 禁用内联按钮的 Telegram 会发送文本回退。
- 不支持选择控件的频道会将选择选项列为文本。
- 不支持原生图表的频道会将图表数据列为文本。
- 不支持原生表格的频道会将每一行表格内容列为文本。
- 仅支持 URL 的按钮会变为原生链接按钮或回退 URL 行。
- 可选的置顶失败不会导致已投递消息失败。
delivery.pin.required: true;如果请求置顶为必需,而频道无法将已发送消息置顶,则投递会报告失败。
提供方映射
当前内置渲染器:
提供方原生载荷兼容性是为现有回复生产者提供的过渡性便利。它不是新增共享原生字段的理由。
展示 vs 交互回复
InteractiveReply 是较早的内部子集,由审批和交互辅助工具使用。它支持:
- 文本
- 按钮
- 选择器
MessagePresentation 是规范的共享发送契约。它新增了:
- 标题
- 语调
- 上下文
- 分隔线
- 图表
- 表格
- 仅包含 URL 的按钮
- 通过
ReplyPayload.delivery提供的通用发送元数据
openclaw/plugin-sdk/interactive-runtime 中的辅助函数:
MessagePresentation。现有的
interactive 载荷是 presentation 的已弃用子集;运行时
仍会为旧生产者保留支持。
值得了解的非弃用辅助函数:
normalizeMessagePresentation(raw)/hasMessagePresentationBlocks(value)将未类型化载荷(例如来自 CLI--presentation标志的 JSON)验证并转换为MessagePresentation。isMessagePresentationInteractiveBlock(block)将一个块缩窄为buttons|select联合类型。resolveMessagePresentationButtonAction(button)和resolveMessagePresentationOptionAction(option)在接受已弃用边界字段的同时,返回规范的强类型 action。显式的action始终优先。resolveMessagePresentationActionValue(action)/resolveMessagePresentationControlValue(control)仅读取命令/回调 标量值。非标量的规范 action 不会回退到旧版隐藏的value,因此审批 ID 和链接目标会保持类型安全。renderMessagePresentationChartFallbackText(block)/renderMessagePresentationTableFallbackText(block)将一个结构化 数据块渲染为确定性的文本,用于特定频道的回退路径。
InteractiveReply* 类型和转换辅助函数已标记为
@deprecated:
InteractiveReply、InteractiveReplyBlock、InteractiveReplyButton和InteractiveReplyOptionnormalizeInteractiveReply(...)hasInteractiveReplyBlocks(...)interactiveReplyToPresentation(...)presentationToInteractiveReply(...)presentationToInteractiveControlsReply(...)resolveInteractiveTextFallback(...)reduceInteractiveReply(...)
presentationToInteractiveReply(...) 和
presentationToInteractiveControlsReply(...) 仍可作为旧版频道实现的渲染
桥接函数使用。新的生产者代码不应调用它们;发送 presentation 并让 core/频道适配处理渲染。
审批辅助工具也有以 presentation 为优先的替代方案:
- 使用
buildApprovalPresentation(...)代替buildApprovalInteractiveReply(...) - 使用
buildExecApprovalPresentation(...)代替buildExecApprovalInteractiveReply(...)
buildTypedApprovalPresentation(...)、
buildTypedExecApprovalPendingReplyPayload(...) 或
buildTypedPluginApprovalPendingReplyPayload(...),以便传输层接收到显式的
approval action,而不是从 /approve 文本推断语义。
renderMessagePresentationFallbackText(...) 会为
没有文本回退内容的 presentation 块返回空字符串,例如仅包含分隔线的 presentation。要求发送正文非空的传输层可以传入
emptyFallback,在不改变默认回退
契约的情况下启用一个最小正文。
投递置顶
置顶属于投递行为,而不是展示。请使用delivery.pin,而不是诸如 channelData.telegram.pin 之类的提供方原生字段。
语义:
pin: true会置顶第一条成功投递的消息。pin.notify默认值为false。pin.required默认值为false。- 可选置顶失败会降级处理,并保留已发送消息。
- 必需置顶失败会导致投递失败。
- 分块消息会置顶第一块成功投递的内容,而不是尾块。
pin、unpin 和 pins 消息动作仍然存在,供支持这些操作的提供方在现有消息上使用。
插件作者检查清单
- 当通道可以渲染或安全降级语义呈现时,从
describeMessageTool(...)声明presentation。 - 将
presentationCapabilities添加到运行时出站适配器。 - 在运行时代码中实现
renderPresentation,而不是在控制平面插件设置代码中实现。 - 避免在高频设置/目录路径中引入原生 UI 库。
- 当已知时,在
presentationCapabilities.limits上声明通用能力限制。 - 在渲染器和测试中保留最终的平台限制。
- 为不支持的图表、表格、按钮、选择器、URL 按钮、标题/文本重复,以及混合
message和presentation发送添加回退测试。 - 仅当提供方可以固定已发送消息 ID 时,才通过
deliveryCapabilities.pin和pinDeliveredMessage添加发送固定支持。 - 不要通过共享消息操作 schema 暴露新的 provider 原生 card/block/component/button 字段。