Plugin maintainer reference
插件架构内部机制
对于公共能力模型、插件形态以及所有权/执行契约,请参阅插件架构。本页介绍内部机制:加载流水线、注册表、运行时钩子、Gateway 网关 HTTP 路由、导入路径和架构表。
加载流水线
启动时,OpenClaw 大致执行以下操作:
- 发现候选插件根目录
- 读取原生或兼容的捆绑包清单及软件包元数据
- 拒绝不安全的候选项
- 规范化插件配置(
plugins.enabled、allow、deny、entries、slots、load.paths) - 决定是否启用每个候选项
- 加载已启用的原生模块:构建后的内置模块使用原生加载器; 第三方本地源代码 TypeScript 使用应急 Jiti 回退方案
- 调用原生
register(api)钩子,并将注册项收集到插件注册表中 - 向命令/运行时界面公开注册表
安全门控在运行时执行之前运行。出现以下情况时,发现流程会阻止候选项:
- 其解析后的入口逸出插件根目录
- 其路径(或根目录)可由所有用户写入
- 对于非内置插件,路径所有权与当前 uid(或 root)不匹配
对于所有用户均可写入的内置目录,门控重新检查前会先尝试就地执行 chmod 修复
(npm/全局安装可能会使软件包目录处于 0777);对于内置来源,
完全跳过所有权检查。
如果已知被阻止候选项的插件 ID,发出的诊断信息仍会携带该 ID(包括从原本会被拒绝的目录内的清单中解析出的 ID),因此引用该 ID 的配置会看到与路径安全警告关联的被阻止插件,而不是无关的“未知插件”错误。
清单优先行为
清单是控制平面的事实来源。OpenClaw 使用它来:
- 识别插件
- 发现已声明的渠道/Skills/配置架构或捆绑包能力
- 验证
plugins.entries.<id>.config - 补充 Control UI 标签/占位符
- 显示安装/目录元数据
- 保留轻量的激活和设置描述符,而不加载插件运行时
对于原生插件,运行时模块属于数据平面部分。它注册钩子、工具、命令或提供商流程等实际行为。
可选的清单 activation 和 setup 块保留在控制平面上。
它们仅是用于激活规划和设置发现的元数据描述符;
不能取代运行时注册、register(...) 或 setupEntry。
实时激活使用方利用清单中的命令、渠道和提供商提示,在进行更广泛的注册表实体化之前缩小插件加载范围:
- CLI 加载将范围缩小到拥有所请求主命令的插件
- 渠道设置/插件解析将范围缩小到拥有所请求 渠道 ID 的插件
- 显式提供商设置/运行时解析将范围缩小到拥有所请求 提供商 ID 的插件
- Gateway 网关启动规划使用
activation.onStartup进行显式启动 导入;没有启动元数据的插件仅通过范围更窄的 激活触发器加载
激活规划器既为现有调用方提供仅含 ID 的 API,也为诊断提供规划 API。规划条目会报告选择插件的原因,并将显式 activation.* 提示与清单所有权回退分开:
原因(来自 activation.* 提示) |
原因(来自清单所有权) |
|---|---|
activation-agent-harness-hint |
— |
activation-capability-hint |
— |
activation-channel-hint |
manifest-channel-owner(channels) |
activation-command-hint |
manifest-command-alias(commandAliases) |
activation-provider-hint |
manifest-provider-owner(providers)、manifest-setup-provider-owner(setup.providers) |
activation-route-hint |
— |
| —(钩子触发器没有提示变体) | manifest-hook-owner(hooks)、manifest-tool-contract(contracts.tools) |
这种原因划分是兼容性边界:现有插件元数据继续有效,而新代码可以检测宽泛提示或回退行为,无需更改运行时加载语义。
请求时运行时预加载若请求宽泛的 all 作用域,仍会根据配置、启动规划、已配置的渠道、槽位和自动启用规则
(src/plugins/effective-plugin-ids.ts 中的 resolveEffectivePluginIds)
派生出一组显式的有效插件 ID。如果派生出的集合为空,OpenClaw 会保持作用域为空,而不会扩大到每个可发现的插件。
设置发现优先使用描述符拥有的 ID(例如 setup.providers 和
setup.cliBackends)来缩小候选插件范围,然后才回退到
setup-api,以处理仍需要设置时运行时钩子的插件。提供商设置列表使用清单 providerAuthChoices、从描述符派生的设置选项和安装目录元数据,而不加载提供商运行时。显式的
setup.requiresRuntime: false 是仅描述符截止标记;省略
requiresRuntime 则会保留旧版设置 API 回退,以实现兼容性。如果多个已发现插件声明同一个规范化设置提供商或 CLI 后端 ID,设置查找会拒绝归属不明确的所有者,而不是依赖发现顺序。设置运行时执行时,注册表诊断会报告 setup.providers / setup.cliBackends 与设置 API 实际注册的提供商或 CLI 后端之间的偏差,但不会阻止旧版插件。
插件缓存边界
OpenClaw 不会将插件发现结果或直接清单注册表数据缓存在基于挂钟时间的窗口之后。安装、清单编辑和加载路径更改必须在下一次显式元数据读取或快照重建时可见。 清单文件解析器会维护一个有界文件签名缓存,其键由打开的清单路径以及设备/inode、大小和 mtime/ctime 组成;该缓存仅避免重新解析未更改的字节,不得缓存发现、注册表、所有者或策略答案。
安全的元数据快速路径是显式对象所有权,而不是隐藏缓存。
Gateway 网关启动热路径应通过调用链传递当前 PluginMetadataSnapshot、
派生的 PluginLookUpTable 或显式清单注册表。只要这些对象仍代表当前配置和插件清单,配置验证、启动时自动启用、插件引导和提供商选择就可以复用它们。设置查找仍会按需重建清单元数据,除非特定设置路径接收到显式清单注册表;应将其保留为冷路径回退,而不是添加隐藏的查找缓存。输入发生变化时,应重建并替换快照,而不是改变快照或保留历史副本。活动插件注册表的视图和内置渠道引导辅助函数应根据当前注册表/根目录重新计算。可以在单次调用内使用短期映射来去重工作或防止重入,但不得将其变成进程元数据缓存。
对于插件加载,持久缓存层是运行时加载。当代码或已安装工件实际加载时,它可以复用加载器状态,例如:
PluginLoaderCacheState和兼容的活动运行时注册表- 用于避免重复导入同一运行时界面的 jiti/模块缓存和公共界面加载器缓存
- 已安装插件工件的文件系统缓存
- 用于路径规范化或重复项解析的短期单次调用映射
这些缓存是数据平面的实现细节。它们不得回答“哪个插件拥有此提供商?”之类的控制平面问题,除非调用方有意请求运行时加载。
不要为以下内容添加持久缓存或基于挂钟时间的缓存:
- 发现结果
- 直接清单注册表
- 根据已安装插件索引重建的清单注册表
- 提供商所有者查找、模型抑制、提供商策略或公共工件 元数据
- 任何其他清单派生的答案,其中已更改的清单、已安装索引 或加载路径应在下一次元数据读取时可见
从持久化的已安装插件索引重建清单元数据的调用方会按需重建该注册表。已安装索引是持久化的源平面状态;它不是隐藏的进程内元数据缓存。
注册表模型
已加载的插件不会直接改变任意核心全局变量。它们会注册到中央插件注册表(src/plugins/registry-types.ts 中的 PluginRegistry)中,该注册表会跟踪插件记录(身份、来源、源类型、状态和诊断),以及每种能力的数组:工具、旧版钩子和类型化钩子、渠道、提供商、Gateway 网关 RPC 处理程序、HTTP 路由、CLI 注册器、后台服务、插件自有命令,以及其他数十种类型化提供商系列(语音、嵌入、图像/视频/音乐生成、Web 获取/搜索、Agent harness、会话操作等)。
随后,核心功能会从该注册表读取,而不是直接与插件模块通信。这使加载保持单向:
- 插件模块 -> 注册表注册
- 核心运行时 -> 注册表使用
这种分离对可维护性至关重要。这意味着大多数核心界面只需要一个集成点:“读取注册表”,而不是“为每个插件模块添加特殊处理”。
对话绑定回调
绑定对话的插件可以在审批解决后作出响应。
使用 api.onConversationBindingResolved(...),可在绑定请求获批或被拒后接收回调:
export default { id: "my-plugin", register(api) { api.onConversationBindingResolved(async (event) => { if (event.status === "approved") { // 此插件 + 对话现在已有一个绑定。 console.log(event.binding?.conversationId); return; } // 请求已被拒绝;清除所有本地待处理状态。 console.log(event.request.conversation.conversationId); }); },};回调载荷字段:
status:"approved"或"denied"decision:"allow-once"、"allow-always"或"deny"binding:已获批请求解析后的绑定request:原始请求摘要、分离提示、发送者 ID 和 对话元数据
此回调仅用于通知。它不会改变谁有权绑定对话,并且会在核心审批处理完成后运行。
提供商运行时钩子
提供商插件分为三层:
- 用于低成本运行时前查找的清单元数据:
setup.providers[].envVars、providerAuthAliases、providerAuthChoices和channelConfigs。 - 配置时钩子:
catalog加applyConfigDefaults。 - 运行时钩子:40 多个可选钩子,涵盖身份验证、模型解析、 流封装、思考级别、重放策略和用量端点。请参阅 钩子顺序和用法。
OpenClaw 仍负责通用 Agent loop、故障转移、会话记录处理和 工具策略。这些钩子为提供商特定行为提供了扩展接口, 无需实现完整的自定义推理传输层。
当提供商具有基于环境变量的凭据,且需要让通用身份验证、状态和模型选择器路径在不
加载插件运行时的情况下识别这些凭据时,请使用清单 setup.providers[].envVars。
当一个提供商 ID 应复用另一个提供商 ID 的环境变量、身份验证配置文件、
配置支持的身份验证以及 API 密钥新手引导选项时,请使用清单 providerAuthAliases。
当新手引导和身份验证选项 CLI 界面需要在不加载提供商运行时的情况下了解
提供商的选项 ID、分组标签和简单的单标志身份验证接线时,请使用清单
providerAuthChoices。将提供商运行时
envVars 保留用于面向操作员的提示,例如新手引导标签或 OAuth
客户端 ID/客户端密钥设置变量。
通过所属的 channelConfigs.<id>.schema 和设置描述符来描述由环境变量驱动的
渠道设置和身份验证。
钩子顺序和用法
对于模型/提供商插件,OpenClaw 大致按以下顺序调用钩子。
“何时使用”列是快速决策指南。
OpenClaw 不再调用的仅兼容性提供商字段(例如
ProviderPlugin.capabilities 和 suppressBuiltInModel)有意未在此列出。
| 钩子 | 功能 | 使用时机 |
|---|---|---|
catalog |
在生成 models.json 期间将提供商配置发布到 models.providers |
提供商拥有目录或基础 URL 默认值 |
applyConfigDefaults |
在配置具体化期间应用提供商拥有的全局配置默认值 | 默认值取决于身份验证模式、环境变量或提供商模型系列语义 |
| (内置模型查找) | OpenClaw 首先尝试常规注册表/目录路径 | (不是插件钩子) |
normalizeModelId |
查找前规范化旧版或预览版模型 ID 别名 | 提供商负责在解析规范模型前清理别名 |
normalizeTransport |
在通用模型组装前规范化提供商系列的 api / baseUrl |
提供商负责清理同一传输系列中自定义提供商 ID 的传输配置 |
normalizeConfig |
在运行时/提供商解析前规范化 models.providers.<id> |
提供商需要应由插件负责的配置清理;内置 Google 系列辅助程序还会为受支持的 Google 配置条目提供后备保障 |
applyNativeStreamingUsageCompat |
对配置中的提供商应用原生流式用量兼容性重写 | 提供商需要修复由端点驱动的原生流式用量元数据 |
resolveConfigApiKey |
在加载运行时身份验证前,为配置中的提供商解析环境变量标记身份验证 | 提供商公开自己的环境变量标记 API 密钥解析钩子 |
resolveSyntheticAuth |
公开本地/自托管或配置支持的身份验证,而不持久化明文 | 提供商可以使用合成/本地凭据标记运行 |
resolveExternalAuthProfiles |
叠加提供商拥有的外部身份验证配置文件;对于 CLI/应用拥有的凭据,默认 persistence 为 runtime-only |
提供商复用外部身份验证凭据而不持久化复制的刷新令牌;在清单中声明 contracts.externalAuthProviders |
shouldDeferSyntheticProfileAuth |
降低由环境变量/配置支持的身份验证背后已存储合成配置文件占位符的优先级 | 提供商存储不应获得优先级的合成占位符配置文件 |
resolveDynamicModel |
为本地注册表中尚不存在的提供商自有模型 ID 提供同步后备 | 提供商接受任意上游模型 ID |
prepareDynamicModel |
异步预热,然后再次运行 resolveDynamicModel |
提供商在解析未知 ID 前需要网络元数据 |
normalizeResolvedModel |
嵌入式运行器使用已解析模型前的最终重写 | 提供商需要传输重写,但仍使用核心传输 |
normalizeToolSchemas |
在嵌入式运行器接收工具架构前对其进行规范化 | 提供商需要清理传输系列的架构 |
inspectToolSchemas |
规范化后公开提供商拥有的架构诊断信息 | 提供商希望发出关键字警告,而无需让核心了解提供商特定规则 |
resolveReasoningOutputMode |
选择原生或带标签的推理输出契约 | 提供商需要带标签的推理/最终输出,而不是原生字段 |
prepareExtraParams |
在通用流选项包装器前规范化请求参数 | 提供商需要默认请求参数或按提供商清理参数 |
createStreamFn |
使用自定义传输完全替换常规流路径 | 提供商需要自定义线协议,而不只是包装器 |
wrapStreamFn |
应用通用包装器后的流包装器 | 提供商需要请求标头/正文/模型兼容性包装器,但不需要自定义传输 |
resolveTransportTurnState |
附加原生的逐轮次传输标头或元数据 | 提供商希望通用传输发送提供商原生的轮次标识 |
resolveWebSocketSessionPolicy |
附加原生 WebSocket 标头或会话冷却策略 | 提供商希望通用 WS 传输调整会话标头或后备策略 |
formatApiKey |
身份验证配置文件格式化程序:将已存储配置文件转换为运行时 apiKey 字符串 |
提供商存储额外的身份验证元数据,并需要自定义运行时令牌形式 |
refreshOAuth |
针对自定义刷新端点或刷新失败策略的 OAuth 刷新覆盖 | 提供商不适用于共享的 OpenClaw 刷新程序 |
buildAuthDoctorHint |
OAuth 刷新失败时附加的修复提示 | 提供商需要在刷新失败后提供由其负责的身份验证修复指引 |
matchesContextOverflowError |
提供商拥有的上下文窗口溢出匹配器 | 提供商存在通用启发式规则无法识别的原始溢出错误 |
classifyFailoverReason |
提供商拥有的故障转移原因分类 | 提供商可以将原始 API/传输错误映射为速率限制/过载等 |
isCacheTtlEligible |
代理/回程提供商的提示词缓存策略 | 提供商需要代理特定的缓存 TTL 门控 |
buildMissingAuthMessage |
替代通用的缺少身份验证恢复消息 | 提供商需要提供商特定的缺少身份验证恢复提示 |
augmentModelCatalog |
设备发现后附加的合成/最终目录行(已弃用,见下文) | 提供商需要在 models list 和选择器中添加用于前向兼容的合成行 |
resolveThinkingProfile |
特定模型的 /think 级别集、显示标签和默认值 |
提供商为选定模型公开自定义思考阶梯或二元标签 |
isBinaryThinking |
开启/关闭推理切换兼容性钩子 | 提供商仅公开二元思考开启/关闭 |
supportsXHighThinking |
xhigh 推理支持兼容性钩子 |
提供商只希望在模型子集上启用 xhigh |
resolveDefaultThinkingLevel |
默认 /think 级别兼容性钩子 |
提供商拥有模型系列的默认 /think 策略 |
isModernModelRef |
用于实时配置文件筛选和冒烟测试选择的现代模型匹配器 | 提供商拥有实时/冒烟测试首选模型匹配 |
prepareRuntimeAuth |
在推理前将配置的凭据兑换为实际的运行时令牌/密钥 | 提供商需要令牌兑换或短期请求凭据 |
resolveUsageAuth |
为 /usage 及相关状态界面解析用量/计费凭据 |
提供商需要自定义用量/配额令牌解析或不同的用量凭据 |
fetchUsageSnapshot |
身份验证解析后,获取并规范化提供商特定的用量/配额快照 | 提供商需要提供商特定的用量端点或有效载荷解析器 |
createEmbeddingProvider |
为记忆/搜索构建由提供商拥有的嵌入适配器 | 记忆嵌入行为归提供商插件所有 |
buildReplayPolicy |
返回控制该提供商对话记录处理方式的重放策略 | 提供商需要自定义对话记录策略(例如移除思考块) |
sanitizeReplayHistory |
在通用对话记录清理后重写重放历史 | 提供商需要超出共享压缩辅助工具能力范围的提供商专属重放重写 |
validateReplayTurns |
在嵌入式运行器执行前进行最终重放轮次验证或重塑 | 提供商传输层需要在通用清理后进行更严格的轮次验证 |
onModelSelected |
运行由提供商拥有的选择后副作用 | 模型激活时,提供商需要遥测或由提供商拥有的状态 |
normalizeModelId、normalizeTransport 和 normalizeConfig 会先检查匹配的提供商插件,然后依次尝试其他支持钩子的提供商插件,直到其中一个实际更改模型 ID 或传输方式/配置。这样可以让别名/兼容性提供商 shim 继续工作,而无需调用方知道由哪个内置插件负责重写。如果没有提供商钩子重写受支持的 Google 系列配置条目,内置的 Google 配置规范化器仍会执行该兼容性清理。
如果提供商需要完全自定义的传输协议或自定义请求执行器,则属于另一类扩展。这些钩子适用于仍在 OpenClaw 常规推理循环中运行的提供商行为。
resolveUsageAuth 决定 OpenClaw 应调用 fetchUsageSnapshot,还是在用量/状态界面回退到通用凭据解析。当提供商具有用量凭据时,返回 { token, accountId?, subscriptionType?, rateLimitTier? }(可选的套餐元数据会传入 fetchUsageSnapshot);当提供商自有的用量身份验证已处理请求且必须禁止回退到通用 API key/OAuth 时,返回 { handled: true };当提供商未处理用量身份验证时,返回 null 或 undefined。
在清单的 providerUsageAuthEnvVars 中声明组织或账单凭据。这样,通用设备发现和密钥清理界面便可识别这些凭据,而不会将其作为推理身份验证的候选项。
提供商示例
api.registerProvider({ id: "example-proxy", label: "Example Proxy", auth: [], catalog: { order: "simple", run: async (ctx) => { const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey; if (!apiKey) { return null; } return { provider: { baseUrl: "https://proxy.example.com/v1", apiKey, api: "openai-completions", models: [{ id: "auto", name: "Auto" }], }, }; }, }, resolveDynamicModel: (ctx) => ({ id: ctx.modelId, name: ctx.modelId, provider: "example-proxy", api: "openai-completions", baseUrl: "https://proxy.example.com/v1", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128000, maxTokens: 8192, }), prepareRuntimeAuth: async (ctx) => { const exchanged = await exchangeToken(ctx.apiKey); return { apiKey: exchanged.token, baseUrl: exchanged.baseUrl, expiresAt: exchanged.expiresAt, }; }, resolveUsageAuth: async (ctx) => { const auth = await ctx.resolveOAuthToken(); return auth ? { token: auth.token } : null; }, fetchUsageSnapshot: async (ctx) => { return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn); },});内置示例
内置提供商插件会组合上述钩子,以满足各供应商在目录、身份验证、思考、重放和用量方面的需求。权威钩子集合位于 extensions/ 下的各个插件中;本页仅说明其形式,而不复刻完整列表。
直通目录提供商
OpenRouter、Kilocode、Z.AI、xAI 会注册 catalog 以及
resolveDynamicModel / prepareDynamicModel,以便在 OpenClaw 的静态目录之前公开上游模型 ID。
OAuth 和用量端点提供商
GitHub Copilot、Gemini CLI、ChatGPT Codex、MiniMax、Xiaomi、z.ai 会将
prepareRuntimeAuth 或 formatApiKey 与 resolveUsageAuth +
fetchUsageSnapshot 配合使用,以负责令牌交换和 /usage 集成。
重放和转录清理系列
共享的命名系列(google-gemini、passthrough-gemini、
anthropic-by-model、hybrid-anthropic-openai)允许提供商通过
buildReplayPolicy 选择启用转录策略,而无需每个插件重复实现清理逻辑。
仅目录提供商
byteplus、cloudflare-ai-gateway、huggingface、kimi-coding、nvidia、
qianfan、synthetic、together、venice、vercel-ai-gateway 和
volcengine 仅注册 catalog,并使用共享推理循环。
Anthropic 专用流辅助函数
Beta 标头、/fast / serviceTier 和 context1m 位于
Anthropic 插件的公共 api.ts / contract-api.ts 接口
(wrapAnthropicProviderStream、resolveAnthropicBetas、
resolveAnthropicFastMode、resolveAnthropicServiceTier)中,而非通用 SDK 中。
运行时辅助函数
插件可以通过 api.runtime 访问选定的核心辅助函数。对于 TTS:
const clip = await api.runtime.tts.textToSpeech({ text: "Hello from OpenClaw", cfg: api.config,}); const result = await api.runtime.tts.textToSpeechTelephony({ text: "Hello from OpenClaw", cfg: api.config,}); const voices = await api.runtime.tts.listVoices({ provider: "elevenlabs", cfg: api.config,});说明:
textToSpeech返回用于文件/语音消息界面的常规核心 TTS 输出负载。- 使用核心
tts配置和提供商选择。 - 返回 PCM 音频缓冲区和采样率。插件必须为提供商重新采样/编码。
listVoices对每个提供商均为可选。可将其用于供应商自有的语音选择器或设置流程。- 核心会向提供商的
listVoices钩子传递已解析的请求截止时间;提供商特定的超时设置可覆盖该时间。 - 语音列表可以包含更丰富的元数据,例如区域设置、性别和个性标签,以供提供商感知型选择器使用。
- OpenAI 和 ElevenLabs 目前支持电话音频。Microsoft 不支持。
插件还可以通过 api.registerSpeechProvider(...) 注册语音提供商。
api.registerSpeechProvider({ id: "acme-speech", label: "Acme Speech", isConfigured: ({ config }) => Boolean(config.messages?.tts), synthesize: async (req) => { return { audioBuffer: Buffer.from([]), outputFormat: "mp3", fileExtension: ".mp3", voiceCompatible: false, }; },});说明:
- 将 TTS 策略、回退和回复投递保留在核心中。
- 使用语音提供商实现供应商自有的合成行为。
- 旧版 Microsoft
edge输入会被规范化为microsoft提供商 ID。 - 首选的所有权模型以公司为导向:随着 OpenClaw 添加这些能力契约,一个供应商插件可以负责文本、语音、图像以及未来的媒体提供商。
对于图像/音频/视频理解,插件应注册一个类型化的媒体理解提供商,而不是使用通用键值集合:
api.registerMediaUnderstandingProvider({ id: "google", capabilities: ["image", "audio", "video"], describeImage: async (req) => ({ text: "..." }), transcribeAudio: async (req) => ({ text: "..." }), describeVideo: async (req) => ({ text: "..." }),});说明:
- 将编排、回退、配置和渠道接线保留在核心中。
- 将供应商行为保留在提供商插件中。
- 增量扩展应保持类型化:新增可选方法、新增可选结果字段、新增可选能力。
- 视频生成已遵循相同模式:
- 核心负责能力契约和运行时辅助函数
- 供应商插件注册
api.registerVideoGenerationProvider(...) - 功能/渠道插件使用
api.runtime.videoGeneration.*
对于媒体理解运行时辅助函数,插件可以调用:
const image = await api.runtime.mediaUnderstanding.describeImageFile({ filePath: "/tmp/inbound-photo.jpg", cfg: api.config, agentDir: "/tmp/agent",}); const video = await api.runtime.mediaUnderstanding.describeVideoFile({ filePath: "/tmp/inbound-video.mp4", cfg: api.config,}); const extraction = await api.runtime.mediaUnderstanding.extractStructuredWithModel({ provider: "codex", model: "gpt-5.6-sol", input: [ { type: "image", buffer: receiptImageBuffer, fileName: "receipt.png", mime: "image/png", }, { type: "text", text: "Use the printed fields as the source of truth." }, ], instructions: "Return entities and searchable tags.", schemaName: "example.evidence", jsonSchema: { type: "object", properties: { entities: { type: "array", items: { type: "string" } }, tags: { type: "array", items: { type: "string" } }, }, }, cfg: api.config,});对于音频转录,插件可以使用媒体理解运行时或较早的 STT 别名:
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ filePath: "/tmp/inbound-audio.ogg", cfg: api.config, // Optional when MIME cannot be inferred reliably: mime: "audio/ogg",});说明:
api.runtime.mediaUnderstanding.*是图像/音频/视频理解的首选共享界面。extractStructuredWithModel(...)是面向插件的接口,用于由提供商负责、范围受限且以图像为主的提取。至少包含一个图像输入;文本输入仅作为补充上下文。产品插件负责其路由和 schema,而 OpenClaw 负责提供商/运行时边界。- 使用核心媒体理解音频配置(
tools.media.audio)和提供商回退顺序。 - 未产生转录输出时(例如输入被跳过/不受支持),返回
{ text: undefined }。
插件还可以通过 api.runtime.subagent 启动后台子智能体运行:
const result = await api.runtime.subagent.run({ sessionKey: "agent:main:subagent:search-helper", message: "Expand this query into focused follow-up searches.", toolsAlsoAllow: ["my_plugin_progress"], provider: "openai", model: "gpt-4.1-mini", deliver: false,});说明:
provider和model是可选的单次运行覆盖项,而非持久会话更改。toolsAlsoAllow接受由调用插件注册且归属唯一的精确工具名称。核心工具名称和有歧义的名称会被拒绝。它是在常规配置文件基础上的增量补充,但操作员的允许列表和拒绝规则仍具有最终决定权。- OpenClaw 仅对受信任的调用方采用这些覆盖字段。
- 对于插件自有的回退运行,操作员必须通过
plugins.entries.<id>.subagent.allowModelOverride: true明确选择启用。 - 使用
plugins.entries.<id>.subagent.allowedModels将受信任插件限制到特定的规范provider/model目标,或使用"*"显式允许任意目标。 - 不受信任插件的子智能体运行仍可工作,但覆盖请求会被拒绝,而不是静默回退。
- 插件创建的子智能体会话会标记创建它的插件 ID。回退
api.runtime.subagent.deleteSession(...)只能删除这些归属会话;删除任意会话仍需要具备管理员权限范围的 Gateway 网关请求。
对于 Web 搜索,插件可以使用共享运行时辅助函数,而无需深入访问智能体工具接线:
const providers = api.runtime.webSearch.listProviders({ config: api.config,}); const result = await api.runtime.webSearch.search({ config: api.config, args: { query: "OpenClaw plugin runtime helpers", count: 5, },});插件还可以通过 api.registerWebSearchProvider(...) 注册 Web 搜索提供商。
说明:
- 将提供商选择、凭据解析和共享请求语义保留在核心中。
- 使用 Web 搜索提供商实现供应商特定的搜索传输。
api.runtime.webSearch.*是需要搜索行为但不依赖智能体工具包装器的功能/渠道插件的首选共享界面。
api.runtime.imageGeneration
const result = await api.runtime.imageGeneration.generate({ config: api.config, args: { prompt: "A friendly lobster mascot", size: "1024x1024" },}); const providers = api.runtime.imageGeneration.listProviders({ config: api.config,});generate(...):使用已配置的图像生成提供商链生成图像。listProviders(...):列出可用的图像生成提供商及其能力。
Gateway HTTP 路由
插件可以使用 api.registerHttpRoute(...) 公开 HTTP 端点。
api.registerHttpRoute({ path: "/acme/webhook", auth: "plugin", match: "exact", handler: async (_req, res) => { res.statusCode = 200; res.end("ok"); return true; },});路由字段:
path:Gateway 网关 HTTP 服务器下的路由路径。auth:必填,"gateway"或"plugin"。使用"gateway"要求常规 Gateway 网关身份验证,或使用"plugin"进行插件管理的身份验证/webhook 验证。match:可选。"exact"(默认)或"prefix"。handleUpgrade:可选,用于处理同一路由上的 WebSocket 升级请求。replaceExisting:可选。允许同一插件替换自己现有的路由注册。handler:路由已处理请求时返回true。
注意:
api.registerHttpHandler(...)已被移除,并会导致插件加载错误。请改用api.registerHttpRoute(...)。- 插件路由必须显式声明
auth。 - 除非设置
replaceExisting: true,否则完全相同的path + match冲突会被拒绝,并且一个插件无法替换另一个插件的路由。 - 具有不同
auth级别的重叠路由会被拒绝。exact/prefix回退链只能使用同一身份验证级别。 auth: "plugin"路由不会自动获得操作员运行时权限范围。它们用于插件管理的 webhook/签名验证,而非特权 Gateway 网关辅助调用。auth: "gateway"路由在 Gateway 网关请求运行时权限范围内运行。默认表面(gatewayRuntimeScopeSurface: "write-default")有意采用保守策略:- 共享密钥持有者身份验证(
gateway.auth.mode = "token"/"password")以及任何非可信代理身份验证方式仅获得一个operator.write权限范围,即使调用方发送了x-openclaw-scopes - 未携带显式
x-openclaw-scopes标头的trusted-proxy调用方也会保留旧版仅限operator.write的表面 - 发送
x-openclaw-scopes的trusted-proxy调用方将改为获得声明的权限范围 - 路由可以选择加入
gatewayRuntimeScopeSurface: "trusted-operator",以便对于携带身份的身份验证模式始终遵循x-openclaw-scopes(缺少该标头时回退到完整的 CLI 默认权限范围集)
- 共享密钥持有者身份验证(
- 由
auth: "gateway"路由支持的沙箱隔离外部 Control UI 标签页使用一种短期有效的签名 Cookie 授权,该授权仅由经过身份验证的引导程序签发;插件身份验证标签页则保留其直接 iframe 路径。挂载前,父级会在同一个不透明沙箱中运行路由自有的探测,并在浏览器隐私策略阻止 Cookie 时按关闭策略失败。该授权绑定到所属插件、匹配的路由根路径和当前身份验证代次;其进程随机 Cookie 名称可防止同一主机上受信任的 Gateway 网关相互覆盖,但 Cookie 绝不会隔离 TCP 端口。因此,Gateway 网关主机名构成一个凭据边界:不要在该主机名下共同托管互不信任的服务,包括其他端口上的服务。路由分发会拒绝将授权复用于另一个插件拥有的嵌套路由。由于沙箱后代在 Cookie 语义中属于跨站点,因此该授权仅接受带有operator.read的GET和HEAD;变更操作和 WebSocket 升级仍须使用显式经过 Gateway 网关身份验证的表面。该 Cookie 有意不使用 CHIPS:当前浏览器会在分区键中包含跨站点祖先位,因此嵌套的不透明沙箱框架将无法访问同一路由的资源。该 Cookie 需要安全上下文以及浏览器授予的跨站点 Cookie 权限,因此,在纯 HTTP 局域网源或完全阻止第三方 Cookie 的环境下,使用 Gateway 网关身份验证的外部标签页不可用;请使用 HTTPS/Tailscale Serve,或采用兼容 Cookie 策略且受浏览器信任的回环地址。 - 该授权可防止 Gateway 网关持有者令牌泄露以及意外复用路由/权限范围;但它不会在原生插件之间建立安全边界。原生插件代码及其提供的 UI 内容仍属于同一个受信任的进程内插件边界。
- 实用规则:不要假定使用 Gateway 网关身份验证的插件路由隐式具有管理员表面。如果路由需要仅限管理员的行为,请选择加入
trusted-operator权限范围表面,要求使用携带身份的身份验证模式,并记录明确的x-openclaw-scopes标头约定。 - 完成路由匹配和身份验证后,常规处理程序会参与 Gateway 网关根工作准入。处于准备或重启状态的 Gateway 网关会在调用处理程序前返回
503。唯一的有限例外是清单授予权限的auth: "gateway"路由,且该路由还选择加入路由特定的trusted-operator表面;该路由仍可访问,以免暂停控制分发陷入停滞,而同一插件的常规同级路由仍位于准入边界之后。WebSockethandleUpgrade所有权使用相同的原子准入边界;处理程序接受套接字后,该套接字的后续生命周期由插件负责,不受此边界跟踪。
插件 SDK 导入路径
编写新插件时,请使用粒度较细的 SDK 子路径,而不是单体 openclaw/plugin-sdk 根
聚合导出。核心子路径:
| 子路径 | 用途 |
|---|---|
openclaw/plugin-sdk/plugin-entry |
插件注册原语 |
openclaw/plugin-sdk/channel-core |
渠道入口/构建辅助函数 |
openclaw/plugin-sdk/core |
通用共享辅助函数和总括约定 |
渠道插件可从一系列粒度较细的接口中选择——channel-setup、
setup-runtime、setup-tools、channel-pairing、
channel-contract、channel-feedback、channel-inbound、channel-outbound、
command-auth、secret-input、webhook-ingress、
channel-targets 和 channel-actions。审批行为应统一使用
一个 approvalCapability 约定,而不是混用不相关的
插件字段。参阅渠道插件。
运行时和配置辅助函数位于相应的专用 *-runtime 子路径下
(approval-runtime、agent-runtime、lazy-runtime、directory-runtime、
text-runtime、runtime-store、system-event-runtime、heartbeat-runtime、
channel-activity-runtime 等)。应优先使用 config-contracts、
plugin-config-runtime、runtime-config-snapshot 和 config-mutation,
而不是宽泛的 config-runtime 兼容性聚合导出。
仓库内部入口点(按每个内置插件包根目录划分):
index.js— 内置插件入口api.js— 辅助函数/类型聚合导出runtime-api.js— 仅运行时聚合导出setup-entry.js— 设置插件入口
外部插件只能导入 openclaw/plugin-sdk/* 子路径。切勿从核心或另一个插件中
导入其他插件包的 src/*。
通过外观加载的入口点在存在活动运行时配置快照时优先使用该快照,
否则回退到磁盘上解析出的配置文件。
image-generation、media-understanding
和 speech 等特定能力子路径之所以存在,是因为内置插件目前正在使用它们。它们并非
自动成为长期冻结的外部约定——依赖这些子路径时,请查阅相关 SDK
参考页面。
消息工具架构
对于表情回应、已读状态和投票等非消息原语,插件应自行提供渠道特定的 describeMessageTool(...) 架构
扩展。
共享发送呈现应使用通用 MessagePresentation 约定,
而不是提供商原生的按钮、组件、区块或卡片字段。
有关约定、回退规则、提供商映射和插件作者检查清单,
请参阅消息呈现。
支持发送的插件通过消息能力声明其可呈现的内容:
presentation用于语义呈现区块(text、context、divider、chart、table、buttons、select)delivery-pin用于置顶投递请求
核心决定是以原生方式呈现,还是降级为文本。 不要通过通用消息工具公开提供商原生 UI 的逃生通道。 用于旧版原生架构的已弃用 SDK 辅助函数仍会导出,以供现有 第三方插件使用,但新插件不应使用它们。
渠道目标解析
渠道插件应自行管理渠道特定的目标语义。保持共享 出站主机的通用性,并通过消息适配器表面实现提供商规则:
messaging.inferTargetChatType({ to })决定规范化目标 在目录查找前应被视为direct、group还是channel。messaging.targetResolver.looksLikeId(raw, normalized)告知核心某个 输入是否应跳过目录搜索,直接进行类似 ID 的解析。messaging.targetResolver.reservedLiterals列出对该提供商而言属于 渠道/会话引用的裸词。解析会在拒绝保留字面量之前保留已配置的 目录条目,然后在目录未命中时按关闭策略失败。messaging.targetResolver.resolveTarget(...)是当核心 在规范化后或目录未命中后需要最终由提供商负责解析时使用的插件回退。messaging.resolveOutboundSessionRoute(...)在目标解析完成后负责构建 提供商特定的会话路由。
建议的职责划分:
- 对于应在搜索对等方/群组之前进行的类别判断,使用
inferTargetChatType。 - 对于“将其视为显式/原生目标 ID”的检查,使用
looksLikeId。 - 将
resolveTarget用于提供商特定的规范化回退,而非 宽泛的目录搜索。 - 将聊天 ID、话题 ID、JID、用户名和房间
ID 等提供商原生 ID 保留在
target值或提供商特定参数中,而不要放入通用 SDK 字段。
配置支持的目录
从配置派生目录条目的插件应将该逻辑保留在
插件中,并复用
openclaw/plugin-sdk/directory-runtime 中的共享辅助函数。
当渠道需要以下由配置支持的对等方/群组时,请使用此方式:
- 由允许列表驱动的私信对等方
- 已配置的渠道/群组映射
- 账户范围的静态目录回退
directory-runtime 中的共享辅助函数仅处理通用操作:
- 查询筛选
- 应用数量限制
- 去重/规范化辅助函数
- 构建
ChannelDirectoryEntry[]
渠道特定的账户检查和 ID 规范化应保留在 插件实现中。
提供商目录
提供商插件可以使用 registerProvider({ catalog: { run(...) { ... } } })
定义用于推理的模型目录。
catalog.run(...) 返回的结构与 OpenClaw 写入
models.providers 的结构相同:
{ provider }用于单个提供商条目{ providers }用于多个提供商条目
当插件拥有提供商特定的模型 ID、基础 URL 默认值或需要身份验证才能访问的模型元数据时,请使用 catalog。
catalog.order 控制插件目录相对于 OpenClaw 内置隐式提供商的合并时机:
simple:使用普通 API 密钥或由环境变量驱动的提供商profile:存在身份验证配置文件时显示的提供商paired:合成多个相关提供商条目的提供商late:在其他隐式提供商之后执行的最后一轮
发生键冲突时,后出现的提供商胜出,因此插件可以有意使用相同的提供商 ID 覆盖内置提供商条目。
插件还可以通过 api.registerModelCatalogProvider({ provider, kinds, staticCatalog, liveCatalog }) 发布只读模型行。这是列表、帮助和选择器界面的后续演进路径,并支持 text、voice、image_generation、video_generation 和 music_generation 行。提供商插件仍负责实时端点调用、令牌交换和供应商响应映射;核心负责通用行结构、来源标签和媒体工具帮助的格式化。媒体生成提供商注册会根据 defaultModel、models 和 capabilities 自动合成静态目录行。
兼容性:
discovery仍可作为旧版别名使用,但会发出弃用警告- 如果同时注册了
catalog和discovery,OpenClaw 会使用catalog并发出警告 augmentModelCatalog已弃用;内置提供商应通过registerModelCatalogProvider发布补充行
只读渠道检查
如果你的插件注册了渠道,建议在实现 resolveAccount(...) 的同时实现 plugin.config.inspectAccount(cfg, accountId)。
原因:
resolveAccount(...)是运行时路径。它可以假定凭据已完全具现化,并可在缺少必需机密时快速失败。- 只读命令路径(例如
openclaw status、openclaw status --all、openclaw channels status、openclaw channels resolve)以及 Doctor/配置 修复流程不应仅为描述配置而需要具现化运行时凭据。
建议的 inspectAccount(...) 行为:
- 仅返回描述性的账户状态。
- 保留
enabled和configured。 - 在相关时包含凭据来源/状态字段,例如:
tokenSource、tokenStatusbotTokenSource、botTokenStatusappTokenSource、appTokenStatussigningSecretSource、signingSecretStatus
- 仅为报告只读可用性,无需返回原始令牌值。对于状态类命令,返回
tokenStatus: "available"(以及匹配的来源字段)即可。 - 当凭据通过 SecretRef 配置但在当前命令路径中不可用时,请使用
configured_unavailable。
这样,只读命令便可报告“已配置,但在此命令路径中不可用”,而不会崩溃或错误地将账户报告为未配置。
软件包包组
插件目录可以包含带有 openclaw.extensions 的 package.json:
{ "name": "my-pack", "openclaw": { "extensions": ["./src/safety.ts", "./src/tools.ts"], "setupEntry": "./src/setup-entry.ts" }}每个条目都会成为一个插件。如果包组列出了多个扩展,插件 ID 将变为 <manifestOrPackageName>/<fileBase>(存在清单 ID 时以其为准;否则使用无作用域的 package.json 名称)。
如果插件导入 npm 依赖项,请将它们安装在该目录中,以确保 node_modules 可用(npm install / pnpm install)。
安全防护:每个 openclaw.extensions 条目在解析符号链接后都必须位于插件目录内。会逸出软件包目录的条目将被拒绝。
安全说明:openclaw plugins install 使用项目本地的 npm install --omit=dev --ignore-scripts 安装插件依赖项(不运行生命周期脚本,运行时不安装开发依赖项),并忽略继承的全局 npm 安装设置。请保持插件依赖树为“纯 JS/TS”,并避免使用需要 postinstall 构建的软件包。
可选:openclaw.setupEntry 可以指向轻量的仅设置模块。当 OpenClaw 需要已禁用渠道插件的设置界面,或者渠道插件已启用但尚未配置时,它会加载 setupEntry,而不是完整的插件入口。这样,当主插件入口还连接了工具、钩子或其他仅运行时代码时,可以减轻启动和设置负担。
可选:openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen
可以让渠道插件在 Gateway 网关监听前的启动阶段进入相同的 setupEntry 路径,即使该渠道已配置也是如此。
仅当 setupEntry 完全覆盖 Gateway 网关开始监听前必须存在的启动界面时,才使用此选项。实际上,这意味着设置入口必须注册启动所依赖的每项渠道自有能力,例如:
- 渠道注册本身
- Gateway 网关开始监听前必须可用的所有 HTTP 路由
- 同一时间窗口内必须存在的所有 Gateway 网关方法、工具或服务
如果完整入口仍负责任何必需的启动能力,请勿启用此标志。让插件保持默认行为,并由 OpenClaw 在启动期间加载完整入口。
内置渠道还可以发布仅设置的契约界面辅助程序,供核心在加载完整渠道运行时之前查询。当前的设置提升界面为:
singleAccountKeysToMovenamedAccountPromotionKeysresolveSingleAccountPromotionTarget(...)
当核心需要在不加载完整插件入口的情况下,将旧版单账户渠道配置提升到 channels.<id>.accounts.* 时,会使用该界面。Matrix 是当前的内置示例:当命名账户已存在时,它仅将身份验证/引导键移入一个命名的已提升账户;它还可以保留已配置的非规范默认账户键,而不是始终创建 accounts.default。
这些设置补丁适配器使内置契约界面保持惰性发现。导入时仍保持轻量;仅在首次使用时加载提升界面,而不会在模块导入时重新进入内置渠道启动流程。
当这些启动界面包含 Gateway RPC 方法时,请将它们置于插件专用前缀下。核心管理命名空间(config.*、exec.approvals.*、wizard.*、update.*)仍为保留命名空间,并且始终解析为 operator.admin,即使插件请求了更窄的权限范围也是如此。
示例:
{ "name": "@scope/my-channel", "openclaw": { "extensions": ["./index.ts"], "setupEntry": "./setup-entry.ts", "startup": { "deferConfiguredChannelFullLoadUntilAfterListen": true } }}渠道目录元数据
渠道插件可以通过 openclaw.channel 公布设置/发现元数据,并通过 openclaw.install 公布安装提示。这样可以使核心目录不包含具体数据。
示例:
{ "name": "@openclaw/nextcloud-talk", "openclaw": { "extensions": ["./index.ts"], "channel": { "id": "nextcloud-talk", "label": "Nextcloud Talk", "selectionLabel": "Nextcloud Talk(自托管)", "docsPath": "/channels/nextcloud-talk", "docsLabel": "nextcloud-talk", "blurb": "通过 Nextcloud Talk webhook Bot 提供自托管聊天。", "order": 65, "aliases": ["nc-talk", "nc"] }, "install": { "npmSpec": "@openclaw/nextcloud-talk", "localPath": "<bundled-plugin-local-path>", "defaultChoice": "npm" } }}除最小示例外,其他有用的 openclaw.channel 字段:
detailLabel:用于内容更丰富的目录/状态界面的辅助标签docsLabel:覆盖文档链接的链接文本preferOver:此目录条目应优先于的低优先级插件/渠道 IDselectionDocsPrefix、selectionDocsOmitLabel、selectionExtras:选择界面的文案控制项markdownCapable:将渠道标记为支持 Markdown,用于出站格式决策exposure.configured:设为false时,在已配置渠道列表界面中隐藏该渠道exposure.setup:设为false时,在交互式设置/配置选择器中隐藏该渠道exposure.docs:将渠道标记为供文档导航界面使用的内部/私有渠道quickstartAllowFrom:使渠道加入标准快速开始allowFrom流程forceAccountBinding:即使仅存在一个账户,也要求显式绑定账户preferSessionLookupForAnnounceTarget:解析公告目标时优先查找会话
OpenClaw 还可以合并外部渠道目录(例如 MPM 注册表导出)。将 JSON 文件放在以下任一位置:
~/.openclaw/mpm/plugins.json~/.openclaw/mpm/catalog.json~/.openclaw/plugins/catalog.json
或者将 OPENCLAW_PLUGIN_CATALOG_PATHS(或 OPENCLAW_MPM_CATALOG_PATHS)指向一个或多个 JSON 文件(以逗号、分号或 PATH 分隔)。每个文件应包含 { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }。解析器还接受 "packages" 或 "plugins" 作为 "entries" 键的旧版别名。
生成的渠道目录条目和提供商安装目录条目会在原始 openclaw.install 块旁公开规范化安装来源事实。规范化事实会标识 npm 规范是精确版本还是浮动选择器、是否存在预期的完整性元数据,以及本地来源路径是否也可用。当目录/软件包身份已知时,如果解析后的 npm 软件包名称偏离该身份,规范化事实会发出警告。当 defaultChoice 无效、指向不可用的来源,或存在 npm 完整性元数据却没有有效的 npm 来源时,它们也会发出警告。使用方应将 installSource 视为可选的附加字段,这样手工构建的条目和目录适配层就不必合成它。
这使新手引导和诊断功能无需导入插件运行时,即可说明来源平面的状态。
官方外部 npm 条目应优先使用精确的 npmSpec 和 expectedIntegrity。为保持兼容性,裸软件包名称和 dist-tag 仍然有效,但它们会显示来源平面警告,以便目录逐步转向固定版本且经过完整性检查的安装,同时不破坏现有插件。当新手引导从本地目录路径安装时,它会记录一个托管插件索引条目,其中包含 source: "path",并在可能时包含相对于工作区的 sourcePath。绝对操作加载路径仍保留在 plugins.load.paths 中;安装记录不会将本地工作站路径重复写入长期配置。这样可以让本地开发安装对来源平面诊断可见,同时不会增加另一个公开原始文件系统路径的界面。持久化的 installed_plugin_index SQLite 表是安装信息的唯一事实来源,并且可以在不加载插件运行时模块的情况下刷新。即使插件清单缺失或无效,其 installRecords 映射仍会持久保留;其 plugins 载荷则是可重建的清单视图。
上下文引擎插件
上下文引擎插件负责摄取、组装和压缩过程中的会话上下文编排。使用 api.registerContextEngine(id, factory) 从插件中注册它们,然后使用 plugins.slots.contextEngine 选择活动引擎。
当插件需要替换或扩展默认上下文管线,而不只是添加记忆搜索或钩子时,请使用此功能。
export default function (api) { api.registerContextEngine("lossless-claw", (ctx) => ({ info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true }, async ingest() { return { ingested: true }; }, async assemble({ messages, sessionKey, availableTools, citationsMode }) { return { messages, estimatedTokens: 0, systemPromptAddition: buildMemorySystemPromptAddition({ availableTools: availableTools ?? new Set(), citationsMode, agentSessionKey: sessionKey, }), }; }, async compact() { return { ok: true, compacted: false }; }, }));}工厂 ctx 公开了可选的 config、agentDir 和 workspaceDir
值,用于构造时初始化。
在调用非旧版引擎的 assemble() 之前,宿主会完成已注册的异步记忆提示词准备。buildMemorySystemPromptAddition(...) 保持
同步,并在 assemble() 处于活动状态时读取该不可变的运行快照。
请将提供的工具和引用上下文原样传递,确保快照不会跨越运行边界。
当活动 harness 具有持久化后端线程时,assemble() 可以返回 contextProjection。
对于旧版的逐轮投影,请省略此项。当组装后的上下文应当
一次性注入后端线程,并复用至 epoch 发生变化时,返回
{ mode: "thread_bootstrap", epoch }。在引擎的语义上下文发生变化后更改
epoch,例如在引擎负责的压缩流程之后。宿主可以在线程引导投影中保留工具调用元数据、输入
结构和已脱敏的工具结果,使新建的后端线程能够保持工具连续性,而无需复制包含原始机密信息的
载荷。
如果你的引擎不负责压缩算法,请保留 compact()
的实现,并显式委托该操作:
buildMemorySystemPromptAddition, delegateCompactionToRuntime,} from "openclaw/plugin-sdk/core"; export default function (api) { api.registerContextEngine("my-memory-engine", (ctx) => ({ info: { id: "my-memory-engine", name: "My Memory Engine", ownsCompaction: false, }, async ingest() { return { ingested: true }; }, async assemble({ messages, sessionKey, availableTools, citationsMode }) { return { messages, estimatedTokens: 0, systemPromptAddition: buildMemorySystemPromptAddition({ availableTools: availableTools ?? new Set(), citationsMode, agentSessionKey: sessionKey, }), }; }, async compact(params) { return await delegateCompactionToRuntime(params); }, }));}添加新能力
当插件需要的行为不适合当前 API 时,不要通过私有的内部访问绕过 插件系统。应添加缺少的能力。
推荐顺序:
- 定义核心契约。 确定核心应负责哪些共享行为: 策略、回退、配置合并、生命周期、面向渠道的语义以及 运行时辅助程序的结构。
- 添加类型化的插件注册和运行时接口。 使用最小且实用的类型化
能力接口扩展
OpenClawPluginApi和/或api.runtime。 - 连接核心与渠道/功能使用方。 渠道和功能插件 应通过核心使用新能力,而不是直接导入某个供应商的 实现。
- 注册供应商实现。 然后由供应商插件针对该能力注册其 后端。
- 添加契约覆盖。 添加测试,使所有权和注册结构 随时间推移仍保持明确。
OpenClaw 正是通过这种方式保持明确的设计取向,同时避免硬编码为某个 提供商的世界观。有关具体的文件检查清单和完整示例,请参阅能力扩展手册。
能力检查清单
添加新能力时,实现通常应同时涉及以下 接口:
src/<capability>/types.ts中的核心契约类型src/<capability>/runtime.ts中的核心运行器/运行时辅助程序src/plugins/types.ts中的插件 API 注册接口src/plugins/registry.ts中的插件注册表连接- 当功能/渠道插件需要使用该能力时,在
src/plugins/runtime/*中 公开插件运行时 src/test-utils/plugin-registration.ts中的捕获/测试辅助程序src/plugins/contracts/registry.ts中的所有权/契约断言docs/中的操作员/插件文档
如果缺少其中某个接口,通常表明该能力 尚未完全集成。
能力模板
最小模式:
// core contractexport type VideoGenerationProviderPlugin = { id: string; label: string; generateVideo: (req: VideoGenerationRequest) => Promise<VideoGenerationResult>;}; // plugin APIapi.registerVideoGenerationProvider({ id: "openai", label: "OpenAI", async generateVideo(req) { return await generateOpenAiVideo(req); },}); // shared runtime helper for feature/channel pluginsconst clip = await api.runtime.videoGeneration.generate({ prompt: "Show the robot walking through the lab.", cfg,});契约测试模式(src/plugins/contracts/registry.ts 公开了所有权
查询,例如 providerContractPluginIds;测试断言插件的
contracts.videoGenerationProviders 列表与其实际注册内容一致):
expect(pluginManifest.contracts?.videoGenerationProviders).toEqual(["openai"]);这样可以使规则保持简单:
- 核心负责能力契约和编排
- 供应商插件负责供应商实现
- 功能/渠道插件使用运行时辅助程序
- 契约测试确保所有权保持明确
相关内容
- 插件架构 — 公共能力模型和结构
- 插件 SDK 子路径
- 插件 SDK 设置
- 构建插件