跳转至

消息展示

消息展示(Message presentation)是 OpenClaw 为出站聊天富 UI 提供的共享契约。它让智能体、CLI 命令、审批流和插件只需描述一次消息意图,而每个渠道插件都会渲染出各自能做到的最佳原生形态。

请使用消息展示实现可移植的消息 UI:文本区块、小号上下文/页脚文本、分隔线、图表、表格、按钮、选择菜单,以及卡片标题/色调。

默认情况下,请勿向共享消息工具添加提供商原生字段。原生扩展需要维护者的明确批准、渠道自有的 schema、文档化的跨渠道行为,以及 presentation 无法表达的能力。Discord components 是已获批准的内置例外,用于模态表单、媒体画廊、文件块、带配件的区块和样式化容器。对于可移植的富消息,请使用 presentation。

契约

插件作者从以下位置导入公共契约:

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: "chart";
      chartType: "pie";
      title: string;
      segments: Array<{ label: string; value: number }>;
    }
  | {
      type: "chart";
      chartType: "bar" | "area" | "line";
      title: string;
      categories: string[];
      series: Array<{ name: string; values: number[] }>;
      xLabel?: string;
      yLabel?: string;
    }
  | {
      type: "table";
      caption: string;
      headers: string[];
      rows: Array<Array<string | number>>;
      rowHeaderColumnIndex?: number;
    };

type MessagePresentationAction =
  | { type: "command"; command: string }
  | { type: "callback"; value: string }
  | {
      type: "approval";
      approvalId: string;
      approvalKind: "exec" | "plugin";
      decision: "allow-once" | "allow-always" | "deny";
    }
  | {
      type: "question";
      questionId: string;
      optionValue: string;
    }
  | {
      type: "question";
      questionId: string;
      intent: "custom-input";
    }
  | { type: "url"; url: string }
  | {
      type: "web-app";
      url: string;
      widgetId?: string;
    }
  | {
      type: "web-app";
      url?: string;
      widgetId: string;
    };

type MessagePresentationButton = {
  label: string;
  action?: MessagePresentationAction;
  /** Legacy callback value. Prefer action for new controls. */
  value?: string;
  /** @deprecated Use an action with type "url". */
  url?: string;
  /** @deprecated Use an action with type "web-app". */
  webApp?: { url: string };
  /** @deprecated Use an action with type "web-app". */
  web_app?: { url: string };
  priority?: number;
  disabled?: boolean;
  reusable?: boolean;
  style?: "primary" | "secondary" | "success" | "danger";
};

type MessagePresentationOption = {
  label: string;
  action?: Extract<MessagePresentationAction, { type: "command" | "callback" }>;
  /** Legacy callback value. Prefer action for new controls. */
  value?: string;
};

type ReplyPayloadDelivery = {
  pin?:
    | boolean
    | {
        enabled: boolean;
        notify?: boolean;
        required?: boolean;
      };
};

按钮语义:

  • action.type: "command" 通过核心的命令路径运行原生斜杠命令。请将其用于内置命令按钮和菜单。
  • action.type: "callback" 通过渠道的交互路径携带不透明的插件数据。渠道插件不得将回调数据重新解释为斜杠命令。
  • action.type: "approval" 标识一个持久的操作员审批、其明确的 exec 或 plugin 类型,以及所请求的决策。渠道插件将该操作编码为传输私有回调,并通过审批服务解析它;它们不得解析 /approve 命令文本,也不得从 ID 推断类型。
  • action.type: "question" 标识一个实时、运行时创建的 ask_user 问题的选项。与 approval 一样,这是 OpenClaw 运行时操作;智能体和插件不得合成问题 ID。Telegram、Discord、Slack、Mattermost 和 LINE 私聊将其映射为传输私有原生回调,并通过 Gateway 解析该选项。当问题被回答、过期或取消时,Telegram、Discord 和 Slack 会编辑已投递的消息,移除其操作,并附加终止状态。Mattermost 仅在其接受的那次点击上使提示失效,因此在其他地方结束的问题仍会保留其按钮;后续点击会收到私有反馈。被拒绝的 Mattermost 点击也会收到私有反馈,并保持提示不变。LINE 群聊和多人聊天会保留可读选项,因为其 postback 不包含接受回答所需的发送者身份。未知的 LINE 目标也使用文本。LINE 无法编辑已投递的消息,因此在问题结束后点击会收到通知。LINE 在单张卡片上最多绘制四个控件,符合其二到四个选项的限制。WhatsApp、Signal 和 iMessage 将最多四个单选选项渲染为 1️⃣ 到 4️⃣ 的回应。其他问题形态会退化为标签文本,用户可以通过纯文本回复来回答。
  • intent: "custom-input" 将实时问题切换到其自由文本回答路径,而无需解析它。生产者还必须在可见文本中说明自由文本路径。当渠道无法安全地定位到文本输入框时,可以在保持已声明选项控件为原生控件的同时省略这一个原生控件。Telegram 将其映射为 其他… 和 Force Reply。Discord、Slack、Mattermost 和 LINE 保留可见文本路径。
  • action.type: "url" 打开普通链接。
  • action.type: "web-app" 启动一个渠道原生的 Web 应用。对于基于 URL 的应用,请设置 url;对于启动机制由渠道拥有的 OpenClaw 托管小部件,请设置 widgetId;至少需要提供其中一个。当两者同时存在时,渠道可以优先使用其原生托管小部件启动方式,并在该机制不可用时使用 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 选择启用。 其他渠道会收到标题和每一行,作为确定性线性 文本,内部空白会被折叠:
Open pipeline (table)
- Account: Acme; Stage: Won; ARR: 125000
- Account: Globex; Stage: Review; ARR: 82000

没有单独的 report 判别器。请使用 title、 tone、text、context、chart、table 和操作块来组合报告。这使每个 块都可以独立渲染,并为完整报告提供相同的 确定性文本回退。

生产者示例

简单卡片:

{
  "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",
          "action": { "type": "callback", "value": "deploy:approve" },
          "style": "success"
        },
        {
          "label": "Decline",
          "action": { "type": "callback", "value": "deploy:decline" },
          "style": "danger"
        }
      ]
    }
  ]
}

仅 URL 链接按钮:

{
  "blocks": [
    { "type": "text", "text": "Release notes are ready." },
    {
      "type": "buttons",
      "buttons": [
        {
          "label": "Open notes",
          "action": { "type": "url", "url": "https://example.com/release" }
        }
      ]
    }
  ]
}

Telegram Mini App 按钮:

{
  "blocks": [
    {
      "type": "buttons",
      "buttons": [
        {
          "label": "Launch",
          "action": { "type": "web-app", "url": "https://example.com/app" }
        }
      ]
    }
  ]
}

选择菜单:

{
  "title": "Choose environment",
  "blocks": [
    {
      "type": "select",
      "placeholder": "Environment",
      "options": [
        { "label": "Canary", "value": "env:canary" },
        { "label": "Production", "value": "env:prod" }
      ]
    }
  ]
}

图表:

{
  "blocks": [
    {
      "type": "chart",
      "chartType": "line",
      "title": "Quarterly revenue",
      "categories": ["Q1", "Q2", "Q3"],
      "series": [
        { "name": "Product", "values": [120, 145, 138] },
        { "name": "Services", "values": [80, 95, 104] }
      ],
      "xLabel": "Quarter",
      "yLabel": "Revenue"
    }
  ]
}

表格报告:

{
  "title": "Pipeline report",
  "tone": "info",
  "blocks": [
    { "type": "text", "text": "Current opportunities by stage." },
    {
      "type": "table",
      "caption": "Open pipeline",
      "headers": ["Account", "Stage", "ARR"],
      "rows": [
        ["Acme", "Won", 125000],
        ["Globex", "Review", 82000]
      ],
      "rowHeaderColumnIndex": 0
    },
    { "type": "context", "text": "Updated from the CRM snapshot." }
  ]
}

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,
    charts: false,
    tables: false,
    limits: {
      actions: {
        maxActions: 25,
        maxActionsPerRow: 5,
        maxRows: 5,
        maxLabelLength: 80,
        maxValueBytes: 100,
        supportsStyles: true,
        supportsDisabled: false,
      },
      selects: {
        maxOptions: 25,
        maxLabelLength: 100,
        maxValueBytes: 100,
      },
      text: {
        maxLength: 2000,
        encoding: "characters",
        markdownDialect: "discord-markdown",
      },
    },
  },
  deliveryCapabilities: {
    pin: true,
  },
  renderPresentation({ payload, presentation, ctx }) {
    return renderNativePayload(payload, presentation, ctx);
  },
  async pinDeliveredMessage({ target, messageId, pin, assertDirectAdapterHandoff }) {
    assertDirectAdapterHandoff?.();
    await pinNativeMessage(target, messageId, { notify: pin.notify === true });
  },
};

当某项能力依赖于按账户配置或投递的文本漏斗——例如 Telegram 仅在 richMessages 账户上渲染原生表格,且仅在 markdown 路径上渲染——请在静态对象旁边声明可选的 resolvePresentationCapabilities({ cfg, accountId, formatting }) 钩子。Core 会为每次投递解析一次能力,并且该钩子优先于静态声明;请将静态对象保留为与账户无关的基线。

const adapter: ChannelOutboundAdapter = {
  presentationCapabilities: BASE_CAPABILITIES,
  resolvePresentationCapabilities: ({ cfg, accountId, formatting }) => ({
    ...BASE_CAPABILITIES,
    tables: isRichAccount(cfg, accountId) && formatting?.parseMode !== "HTML",
  }),
  // ...
};

能力布尔值描述渲染器可以使其交互的内容。可选的 limits 描述 Core 在调用渲染器之前可以适配的通用信封:

type ChannelPresentationCapabilities = {
  supported?: boolean;
  buttons?: boolean;
  selects?: boolean;
  context?: boolean;
  divider?: boolean;
  charts?: boolean;
  tables?: boolean;
  limits?: {
    actions?: {
      maxActions?: number;
      maxActionsPerRow?: number;
      maxRows?: number;
      maxLabelLength?: number;
      maxValueBytes?: number;
      supportsStyles?: boolean;
      supportsDisabled?: boolean;
      supportsLayoutHints?: boolean;
    };
    selects?: {
      maxOptions?: number;
      maxLabelLength?: number;
      maxValueBytes?: number;
    };
    text?: {
      maxLength?: number;
      encoding?: "characters" | "utf8-bytes" | "utf16-units";
      markdownDialect?: "plain" | "markdown" | "html" | "slack-mrkdwn" | "discord-markdown";
      supportsEdit?: boolean;
    };
  };
};

Core 会在渲染前对语义控件应用通用限制。渲染器仍然负责最终的提供商特定验证和裁剪,包括原生块数量、卡片大小、URL 限制以及无法在通用契约中表达的提供商特殊行为。如果限制移除了块中的所有控件,Core 会将标签保留为非交互上下文文本,以便投递的消息仍具有可见的回退内容。

核心渲染流程

在 CLI 和标准消息操作使用的规范出站路径上,Core:

  1. 规范化展示负载。
  2. 解析目标渠道的出站适配器。
  3. 读取 presentationCapabilities。
  4. 当适配器声明这些限制时,应用通用能力限制,例如操作数量、标签长度和选择选项数量。除非适配器分别明确声明 charts: true 或 tables: true,否则图表和表格块会变为确定性文本。
  5. 当适配器能够渲染该负载时,调用 renderPresentation。其 presentation 会针对原生限制进行适配;sourcePresentation 保留规范化后的原始内容,用于渠道特定的文本回退。
  6. 当适配器不存在或无法渲染时,回退到保守文本。
  7. 通过常规渠道投递路径发送生成的负载。
  8. 在第一条成功发送的消息之后,应用投递元数据,例如 delivery.pin。

直接消费 ReplyPayload 的渠道本地回复或预览漏斗,必须进入该规范路径,或者在将负载投影为纯文本/媒体之前,实例化相同的展示回退。

Core 负责回退行为,以便生产者可以保持渠道无关。渠道插件负责原生渲染和交互处理。当渲染器因完整原生卡片无法容纳而回退到文本时,请使用 sourcePresentation 保留完整标签,并应用该渠道的文本和 URL 清理。继续将 presentation 用于原生控件。

降级规则

展示内容必须能够在受限渠道上安全发送。

手动编写相同事实的纯文本渲染的生产者,可以在回复负载上使用 presentationTextMode: "fallback" 进行标记。原生渲染展示数据块的渠道会丢弃该文本;当所有 table 和 chart 块都降级且没有剩余交互块时,将原样发送已编写的文本,而不是使用下面的通用扁平化。

回退文本包括:

  • title 作为第一行
  • text 块作为普通段落
  • context 块作为紧凑上下文行
  • divider 块作为视觉分隔符
  • 按钮标签,包括链接按钮的 URL
  • 选择选项标签
  • 图表标题、类型、坐标轴、类别、系列和值
  • 表格标题、表头和每一行的值

按钮值回退可见性

当渠道无法渲染交互控件时,按钮和选择值会回退为纯文本。回退行为在保持可用性的同时,会保持不透明回调数据的私密性:

  • command 类型操作 渲染为 label: `command`,以便用户可以复制该命令并在渠道输入框中手动运行。
  • callback 类型操作 和旧版 value 字段仅渲染为标签。不透明回调值不会在回退文本中暴露。
  • approval 类型操作 仅渲染为标签。审批 ID 和决策是传输数据,不会通过通用标量辅助函数或回退文本暴露。
  • url 操作、基于 URL 的 web-app 操作,以及已弃用的 url / webApp / web_app 输入会连同按钮标签一起渲染 URL 文本,因为 URL 是面向用户的。仅托管小部件的操作在没有原生小部件启动的渠道上仅渲染为标签。
  • 选择选项 遵循相同规则:命令类型包含命令文本;不透明回调操作和旧版值仍仅渲染为标签。

在回退 UI 中添加手动命令指引的渠道适配器(例如 Feishu 文档评论说明),必须从回退渲染器使用的相同展示块派生命令存在检查,以便指引文本仅在实际显示手动命令时出现。

不支持的原生控件应当降级,而不是导致整个发送失败。示例:

  • 当 Telegram 的内联按钮被禁用时,会发送文本回退。
  • 不支持 select 的渠道会将 select 选项以文本形式列出。
  • 不支持原生 chart 的渠道会将 chart 数据以文本形式列出。
  • 不支持原生 table 的渠道会将每一行 table 以文本形式列出。
  • 仅 URL 按钮会变为原生链接按钮或回退 URL 行。
  • 可选固定失败不会导致已投递消息失败。

主要例外是 delivery.pin.required: true;如果请求将固定作为必需,且渠道无法固定已发送消息,则投递会报告失败。

提供商映射

当前内置渲染器:

渠道 原生渲染目标 说明
Discord 组件和组件容器 支持文档中记录的 Discord 专用 components 扩展,用于表达 presentation 无法表达的原生布局。可移植的共享发送应使用 presentation。
Feishu 交互式卡片 卡片标题使用一次 title。在原生卡片中,禁用或不受支持的按钮仅保留标签文本;被拒绝的 URL 目标和不可解析的回调值会被省略。
Matrix 文本回退加结构化事件字段 buttons/select 会声明为受支持,但每个块都会渲染为 renderMessagePresentationFallbackText 输出,并承载在 com.openclaw.presentation 事件字段中,而不是原生交互式控件。
Mattermost 文本加交互式 props select 和 divider 不受支持;这些块会降级为文本。
Microsoft Teams Adaptive Cards 当同时提供时,普通 message 文本会随卡片一起包含。select、样式和禁用状态不受支持。
Slack Block Kit 将 chart 渲染为原生 data_visualization,将 table 渲染为原生 data_table;保留旧版 channelData.slack.blocks,但新的共享发送应使用 presentation。
Telegram 文本加内联键盘 buttons/select 需要目标界面具备内联按钮能力;否则使用文本回退。
普通渠道 文本回退 没有渲染器的渠道仍会获得可读输出。

仅对现有回复生产者保留提供商原生负载兼容性。 新的原生字段需要上述明确的例外审查。

Presentation 与 InteractiveReply

InteractiveReply 是较旧的内部子集,由审批和交互辅助函数使用。它支持:

  • text
  • buttons
  • selects

MessagePresentation 是规范的共享发送契约。它新增:

  • title
  • tone
  • context
  • divider
  • chart
  • table
  • 仅 URL 按钮
  • 通过 ReplyPayload.delivery 的通用投递元数据

在桥接旧代码时,请使用来自 openclaw/plugin-sdk/interactive-runtime 的辅助函数:

import {
  adaptMessagePresentationForChannel,
  applyPresentationActionLimits,
  hasMessagePresentationBlocks,
  interactiveReplyToPresentation,
  isMessagePresentationInteractiveBlock,
  normalizeMessagePresentation,
  presentationPageSize,
  presentationToInteractiveControlsReply,
  presentationToInteractiveReply,
  renderMessagePresentationChartFallbackText,
  renderMessagePresentationFallbackText,
  renderMessagePresentationTableFallbackText,
  resolveMessagePresentationActionValue,
  resolveMessagePresentationButtonAction,
  resolveMessagePresentationControlValue,
  resolveMessagePresentationOptionAction,
} from "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) 仅读取 command/callback 标量值。非标量规范 action 永远不会回退到旧版影子 value,因此审批 ID 和链接目标保持类型化。
  • renderMessagePresentationChartFallbackText(block) / renderMessagePresentationTableFallbackText(block) 将一个结构化数据块渲染为确定性文本,用于特定渠道的回退路径。

旧版 InteractiveReply* 类型和转换辅助函数在 SDK 中被标记为 @deprecated。兼容性注册表将它们记录为 message-presentation-legacy-bridges,弃用日期为 2026-07-25,removeAfter 日期为 2026-10-01:

  • InteractiveReply、InteractiveReplyBlock、InteractiveReplyButton 和 InteractiveReplyOption
  • normalizeInteractiveReply(...)
  • hasInteractiveReplyBlocks(...)
  • interactiveReplyToPresentation(...)
  • presentationToInteractiveReply(...)
  • presentationToInteractiveControlsReply(...)
  • resolveInteractiveTextFallback(...)
  • reduceInteractiveReply(...)

presentationToInteractiveReply(...) 和 presentationToInteractiveControlsReply(...) 仍可作为渲染器桥接,供旧版渠道实现使用。新的生产者代码不应调用 它们;应发送 presentation,并让核心/渠道适配层处理渲染。

审批辅助函数也有以 presentation 优先的替代项:

  • 使用 buildApprovalPresentation(...) 替代 buildApprovalInteractiveReply(...)
  • 使用 buildExecApprovalPresentation(...) 替代 buildExecApprovalInteractiveReply(...)

这些已发布的构建器仍保持由命令支撑,以兼容插件。拥有持久审批类型的 Gateway 和捆绑渠道代码应使用 buildTypedApprovalPresentation(...)、 buildTypedExecApprovalPendingReplyPayload(...) 或 buildTypedPluginApprovalPendingReplyPayload(...),以便传输层收到显式的 approval 操作,而不是从 /approve 文本推断语义。

renderMessagePresentationFallbackText(...) 对于没有文本回退的 presentation 块返回空字符串,例如仅包含分隔线的 presentation。要求非空发送正文的传输层可以传入 emptyFallback,以选择最小正文,而不改变默认回退契约。

投递置顶

置顶是投递行为,而不是 presentation。请使用 delivery.pin,而不是 channelData.telegram.pin 等提供商原生字段。

语义:

  • pin: true 会置顶第一条成功投递的消息。
  • pin.notify 默认为 false。
  • pin.required 默认为 false。
  • 可选置顶失败会降级,并保持已发送消息完整。
  • 必需置顶失败会报告部分投递失败,同时保留已接受的消息及其回执。
  • 分块消息会置顶第一个已投递分块,而不是末尾分块。

手动 pin、unpin 和 pins 消息操作仍然存在,用于提供商支持这些操作的现有消息。

pinDeliveredMessage 上下文包含一个可选的 assertDirectAdapterHandoff 回调。成功发送不会使其所有者无限期保持活动状态:所有者可以在适配器准备或等待 置顶该消息时关闭。核心在进入置顶钩子之前会检查该断言,适配器必须在准备、速率限制队列和重试期间保留它, 并在每次提供商请求之前同步地立即检查它。如果原生置顶辅助函数执行这些等待,也请将断言转发到其现有的 请求守卫中。

即使权限方在等待其响应时关闭,也要让已提交的置顶请求完成。不要丢弃已接受的置顶,也不要重复发送消息。这个 可选上下文字段保留了不提供断言的旧调用方;它不添加任何配置或置顶权限,并且不得序列化到 payload 中。

插件作者检查清单

  • 当渠道能够渲染或安全降级语义化 presentation 时,从 describeMessageTool(...) 声明 presentation。
  • 将 presentationCapabilities 添加到运行时出站适配器。
  • 在运行时代码中实现 renderPresentation,而不是控制平面插件 设置代码。
  • 将原生 UI 库排除在热设置/目录路径之外。
  • 当已知时,在 presentationCapabilities.limits 上声明通用能力限制。
  • 在渲染器和测试中保留最终平台限制。
  • 为不支持的图表、表格、按钮、选择器、URL 按钮、标题/文本重复,以及混合 message 加 presentation 发送添加回退测试。
  • 仅当提供商可以置顶已发送消息 id 时,才通过 deliveryCapabilities.pin 和 pinDeliveredMessage 添加投递置顶支持。
  • 未经上述明确例外审查,不要通过共享消息操作模式暴露提供商原生的卡片/块/组件/按钮字段。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw