·41 min read·Admin·技术

OpenClaw Plugin SDK 完整指引

openclaw 新的 plugin,对于我们openclaw的生态挺重要的

OpenClaw Plugin SDK 完整指引
<br />

title: OpenClaw Plugin SDK 完整指引 date: 2026-03-24 source:


OpenClaw Plugin SDK 完整指引#

本文基于 OpenClaw 官方 Plugin SDK 系列文档递归整理,覆盖 SDK 总览、插件构建、Channel 插件、Provider 插件、Runtime 辅助、入口定义、配置与打包、测试、迁移、架构内幕共 10 篇文档。目标是把分散在各页面中的关键信息整合成一篇可以直接参照开发的中文指引。


1. SDK 总览:导入约定与注册 API#

Plugin SDK 是插件与 OpenClaw 核心之间的类型契约。它决定了「你能 import 什么」和「你能注册什么」。

1.1 导入规范#

永远从具体子路径导入,不要用已废弃的根路径:

TypeScript
// 正确
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-entrydefinePluginEntry
plugin-sdk/coredefineChannelPluginEntry, 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-schemaChannel 配置 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-contractChannel 契约类型
plugin-sdk/channel-feedback反馈 / reaction

Provider 相关

子路径用途
plugin-sdk/provider-authAPI 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-fromallowFrom 格式化 (formatAllowFromLowercase)
plugin-sdk/secret-input秘密输入解析
plugin-sdk/webhook-ingressWebhook 请求 / 目标辅助

Runtime / Storage

子路径用途
plugin-sdk/runtime-storecreatePluginRuntimeStore
plugin-sdk/config-runtime配置读写
plugin-sdk/agent-runtimeAgent 目录 / 身份 / 工作区
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-resolutionallowlist 输入映射 (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.idstring插件 id
api.namestring显示名
api.versionstring?插件版本(可选)
api.descriptionstring?插件描述(可选)
api.sourcestring插件来源路径
api.rootDirstring?根目录(可选)
api.configOpenClawConfig当前配置快照
api.pluginConfigRecord<string, unknown>插件私有配置
api.runtimePluginRuntimeRuntime 辅助命名空间
api.loggerPluginLogger日志(debug/info/warn/error)
api.registrationModePluginRegistrationMode"full" / "setup-only" / "setup-runtime"
api.resolvePath(input)(string) => string相对于插件根目录解析路径

1.5 内部模块约定#

text
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-sdk barrel
  • 优先使用窄范围的稳定原语
  • plugin-sdk/channel-runtime 仅作为兼容垫片保留,新代码应 import 更细粒度的原语
  • Bundled 扩展特定的 helper barrel 默认不稳定,如果 helper 仅被一个 bundled 扩展使用,应保持在该扩展自己的 api.js / runtime-api.js 后面
  • 能力特定子路径(image-generationmedia-understandingspeech)的存在不代表每个导出的 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

JSON
{
  "name": "@myorg/openclaw-my-plugin",
  "version": "1.0.0",
  "type": "module",
  "openclaw": {
    "extensions": ["./index.ts"]
  }
}

openclaw.plugin.json

JSON
{
  "id": "my-plugin",
  "name": "My Plugin",
  "description": "Adds a custom tool to OpenClaw",
  "configSchema": {
    "type": "object",
    "additionalProperties": false
  }
}

Step 2:入口文件

TypeScript
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/STTapi.registerSpeechProvider()Provider 章节
媒体理解api.registerMediaUnderstandingProvider()Provider 章节
图像生成api.registerImageGenerationProvider()Provider 章节
网页搜索api.registerWebSearchProvider()Provider 章节
Agent 工具api.registerTool()本节
自定义命令api.registerCommand()Entry Points
事件 hookapi.registerHook()Entry Points
HTTP 路由api.registerHttpRoute()Architecture
CLI 子命令api.registerCli()Entry Points

2.5 Agent 工具:必选 vs 可选#

TypeScript
// 必选工具 — 默认对 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 },
);

可选工具需要在配置中声明才能启用:

JSON
{
  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 字段(idlabelblurb),可选 openclaw.setupEntryopenclaw.startup
  • openclaw.plugin.json — 指定 kind: "channel",定义 channels 数组和配置 schema
  • index.ts — 使用 defineChannelPluginEntry 导出
  • setup-entry.ts — 使用 defineSetupPluginEntry 的轻量入口
  • src/channel.ts — 使用 createChatChannelPlugin 构建 ChannelPlugin

3.2 构建 Channel Plugin#

createChatChannelPlugin 接受声明式选项:

  • resolveAccount — 从 OpenClaw 配置提取账号信息
  • inspectAccount(可选) — 只读状态报告,不暴露密钥
  • 安全配置 — channel key、策略解析器、allowlist 解析器、默认策略
  • 配对选项 — 身份标签、验证消息文本、通知函数
  • 线程模式 — "reply"
  • 出站配置 — sendTextsendMedia

3.3 Message Tool 架构#

Channel 插件与核心之间的边界:

  • Core:共享工具宿主、prompt wiring、session/thread 簿记、dispatch
  • Channel 插件:作用域内的 action 发现、能力发现、schema 贡献
  • 发现接口ChannelMessageActionAdapter.describeMessageTool() 返回可见 action、能力和 schema 片段

运行时传递给发现的 scope 包括 accountIdcurrentChannelIdcurrentThreadTscurrentMessageIdsessionKeysessionIdagentIdrequesterSenderId(受信任的请求者身份),使插件可以基于活跃账号、当前房间/线程/消息或受信任的请求者做上下文感知的行为。

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 还是 channel
  • messaging.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 statusopenclaw channels statusopenclaw doctor),不需要物化运行时凭证

推荐的 inspectAccount 行为:

  • 仅返回描述性账号状态
  • 保留 enabledconfigured 字段
  • 包含凭证来源/状态字段(如 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 — 定义认证方法的数组,包含 providermethodchoiceIdchoiceLabelgroupIdgroupLabelcliFlagcliOptioncliDescription

4.2 最小结构#

一个最小 provider 需要四要素:标识符、显示名、认证方法、模型目录。

完整模式 — 适合多 provider 场景:

TypeScript
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 认证:

TypeScript
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作用使用时机
1catalog模型生成时发布 provider 配置到 models.providersprovider 拥有特定模型 ID、base URL 默认值或认证门控的模型元数据
2(内置模型查找)
3resolveDynamicModel本地注册表未找到的 provider-owned 模型 ID 的同步降级provider 接受任意上游 ID
4prepareDynamicModelresolveDynamicModel 再次运行前的异步预热需要网络获取元数据
5normalizeResolvedModelrunner 使用前的最终重写传输层重写
6capabilitiesprovider-owned 的 transcript / tooling 元数据自定义模型能力
7prepareExtraParams通用 stream option wrapper 之前的请求参数规范化默认请求参数
8wrapStreamFn通用 wrapper 之后的流包装自定义 header / body
9formatApiKey存储 profile 格式化自定义 runtime token 形状
10refreshOAuth自定义 OAuth 刷新覆盖OAuth 流程
11buildAuthDoctorHintOAuth 刷新失败时的修复提示认证修复引导
12isCacheTtlEligibleprompt cache TTL 策略控制缓存资格
13buildMissingAuthMessage替代通用缺失认证恢复消息自定义缺失认证提示
14suppressBuiltInModel过时上游模型抑制隐藏不再可用的模型
15augmentModelCatalog合成 / 最终目录行前向兼容行
16isBinaryThinking开/关推理切换二进制思维标记
17supportsXHighThinkingxhigh 推理支持高级推理
18resolveDefaultThinkingLevel默认 /think 级别默认推理策略
19isModernModelRef现代模型匹配器live/smoke 模型匹配
20prepareRuntimeAuth将配置凭证交换为实际运行时 tokentoken 交换
21resolveUsageAuth解析用量/计费凭证用量凭证
22fetchUsageSnapshot拉取和规范化 provider 特定的用量/配额自定义用量端点

大多数 provider 只需实现 2-3 个 hook。OpenClaw 仍然拥有通用 agent 循环、failover、transcript 处理和 tool 策略。

内置 Provider 使用示例

Provider主要 Hook
AnthropicresolveDynamicModel, capabilities, buildAuthDoctorHint, resolveUsageAuth, fetchUsageSnapshot, isCacheTtlEligible, resolveDefaultThinkingLevel, isModernModelRef
OpenAIresolveDynamicModel, normalizeResolvedModel, capabilities, buildMissingAuthMessage, suppressBuiltInModel, augmentModelCatalog, supportsXHighThinking, isModernModelRef
OpenRoutercatalog, resolveDynamicModel, prepareDynamicModel, capabilities, wrapStreamFn, isCacheTtlEligible
GitHub Copilotcatalog, auth, resolveDynamicModel, capabilities, prepareRuntimeAuth, fetchUsageSnapshot
Z.AIresolveDynamicModel, prepareExtraParams, wrapStreamFn, isCacheTtlEligible, isBinaryThinking, isModernModelRef, resolveUsageAuth, fetchUsageSnapshot

4.5 额外能力#

Provider 可以在文本推理之外同时注册语音合成、媒体理解、图像生成、网页搜索,形成混合能力插件(hybrid-capability),推荐用于公司/厂商插件:

  • registerSpeechProvideridlabelisConfiguredsynthesize(返回 audio buffer + 格式 + 扩展名)
  • registerMediaUnderstandingProvider — 声明 capabilities 数组,实现 describeImagetranscribeAudio
  • registerImageGenerationProvideridlabelgenerate

4.6 模型目录排序#

catalog.order 决定合并时机:

说明适用场景
simple最先合并普通 API-key provider
profilesimple 之后需要 auth profile 才出现
pairedprofile 之后合成多个相关 provider 条目
late最后,覆盖已有需要覆盖已有 provider

后来的 provider 在 key 冲突时获胜,允许插件有意覆盖内置 provider。兼容性注:discovery 仍作为遗留别名可用;如果同时注册了 catalogdiscovery,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):

TypeScript
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):

TypeScript
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):

TypeScript
const result = await api.runtime.webSearch.search({
  config: api.config,
  args: { query: "OpenClaw plugins", count: 5 },
});
// listProviders() 列出可用搜索 provider

5.4 工具命名空间#

命名空间用途
api.runtime.media低层媒体工具:loadWebMedia, detectMime, mediaKindFromMime, isVoiceCompatibleAudio, getImageMetadata, resizeToJpeg
api.runtime.configloadConfig(), writeConfigFile()
api.runtime.systemenqueueSystemEvent(), requestHeartbeatNow(), runCommandWithTimeout(), formatNativeDependencyHint()
api.runtime.eventsonAgentEvent(), onSessionTranscriptUpdate()
api.runtime.loggingshouldLogVerbose(), getChildLogger()
api.runtime.modelAuthgetApiKeyForModel(), resolveApiKeyForProvider()
api.runtime.stateresolveStateDir()
api.runtime.toolscreateMemoryGetTool(), createMemorySearchTool(), registerMemoryCli()
api.runtime.channelChannel 特定的运行时工具(channel 插件加载时可用)

5.5 Runtime Store 模式#

在注册回调之外保存运行时引用:

TypeScript
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 插件。需要 idnamedescriptionregister。可选 kind(如 "memory""context-engine" 独占槽)、configSchema(支持通过函数惰性求值)。

6.2 defineChannelPluginEntry#

专为消息渠道封装,自动处理 channel 注册(自动调用 api.registerChannel({ plugin }))。包含可选的 setRuntimeregisterFull 回调。registerFull 仅在 api.registrationMode === "full" 时执行,setup-only 加载时跳过。

6.3 defineSetupPluginEntry#

用于 setup-entry.ts 的轻量入口,只返回插件对象,不做运行时或 CLI 连线。

6.4 注册模式#

模式含义
"full"正常启动,全量注册
"setup-only"仅注册 channel(未配置/已禁用时)
"setup-runtime"Setup 流程中有 runtime 可用

手动模式处理示例:

TypeScript
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.channelidlabelblurbselectionLabeldocsPathdocsLabelorderaliases
  • openclaw.startup:延迟加载配置(可选)
  • openclaw.installnpmSpeclocalPathdefaultChoice 安装提示(可选)

Provider 插件用 openclaw.providers 列出 provider id。

7.2 延迟全量加载#

Channel 插件可以通过 openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen: true 选择在 gateway listen 前只加载 setupEntry,已配置的 channel 也延迟到 listen 后才加载全量入口。

限制:只有当 setupEntry 注册了 gateway listen 前所需的一切时才启用。这包括:channel 注册本身、必须在 listen 前可用的 HTTP 路由、任何必须在此窗口存在的 gateway 方法/工具/服务。

JSON
{
  "name": "@scope/my-channel",
  "openclaw": {
    "extensions": ["./index.ts"],
    "setupEntry": "./setup-entry.ts",
    "startup": {
      "deferConfiguredChannelFullLoadUntilAfterListen": true
    }
  }
}

7.3 openclaw.plugin.json#

每个原生插件都必须有这个文件:

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 — 凭证输入(inputKeyproviderHintcredentialLabelpreferredEnvVarinspect
  • 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.channelopenclaw.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>

JSON
{
  "name": "my-pack",
  "openclaw": {
    "extensions": ["./src/safety.ts", "./src/tools.ts"]
  }
}

安全保护:每个 openclaw.extensions 条目在符号链接解析后必须留在插件目录内部。逃出包目录的条目会被拒绝。


8. 测试#

8.1 测试工具#

openclaw/plugin-sdk/testing 导出:

  • installCommonResolveTargetErrorCases — 目标解析错误的共享测试用例
  • shouldAckReaction — 判断 channel 是否应加确认 reaction
  • removeAckReactionAfterReply — 回复后移除确认 reaction

类型导出:ChannelAccountSnapshotChannelGatewayContextOpenClawConfigPluginRuntimeRuntimeEnvMockFn

8.2 测试模式#

插件类型测试重点
Channel账号解析(resolveAccount)、配置检查(inspectAccount)、不暴露敏感信息
Provider动态模型解析、目录可用性(有 API Key 时)、目录为 null(无凭证时)
Runtime mockcreatePluginRuntimeStore 隔离测试,独立 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 规则(仓库内插件)#

  1. 不允许从 openclaw/plugin-sdk 根路径 barrel import
  2. 不允许直接 import src/ 目录
  3. 不允许自引用插件 import

外部插件不受限制,但建议遵循同样规范。


9. 迁移指南#

9.1 废弃的导入面#

两个已废弃的 import 路径:

  • openclaw/plugin-sdk/compat — 以前批量 re-export 的辅助函数
  • openclaw/extension-api — 直接访问宿主内部工具

向后兼容层将在下一个大版本移除。当前阶段废弃面会发出运行时警告。

9.2 旧方式的问题#

  1. 性能:import 一个 helper 会加载几十个不相关的模块
  2. 循环依赖:宽泛的 re-export 引发循环 import 问题
  3. API 清晰度:稳定导出和内部导出的界限模糊

9.3 迁移步骤#

Step 1:定位废弃 import

Shell
grep -r "plugin-sdk/compat" src/
grep -r "extension-api" src/

Step 2:替换为现代 import

TypeScript
// 旧
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/compat";
// 新
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";

对于宿主辅助函数,改用注入的 runtime:

TypeScript
// 旧
import { runEmbeddedPiAgent } from "openclaw/extension-api";
// 新
const result = await api.runtime.agent.runEmbeddedPiAgent({ sessionId, prompt });

Step 3:构建和测试

Shell
pnpm build && pnpm test

9.4 完整 Import 路径映射#

旧路径新路径
plugin-sdk/compatcreateChannelReplyPipelineplugin-sdk/channel-reply-pipeline
plugin-sdk/compat → channel 相关对应的 plugin-sdk/channel-* 子路径
extension-apiresolveAgentDirapi.runtime.agent.resolveAgentDir
extension-apiresolveAgentWorkspaceDirapi.runtime.agent.resolveAgentWorkspaceDir
extension-apiresolveAgentIdentityapi.runtime.agent.resolveAgentIdentity
extension-apiresolveThinkingDefaultapi.runtime.agent.resolveThinkingDefault
extension-apiresolveAgentTimeoutMsapi.runtime.agent.resolveAgentTimeoutMs
extension-apiensureAgentWorkspaceapi.runtime.agent.ensureAgentWorkspace
extension-api → Session storeapi.runtime.agent.session.*

9.5 临时警告抑制#

Shell
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()google
消息渠道registerChannel()msteams, matrix

注册零能力但提供 hook/tool/service 的插件被归类为 "legacy hook-only",仍然完全支持。

10.2 外部兼容性立场#

  • 现有外部插件:基于 hook 的集成是兼容性基线
  • 新 bundled/native 插件:应优先使用显式能力注册
  • 外部插件采用能力注册:可以,但能力特定的 helper 被视为演进中,除非明确标记为稳定

实际规则:能力注册 API 是目标方向,而遗留 hook 是过渡期间最安全的不破坏路径。

10.3 四层加载管线#

  1. Manifest + Discovery — 从配置路径、工作区根、全局扩展根、bundled extensions 发现候选插件,读取 openclaw.plugin.json
  2. Enablement + Validation — 确定插件状态(enabled / disabled / blocked / exclusive slot selected),校验配置
  3. Runtime Loading — 原生插件通过 jiti 在进程内加载,将能力注册进中央注册表。兼容 bundle 被规范化为注册表记录,无需导入运行时代码
  4. 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 中。

两层执行:

  1. 运行时注册执行:插件注册表在插件加载时校验注册,对重复或格式错误的注册产生插件诊断而非未定义行为
  2. Contract 测试:Bundled 插件在测试运行期间被捕获到 contract registry 中,OpenClaw 可以显式断言模型 provider、speech provider、web search provider 和 bundled 注册的所有权

好的插件契约是类型化的、小的、能力特定的、由 core 拥有的、可被多个插件复用的、可被 channel/feature 在不知道厂商的情况下消费的。

10.7 HTTP 路由#

TypeScript
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 doctoropenclaw 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 上下文引擎插件#

上下文引擎插件拥有会话上下文编排(摄取、组装和压缩)。注册方式:

TypeScript
api.registerContextEngine(id, factory);

然后通过 plugins.slots.contextEngine 选择活跃引擎。

当你的引擎拥有压缩算法时,保持 compact() 实现并使用 delegateCompactionToRuntime(来自 openclaw/plugin-sdk/core)显式委托。

10.15 添加新能力的推荐顺序#

  1. 定义核心契约 — 共享行为、策略、兜底、配置合并、生命周期、channel-facing 语义、runtime helper 形状
  2. 添加类型化注册/runtime 面 — 在 OpenClawPluginApi 和/或 api.runtime 中扩展最小有用的类型化能力面
  3. 对接核心 + channel/feature 消费者 — 通过 core 消费新能力,不直接 import 厂商实现
  4. 注册厂商实现 — 厂商插件注册后端
  5. 添加 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. 总结:开发一个插件的推荐流程#

  1. 明确边界 — 厂商插件还是功能插件?拥有哪些能力?
  2. 设计标识 — 插件 id、包名(bundled 用 @openclaw/<id>,可用 -provider-plugin-speech-sandbox-media-understanding 后缀)、目录结构
  3. 编写 manifestopenclaw.plugin.json + configSchema + uiHints(channel 插件加 kind: "channel" + channels
  4. 配置 package.jsonopenclaw.extensions,channel 插件加 openclaw.channel,provider 插件加 openclaw.providers + providerAuthEnvVars + providerAuthChoices
  5. 实现入口 — 选对入口函数:definePluginEntry / defineChannelPluginEntry / defineSingleProviderPluginEntry
  6. 注册核心能力 — 先做最关键的一种(tool / provider / channel)
  7. 补充辅助面 — command、hook、service、HTTP route、CLI、gateway method
  8. 编写测试 — Vitest + contract test + 覆盖率阈值
  9. 检查 import — 确认使用细粒度子路径,无废弃 import
  10. 配置并验证plugins.entries.<id>.config → 重启 Gateway → openclaw plugins list / inspect
  11. 发布 — ClawHub / npm / 仓库内 extensions/
<br />

相关文章