Skip to main content
频道插件通过 openclaw/plugin-sdk/channel-outbound 暴露出站消息行为。使用 openclaw/plugin-sdk/channel-inbound 进行接收/上下文/分发 编排。 核心负责队列处理、持久化、持久化的入口监控与排空createChannelIngressMonitorcreateChannelIngressDrainopenChannelIngressDrain)、通用重试策略、回合接管生命周期 (turnAdoptionLifecycle / bindIngressLifecycleToReplyOptions)、钩子、 回执以及共享的 message 工具。插件负责原生的 发送/编辑/删除调用、目标规范化、平台线程处理、选定的引用、通知标志、 账户状态、入口检查与负载编码、通道键、不可重试谓词、可选的替换授权以及 平台特定的副作用。

持久化入口监控器

当通道必须在分发之前持久化已接受的传输事件时,请使用 createChannelIngressMonitor(...)。它将通道入口队列和排空流程与共享的接纳、轮询、清理、投递和关闭生命周期组合在一起。仅当传输层拥有实质不同的接纳或泵送契约时,才使用较底层的 createChannelIngressDrain(...) 必需的选项如下: 监控器会串行化接纳操作,因此追加退避不会颠倒某个 lane 的顺序。默认的有界追加延迟为 0100300 毫秒;耗尽后会拒绝传输回调,而不是分发尚未持久化的事件。在声明时,它会解码带版本的负载,重新运行 inspect,并在投递前拒绝 id 或 lane 不匹配的情况。 deliver 会接收 onAdoptedonDeferredonAdoptionFinalizingonAbandonedabortSignal。如果返回时没有进行显式移交,则会将该终止状态的无分发事件标记为已接管。admission 始终为 exclusive。延迟移交会保持声明,关闭或中止则会使未接管的工作保持可重试状态。监控器会独立于声明结算跟踪投递,因为接管可能会在通道的投递 promise 返回之前将某一行标记为墓碑。 可选设置包括自定义追加延迟、用于高级排空排序/并发/重试策略的 drain 选项块、外部 abortSignal、时钟、泵错误报告、停止错误工厂以及接纳策略。返回的监控器会暴露 admitensureQueueAvailablestartpausestopwaitForIdleisRunningisStopped。当插件负责的迁移或准备工作必须在队列打开后、排空开始前运行时,请使用幂等的 ensureQueueAvailable() 检查。stop 会先结算已接受的接纳操作,然后中止并释放排空流程,等待泵和活动投递完成,最后再次释放,以关闭延迟创建竞态。 将传输特定的脱敏、原始封装验证、不可重试分类以及持久化负载形态保留在插件中。Webhook 传输应仅在 admit 完成后确认;不可重放传输应显式报告持久化追加耗尽,而不是静默分发。

适配器

大多数插件会定义一个 message 适配器:
只声明原生传输实际会保留的能力。请使用从此子路径导出的契约辅助函数,覆盖每一个已声明的发送、回执、实时预览和接收确认能力。

出站回声抑制

当平台可能将插件自身发送的出站消息重新作为入站消息投递时,请使用频道、账户、会话以及稳定的平台消息或来源标识调用 recordOutboundMessageIdentity(...)。共享的入站轮次路径会在会话记录或代理分发之前,在有界的 30 秒时间窗口内丢弃匹配的标识;来源标识可以在发送前预留,或在移除频道路由时刷新,以消除投递竞态。isRecentOutboundMessageIdentity(...) 为频道诊断和测试提供相同的查询功能。不要针对同一个稳定标识维护并行的频道本地 TTL 缓存。

纯文本清理

当出站适配器需要将受支持的 HTML 格式标签转换为轻量级文本标记时,请使用 sanitizeForPlainText(...)。默认会保留现有的聊天风格加粗和删除线标记。仅当该渠道会将结果重新解析为 Markdown 时,才传入 { style: "markdown" }
Markdown 风格使用 **bold**~~strikethrough~~;斜体和行内代码在两种风格中都保留 _italic_ 和反引号标记。应在渠道边界选择样式,而不是在清理后重写标记文本。

投递证据

MessageReceipt 记录由通道适配器返回的结果。具体的平台消息标识符表明平台发送路径已接受该消息;它们并不能证明收件人的设备已经显示或读取了该消息。不带平台消息标识符的收据仅是本地收据元数据。对于具有已读回执或设备投递状态的通道,应通过单独的、通道特定的路径来跟踪这些事实。 如果某个通道适配器能够证明,重试失败不会导致收件人可见的发送重复,并且尚未开始任何具备最终化能力的调用,则应从 openclaw/plugin-sdk/error-runtime 抛出 new PlatformMessageNotDispatchedError("...", { cause: error })。这样 Core 就可以清除过时的发送尝试证据,并安全地重试队列中的意图。只有拥有最终分发边界的适配器才能做出该断言。切勿在最终化/发送调用开始后或返回歧义结果后使用该标记;错误标记会导致消息重复。

现有的出站适配器

如果频道已经有兼容的 outbound 适配器,请从中派生消息适配器,而不是重复发送代码:

持久化发送

运行时发送辅助工具也位于 channel-outbound
  • sendDurableMessageBatch(...)
  • withDurableMessageSendContext(...)
  • deliverInboundReplyWithMessageSendContext(...)
  • 草稿流式/进度辅助工具,例如 resolveChannelDraftStreamingChunking(...)
sendDurableMessageBatch(...) 返回一个明确结果: 当一个批次混合了已发送、已抑制和失败的有效载荷时,请使用 payloadOutcomes。不要根据空的旧版直接投递结果推断 hook 被取消。 当传输在首次成功发送期间创建线程时,出站适配器可以实现 adoptTargetFromDelivery(...)。从平台回执中返回类型化的线程 ID,Core 会将其传递到该持久化批次中的后续有效载荷、固定项和投递后 hook。Core 永远不会替换调用方明确指定的线程,也不会在适配器未选择加入的情况下,根据 receipt.threadId 推断是否采用线程。

自动未知发送协调

仅当插件能够基于持久化的策略后状态协调存在歧义的提供商发送,且无需重新运行修改型 hook 或重新生成提供商有效载荷时,才设置 message.durableFinal.automaticUnknownSendReconciliation。Core 会在 hook 和取消处理完成后将其视为选择加入,并且仅适用于恰好一个已接受的已准备有效载荷。多有效载荷批次不会自动选择加入。 适配器还必须声明 capabilities.reconcileUnknownSend: true,并提供 reconcileUnknownSend(...)。使用 reconcileUnknownSendKinds 指明插件能够证明的具体传输分支,例如 textmedia。如果存在该种类映射,则选定的分支必须为 true。省略该映射意味着回调声明支持所有选定分支,因此对于新插件,建议使用显式映射。 回调必须使用提供商自有的幂等机制或权威回读,在能够证明发送成功时返回带有实际提供商回执的 sent;仅当能够证明新发送是安全的情况下返回 not_sent;当两种结果都无法证明时返回 unresolved。明确要求协调时,不支持的已准备形状必须在提供商 I/O 之前失败。在恢复期间,缺失、不完整或不匹配的提供商证明必须采取故障关闭,而不是重放可能已经可见的内容。 如果协调需要由提供商持有的持久化证据,请实现 afterUnknownSendTerminal(...)。Core 会在歧义队列行权威地转为失败后调用它,包括重试预算耗尽的情况。使用它移除不再需要的提供商持有的计划或有效载荷。清理操作应尽力执行且必须具备幂等性;失败会被记录,但不会使终态队列行再次可重放。

延迟投递准入

当已解析的账户无法安全接受由 core 管理的出站或延迟投递时,请使用 message.durableFinal.admitDeferredDelivery(...)。Core 会在实时出站工作之前同步调用此钩子,包括跳过队列持久化的路径,并且会在回放已恢复的意图之前再次调用。上下文包含 cfgchanneltoaccountId,以及值为 liverecoveryphase 返回 { status: "allowed" } 以继续。若投递不得被持久化、直接发送或回放,则返回 { status: "permanent_rejection", reason }。实时拒绝会在创建队列、消息钩子或平台工作之前失败。恢复拒绝会将队列记录标记为失败,并跳过协调与回放。省略该钩子则表示允许。 该钩子是同步的准入决策,而不是发送路径。只读取已经加载的配置或运行时状态;不要进行网络、文件系统或其他异步 I/O。契约测试应通过来自 openclaw/plugin-sdk/channel-outboundChannelMessageDurableFinalAdapter 覆盖两个阶段以及两种结果变体。

兼容性分发

通过 channel-inbound 中的 dispatchChannelInboundReply(...) 组装入站回复调度。将平台投递保留在投递适配器中;对消息适配器、持久化发送、回执、实时预览以及回复管道选项,使用 channel-outbound