title: OpenClaw Plugin SDK 完整指引 date: 2026-03-24 source:
- https://docs.openclaw.ai/plugins/sdk-overview
- https://docs.openclaw.ai/plugins/building-plugins
- https://docs.openclaw.ai/plugins/sdk-channel-plugins
- https://docs.openclaw.ai/plugins/sdk-provider-plugins
- https://docs.openclaw.ai/plugins/sdk-runtime
- https://docs.openclaw.ai/plugins/sdk-entrypoints
- https://docs.openclaw.ai/plugins/sdk-setup
- https://docs.openclaw.ai/plugins/sdk-testing
- https://docs.openclaw.ai/plugins/sdk-migration
- https://docs.openclaw.ai/plugins/architecture
OpenClaw Plugin SDK 完整指引#
本文基于 OpenClaw 官方 Plugin SDK 系列文档递归整理,覆盖 SDK 总览、插件构建、Channel 插件、Provider 插件、Runtime 辅助、入口定义、配置与打包、测试、迁移、架构内幕共 10 篇文档。目标是把分散在各页面中的关键信息整合成一篇可以直接参照开发的中文指引。
1. SDK 总览:导入约定与注册 API#
Plugin SDK 是插件与 OpenClaw 核心之间的类型契约。它决定了「你能 import 什么」和「你能注册什么」。
1.1 导入规范#
永远从具体子路径导入,不要用已废弃的根路径:
// 正确
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
// 已废弃,下一个大版本会移除
import { definePluginEntry } from "openclaw/plugin-sdk";每个子路径都是一个小型独立模块,启动更快、不会产生循环依赖。
1.2 常用子路径速查#
SDK 有 100+ 子路径,按用途分为几大类:
插件入口
| 子路径 | 关键导出 |
|---|---|
plugin-sdk/plugin-entry | definePluginEntry |
plugin-sdk/core | defineChannelPluginEntry, createChatChannelPlugin, createChannelPluginBase, defineSetupPluginEntry, buildChannelConfigSchema |
Channel 相关
| 子路径 | 用途 |
|---|---|
plugin-sdk/channel-setup | 可选的 Channel Setup 界面 |
plugin-sdk/channel-pairing | 配对控制器 |
plugin-sdk/channel-reply-pipeline | 回复管线 |
plugin-sdk/channel-config-helpers | 混合配置适配器 |
plugin-sdk/channel-config-schema | Channel 配置 schema 类型 |
plugin-sdk/channel-policy | 群组 @mention 策略 |
plugin-sdk/channel-lifecycle | 账号状态 sink |
plugin-sdk/channel-inbound | 防抖、@mention 匹配、信封辅助 |
plugin-sdk/channel-send-result | 发送结果类型 |
plugin-sdk/channel-actions | 按钮 / 卡片 schema (createMessageToolButtonsSchema, createMessageToolCardSchema) |
plugin-sdk/channel-targets | 目标解析 / 匹配 |
plugin-sdk/channel-contract | Channel 契约类型 |
plugin-sdk/channel-feedback | 反馈 / reaction |
Provider 相关
| 子路径 | 用途 |
|---|---|
plugin-sdk/provider-auth | API Key 认证方法、upsertAuthProfile |
plugin-sdk/provider-models | 模型兼容性规范化 (normalizeModelCompat) |
plugin-sdk/provider-catalog | 模型目录类型 |
plugin-sdk/provider-usage | 用量查询 (fetchClaudeUsage 等) |
plugin-sdk/provider-stream | 流式包装类型 |
plugin-sdk/provider-onboard | 初始化配置补丁 |
Auth / Security
| 子路径 | 用途 |
|---|---|
plugin-sdk/command-auth | 控制命令鉴权 (resolveControlCommandGate) |
plugin-sdk/allow-from | allowFrom 格式化 (formatAllowFromLowercase) |
plugin-sdk/secret-input | 秘密输入解析 |
plugin-sdk/webhook-ingress | Webhook 请求 / 目标辅助 |
Runtime / Storage
| 子路径 | 用途 |
|---|---|
plugin-sdk/runtime-store | createPluginRuntimeStore |
plugin-sdk/config-runtime | 配置读写 |
plugin-sdk/agent-runtime | Agent 目录 / 身份 / 工作区 |
plugin-sdk/infra-runtime | 系统事件 / 心跳 |
plugin-sdk/directory-runtime | 配置驱动的目录查询 / 去重 |
plugin-sdk/keyed-async-queue | 带 key 异步队列 |
能力 / 测试
| 子路径 | 用途 |
|---|---|
plugin-sdk/image-generation | 图像生成 provider 类型 |
plugin-sdk/media-understanding | 媒体理解 provider 类型 |
plugin-sdk/speech | 语音 provider 类型 |
plugin-sdk/testing | 测试工具(错误用例、ack reaction、移除 ack reaction) |
其他域子路径
| 子路径 | 用途 |
|---|---|
plugin-sdk/allowlist-resolution | allowlist 输入映射 (mapAllowlistResolutionInputs) |
plugin-sdk/reply-payload | 消息回复类型 |
plugin-sdk/lazy-runtime | 延迟运行时加载 |
plugin-sdk/reply-history | 回复历史 |
plugin-sdk/routing | 路由辅助 |
plugin-sdk/status-helpers | 状态辅助 |
1.3 注册 API 一览#
register(api) 回调接收 OpenClawPluginApi 对象,提供以下注册方法:
能力注册
| 方法 | 注册内容 |
|---|---|
api.registerProvider(...) | 文本推理 (LLM) |
api.registerChannel(...) | 消息渠道 |
api.registerSpeechProvider(...) | TTS / STT |
api.registerMediaUnderstandingProvider(...) | 图片 / 音频 / 视频分析 |
api.registerImageGenerationProvider(...) | 图像生成 |
api.registerWebSearchProvider(...) | 网页搜索 |
工具与命令
| 方法 | 注册内容 |
|---|---|
api.registerTool(tool, opts?) | Agent 工具(默认必选,可 { optional: true }) |
api.registerCommand(def) | 自定义命令(不经 LLM) |
基础设施
| 方法 | 注册内容 |
|---|---|
api.registerHook(events, handler, opts?) | 事件 hook |
api.registerHttpRoute(params) | Gateway HTTP 路由 |
api.registerGatewayMethod(name, handler) | Gateway RPC 方法 |
api.registerCli(registrar, opts?) | CLI 子命令 |
api.registerService(service) | 后台服务 |
api.registerInteractiveHandler(registration) | 交互式处理器 |
独占插槽
| 方法 | 注册内容 |
|---|---|
api.registerContextEngine(id, factory) | 上下文引擎(同一时刻仅一个) |
api.registerMemoryPromptSection(builder) | 记忆 prompt 段构建器 |
生命周期
| 方法 | 作用 |
|---|---|
api.on(hookName, handler, opts?) | 强类型生命周期 hook |
api.onConversationBindingResolved(handler) | 会话绑定回调 |
1.4 api 对象上的常用字段#
| 字段 | 类型 | 说明 |
|---|---|---|
api.id | string | 插件 id |
api.name | string | 显示名 |
api.version | string? | 插件版本(可选) |
api.description | string? | 插件描述(可选) |
api.source | string | 插件来源路径 |
api.rootDir | string? | 根目录(可选) |
api.config | OpenClawConfig | 当前配置快照 |
api.pluginConfig | Record<string, unknown> | 插件私有配置 |
api.runtime | PluginRuntime | Runtime 辅助命名空间 |
api.logger | PluginLogger | 日志(debug/info/warn/error) |
api.registrationMode | PluginRegistrationMode | "full" / "setup-only" / "setup-runtime" |
api.resolvePath(input) | (string) => string | 相对于插件根目录解析路径 |
1.5 内部模块约定#
my-plugin/
api.ts # 公开导出给外部消费者
runtime-api.ts # 仅内部用的运行时导出
index.ts # 插件入口
setup-entry.ts # 轻量 setup-only 入口(可选)注意:生产代码中永远不要通过 openclaw/plugin-sdk/<your-plugin> 引用自己的插件,内部 import 走 ./api.ts 或 ./runtime-api.ts。SDK 路径仅代表外部契约。
1.6 SDK Import 路径兼容性#
- 避免在新代码中使用根
openclaw/plugin-sdkbarrel - 优先使用窄范围的稳定原语
plugin-sdk/channel-runtime仅作为兼容垫片保留,新代码应 import 更细粒度的原语- Bundled 扩展特定的 helper barrel 默认不稳定,如果 helper 仅被一个 bundled 扩展使用,应保持在该扩展自己的
api.js/runtime-api.js后面 - 能力特定子路径(
image-generation、media-understanding、speech)的存在不代表每个导出的 helper 都是长期冻结的外部契约
2. 从零构建插件#
2.1 前置要求#
- Node 22+ (npm 或 pnpm)
- TypeScript / ESM
- 如果是仓库内插件:clone 仓库并安装依赖
2.2 插件类型#
| 类型 | 说明 |
|---|---|
| Channel 插件 | 连接消息平台(Teams、Matrix 等) |
| Provider 插件 | 接入 LLM 或模型端点 |
| Tool / Hook 插件 | 注册 Agent 工具和服务 |
2.3 快速起步:Tool 插件#
Step 1:包配置
package.json:
{
"name": "@myorg/openclaw-my-plugin",
"version": "1.0.0",
"type": "module",
"openclaw": {
"extensions": ["./index.ts"]
}
}openclaw.plugin.json:
{
"id": "my-plugin",
"name": "My Plugin",
"description": "Adds a custom tool to OpenClaw",
"configSchema": {
"type": "object",
"additionalProperties": false
}
}Step 2:入口文件
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { Type } from "@sinclair/typebox";
export default definePluginEntry({
id: "my-plugin",
name: "My Plugin",
description: "Adds a custom tool to OpenClaw",
register(api) {
api.registerTool({
name: "my_tool",
description: "Do a thing",
parameters: Type.Object({ input: Type.String() }),
async execute(_id, params) {
return { content: [{ type: "text", text: `Got: ${params.input}` }] };
},
});
},
});Step 3:发布
- 外部插件发布到 ClawHub 或 npm,然后
openclaw plugins install <package-name> - 安装源可显式指定:
clawhub:或npm:前缀 - 仓库内插件放在
extensions/目录下自动发现 - 安装命令使用
npm install --ignore-scripts(无生命周期脚本),需要纯 JS/TS 依赖树
2.4 能力注册速查#
| 功能 | 注册方法 | 参考 |
|---|---|---|
| 文本推理 | api.registerProvider() | Provider 插件 |
| 消息渠道 | api.registerChannel() | Channel 插件 |
| 语音 TTS/STT | api.registerSpeechProvider() | Provider 章节 |
| 媒体理解 | api.registerMediaUnderstandingProvider() | Provider 章节 |
| 图像生成 | api.registerImageGenerationProvider() | Provider 章节 |
| 网页搜索 | api.registerWebSearchProvider() | Provider 章节 |
| Agent 工具 | api.registerTool() | 本节 |
| 自定义命令 | api.registerCommand() | Entry Points |
| 事件 hook | api.registerHook() | Entry Points |
| HTTP 路由 | api.registerHttpRoute() | Architecture |
| CLI 子命令 | api.registerCli() | Entry Points |
2.5 Agent 工具:必选 vs 可选#
// 必选工具 — 默认对 agent 可见
api.registerTool({
name: "my_tool",
description: "Do a thing",
parameters: Type.Object({ input: Type.String() }),
async execute(_id, params) {
return { content: [{ type: "text", text: params.input }] };
},
});
// 可选工具 — 需用户在配置中显式开启
api.registerTool(
{
name: "workflow_tool",
description: "Run a workflow",
parameters: Type.Object({ pipeline: Type.String() }),
async execute(_id, params) {
return { content: [{ type: "text", text: params.pipeline }] };
},
},
{ optional: true },
);可选工具需要在配置中声明才能启用:
{
tools: { allow: ["workflow_tool"] },
}工具指南:
- 名称不得与核心工具冲突(冲突会被跳过)
- 对有副作用或需要额外二进制的工具使用
optional: true - 用户可以通过将插件 ID 加入
tools.allow来启用该插件所有工具
2.6 提交前检查清单#
package.json有正确的openclaw元数据openclaw.plugin.json存在且通过校验- 使用了正确的入口函数
- 使用了细粒度 SDK import 路径
- 内部 import 走本地 barrel 文件
- 测试通过
- 类型检查通过
3. Channel 插件#
Channel 插件用于将 OpenClaw 接入消息平台。核心设计:Channel 插件不需要自己的 send/edit/react 工具,OpenClaw 核心维护一个共享的 message tool,插件只负责以下部分:
- 账号解析与检查
- DM 安全策略与白名单
- 文本配对流程(验证码)
- 回复线程模式
- 出站消息发送(文本与媒体)
3.1 包结构#
Channel 插件需要以下文件:
package.json— 声明openclaw.channel字段(id、label、blurb),可选openclaw.setupEntry、openclaw.startupopenclaw.plugin.json— 指定kind: "channel",定义channels数组和配置 schemaindex.ts— 使用defineChannelPluginEntry导出setup-entry.ts— 使用defineSetupPluginEntry的轻量入口src/channel.ts— 使用createChatChannelPlugin构建ChannelPlugin
3.2 构建 Channel Plugin#
createChatChannelPlugin 接受声明式选项:
resolveAccount— 从 OpenClaw 配置提取账号信息inspectAccount(可选) — 只读状态报告,不暴露密钥- 安全配置 — channel key、策略解析器、allowlist 解析器、默认策略
- 配对选项 — 身份标签、验证消息文本、通知函数
- 线程模式 —
"reply"等 - 出站配置 —
sendText、sendMedia
3.3 Message Tool 架构#
Channel 插件与核心之间的边界:
- Core:共享工具宿主、prompt wiring、session/thread 簿记、dispatch
- Channel 插件:作用域内的 action 发现、能力发现、schema 贡献
- 发现接口:
ChannelMessageActionAdapter.describeMessageTool()返回可见 action、能力和 schema 片段
运行时传递给发现的 scope 包括 accountId、currentChannelId、currentThreadTs、currentMessageId、sessionKey、sessionId、agentId、requesterSenderId(受信任的请求者身份),使插件可以基于活跃账号、当前房间/线程/消息或受信任的请求者做上下文感知的行为。
Channel 特定的执行辅助应保持在各自的 extension 模块内部。Core 不再拥有 Discord、Slack、Telegram 或 WhatsApp 的 message-action runtime。
3.4 Message Tool Schema#
插件应拥有 channel 特定的 describeMessageTool(...) schema 贡献。共享的便携 schema 片段通过 openclaw/plugin-sdk/channel-actions 复用:
createMessageToolButtonsSchema()— 按钮网格样式createMessageToolCardSchema()— 结构化卡片
如果一个 schema 形状只对一个 provider 有意义,在该插件自己的源码中定义,不要提升到共享 SDK。
3.5 Channel Target 解析#
Channel 插件应拥有 channel 特定的目标语义:
messaging.inferTargetChatType({ to })— 判断目标应视为 direct、group 还是 channelmessaging.targetResolver.looksLikeId(raw, normalized)— 判断输入是否应直接跳到 ID 解析messaging.targetResolver.resolveTarget(...)— 插件兜底,用于 provider 特定的规范化messaging.resolveOutboundSessionRoute(...)— provider 特定的 session 路由构建
建议拆分:
- 用
inferTargetChatType做分类决策 - 用
looksLikeId做"当作显式/原生目标 ID"检查 - 用
resolveTarget做 provider 特定的规范化兜底,而非宽泛的目录搜索 - 保持 provider 原生 ID(chat ID、thread ID、JID、handle、room ID)在目标值或 provider 特定参数内
3.6 只读 Channel 检查#
如果你的插件注册了 channel,建议在 resolveAccount(...) 旁边实现 inspectAccount(cfg, accountId):
resolveAccount是运行时路径,可以假设凭证已完全物化inspectAccount用于只读命令路径(openclaw status、openclaw channels status、openclaw doctor),不需要物化运行时凭证
推荐的 inspectAccount 行为:
- 仅返回描述性账号状态
- 保留
enabled和configured字段 - 包含凭证来源/状态字段(如
tokenSource/tokenStatus) - 凭证不可用时使用
configured_unavailable状态
3.7 Config-Backed 目录#
从配置派生目录条目的插件应在插件内部保持该逻辑,并复用 openclaw/plugin-sdk/directory-runtime 的共享辅助:
- 泛型操作:查询过滤、limit 应用、去重/规范化、构建
ChannelDirectoryEntry[] - Channel 特定的账号检查和 ID 规范化应留在插件实现内
3.8 入站消息处理#
插件通过注册的 HTTP 路由接收平台 webhook,解析 webhook 载荷并通过 inbound handler 分发。Channel 插件拥有自己的完整入站管线。
3.9 Poll 执行#
Poll 执行有两条路径:
outbound.sendPoll— 适用于通用 poll 模型actions.handleAction("poll")— 用于 channel 特定的 poll 语义
Core 在插件 poll dispatch 拒绝后才运行共享 poll 解析,允许插件处理 channel 特定的 poll 字段。
4. Provider 插件#
Provider 插件用于将模型提供商(LLM)接入 OpenClaw。
4.1 包和 Manifest 配置#
package.json 需要:
openclaw.extensions— 入口文件openclaw.providers— provider ID 列表
openclaw.plugin.json 需要:
providers数组providerAuthEnvVars— 映射 provider ID 到所需环境变量(无需加载插件 runtime 即可检测凭证)providerAuthChoices— 定义认证方法的数组,包含provider、method、choiceId、choiceLabel、groupId、groupLabel、cliFlag、cliOption、cliDescription
4.2 最小结构#
一个最小 provider 需要四要素:标识符、显示名、认证方法、模型目录。
完整模式 — 适合多 provider 场景:
export default definePluginEntry({
id: "my-provider",
name: "My Provider",
register(api) {
api.registerProvider({
id: "my-provider",
label: "My Provider",
docsPath: "/providers/my-provider",
envVars: ["MY_API_KEY"],
auth: [createProviderApiKeyAuthMethod({
providerId: "my-provider",
methodId: "api-key",
label: "API Key",
hint: "...",
optionKey: "...",
flagName: "...",
envVar: "MY_API_KEY",
promptMessage: "...",
defaultModel: "...",
})],
catalog: {
order: "simple",
run: async (ctx) => ({
provider: {
baseUrl: "https://api.example.com/v1",
apiKey: ctx.apiKey,
api: "openai-completions",
models: [
{
id: "model-1",
name: "Model 1",
reasoning: false,
input: ["text", "image"],
cost: { input: 0.001, output: 0.002, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 4096,
},
],
},
}),
},
});
},
});简化模式 — 适合单文本 provider + API Key 认证:
export default defineSingleProviderPluginEntry({
id: "my-provider",
// 接受 provider 对象,包含 label、docsPath、auth、catalog(使用 buildProvider 代替 run)
});4.3 动态模型解析#
如果 provider 接受任意模型 ID(代理、路由器),实现 resolveDynamicModel。需要网络调用时,用 prepareDynamicModel 在解析前做异步初始化。
4.4 Provider Runtime Hooks(完整 22 Hook 顺序表)#
Provider 有两层扩展面:
Manifest 元数据(无需加载运行时):
providerAuthEnvVars— 环境认证快速查找providerAuthChoices— 入门/认证选择 CLI 标签和标志
运行时 Hook(按执行顺序):
| # | Hook | 作用 | 使用时机 |
|---|---|---|---|
| 1 | catalog | 模型生成时发布 provider 配置到 models.providers | provider 拥有特定模型 ID、base URL 默认值或认证门控的模型元数据 |
| 2 | (内置模型查找) | — | — |
| 3 | resolveDynamicModel | 本地注册表未找到的 provider-owned 模型 ID 的同步降级 | provider 接受任意上游 ID |
| 4 | prepareDynamicModel | resolveDynamicModel 再次运行前的异步预热 | 需要网络获取元数据 |
| 5 | normalizeResolvedModel | runner 使用前的最终重写 | 传输层重写 |
| 6 | capabilities | provider-owned 的 transcript / tooling 元数据 | 自定义模型能力 |
| 7 | prepareExtraParams | 通用 stream option wrapper 之前的请求参数规范化 | 默认请求参数 |
| 8 | wrapStreamFn | 通用 wrapper 之后的流包装 | 自定义 header / body |
| 9 | formatApiKey | 存储 profile 格式化 | 自定义 runtime token 形状 |
| 10 | refreshOAuth | 自定义 OAuth 刷新覆盖 | OAuth 流程 |
| 11 | buildAuthDoctorHint | OAuth 刷新失败时的修复提示 | 认证修复引导 |
| 12 | isCacheTtlEligible | prompt cache TTL 策略 | 控制缓存资格 |
| 13 | buildMissingAuthMessage | 替代通用缺失认证恢复消息 | 自定义缺失认证提示 |
| 14 | suppressBuiltInModel | 过时上游模型抑制 | 隐藏不再可用的模型 |
| 15 | augmentModelCatalog | 合成 / 最终目录行 | 前向兼容行 |
| 16 | isBinaryThinking | 开/关推理切换 | 二进制思维标记 |
| 17 | supportsXHighThinking | xhigh 推理支持 | 高级推理 |
| 18 | resolveDefaultThinkingLevel | 默认 /think 级别 | 默认推理策略 |
| 19 | isModernModelRef | 现代模型匹配器 | live/smoke 模型匹配 |
| 20 | prepareRuntimeAuth | 将配置凭证交换为实际运行时 token | token 交换 |
| 21 | resolveUsageAuth | 解析用量/计费凭证 | 用量凭证 |
| 22 | fetchUsageSnapshot | 拉取和规范化 provider 特定的用量/配额 | 自定义用量端点 |
大多数 provider 只需实现 2-3 个 hook。OpenClaw 仍然拥有通用 agent 循环、failover、transcript 处理和 tool 策略。
内置 Provider 使用示例:
| Provider | 主要 Hook |
|---|---|
| Anthropic | resolveDynamicModel, capabilities, buildAuthDoctorHint, resolveUsageAuth, fetchUsageSnapshot, isCacheTtlEligible, resolveDefaultThinkingLevel, isModernModelRef |
| OpenAI | resolveDynamicModel, normalizeResolvedModel, capabilities, buildMissingAuthMessage, suppressBuiltInModel, augmentModelCatalog, supportsXHighThinking, isModernModelRef |
| OpenRouter | catalog, resolveDynamicModel, prepareDynamicModel, capabilities, wrapStreamFn, isCacheTtlEligible |
| GitHub Copilot | catalog, auth, resolveDynamicModel, capabilities, prepareRuntimeAuth, fetchUsageSnapshot |
| Z.AI | resolveDynamicModel, prepareExtraParams, wrapStreamFn, isCacheTtlEligible, isBinaryThinking, isModernModelRef, resolveUsageAuth, fetchUsageSnapshot |
4.5 额外能力#
Provider 可以在文本推理之外同时注册语音合成、媒体理解、图像生成、网页搜索,形成混合能力插件(hybrid-capability),推荐用于公司/厂商插件:
registerSpeechProvider—id、label、isConfigured、synthesize(返回 audio buffer + 格式 + 扩展名)registerMediaUnderstandingProvider— 声明capabilities数组,实现describeImage、transcribeAudio等registerImageGenerationProvider—id、label、generate
4.6 模型目录排序#
catalog.order 决定合并时机:
| 值 | 说明 | 适用场景 |
|---|---|---|
simple | 最先合并 | 普通 API-key provider |
profile | simple 之后 | 需要 auth profile 才出现 |
paired | profile 之后 | 合成多个相关 provider 条目 |
late | 最后,覆盖已有 | 需要覆盖已有 provider |
后来的 provider 在 key 冲突时获胜,允许插件有意覆盖内置 provider。兼容性注:discovery 仍作为遗留别名可用;如果同时注册了 catalog 和 discovery,OpenClaw 使用 catalog。
4.7 Preset Helper#
对于需要修改模型预设的认证流程,使用 openclaw/plugin-sdk/provider-onboard 中的辅助:
createDefaultModelPresetAppliers(...)createDefaultModelsPresetAppliers(...)createModelCatalogPresetAppliers(...)
5. Runtime 辅助#
api.runtime 对象注入了宿主辅助工具,不要直接 import 宿主内部模块。
5.1 Agent 管理 (api.runtime.agent)#
- 目录解析:
resolveAgentDir(),resolveAgentWorkspaceDir() - 身份与配置:
resolveAgentIdentity(),resolveThinkingDefault(),resolveAgentTimeoutMs() - 工作区创建:
ensureAgentWorkspace() - 嵌入式执行:
runEmbeddedPiAgent()
Session store(api.runtime.agent.session):resolveStorePath(), loadSessionStore(), saveSessionStore(), resolveSessionFilePath()
默认值(api.runtime.agent.defaults):model(如 "anthropic/claude-sonnet-4-6"), provider(如 "anthropic")
5.2 Subagent 管理 (api.runtime.subagent)#
run()— 启动执行,可选 provider/model 覆盖waitForRun()— 阻塞等待完成(带超时)getSessionMessages()— 获取会话历史deleteSession()— 清理 session
注意:model 覆盖需要 operator 在配置中显式允许:plugins.entries.<id>.subagent.allowModelOverride: true。使用 plugins.entries.<id>.subagent.allowedModels 限制受信任插件可使用的模型目标,或用 "*" 显式允许任意目标。不受信任的插件 subagent 运行仍然可以工作,但覆盖请求会被拒绝。
5.3 媒体与通信#
TTS (api.runtime.tts):
const clip = await api.runtime.tts.textToSpeech({
text: "Hello from OpenClaw",
cfg: api.config,
});
// 返回 PCM audio buffer + sample rate,插件需自行重采样/编码支持 textToSpeechTelephony() 和 listVoices()(可选,per-provider)。Voice listing 可包含更丰富的元数据(locale、gender、personality tags)。
媒体理解 (api.runtime.mediaUnderstanding):
const image = await api.runtime.mediaUnderstanding.describeImageFile({
filePath: "/tmp/photo.jpg",
cfg: api.config,
agentDir: "/tmp/agent",
});
// transcribeAudioFile、describeVideoFile、runFile
// 返回 { text: undefined } 当无转写输出时
// api.runtime.stt.transcribeAudioFile(...) 保留为兼容别名图像生成 (api.runtime.imageGeneration):generate(), listProviders()
网页搜索 (api.runtime.webSearch):
const result = await api.runtime.webSearch.search({
config: api.config,
args: { query: "OpenClaw plugins", count: 5 },
});
// listProviders() 列出可用搜索 provider5.4 工具命名空间#
| 命名空间 | 用途 |
|---|---|
api.runtime.media | 低层媒体工具:loadWebMedia, detectMime, mediaKindFromMime, isVoiceCompatibleAudio, getImageMetadata, resizeToJpeg |
api.runtime.config | loadConfig(), writeConfigFile() |
api.runtime.system | enqueueSystemEvent(), requestHeartbeatNow(), runCommandWithTimeout(), formatNativeDependencyHint() |
api.runtime.events | onAgentEvent(), onSessionTranscriptUpdate() |
api.runtime.logging | shouldLogVerbose(), getChildLogger() |
api.runtime.modelAuth | getApiKeyForModel(), resolveApiKeyForProvider() |
api.runtime.state | resolveStateDir() |
api.runtime.tools | createMemoryGetTool(), createMemorySearchTool(), registerMemoryCli() |
api.runtime.channel | Channel 特定的运行时工具(channel 插件加载时可用) |
5.5 Runtime Store 模式#
在注册回调之外保存运行时引用:
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
const store = createPluginRuntimeStore<PluginRuntime>("plugin not initialized");
export default defineChannelPluginEntry({
id: "my-plugin",
setRuntime: store.setRuntime,
});
// 之后使用:
export function getRuntime() { return store.getRuntime(); }
export function tryGetRuntime() { return store.tryGetRuntime(); }6. 入口函数#
SDK 提供三个入口辅助函数:
6.1 definePluginEntry#
用于 provider、tool、hook 等非 channel 插件。需要 id、name、description、register。可选 kind(如 "memory"、"context-engine" 独占槽)、configSchema(支持通过函数惰性求值)。
6.2 defineChannelPluginEntry#
专为消息渠道封装,自动处理 channel 注册(自动调用 api.registerChannel({ plugin }))。包含可选的 setRuntime 和 registerFull 回调。registerFull 仅在 api.registrationMode === "full" 时执行,setup-only 加载时跳过。
6.3 defineSetupPluginEntry#
用于 setup-entry.ts 的轻量入口,只返回插件对象,不做运行时或 CLI 连线。
6.4 注册模式#
| 模式 | 含义 |
|---|---|
"full" | 正常启动,全量注册 |
"setup-only" | 仅注册 channel(未配置/已禁用时) |
"setup-runtime" | Setup 流程中有 runtime 可用 |
手动模式处理示例:
register(api) {
api.registerChannel({ plugin: myPlugin });
if (api.registrationMode !== "full") return;
// 重量级运行时注册
api.registerCli(/* ... */);
api.registerService(/* ... */);
}defineChannelPluginEntry 辅助函数自动管理这个拆分。
6.5 插件分类#
| 类型 | 说明 |
|---|---|
| plain-capability | 单能力类型(如 mistral 仅做 provider) |
| hybrid-capability | 多能力类型(如 OpenAI 同时有 text、speech、media、image) |
| hook-only | 只注册 hook,无 capability/tool/command/service |
| non-capability | 有 tool/command/service 但无 capability |
可用 openclaw plugins inspect <id> 查看插件形状。
7. Setup 与配置#
7.1 package.json 元数据#
Channel 插件必须指定:
openclaw.extensions:入口文件openclaw.setupEntry:轻量 setup-only 加载器(可选)openclaw.channel:id、label、blurb、selectionLabel、docsPath、docsLabel、order、aliasesopenclaw.startup:延迟加载配置(可选)openclaw.install:npmSpec、localPath、defaultChoice安装提示(可选)
Provider 插件用 openclaw.providers 列出 provider id。
7.2 延迟全量加载#
Channel 插件可以通过 openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen: true 选择在 gateway listen 前只加载 setupEntry,已配置的 channel 也延迟到 listen 后才加载全量入口。
限制:只有当 setupEntry 注册了 gateway listen 前所需的一切时才启用。这包括:channel 注册本身、必须在 listen 前可用的 HTTP 路由、任何必须在此窗口存在的 gateway 方法/工具/服务。
{
"name": "@scope/my-channel",
"openclaw": {
"extensions": ["./index.ts"],
"setupEntry": "./setup-entry.ts",
"startup": {
"deferConfiguredChannelFullLoadUntilAfterListen": true
}
}
}7.3 openclaw.plugin.json#
每个原生插件都必须有这个文件:
{
"id": "my-plugin",
"name": "My Plugin",
"description": "...",
"configSchema": { "type": "object", "additionalProperties": false }
}即使没有配置项也必须提供 schema。
Channel 插件额外需要 kind: "channel" 和 channels 数组。
7.4 配置位置#
- 插件级配置:
plugins.entries.[name].config - Channel 专属配置:
channels.[name]
7.5 Setup 入口#
setup-entry.ts 只加载配置阶段必须的最少内容:
- 注册 channel 插件对象
- 注册必要 HTTP 路由
- 注册必要 gateway 方法
- 不要注册 CLI、后台服务或做重量级 import
7.6 Setup Wizard#
Channel 插件可以提供交互式 ChannelSetupWizard,支持:
credentials— 凭证输入(inputKey、providerHint、credentialLabel、preferredEnvVar、inspect)textInputs— 文本输入dmPolicy— DM 策略allowFrom— 白名单配置groupAccess— 群组访问prepare/finalize— 准备和完成函数
共享 setup helper(来自 openclaw/plugin-sdk/channel-setup 和相关子路径):
createOptionalChannelSetupSurface(...)createPromptParsedAllowFromForAccount(...)createTopLevelChannelParsedAllowFromPrompt(...)createNestedChannelParsedAllowFromPrompt(...)createStandardChannelSetupStatus(...)
7.7 Channel Catalog 元数据#
Channel 插件可以通过 openclaw.channel 和 openclaw.install 广告 setup/发现元数据。OpenClaw 也可以合并外部 channel catalog,放置 JSON 文件到:
~/.openclaw/mpm/plugins.json~/.openclaw/mpm/catalog.json~/.openclaw/plugins/catalog.json
或者通过 OPENCLAW_PLUGIN_CATALOG_PATHS(或 OPENCLAW_MPM_CATALOG_PATHS)指向 JSON 文件。
7.8 Package Packs#
插件目录可以通过 openclaw.extensions 列出多个入口,每个成为一个插件。如果 pack 列出多个 extension,插件 ID 变为 name/<fileBase>:
{
"name": "my-pack",
"openclaw": {
"extensions": ["./src/safety.ts", "./src/tools.ts"]
}
}安全保护:每个 openclaw.extensions 条目在符号链接解析后必须留在插件目录内部。逃出包目录的条目会被拒绝。
8. 测试#
8.1 测试工具#
openclaw/plugin-sdk/testing 导出:
installCommonResolveTargetErrorCases— 目标解析错误的共享测试用例shouldAckReaction— 判断 channel 是否应加确认 reactionremoveAckReactionAfterReply— 回复后移除确认 reaction
类型导出:ChannelAccountSnapshot、ChannelGatewayContext、OpenClawConfig、PluginRuntime、RuntimeEnv、MockFn
8.2 测试模式#
| 插件类型 | 测试重点 |
|---|---|
| Channel | 账号解析(resolveAccount)、配置检查(inspectAccount)、不暴露敏感信息 |
| Provider | 动态模型解析、目录可用性(有 API Key 时)、目录为 null(无凭证时) |
| Runtime mock | 用 createPluginRuntimeStore 隔离测试,独立 mock agent/config/runtime |
8.3 Contract 测试#
仓库内 bundled 插件必须通过 contract 测试,验证:
- 注册所有权
- Provider 映射
- 形状正确性
- 运行时合规
运行:pnpm test -- src/plugins/contracts/
8.4 最佳实践#
- 使用 per-instance stub,不要操作 prototype
- 使用 Vitest + V8 覆盖率阈值
pnpm test -- [path]运行定向测试- 运行 contract test 验证注册所有权和形状
- 内存受限环境:
OPENCLAW_TEST_PROFILE=low OPENCLAW_TEST_SERIAL_GATEWAY=1 pnpm test
8.5 Lint 规则(仓库内插件)#
- 不允许从
openclaw/plugin-sdk根路径 barrel import - 不允许直接 import
src/目录 - 不允许自引用插件 import
外部插件不受限制,但建议遵循同样规范。
9. 迁移指南#
9.1 废弃的导入面#
两个已废弃的 import 路径:
openclaw/plugin-sdk/compat— 以前批量 re-export 的辅助函数openclaw/extension-api— 直接访问宿主内部工具
向后兼容层将在下一个大版本移除。当前阶段废弃面会发出运行时警告。
9.2 旧方式的问题#
- 性能:import 一个 helper 会加载几十个不相关的模块
- 循环依赖:宽泛的 re-export 引发循环 import 问题
- API 清晰度:稳定导出和内部导出的界限模糊
9.3 迁移步骤#
Step 1:定位废弃 import
grep -r "plugin-sdk/compat" src/
grep -r "extension-api" src/Step 2:替换为现代 import
// 旧
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/compat";
// 新
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";对于宿主辅助函数,改用注入的 runtime:
// 旧
import { runEmbeddedPiAgent } from "openclaw/extension-api";
// 新
const result = await api.runtime.agent.runEmbeddedPiAgent({ sessionId, prompt });Step 3:构建和测试
pnpm build && pnpm test9.4 完整 Import 路径映射#
| 旧路径 | 新路径 |
|---|---|
plugin-sdk/compat → createChannelReplyPipeline | plugin-sdk/channel-reply-pipeline |
plugin-sdk/compat → channel 相关 | 对应的 plugin-sdk/channel-* 子路径 |
extension-api → resolveAgentDir | api.runtime.agent.resolveAgentDir |
extension-api → resolveAgentWorkspaceDir | api.runtime.agent.resolveAgentWorkspaceDir |
extension-api → resolveAgentIdentity | api.runtime.agent.resolveAgentIdentity |
extension-api → resolveThinkingDefault | api.runtime.agent.resolveThinkingDefault |
extension-api → resolveAgentTimeoutMs | api.runtime.agent.resolveAgentTimeoutMs |
extension-api → ensureAgentWorkspace | api.runtime.agent.ensureAgentWorkspace |
extension-api → Session store | api.runtime.agent.session.* |
9.5 临时警告抑制#
OPENCLAW_SUPPRESS_PLUGIN_SDK_COMPAT_WARNING=1 openclaw gateway run
OPENCLAW_SUPPRESS_EXTENSION_API_WARNING=1 openclaw gateway run这是临时逃生通道,不是长期方案。
10. 架构内幕#
10.1 能力模型#
OpenClaw 插件系统是公开能力模型 — 原生插件按标准化能力类型注册,而非松散的集成。
插件形状分类:
| 形状 | 说明 | 例子 |
|---|---|---|
| plain-capability | 注册一种能力 | mistral |
| hybrid-capability | 注册多种能力 | openai(text + speech + media + image) |
| hook-only | 仅注册 hook | — |
| non-capability | 有 tool/command/service 但不注册能力 | — |
六大能力类别:
| 能力 | 注册方法 | 例子 |
|---|---|---|
| 文本推理 | registerProvider() | openai, anthropic |
| 语音 | registerSpeechProvider() | elevenlabs, microsoft |
| 媒体理解 | registerMediaUnderstandingProvider() | openai, google |
| 图像生成 | registerImageGenerationProvider() | openai, google |
| 网页搜索 | registerWebSearchProvider() | |
| 消息渠道 | registerChannel() | msteams, matrix |
注册零能力但提供 hook/tool/service 的插件被归类为 "legacy hook-only",仍然完全支持。
10.2 外部兼容性立场#
- 现有外部插件:基于 hook 的集成是兼容性基线
- 新 bundled/native 插件:应优先使用显式能力注册
- 外部插件采用能力注册:可以,但能力特定的 helper 被视为演进中,除非明确标记为稳定
实际规则:能力注册 API 是目标方向,而遗留 hook 是过渡期间最安全的不破坏路径。
10.3 四层加载管线#
- Manifest + Discovery — 从配置路径、工作区根、全局扩展根、bundled extensions 发现候选插件,读取
openclaw.plugin.json - Enablement + Validation — 确定插件状态(enabled / disabled / blocked / exclusive slot selected),校验配置
- Runtime Loading — 原生插件通过 jiti 在进程内加载,将能力注册进中央注册表。兼容 bundle 被规范化为注册表记录,无需导入运行时代码
- Surface Consumption — 核心系统读取注册表,暴露 tool、channel、provider、hook、route、service
发现和校验基于 manifest 元数据完成,无需执行插件代码。
安全门禁在运行时执行前触发。候选被阻止的情况:
- 入口逃出插件根目录
- 路径是 world-writable
- 非 bundled 插件的路径所有权可疑
10.4 能力所有权原则#
插件是能力的所有权边界,不是杂物袋:
- 一家公司的插件应拥有该公司所有面向 OpenClaw 的能力
- 功能插件拥有它引入的完整功能面
- Channel 消费共享核心能力,不重新实现 provider 行为
能力分层:
| 层 | 职责 |
|---|---|
| Core 能力层 | 共享编排、策略、兜底、配置合并、交付语义、类型契约 |
| Vendor 插件层 | 厂商 API、认证、模型目录、合成后端 |
| Channel / Feature 插件层 | 集成,消费核心能力并呈现在目标平台 |
关键区分:插件 = 所有权边界;能力 = 核心契约,多个插件可以实现或消费。
10.5 中央注册表#
加载的插件注册进中央注册表(而非修改核心全局变量)。注册表追踪:
- 插件记录(身份、来源、origin、状态、诊断)
- 工具、typed/legacy hook
- Channel 和 Provider
- Gateway RPC handler 和 HTTP 路由
- CLI registrar 和后台服务
- 插件自有命令
数据流始终是单向的:插件模块 → 注册表注册;核心运行时 → 注册表消费。
这种分离对维护性很重要:大多数核心面只需一个集成点 —"读取注册表",而非特殊处理每个插件模块。
10.6 契约与执行#
插件 API 面有意地类型化并集中在 OpenClawPluginApi 中。
两层执行:
- 运行时注册执行:插件注册表在插件加载时校验注册,对重复或格式错误的注册产生插件诊断而非未定义行为
- Contract 测试:Bundled 插件在测试运行期间被捕获到 contract registry 中,OpenClaw 可以显式断言模型 provider、speech provider、web search provider 和 bundled 注册的所有权
好的插件契约是类型化的、小的、能力特定的、由 core 拥有的、可被多个插件复用的、可被 channel/feature 在不知道厂商的情况下消费的。
10.7 HTTP 路由#
api.registerHttpRoute({
path: "/acme/webhook",
auth: "plugin", // 或 "gateway"
match: "exact", // 或 "prefix"
replaceExisting: false, // 可选,允许同一插件替换自己的路由
handler: async (_req, res) => {
res.statusCode = 200;
res.end("ok");
return true;
},
});规则:
auth必须显式声明- 路径冲突会被拒绝(除非
replaceExisting: true) - 一个插件不能覆盖另一个插件的路由
- 不同 auth 级别的重叠路由会被拒绝 — 保持 exact/prefix fallthrough 链在同一 auth 级别
api.registerHttpHandler(...)已过时,使用api.registerHttpRoute(...)。
10.8 执行模型#
原生 OpenClaw 插件在 Gateway 进程内无沙箱运行。加载的原生插件与核心代码有相同的进程级信任边界。
含义:
- 原生插件可以注册 tool、网络 handler、hook 和 service
- 插件 bug 可以导致 gateway 崩溃或不稳定
- 恶意插件代表 OpenClaw 进程内的任意代码执行
兼容 bundle 更安全 — OpenClaw 将其视为元数据/内容包。
使用 allowlist 和显式安装/加载路径处理非 bundled 插件。将工作区插件视为开发时代码,而非生产默认值。
信任注意:plugins.allow 信任插件 ID,而非来源。工作区插件与 bundled 插件同 ID 时会有意覆盖 bundled 副本 — 这对本地开发、补丁测试和热修复是正常且有用的。
10.9 导出边界#
OpenClaw 导出能力,而非实现便利。保持能力注册公开,修剪非契约 helper 导出。
10.10 Manifest-First 行为#
Manifest 是控制平面的事实来源,用于:
- 识别插件
- 发现声明的 channel/skill/配置 schema 或 bundle 能力
- 校验插件配置
- 扩充 Control UI 标签/占位符
- 显示安装/目录元数据
运行时模块是数据平面部分,注册实际行为。
10.11 Loader 缓存#
OpenClaw 保持短期进程内缓存(发现结果、manifest 注册表数据、加载的插件注册表)。
性能选项:
OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1/OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1禁用缓存OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS/OPENCLAW_PLUGIN_MANIFEST_CACHE_MS调整缓存窗口
10.12 兼容性信号#
运行 openclaw doctor 或 openclaw plugins inspect <id> 可查看:
| 标签 | 含义 |
|---|---|
| Config valid | 配置解析正确,插件解析成功 |
| Compatibility advisory | 使用了旧模式(如 hook-only) |
| Legacy warning | 使用了废弃的 before_agent_start hook |
| Hard error | 配置无效或加载失败 |
10.13 会话绑定回调#
插件绑定会话后可以通过 api.onConversationBindingResolved(handler) 在绑定请求被批准或拒绝时收到回调:
回调字段:
event.status—"approved"或"denied"event.decision—"allow-once"、"allow-always"或"deny"event.binding— 批准请求的已解析绑定event.request— 原始请求摘要、detach hint、sender id、会话元数据
这个回调是通知性的,不改变谁被允许绑定会话。
10.14 上下文引擎插件#
上下文引擎插件拥有会话上下文编排(摄取、组装和压缩)。注册方式:
api.registerContextEngine(id, factory);然后通过 plugins.slots.contextEngine 选择活跃引擎。
当你的引擎不拥有压缩算法时,保持 compact() 实现并使用 delegateCompactionToRuntime(来自 openclaw/plugin-sdk/core)显式委托。
10.15 添加新能力的推荐顺序#
- 定义核心契约 — 共享行为、策略、兜底、配置合并、生命周期、channel-facing 语义、runtime helper 形状
- 添加类型化注册/runtime 面 — 在
OpenClawPluginApi和/或api.runtime中扩展最小有用的类型化能力面 - 对接核心 + channel/feature 消费者 — 通过 core 消费新能力,不直接 import 厂商实现
- 注册厂商实现 — 厂商插件注册后端
- 添加 contract 覆盖测试 — 测试让所有权和注册形状随时间保持显式
能力检查清单 — 添加新能力时通常需要同时触及:
- 核心契约类型:
src/<capability>/types.ts - 核心 runner / runtime helper:
src/<capability>/runtime.ts - 插件 API 注册面:
src/plugins/types.ts - 插件注册表连线:
src/plugins/registry.ts - 插件 runtime 暴露:
src/plugins/runtime/* - 捕获/测试辅助:
src/test-utils/plugin-registration.ts - 所有权/契约断言:
src/plugins/contracts/registry.ts - Operator/插件文档:
docs/
如果其中某个面缺失,通常意味着能力尚未完全集成。
11. 总结:开发一个插件的推荐流程#
- 明确边界 — 厂商插件还是功能插件?拥有哪些能力?
- 设计标识 — 插件 id、包名(bundled 用
@openclaw/<id>,可用-provider、-plugin、-speech、-sandbox、-media-understanding后缀)、目录结构 - 编写 manifest —
openclaw.plugin.json+configSchema+uiHints(channel 插件加kind: "channel"+channels) - 配置
package.json—openclaw.extensions,channel 插件加openclaw.channel,provider 插件加openclaw.providers+providerAuthEnvVars+providerAuthChoices - 实现入口 — 选对入口函数:
definePluginEntry/defineChannelPluginEntry/defineSingleProviderPluginEntry - 注册核心能力 — 先做最关键的一种(tool / provider / channel)
- 补充辅助面 — command、hook、service、HTTP route、CLI、gateway method
- 编写测试 — Vitest + contract test + 覆盖率阈值
- 检查 import — 确认使用细粒度子路径,无废弃 import
- 配置并验证 —
plugins.entries.<id>.config→ 重启 Gateway →openclaw plugins list/inspect - 发布 — ClawHub / npm / 仓库内
extensions/

