Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt

Use this file to discover all available pages before exploring further.

消息呈现是 OpenClaw 用于丰富出站聊天 UI 的共享契约。 它让智能体、CLI 命令、审批流程和插件只需描述一次消息意图, 同时每个渠道插件都可以尽可能渲染出最合适的原生形态。 对可移植的消息 UI 使用 presentation:
  • 文本区块
  • 简短的上下文 / 页脚文本
  • 分隔线
  • 按钮
  • 选择菜单
  • 卡片标题和语气
不要向共享消息工具中添加新的提供商原生字段,例如 Discord components、Slack blocks、Telegram buttons、Teams card 或 Feishu card。这些都是由渠道插件负责的渲染器输出。

契约

插件作者从以下位置导入公开契约:
import type {
  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
  }
}

渲染器契约

渠道插件在其出站适配器上声明渲染支持:
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 });
  },
};
能力字段刻意保持为简单布尔值。它们描述的是渲染器能将哪些内容做成交互式, 而不是每一个原生平台限制。渲染器仍然负责处理平台特定限制,例如最大按钮数量、区块数量和卡片大小。

核心渲染流程

ReplyPayload 或消息动作包含 presentation 时,核心会:
  1. 规范化 presentation 载荷。
  2. 解析目标渠道的出站适配器。
  3. 读取 presentationCapabilities
  4. 当适配器可以渲染该载荷时,调用 renderPresentation
  5. 当适配器缺失或无法渲染时,回退为保守的文本。
  6. 通过正常的渠道投递路径发送生成的载荷。
  7. 在第一条消息成功发送后,应用诸如 delivery.pin 之类的投递元数据。
核心负责回退行为,因此生产方可以保持渠道无关。渠道插件则负责原生渲染和交互处理。

降级规则

Presentation 必须能安全地发送到能力受限的渠道。 回退文本包含:
  • 第一行的 title
  • 作为普通段落的 text 区块
  • 作为紧凑上下文行的 context 区块
  • 作为可视分隔符的 divider 区块
  • 按钮标签,包括链接按钮的 URL
  • 选择项标签
不受支持的原生控件应当降级,而不是让整次发送失败。 示例:
  • 当 Telegram 的内联按钮被禁用时,发送文本回退。
  • 不支持选择器的渠道会将选择项列为文本。
  • 仅含 URL 的按钮会变成原生链接按钮,或者回退为一行 URL 文本。
  • 可选的置顶失败不会让已投递的消息失败。
主要例外是 delivery.pin.required: true;如果请求将置顶设为必需, 而该渠道无法置顶已发送消息,则投递会报告失败。

提供商映射

当前内置渲染器:
渠道原生渲染目标说明
DiscordComponents 和 component containers为现有提供商原生载荷生产方保留旧版 channelData.discord.components,但新的共享发送应使用 presentation
SlackBlock Kit为现有提供商原生载荷生产方保留旧版 channelData.slack.blocks,但新的共享发送应使用 presentation
Telegram文本加内联键盘按钮 / 选择器要求目标表面具备内联按钮能力;否则会使用文本回退。
Mattermost文本加交互式 props其他区块会降级为文本。
Microsoft TeamsAdaptive Cards当同时提供普通 message 文本和卡片时,卡片中会包含普通 message 文本。
Feishu交互式卡片卡片头部可以使用 title;正文会避免重复该标题。
纯文本渠道文本回退即使没有渲染器的渠道,也仍能获得可读输出。
对提供商原生载荷的兼容性,是为现有 reply 生产方提供的过渡性便利。 这并不是添加新的共享原生字段的理由。

Presentation 与 InteractiveReply

InteractiveReply 是旧的内部子集,供审批和交互辅助工具使用。它支持:
  • 文本
  • 按钮
  • 选择器
MessagePresentation 是标准的共享发送契约。它新增了:
  • 标题
  • 语气
  • 上下文
  • 分隔线
  • 仅含 URL 的按钮
  • 通过 ReplyPayload.delivery 提供的通用投递元数据
在桥接旧代码时,使用 openclaw/plugin-sdk/interactive-runtime 中的辅助函数:
import {
  interactiveReplyToPresentation,
  normalizeMessagePresentation,
  presentationToInteractiveReply,
  renderMessagePresentationFallbackText,
} from "openclaw/plugin-sdk/interactive-runtime";
新代码应直接接受或生成 MessagePresentation

投递置顶

置顶是投递行为,不是呈现。使用 delivery.pin,而不是提供商原生字段,例如 channelData.telegram.pin 语义:
  • pin: true 会置顶第一条成功投递的消息。
  • pin.notify 默认为 false
  • pin.required 默认为 false
  • 可选的置顶失败会降级,并保留已发送消息不变。
  • 必需的置顶失败会导致投递失败。
  • 分块流式传输消息会置顶第一个已投递分块,而不是末尾分块。
对于提供商支持这些操作的现有消息,手动 pinunpinpins 消息动作仍然存在。

插件作者检查清单

  • 当渠道可以渲染或安全降级语义化 presentation 时,在 describeMessageTool(...) 中声明 presentation
  • presentationCapabilities 添加到运行时出站适配器。
  • 在运行时代码中实现 renderPresentation,而不是在控制平面插件设置代码中实现。
  • 不要将原生 UI 库放入高频的设置 / 目录路径中。
  • 在渲染器和测试中保留平台限制。
  • 为不受支持的按钮、选择器、URL 按钮、标题 / 文本重复,以及混合 messagepresentation 发送添加回退测试。
  • 仅当提供商能够置顶已发送消息 id 时,才通过 deliveryCapabilities.pinpinDeliveredMessage 添加置顶投递支持。
  • 不要通过共享消息动作 schema 暴露新的提供商原生 card / block / component / button 字段。

相关文档