本页内容
本页内容
构建插件
耐用的通道入口
使入站交付持久,并保持重播、保留和重新启动行为可证明。 构建频道插件指南的一部分。
入站入口(实验性)
迁移入站授权的通道可以使用运行时接收路径中的实验性 openclaw/plugin-sdk/channel-ingress-runtime 子路径。它接受平台事实、原始允许列表、路由描述符、命令事实和访问组配置,然后返回 sender/route/command/activation 投影以及有序入口图,而平台查找和副作用则保留在插件中。在传递给解析器的描述符中保留插件身份规范化;不要序列化来自已解析状态或决策的原始匹配值。有关 API 设计、所有权边界和测试期望,请参阅 Channel 入口 API。
将确切的解析器结果作为 channelIngress 传递给主机注入的注册上下文构建器。用于执行的结果必须包括最终的agent/session/message/event contextBinding;仅决策解析器调用可能会忽略它。这通过一次性排队运行准入保留了本机插件的记录、纪元和范围限制的参与者证据,而不将其暴露在消息上下文字段中。独立的公共建筑商并不是权威的替代品。切勿从发件人、路由、房间、帐户、线程、消息、传输或会话值重建证据。仅当路径经过源验证缺乏权威的入口解析器集成时,旧适配器才可以显式传递 channelIngress: "unsupported"。支持的路径必须传递准确的结果;遗漏是无效的生产接线。丢失、伪造、陈旧、重复使用或混合支持的证据项目为未知,绝不作为允许信号。
持久入口和重放重复数据删除
采用持久入口的通道应使用 openclaw/plugin-sdk/channel-outbound 中的 createChannelIngressMonitor,除非它们需要完全不同的入口或泵合同。在单个接收阻塞点将原始传输信封排队(接收时没有标准化),将传输确认门控到 Webhook 传输的持久附加上,为每个会话派生一个序列化通道,并在调度采用时将事件标记为完成。队列的主键是 (queue_name, event_id),并且完成逻辑删除该行而不是删除它,因此在逻辑删除保留窗口中,同一 event_id 的后期平台重新交付将被持久拒绝。监控API和关机合约参见通道出站API。
调度生命周期可选择公开 readLaneBacklog(): Promise<readonly ChannelIngressQueueRecord<unknown, unknown>[]>。它读取尚未移交的同通道持久行,不包括当前事件、重试延迟的待处理行以及本地释放或结束的声明。通道缓冲区可以匹配自己的有效负载以等待已接受的输入;核心流失者仍然是车道状态的所有者。 SDK 入站去抖器的可选 shouldHoldFlush(items) 挂钩可以将安静定时器刷新推迟到现有的截止日期。在检查加入同一批次期间追加;支票可以刷新它,而无需等待另一个安静的窗口。显式刷新绕过检查。
该墓碑是重播防护的分层规则(openclaw/plugin-sdk/persistent-dedupe):只有当防护的身份或保留超过队列的逻辑消息密钥时,耗尽的通道才会保留单独的重播防护(Telegram 重复数据删除 chat_id:message_id,因为去抖动合并可以在新的消息下重新显示消息) update_id),或者比通道的逻辑删除保留时间更长的窗口。如果您的防护钥匙等于排水管 event_id,请在采用排水管和尺寸 completedTtlMs/completedMaxEntries 时删除防护罩以覆盖旧的防护窗。诸如年龄限制之类的非重复数据删除保护与此规则无关。稳定的出站消息 ID 使用来自 openclaw/plugin-sdk/channel-outbound 的共享出站回显注册表,而不是通道本地 TTL 缓存。
持久重放防护等待共享状态工作线程中的 SQLite 读取、比较、写入和遗留文件迁移。在写入事务中再次比较竞争记录。忘记某个键会清除其持久删除所在队列中的内存条目。仅内存清除是最好的努力:进行中的读取可能会在之后重新填充缓存。在确认采用或完成清理之前等待提交和删除。错误钩子保留其现有策略:抛出钩子拒绝操作,而非抛出钩子允许守卫的内存回退。工作线程故障永远不会将持久性切换到同步 SQLite。多键提交和删除会在返回错误之前解决每个接受的写入,因此回滚和关闭不能与仍在运行的同级突变竞争。
传输类别和保留
根据接收边界处的恢复保证对传输进行分类:
- 确认门控的 webhook 或事件投递: 仅在持久化追加之后确认或返回成功。 追加失败必须使该投递仍具备重试资格,或使接收边界失败。此类别包括 Slack、SMS、Zalo、 Microsoft Teams、Google Chat、LINE 和 Synology Chat。
- 等待式轮询或流式投递: 仅在追加之后推进远程游标或发送传输确认。当不存在显式游标时, 保持接收回调串行并等待完成,以确保追加失败不会让接收循环超前运行。Telegram 轮询、Signal 和 Tlon 使用此类别;Telegram webhook 投递遵循上述确认门控规则。
- 不可重放的套接字: IRC、Mattermost、Twitch 和 Zalo Personal 无法请求平台重新投递已接受的事件。它们的持久化队列用于保护进程崩溃窗口,并支持本地重启恢复;完成墓碑对于防止平台重放几乎不起作用。
将 30 天作为整个系统的墓碑 TTL 约定,而不是 SDK 默认值。高流量重投递窗口通常使用 20,000 条已完成条目的上限;低流量的等待式传输和不可重放传输通常使用 1,000-2,000 条。 当前例外包括 LINE 的 4,096 条上限、SMS 的 24 小时已完成 TTL,以及 Tlon 仅按上限保留已完成条目。 失败行上限也可以低于已完成条目上限。TTL 和上限都会清理行,因此有效保留时间在第一个边界达到时结束。 只有在有已记录的平台重试时限、需要保留的已发布重放保护器窗口、预期流量或磁盘预算, 或不可重放传输等情况下才可偏离,并应通过测试覆盖保留契约。
至少一次副作用
Drain 调度在入口行到达其完成墓碑之前运行命令副作用。这些步骤之间的进程崩溃会重播该行,并可能再次执行副作用。这个至少一次崩溃窗口是默认合约。对于不安全重复的工作,例如配置写入、存储清除或回复通道之外的可见确认,请使用 openclaw/plugin-sdk/ingress-effect-once 中的 createIngressEffectOnce(...)。为每个调用指定稳定入口 eventId 以及效果名称。每个入口 queue/account 创建一个帮助程序,并针对该范围使用稳定、唯一的 namespacePrefix,因为传输事件 ID 可能是队列本地的。帮助者只有在效果成功后才承诺其持久主张;抛出的效果会释放该声明,因此排出重试可以再次执行该声明,同时并发调用者等待活动声明。持久状态错误在提供时调用 onDiskError 并拒绝,而不是回退到进程内存。
将帮助程序的 ttlMs 至少设置为通道的入口逻辑删除保留加上效果提交和行完成之间的最大延迟,包括有限的停机时间和排出重试。效果记录的 TTL 在提交时开始,而逻辑删除保留稍后在完成时开始;如果挂起行的生命周期是无限的,则没有有限的 TTL 涵盖任意停机时间。在墓碑无法再重播该行之后,较旧的效果记录就变得毫无意义。为该保留窗口中可能存在的每个不同的 event/effect 密钥设置 stateMaxEntries 的大小,考虑队列的已完成条目限制和每个事件的最大影响。较低的上限会驱逐 TTL 之前最旧的记录,并允许该效果再次执行。如果在效果成功之后但在声明提交之前进程终止或持久性失败,或者如果记录过期而其入口行仍处于挂起状态,则剩余的至少一次窗口将保留。
政策动态发布
网关回复调度为每个新回合选择当前提交的模型运行时配置和目录,包括来自保留其启动配置的监视器的低级 channel.reply.dispatchReplyFromConfig 调用。 Dispatch 在准入之前等待正在进行的模型运行时发布。通道传输和访问策略新鲜度仍然属于帐户监控;回复调度不会取代持久入口或其追加确认合同。对于 SDK 兼容性,旧版 usePublishedModelRuntime 参数仍然被接受,但不再控制网关模型准入。
仅将 reload.noopPrefixes 用于消费者读取提交的运行时配置而不替换通道资源的字段。这些写入仍然会发布经过验证的运行时快照; “noop”表示不重启组件。 * 路径段与一个非空配置键匹配,例如 channels.example.accounts.*.allowFrom。更深层次的界限优先;在相同深度,精确路径优先于通配符。
账户启动时绑定 createRuntimeConfigReader,并在每次新准入时派生一致的策略快照。与该帐户所有者一起保留已解析的名称缓存,并在异步解析后重新检查当前修订。不要在其他消息或交互路径中保留仅启动允许列表。对于异步 shouldSupersedePending 授权,返回一个同步防护,以验证准备好的策略是否仍然是最新的。排水管在取消预采用工作之前立即调用此防护;对于没有异步权限解析的谓词,布尔决策仍然支持。
将凭据、传输设置和帐户生命周期更改保留在重新启动路径上。不要仅仅为了覆盖其策略字段而将整个 accounts 子树声明为动态。包含动态策略和需要重新启动的设置的写入保留现有的原子重新加载和耗尽行为。
账户范围内的重启合约
默认情况下,通道配置变更会重启整个通道。多账户通道只有在以下条件成立时,才可设置 reload.accountScopedRestart: true:配置解析只读取通道范围的共享字段和选定账户,而绝不读取兄弟账户; 并且 Gateway 能够在不替换兄弟运行时的情况下,停止并启动单个 (channel, accountId) 运行时。
范围化路径仅适用于 channels.<channel>.accounts.<non-default-id>.* 下的变更。对共享通道字段、accounts.default、 已删除或无法解析的账户的变更,以及可能影响继承关系的混合变更,都会升级为整个通道重启。 未选择加入的插件始终使用整个通道路径。
网关通过拆卸(包括失败停止重试)保留已接纳帐户的 cfg、已解析的 account 和拥有的 stopAccount 挂钩。即使已发布的配置删除了该帐户或新的插件注册取代了该帐户,清理也必须使用该上下文。
在承诺兑现之前完成 stopAccount 内部的状态更新。在该尝试完成或超时后,网关会通过保留的停止回调忽略写入。即使帐户承诺在中止之前一直处于待处理状态,终端启动状态也会取消之前的 Webhook 切换;它不会撤销当前任务注册入口并在恢复后显式报告就绪的能力。
依赖账户数的策略需要全渠道重新加载。例如,Telegram 更改空帐户 groups 映射在单帐户配置和多帐户配置之间继承默认值的方式。 Synology Chat 还验证跨帐户继承和重复的 Webhook 路径。这些插件不会选择仅帐户重新加载。
对于使用持久入口排水的通道,帐户监视器的停止路径必须首先解决所有接受的传输入口,然后处理并等待其排水。启动帐户会打开相同的帐户键控队列,其初始排出会恢复未分派的持久行。不要添加第二个特定于重新加载的重播通道;队列恢复是标准的重启路径。
当替换其事件 ID 可以与先前标识重叠的已知传输标识时,请在重置其传输游标之前等待核心提供的队列的 purge()。该操作会在一笔事务中删除该队列通道和帐户的所有待处理、已声明、已完成和失败的行,并返回已删除的行计数。在调用之前停止该帐户的生产者并排空。保留以前的身份标记,直到清除提交,以便在启动时再次检测到中断的重置。相同身份重新启动和令牌轮换会保留队列,就像没有已知先前身份的遗留偏移量一样。 purge 在结构队列类型上是可选的,以与插件提供的队列兼容;需要身份重置的调用者必须失败,而不是跳过不可用的清除。运行时提供的清除处理在工作人员准入和提交之前重新检查插件生命周期权限,并在插件运行时退出后拒绝。帐户监视器将其信号传递给 purge({ signal }),因此在授权提交之前取消会保留行。授权提交仍然有效。他们还必须在重置光标之前检查取消,如果取消中断重置,则保留旧标识。
将此标志视为能力声明,而不是性能偏好。契约测试应证明:添加或编辑一个指定账户不会改变兄弟账户的解析配置; 停止一个账户只会完成该账户监控器和排空的处理;新监控器会恰好一次地恢复该账户的行。如果任何保证无法证明, 就不要设置该标志。