From 9afa14148877fcb3fc2b452aa0a0b88b351cbf9b Mon Sep 17 00:00:00 2001 From: "openclaw-docs-i18n[bot]" Date: Tue, 14 Apr 2026 16:29:08 +0000 Subject: [PATCH] chore(i18n): refresh zh-CN translations --- docs/zh-CN/plugins/architecture.md | 844 ++++++++++------------ docs/zh-CN/plugins/sdk-channel-plugins.md | 279 ++++--- 2 files changed, 535 insertions(+), 588 deletions(-) diff --git a/docs/zh-CN/plugins/architecture.md b/docs/zh-CN/plugins/architecture.md index 82654193f..3bd503ad6 100644 --- a/docs/zh-CN/plugins/architecture.md +++ b/docs/zh-CN/plugins/architecture.md @@ -5,13 +5,13 @@ read_when: - 处理插件加载流水线或注册表 - 实现提供商运行时钩子或渠道插件 sidebarTitle: Internals -summary: 插件内部机制:能力模型、归属、契约、加载流水线和运行时辅助工具 +summary: 插件内部机制:能力模型、归属关系、契约、加载流水线和运行时辅助工具 title: 插件内部机制 x-i18n: - generated_at: "2026-04-12T16:25:37Z" + generated_at: "2026-04-14T16:24:18Z" model: gpt-5.4 provider: openai - source_hash: 37361c1e9d2da57c77358396f19dfc7f749708b66ff68f1bf737d051b5d7675d + source_hash: f86798b5d2b0ad82d2397a52a6c21ed37fe6eee1dd3d124a9e4150c4f630b841 source_path: plugins/architecture.md workflow: 15 --- @@ -22,8 +22,8 @@ x-i18n: 这是**深度架构参考**。如需实用指南,请参见: - [安装和使用插件](/zh-CN/tools/plugin) —— 用户指南 - [入门指南](/zh-CN/plugins/building-plugins) —— 第一个插件教程 - - [渠道插件](/zh-CN/plugins/sdk-channel-plugins) —— 构建消息渠道 - - [提供商插件](/zh-CN/plugins/sdk-provider-plugins) —— 构建模型提供商 + - [渠道插件](/zh-CN/plugins/sdk-channel-plugins) —— 构建一个消息渠道 + - [提供商插件](/zh-CN/plugins/sdk-provider-plugins) —— 构建一个模型提供商 - [SDK 概览](/zh-CN/plugins/sdk-overview) —— 导入映射和注册 API @@ -31,116 +31,119 @@ x-i18n: ## 公共能力模型 -能力是 OpenClaw 内部公共的**原生插件**模型。每个原生 OpenClaw 插件都会针对一种或多种能力类型进行注册: +能力是 OpenClaw 内部公开的**原生插件**模型。每个原生 OpenClaw 插件都会针对一种或多种能力类型进行注册: -| 能力 | 注册方法 | 示例插件 | +| 能力 | 注册方式 | 示例插件 | | ---------------------- | ------------------------------------------------ | ------------------------------------ | | 文本推理 | `api.registerProvider(...)` | `openai`, `anthropic` | | CLI 推理后端 | `api.registerCliBackend(...)` | `openai`, `anthropic` | | 语音 | `api.registerSpeechProvider(...)` | `elevenlabs`, `microsoft` | -| 实时转录 | `api.registerRealtimeTranscriptionProvider(...)` | `openai` | +| 实时转写 | `api.registerRealtimeTranscriptionProvider(...)` | `openai` | | 实时语音 | `api.registerRealtimeVoiceProvider(...)` | `openai` | | 媒体理解 | `api.registerMediaUnderstandingProvider(...)` | `openai`, `google` | | 图像生成 | `api.registerImageGenerationProvider(...)` | `openai`, `google`, `fal`, `minimax` | | 音乐生成 | `api.registerMusicGenerationProvider(...)` | `google`, `minimax` | | 视频生成 | `api.registerVideoGenerationProvider(...)` | `qwen` | -| Web 抓取 | `api.registerWebFetchProvider(...)` | `firecrawl` | -| Web 搜索 | `api.registerWebSearchProvider(...)` | `google` | -| 渠道 / 消息传递 | `api.registerChannel(...)` | `msteams`, `matrix` | +| 网页抓取 | `api.registerWebFetchProvider(...)` | `firecrawl` | +| 网页搜索 | `api.registerWebSearchProvider(...)` | `google` | +| 渠道 / 消息 | `api.registerChannel(...)` | `msteams`, `matrix` | -如果一个插件注册了零个能力,但提供了钩子、工具或服务,它就是一个**仅旧版钩子**插件。这种模式仍然受到完整支持。 +如果一个插件没有注册任何能力,但提供了 hooks、工具或服务,那么它就是一个**仅 hooks 的旧版**插件。这种模式仍然受到完整支持。 ### 外部兼容性立场 -能力模型已经在核心中落地,并且当前已被内置 / 原生插件使用,但外部插件兼容性仍然需要比“它已导出,因此它被冻结了”更严格的标准。 +能力模型已经在核心中落地,并且如今已被内置 / 原生插件使用,但对于外部插件的兼容性要求,仍需要比“它已导出,所以它被冻结”更严格的标准。 当前指导原则: -- **现有外部插件:** 保持基于钩子的集成正常工作;将其视为兼容性基线 -- **新的内置 / 原生插件:** 优先使用显式能力注册,而不是供应商特定的内部接入方式,或新的仅钩子设计 -- **采用能力注册的外部插件:** 允许这样做,但除非文档明确将某个契约标记为稳定,否则应将能力特定的辅助接口视为仍在演进中 +- **现有外部插件:** 保持基于 hooks 的集成继续可用;将其视为兼容性的基线 +- **新的内置 / 原生插件:** 优先选择显式能力注册,而不是特定厂商的内部直连方式或新的仅 hooks 设计 +- **采用能力注册的外部插件:** 允许这样做,但除非文档明确将某项契约标记为稳定,否则应将特定能力的辅助表面视为仍在演进中 -实践规则: +实际规则: - 能力注册 API 是预期的发展方向 -- 在过渡期间,对于外部插件,旧版钩子仍然是最安全、最不容易破坏兼容性的路径 -- 已导出的辅助子路径并不完全等价;应优先使用有文档说明的窄契约,而不是偶然暴露出来的辅助导出 +- 在过渡期间,对于外部插件来说,旧版 hooks 仍是最安全、最不容易破坏兼容性的路径 +- 已导出的辅助子路径并不全都同等重要;优先使用文档化的窄契约,而不是偶然暴露出来的辅助导出 ### 插件形态 -OpenClaw 会根据每个已加载插件的实际注册行为(而不仅仅是静态元数据)将其归类为某种形态: +OpenClaw 会根据插件的实际注册行为(而不仅仅是静态元数据)将每个已加载插件归类为一种形态: - **plain-capability** —— 只注册一种能力类型(例如仅提供商插件 `mistral`) - **hybrid-capability** —— 注册多种能力类型(例如 `openai` 同时拥有文本推理、语音、媒体理解和图像生成) -- **hook-only** —— 仅注册钩子(类型化或自定义),不注册能力、工具、命令或服务 -- **non-capability** —— 注册工具、命令、服务或路由,但不注册能力 +- **hook-only** —— 仅注册 hooks(类型化或自定义),不包含能力、工具、命令或服务 +- **non-capability** —— 注册工具、命令、服务或路由,但不包含任何能力 -使用 `openclaw plugins inspect ` 可查看插件的形态和能力明细。详情请参见 [CLI 参考](/cli/plugins#inspect)。 +使用 `openclaw plugins inspect ` 可查看插件的形态和能力拆分。详情请参见 [CLI 参考](/cli/plugins#inspect)。 -### 旧版钩子 +### 旧版 hooks -`before_agent_start` 钩子仍然作为仅钩子插件的兼容路径受到支持。现实中仍有旧版插件依赖它。 +`before_agent_start` hook 仍然作为仅 hooks 插件的兼容路径受到支持。现实中的旧版插件仍然依赖它。 方向如下: -- 保持其可用 +- 保持其继续工作 - 在文档中将其标记为旧版 -- 对于模型 / 提供商覆盖工作,优先使用 `before_model_resolve` -- 对于提示词变更工作,优先使用 `before_prompt_build` -- 只有在实际使用量下降且固定测试覆盖证明迁移安全之后,才考虑移除 +- 在模型 / 提供商覆盖工作中,优先使用 `before_model_resolve` +- 在提示词变更工作中,优先使用 `before_prompt_build` +- 只有在真实使用量下降,并且夹具覆盖证明迁移安全后,才考虑移除 ### 兼容性信号 -当你运行 `openclaw doctor` 或 `openclaw plugins inspect ` 时,你可能会看到以下标签之一: +当你运行 `openclaw doctor` 或 `openclaw plugins inspect ` 时,可能会看到以下标签之一: | 信号 | 含义 | | -------------------------- | ------------------------------------------------------------ | -| **config valid** | 配置解析正常,插件可成功解析 | -| **compatibility advisory** | 插件使用受支持但较旧的模式(例如 `hook-only`) | -| **legacy warning** | 插件使用了 `before_agent_start`,该接口已弃用 | +| **config valid** | 配置解析正常,且插件可成功解析 | +| **compatibility advisory** | 插件使用了受支持但较旧的模式(例如 `hook-only`) | +| **legacy warning** | 插件使用了 `before_agent_start`,该用法已弃用 | | **hard error** | 配置无效,或插件加载失败 | -`hook-only` 和 `before_agent_start` 当前都不会导致你的插件失效——`hook-only` 只是提示性信息,而 `before_agent_start` 只会触发警告。这些信号也会出现在 `openclaw status --all` 和 `openclaw plugins doctor` 中。 +目前,`hook-only` 和 `before_agent_start` 都不会导致你的插件损坏 —— `hook-only` 只是提示性建议,而 `before_agent_start` 仅会触发警告。这些信号同样会出现在 `openclaw status --all` 和 `openclaw plugins doctor` 中。 ## 架构概览 OpenClaw 的插件系统包含四层: 1. **清单 + 发现** - OpenClaw 会从已配置路径、workspace 根目录、全局扩展根目录以及内置扩展中查找候选插件。发现过程会优先读取原生 `openclaw.plugin.json` 清单以及受支持的 bundle 清单。 -2. **启用 + 验证** - 核心决定一个已发现的插件是启用、禁用、阻止,还是被选中用于某个独占槽位(例如 memory)。 + OpenClaw 会从已配置路径、工作区根目录、全局扩展根目录以及内置扩展中查找候选插件。发现阶段会优先读取原生 `openclaw.plugin.json` 清单以及受支持的 bundle 清单。 +2. **启用 + 校验** + 核心决定某个已发现插件是启用、禁用、阻止,还是被选入某个独占槽位,例如 memory。 3. **运行时加载** - 原生 OpenClaw 插件会通过 jiti 在进程内加载,并将能力注册到中央注册表中。兼容的 bundle 会被规范化为注册表记录,而无需导入运行时代码。 -4. **接口消费** - OpenClaw 的其余部分会读取注册表,以暴露工具、渠道、提供商设置、钩子、HTTP 路由、CLI 命令和服务。 + 原生 OpenClaw 插件会通过 jiti 在进程内加载,并将能力注册到中央注册表中。兼容的 bundle 会被标准化为注册表记录,而无需导入运行时代码。 +4. **表面消费** + OpenClaw 的其余部分会读取注册表,以暴露工具、渠道、提供商设置、hooks、HTTP 路由、CLI 命令和服务。 -对于插件 CLI 而言,根命令发现专门分为两个阶段: +对于插件 CLI,根命令发现专门拆分为两个阶段: -- 解析时元数据来自 `registerCli(..., { descriptors: [...] })` -- 真实的插件 CLI 模块可以保持惰性,仅在首次调用时注册 +- 解析阶段元数据来自 `registerCli(..., { descriptors: [...] })` +- 真正的插件 CLI 模块可以保持惰性,并在首次调用时再注册 -这样可以让插件自有的 CLI 代码保留在插件内部,同时仍允许 OpenClaw 在解析前预留根命令名称。 +这样既能让插件自有 CLI 代码保留在插件内部,又能让 OpenClaw 在解析前预留根命令名称。 重要的设计边界是: -- 发现 + 配置验证应该能基于**清单 / schema 元数据**完成,而无需执行插件代码 -- 原生运行时行为来自插件模块的 `register(api)` 路径 +- 发现 + 配置校验应当仅依赖**清单 / schema 元数据**即可完成,而无需执行插件代码 +- 原生运行时行为来自插件模块中的 `register(api)` 路径 -这种拆分使 OpenClaw 能够在完整运行时尚未激活时,就验证配置、解释插件缺失 / 禁用原因,并构建 UI / schema 提示。 +这种拆分使 OpenClaw 能够在完整运行时尚未激活前,就完成配置校验、解释缺失 / 已禁用插件的原因,并构建 UI / schema 提示。 -### 渠道插件和共享 message 工具 +### 渠道插件与共享消息工具 -对于常规聊天操作,渠道插件不需要单独注册发送 / 编辑 / 响应工具。OpenClaw 在核心中保留了一个共享的 `message` 工具,而渠道插件则在其背后拥有渠道特定的发现和执行逻辑。 +对于常规聊天动作,渠道插件不需要单独注册发送 / 编辑 / 反应工具。OpenClaw 在核心中保留了一个共享的 `message` 工具,而渠道插件负责其背后的渠道特定发现与执行。 当前边界如下: -- 核心拥有共享 `message` 工具宿主、提示词接线、会话 / 线程记录以及执行分发 -- 渠道插件拥有作用域动作发现、能力发现,以及任何渠道特定的 schema 片段 -- 渠道插件拥有提供商特定的会话对话语法,例如对话 id 如何编码线程 id,或如何从父会话继承 +- 核心负责共享 `message` 工具宿主、提示词接线、session / 线程簿记以及执行分发 +- 渠道插件负责作用域化动作发现、能力发现以及任何渠道特定的 schema 片段 +- 渠道插件负责提供商特定的 session 会话语法,例如会话 ID 如何编码线程 ID,或如何从父会话继承 - 渠道插件通过其动作适配器执行最终动作 -对于渠道插件,SDK 接口为 `ChannelMessageActionAdapter.describeMessageTool(...)`。这个统一的发现调用允许插件一起返回其可见动作、能力和 schema 扩展,从而避免这些部分发生漂移。 +对于渠道插件,SDK 表面是 `ChannelMessageActionAdapter.describeMessageTool(...)`。这个统一的发现调用允许插件将可见动作、能力和 schema 贡献一起返回,从而避免这些部分彼此漂移。 + +当某个渠道特定的消息工具参数携带媒体来源,例如本地路径或远程媒体 URL 时,插件还应从 `describeMessageTool(...)` 返回 `mediaSourceParams`。核心会使用这个显式列表来应用沙箱路径标准化和出站媒体访问提示,而不是硬编码插件自有参数名。 +这里应优先使用按动作划分的映射,而不是一个覆盖整个渠道的扁平列表,这样仅用于 profile 的媒体参数就不会在 `send` 等无关动作上被标准化。 核心会将运行时作用域传入该发现步骤。重要字段包括: @@ -151,87 +154,87 @@ OpenClaw 的插件系统包含四层: - `sessionKey` - `sessionId` - `agentId` -- 可信入站 `requesterSenderId` +- 受信任的入站 `requesterSenderId` -这对于上下文敏感型插件非常重要。渠道可以根据当前账户、当前房间 / 线程 / 消息,或可信请求者身份来隐藏或暴露消息动作,而不需要在核心 `message` 工具中硬编码渠道特定分支。 +这对于上下文敏感型插件很重要。渠道可以根据活动账户、当前房间 / 线程 / 消息或受信任的请求者身份来隐藏或暴露消息动作,而无需在核心 `message` 工具中硬编码渠道特定分支。 -这也是为什么嵌入式 runner 路由变更仍然属于插件工作:runner 负责将当前聊天 / 会话身份转发到插件发现边界,以便共享 `message` 工具在当前轮次暴露正确的渠道自有接口。 +这也是为什么嵌入式 runner 路由变更仍然属于插件工作:runner 负责将当前聊天 / 会话身份转发到插件发现边界,以便共享 `message` 工具能够为当前轮次暴露正确的渠道自有表面。 -对于渠道自有的执行辅助工具,内置插件应将执行运行时保留在自己的扩展模块中。核心不再拥有位于 `src/agents/tools` 下的 Discord、Slack、Telegram 或 WhatsApp 消息动作运行时。我们不会发布单独的 `plugin-sdk/*-action-runtime` 子路径,内置插件应直接从其扩展自有模块中导入本地运行时代码。 +对于渠道自有执行辅助工具,内置插件应将执行运行时保留在其自身扩展模块中。核心不再拥有位于 `src/agents/tools` 下的 Discord、Slack、Telegram 或 WhatsApp 消息动作运行时。我们不会发布单独的 `plugin-sdk/*-action-runtime` 子路径,内置插件应直接从其扩展自有模块导入本地运行时代码。 -同样的边界也适用于一般性的提供商命名 SDK 接缝:核心不应导入 Slack、Discord、Signal、WhatsApp 或类似扩展的渠道特定便捷 barrel。如果核心需要某种行为,要么使用该内置插件自己的 `api.ts` / `runtime-api.ts` barrel,要么将需求提升为共享 SDK 中某个狭窄的通用能力。 +同样的边界也适用于一般意义上的提供商命名 SDK 接缝:核心不应导入 Slack、Discord、Signal、WhatsApp 或类似扩展的渠道特定便捷 barrel。如果核心需要某种行为,要么消费内置插件自己的 `api.ts` / `runtime-api.ts` barrel,要么将该需求提升为共享 SDK 中的一项狭窄通用能力。 -对于投票,当前有两条执行路径: +对于投票功能,具体有两条执行路径: -- `outbound.sendPoll` 是适用于符合通用投票模型的渠道的共享基线 -- `actions.handleAction("poll")` 是处理渠道特定投票语义或额外投票参数的优选路径 +- `outbound.sendPoll` 是适用于符合通用投票模型渠道的共享基线 +- `actions.handleAction("poll")` 是处理渠道特定投票语义或额外投票参数的首选路径 -现在,核心会在插件投票分发拒绝该动作之后,才延后执行共享投票解析,因此插件自有的投票处理器可以接受渠道特定的投票字段,而不会先被通用投票解析器拦住。 +如今,核心会先尝试插件投票分发,只有在其拒绝该动作后,才会继续共享投票解析。这样,插件自有的投票处理器就能接受渠道特定的投票字段,而不会先被通用投票解析器拦住。 完整启动顺序请参见[加载流水线](#load-pipeline)。 ## 能力归属模型 -OpenClaw 将一个原生插件视为某个**公司**或某项**功能**的归属边界,而不是一堆互不相关集成的集合。 +OpenClaw 将原生插件视为**公司**或**功能**的归属边界,而不是一个随意拼凑的无关集成包。 这意味着: -- 一个公司插件通常应拥有该公司的全部 OpenClaw 对外接口 -- 一个功能插件通常应拥有其引入的完整功能接口 -- 渠道应消费共享的核心能力,而不是临时重新实现提供商行为 +- 公司插件通常应拥有该公司的所有 OpenClaw 对外表面 +- 功能插件通常应拥有它引入的完整功能表面 +- 渠道应消费共享的核心能力,而不是临时重复实现提供商行为 示例: -- 内置 `openai` 插件拥有 OpenAI 模型提供商行为,以及 OpenAI 语音 + 实时语音 + 媒体理解 + 图像生成行为 +- 内置 `openai` 插件拥有 OpenAI 模型提供商行为,以及 OpenAI 的语音 + 实时语音 + 媒体理解 + 图像生成行为 - 内置 `elevenlabs` 插件拥有 ElevenLabs 语音行为 - 内置 `microsoft` 插件拥有 Microsoft 语音行为 -- 内置 `google` 插件拥有 Google 模型提供商行为,以及 Google 媒体理解 + 图像生成 + Web 搜索行为 -- 内置 `firecrawl` 插件拥有 Firecrawl Web 抓取行为 +- 内置 `google` 插件拥有 Google 模型提供商行为,以及 Google 的媒体理解 + 图像生成 + 网页搜索行为 +- 内置 `firecrawl` 插件拥有 Firecrawl 网页抓取行为 - 内置 `minimax`、`mistral`、`moonshot` 和 `zai` 插件拥有它们各自的媒体理解后端 - 内置 `qwen` 插件拥有 Qwen 文本提供商行为,以及媒体理解和视频生成行为 -- `voice-call` 插件是一个功能插件:它拥有通话传输、工具、CLI、路由和 Twilio 媒体流桥接,但它会消费共享的语音能力以及实时转录和实时语音能力,而不是直接导入供应商插件 +- `voice-call` 插件是一个功能插件:它拥有通话传输、工具、CLI、路由和 Twilio 媒体流桥接,但它会消费共享的语音以及实时转写和实时语音能力,而不是直接导入厂商插件 预期的最终状态是: -- 即使 OpenAI 横跨文本模型、语音、图像以及未来的视频能力,它也应存在于同一个插件中 -- 另一个供应商也可以用同样方式覆盖它自己的接口范围 -- 渠道并不关心哪个供应商插件拥有该提供商;它们消费的是核心暴露的共享能力契约 +- 即使 OpenAI 横跨文本模型、语音、图像以及未来的视频能力,它也仍然位于一个插件中 +- 其他厂商也可以用同样的方式统一管理它们自己的表面能力 +- 渠道不关心具体由哪个厂商插件拥有该提供商;它们只消费核心暴露出来的共享能力契约 -这是关键区别: +这就是关键区别: - **plugin** = 归属边界 - **capability** = 可由多个插件实现或消费的核心契约 -因此,如果 OpenClaw 新增一个领域,比如视频,第一个问题不应该是“哪个提供商应该硬编码视频处理?”第一个问题应该是“核心视频能力契约是什么?”一旦这个契约存在,供应商插件就可以针对它进行注册,而渠道 / 功能插件也可以消费它。 +因此,如果 OpenClaw 新增一个领域,例如视频,首先要问的不是“哪个提供商应该硬编码视频处理?”首先要问的是“核心的视频能力契约是什么?”一旦这个契约存在,厂商插件就可以针对它进行注册,而渠道 / 功能插件也可以消费它。 如果该能力尚不存在,通常正确的做法是: 1. 在核心中定义缺失的能力 -2. 通过插件 API / 运行时以类型化方式暴露它 -3. 让渠道 / 功能接入该能力 -4. 让供应商插件注册实现 +2. 以类型化方式通过插件 API / 运行时暴露它 +3. 让渠道 / 功能对接该能力 +4. 让厂商插件注册实现 -这样既能保持归属清晰,又能避免核心行为依赖单一供应商或某条一次性的插件特定代码路径。 +这样既能让归属关系保持明确,又能避免核心行为依赖单一厂商或某条一次性的插件专用代码路径。 ### 能力分层 -在决定代码应放在哪里时,请使用以下思维模型: +在决定代码应放在哪里时,可以使用以下心智模型: -- **核心能力层**:共享编排、策略、回退、配置合并规则、传递语义和类型化契约 -- **供应商插件层**:供应商特定 API、认证、模型目录、语音合成、图像生成、未来视频后端、用量端点 -- **渠道 / 功能插件层**:Slack / Discord / voice-call / 等集成,它们消费核心能力并在某个界面上呈现出来 +- **核心能力层**:共享编排、策略、回退、配置合并规则、交付语义和类型化契约 +- **厂商插件层**:厂商特定 API、认证、模型目录、语音合成、图像生成、未来视频后端、用量端点 +- **渠道 / 功能插件层**:Slack / Discord / voice-call 等集成,它们消费核心能力并将其呈现在某个表面上 -例如,TTS 遵循以下形态: +例如,TTS 遵循如下形态: -- 核心拥有回复时 TTS 策略、回退顺序、偏好和渠道投递 +- 核心拥有回复时 TTS 策略、回退顺序、偏好和渠道交付 - `openai`、`elevenlabs` 和 `microsoft` 拥有合成实现 - `voice-call` 消费电话 TTS 运行时辅助工具 -对于未来的能力,也应优先采用同样的模式。 +未来的能力也应优先采用相同模式。 ### 多能力公司插件示例 -一个公司插件从外部看起来应该是连贯的。如果 OpenClaw 对模型、语音、实时转录、实时语音、媒体理解、图像生成、视频生成、Web 抓取和 Web 搜索都有共享契约,那么一个供应商可以在一个地方拥有其全部接口: +从外部看,公司插件应当呈现出一致性。如果 OpenClaw 已经为模型、语音、实时转写、实时语音、媒体理解、图像生成、视频生成、网页抓取和网页搜索提供了共享契约,那么一个厂商就可以在一个地方拥有其全部表面能力: ```ts import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/plugin-entry"; @@ -285,105 +288,101 @@ const plugin: OpenClawPluginDefinition = { export default plugin; ``` -重要的不是确切的辅助函数名称,而是这种形态: +重要的不是辅助工具名称是否完全一致。关键在于这种形态: -- 一个插件拥有该供应商的接口 +- 一个插件拥有厂商表面 - 核心仍然拥有能力契约 -- 渠道和功能插件消费 `api.runtime.*` 辅助工具,而不是供应商代码 -- 契约测试可以断言该插件确实注册了它声称拥有的能力 +- 渠道和功能插件消费 `api.runtime.*` 辅助工具,而不是厂商代码 +- 契约测试可以断言该插件注册了它声称拥有的那些能力 ### 能力示例:视频理解 -OpenClaw 已经将图像 / 音频 / 视频理解视为一个共享能力。相同的归属模型也适用于这里: +OpenClaw 已经将图像 / 音频 / 视频理解视为一项共享能力。这里同样适用相同的归属模型: -1. 核心定义 media-understanding 契约 -2. 供应商插件按需注册 `describeImage`、`transcribeAudio` 和 `describeVideo` -3. 渠道和功能插件消费共享的核心行为,而不是直接接入供应商代码 +1. 核心定义媒体理解契约 +2. 厂商插件按需注册 `describeImage`、`transcribeAudio` 和 `describeVideo` +3. 渠道和功能插件消费共享核心行为,而不是直接连接到厂商代码 -这样可以避免将某个提供商的视频假设硬编码进核心。插件拥有供应商接口;核心拥有能力契约和回退行为。 +这样可以避免将某个提供商对视频的假设直接烘焙进核心。插件拥有厂商表面;核心拥有能力契约和回退行为。 -视频生成也已经采用同样的顺序:核心拥有类型化能力契约和运行时辅助工具,而供应商插件针对它注册 `api.registerVideoGenerationProvider(...)` 实现。 +视频生成也已经采用了同样的顺序:核心拥有类型化能力契约和运行时辅助工具,而厂商插件则针对它注册 `api.registerVideoGenerationProvider(...)` 实现。 -需要一份具体的发布检查清单吗?请参见 -[能力扩展手册](/zh-CN/plugins/architecture)。 +需要一份具体的发布清单吗?请参见[能力扩展手册](/zh-CN/plugins/architecture)。 ## 契约与强制执行 -插件 API 接口被有意设计为类型化,并集中定义在 -`OpenClawPluginApi` 中。这个契约定义了受支持的注册点,以及插件可以依赖的运行时辅助工具。 +插件 API 表面有意通过 `OpenClawPluginApi` 进行类型化和集中化管理。该契约定义了受支持的注册点,以及插件可依赖的运行时辅助工具。 -这很重要,因为: +这之所以重要,是因为: -- 插件作者可获得一个稳定的内部标准 +- 插件作者可以获得一个稳定的内部标准 - 核心可以拒绝重复归属,例如两个插件注册相同的 provider id -- 启动时可以为格式错误的注册暴露可执行的诊断信息 -- 契约测试可以强制执行内置插件归属,并防止无声漂移 +- 启动时可以为格式错误的注册提供可执行的诊断信息 +- 契约测试可以强制执行内置插件的归属关系,并防止静默漂移 有两层强制执行: 1. **运行时注册强制执行** - 插件注册表会在插件加载时验证注册内容。例如:重复的 provider id、重复的 speech provider id,以及格式错误的注册,都会产生插件诊断,而不是导致未定义行为。 + 插件注册表会在插件加载时校验注册内容。例如:重复的 provider id、重复的语音提供商 id,以及格式错误的注册,都会生成插件诊断,而不是导致未定义行为。 2. **契约测试** - 内置插件会在测试运行期间被捕获到契约注册表中,以便 OpenClaw 能显式断言归属。当前这用于模型提供商、语音提供商、Web 搜索提供商以及内置注册归属。 + 在测试运行期间,内置插件会被捕获到契约注册表中,从而让 OpenClaw 可以显式断言归属关系。如今这已用于模型提供商、语音提供商、网页搜索提供商和内置注册归属。 -其实际效果是,OpenClaw 能够预先知道哪个插件拥有哪个接口。这使核心和渠道能够无缝组合,因为归属是声明式的、类型化的、可测试的,而不是隐式的。 +实际效果是,OpenClaw 会提前知道哪个插件拥有哪个表面。因此核心和渠道可以无缝组合,因为归属关系是显式声明、类型化并可测试的,而不是隐含存在的。 ### 什么应该属于契约 -好的插件契约应该是: +好的插件契约应当具备以下特点: -- 类型化的 -- 小而精的 -- 能力特定的 +- 类型化 +- 小而精 +- 能力专属 - 由核心拥有 - 可被多个插件复用 -- 可被渠道 / 功能消费,而无需了解供应商细节 +- 可在不了解厂商细节的前提下被渠道 / 功能消费 -不好的插件契约是: +糟糕的插件契约则是: -- 隐藏在核心中的供应商特定策略 +- 隐藏在核心中的厂商特定策略 - 绕过注册表的一次性插件逃生口 -- 渠道代码直接深入供应商实现 +- 渠道代码直接深入某个厂商实现 - 不属于 `OpenClawPluginApi` 或 `api.runtime` 的临时运行时对象 -如果不确定,就提升抽象层级:先定义能力,再让插件接入它。 +如果拿不准,就提升抽象层级:先定义能力,再让插件接入该能力。 ## 执行模型 -原生 OpenClaw 插件会与 Gateway 网关 **在同一进程内**运行。它们**没有**被沙箱隔离。一个已加载的原生插件与核心代码处于同样的进程级信任边界内。 +原生 OpenClaw 插件会与 Gateway 网关**在同一进程内**运行。它们没有沙箱隔离。一个已加载的原生插件与核心代码处于相同的进程级信任边界内。 这意味着: -- 原生插件可以注册工具、网络处理器、钩子和服务 -- 原生插件中的 bug 可能导致 gateway 崩溃或失稳 -- 恶意原生插件等同于在 OpenClaw 进程内执行任意代码 +- 原生插件可以注册工具、网络处理器、hooks 和服务 +- 原生插件中的 bug 可能导致网关崩溃或不稳定 +- 恶意原生插件等同于在 OpenClaw 进程内部执行任意代码 -兼容 bundle 默认更安全,因为 OpenClaw 当前将它们视为元数据 / 内容包。在当前版本中,这主要意味着内置的 Skills。 +兼容 bundle 默认更安全,因为 OpenClaw 当前会将它们视为元数据 / 内容包。在当前版本中,这主要意味着内置 Skills。 -对于非内置插件,请使用 allowlist 和显式的安装 / 加载路径。应将 workspace 插件视为开发期代码,而不是生产默认项。 +对于非内置插件,请使用 allowlist 和显式安装 / 加载路径。应将工作区插件视为开发阶段代码,而不是生产环境默认项。 -对于内置 workspace 包名,请让插件 id 锚定在 npm 名称中:默认使用 `@openclaw/`,或在包有意暴露更窄插件角色时,使用已批准的类型化后缀,例如 `-provider`、`-plugin`、`-speech`、`-sandbox` 或 `-media-understanding`。 +对于内置工作区包名称,应让插件 id 锚定在 npm 名称中:默认使用 `@openclaw/`,或者在包有意暴露更窄的插件角色时,使用经批准的类型后缀,例如 `-provider`、`-plugin`、`-speech`、`-sandbox` 或 `-media-understanding`。 -重要的信任说明: +重要信任说明: -- `plugins.allow` 信任的是**插件 id**,而不是来源出处。 -- 与某个内置插件拥有相同 id 的 workspace 插件,在启用 / 加入 allowlist 后,会有意遮蔽内置副本。 +- `plugins.allow` 信任的是**插件 id**,而不是来源证明。 +- 如果某个工作区插件与某个内置插件使用相同 id,那么当该工作区插件被启用 / 加入 allowlist 时,它会有意覆盖内置副本。 - 这属于正常且有用的行为,适用于本地开发、补丁测试和热修复。 ## 导出边界 -OpenClaw 导出的是能力,而不是实现层面的便捷接口。 +OpenClaw 导出的是能力,而不是实现层面的便捷工具。 -应保持能力注册为公共接口,并收紧非契约辅助导出: +保持能力注册为公共接口。裁剪非契约辅助导出: -- 内置插件特定的辅助子路径 -- 不打算作为公共 API 的运行时接线子路径 -- 供应商特定的便捷辅助工具 +- 内置插件专用辅助子路径 +- 不打算作为公共 API 的运行时管线子路径 +- 厂商特定便捷辅助工具 - 属于实现细节的设置 / 新手引导辅助工具 -出于兼容性和内置插件维护需要,一些内置插件辅助子路径仍保留在生成的 SDK 导出映射中。当前示例包括 -`plugin-sdk/feishu`、`plugin-sdk/feishu-setup`、`plugin-sdk/zalo`、 -`plugin-sdk/zalo-setup` 以及若干 `plugin-sdk/matrix*` 接缝。应将这些视为保留的实现细节导出,而不是新第三方插件推荐采用的 SDK 模式。 +出于兼容性和内置插件维护需要,一些内置插件辅助子路径仍然保留在生成的 SDK 导出映射中。当前示例包括 `plugin-sdk/feishu`、`plugin-sdk/feishu-setup`、`plugin-sdk/zalo`、`plugin-sdk/zalo-setup` 以及若干 `plugin-sdk/matrix*` 接缝。应将这些视为保留的实现细节导出,而不是推荐给新的第三方插件使用的 SDK 模式。 ## 加载流水线 @@ -391,67 +390,65 @@ OpenClaw 导出的是能力,而不是实现层面的便捷接口。 1. 发现候选插件根目录 2. 读取原生或兼容 bundle 的清单和包元数据 -3. 拒绝不安全的候选项 -4. 规范化插件配置(`plugins.enabled`、`allow`、`deny`、`entries`、`slots`、`load.paths`) -5. 为每个候选项决定启用状态 +3. 拒绝不安全候选项 +4. 标准化插件配置(`plugins.enabled`、`allow`、`deny`、`entries`、`slots`、`load.paths`) +5. 决定每个候选项的启用状态 6. 通过 jiti 加载已启用的原生模块 -7. 调用原生 `register(api)`(或 `activate(api)` —— 旧版别名)钩子,并将注册内容收集到插件注册表中 -8. 将注册表暴露给命令 / 运行时接口 +7. 调用原生 `register(api)`(或 `activate(api)` —— 一个旧版别名)hooks,并将注册内容收集到插件注册表中 +8. 将注册表暴露给命令 / 运行时表面 -`activate` 是 `register` 的旧版别名——加载器会解析存在的那个(`def.register ?? def.activate`),并在同一时机调用它。所有内置插件都使用 `register`;新插件请优先使用 `register`。 +`activate` 是 `register` 的旧版别名 —— 加载器会解析当前存在的那个(`def.register ?? def.activate`),并在相同位置调用它。所有内置插件都使用 `register`;新插件请优先使用 `register`。 -安全检查会发生在**运行时代码执行之前**。当入口逃逸出插件根目录、路径可被所有人写入,或对于非内置插件而言路径归属看起来可疑时,候选项会被阻止。 +安全门会在运行时代码执行**之前**发生。如果入口逃逸出插件根目录、路径对所有人可写,或者对于非内置插件而言路径所有权可疑,那么候选项就会被阻止。 -### 清单优先行为 +### Manifest 优先行为 -清单是控制平面的事实来源。OpenClaw 用它来: +清单是控制平面的事实来源。OpenClaw 使用它来: - 标识插件 -- 发现声明的渠道 / Skills / 配置 schema 或 bundle 能力 -- 验证 `plugins.entries..config` +- 发现已声明的渠道 / Skills / 配置 schema 或 bundle 能力 +- 校验 `plugins.entries..config` - 增强 Control UI 标签 / 占位符 - 显示安装 / 目录元数据 -- 在不加载插件运行时的情况下保留轻量级激活和设置描述符 +- 在不加载插件运行时的情况下保留轻量激活和设置描述符 -对于原生插件,运行时模块是数据平面部分。它会注册实际行为,例如钩子、工具、命令或提供商流程。 +对于原生插件,运行时模块则是数据平面的组成部分。它会注册实际行为,例如 hooks、工具、命令或提供商流程。 -可选的清单 `activation` 和 `setup` 区块仍属于控制平面。它们是用于激活规划和设置发现的纯元数据描述符;它们不会替代运行时注册、`register(...)` 或 `setupEntry`。 -首批实时激活消费者现在会使用清单中的命令、渠道和提供商提示,在更广泛的注册表实体化之前先缩小插件加载范围: +可选的清单 `activation` 和 `setup` 区块仍然属于控制平面。它们是用于激活规划和设置发现的纯元数据描述符;不会替代运行时注册、`register(...)` 或 `setupEntry`。 +首批实际激活消费者如今会使用清单中的命令、渠道和提供商提示,在更广泛的注册表物化之前先缩小插件加载范围: - CLI 加载会缩小到拥有所请求主命令的插件 -- 渠道设置 / 插件解析会缩小到拥有所请求 channel id 的插件 -- 显式提供商设置 / 运行时解析会缩小到拥有所请求 provider id 的插件 +- 渠道设置 / 插件解析会缩小到拥有所请求渠道 id 的插件 +- 显式的提供商设置 / 运行时解析会缩小到拥有所请求 provider id 的插件 -设置发现现在会优先使用描述符自有 id,例如 `setup.providers` 和 `setup.cliBackends`,以便在回退到 `setup-api` 之前先缩小候选插件范围;后者仅用于那些仍需要设置时运行时钩子的插件。如果多个已发现插件声称拥有相同的规范化 setup provider 或 CLI backend id,则设置查找会拒绝这个歧义归属,而不是依赖发现顺序。 +现在,设置发现会优先使用由描述符拥有的 id,例如 `setup.providers` 和 `setup.cliBackends`,以便在回退到 `setup-api` 之前先缩小候选插件范围;而 `setup-api` 仅用于那些仍然需要设置阶段运行时 hooks 的插件。如果多个已发现插件声明了相同的标准化设置提供商或 CLI 后端 id,那么设置查找会拒绝这个存在歧义的归属方,而不是依赖发现顺序。 ### 加载器会缓存什么 -OpenClaw 会保留一些短生命周期的进程内缓存,用于: +OpenClaw 会在进程内保留一些短期缓存,用于: - 发现结果 - 清单注册表数据 - 已加载的插件注册表 -这些缓存可减少突发式启动和重复命令的开销。你可以将它们视为短期性能缓存,而不是持久化机制。 +这些缓存可以减少突发启动开销和重复命令开销。可以将它们安全地理解为短生命周期的性能缓存,而不是持久化存储。 性能说明: -- 设置 `OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1` 或 - `OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1` 可禁用这些缓存。 -- 使用 `OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS` 和 - `OPENCLAW_PLUGIN_MANIFEST_CACHE_MS` 调整缓存窗口。 +- 设置 `OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1` 或 `OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1` 可禁用这些缓存。 +- 使用 `OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS` 和 `OPENCLAW_PLUGIN_MANIFEST_CACHE_MS` 调整缓存窗口。 ## 注册表模型 -已加载的插件不会直接修改任意核心全局对象。它们会注册到一个中央插件注册表中。 +已加载插件不会直接随意修改核心中的全局状态。它们会注册到一个中央插件注册表中。 -注册表会跟踪: +该注册表会跟踪: -- 插件记录(身份、来源、起源、状态、诊断) +- 插件记录(标识、来源、起源、状态、诊断) - 工具 -- 旧版钩子和类型化钩子 +- 旧版 hooks 和类型化 hooks - 渠道 - 提供商 - Gateway 网关 RPC 处理器 @@ -460,18 +457,18 @@ OpenClaw 会保留一些短生命周期的进程内缓存,用于: - 后台服务 - 插件自有命令 -然后,核心功能会从该注册表读取信息,而不是直接与插件模块交互。这样可保持单向加载: +随后,核心功能会从该注册表读取信息,而不是直接与插件模块对话。这样可以让加载保持单向: - 插件模块 -> 注册表注册 - 核心运行时 -> 注册表消费 -这种分离对于可维护性非常重要。它意味着大多数核心接口只需要一个集成点:“读取注册表”,而不是“为每个插件模块编写特例”。 +这种分离对于可维护性很重要。它意味着大多数核心表面只需要一个集成点:“读取注册表”,而不是“为每个插件模块编写特殊分支”。 -## 对话绑定回调 +## 会话绑定回调 -绑定对话的插件可以在审批结果确定后作出响应。 +绑定会话的插件可以在审批结果确定后作出响应。 -使用 `api.onConversationBindingResolved(...)` 可以在绑定请求被批准或拒绝后收到回调: +使用 `api.onConversationBindingResolved(...)` 可以在绑定请求被批准或拒绝后接收回调: ```ts export default { @@ -495,81 +492,81 @@ export default { - `status`:`"approved"` 或 `"denied"` - `decision`:`"allow-once"`、`"allow-always"` 或 `"deny"` -- `binding`:对于已批准请求,为解析后的绑定 -- `request`:原始请求摘要、分离提示、发送者 id 和对话元数据 +- `binding`:已批准请求对应的已解析绑定 +- `request`:原始请求摘要、分离提示、发送者 id 以及会话元数据 -该回调仅用于通知。它不会改变谁被允许绑定对话,并且会在核心审批处理完成后运行。 +此回调仅用于通知。它不会改变谁有权绑定会话,并且会在核心审批处理完成后才运行。 -## 提供商运行时钩子 +## 提供商运行时 hooks -提供商插件现在分为两层: +提供商插件现在有两层: -- 清单元数据:`providerAuthEnvVars` 用于在运行时加载前进行轻量的提供商环境变量认证查找,`providerAuthAliases` 用于共享认证信息的提供商变体,`channelEnvVars` 用于在运行时加载前进行轻量的渠道环境变量 / 设置查找,此外还有 `providerAuthChoices`,用于在运行时加载前提供轻量的新手引导 / 认证选择标签和 CLI flag 元数据 -- 配置时钩子:`catalog` / 旧版 `discovery` 以及 `applyConfigDefaults` -- 运行时钩子:`normalizeModelId`、`normalizeTransport`、`normalizeConfig`、`applyNativeStreamingUsageCompat`、`resolveConfigApiKey`、`resolveSyntheticAuth`、`resolveExternalAuthProfiles`、`shouldDeferSyntheticProfileAuth`、`resolveDynamicModel`、`prepareDynamicModel`、`normalizeResolvedModel`、`contributeResolvedModelCompat`、`capabilities`、`normalizeToolSchemas`、`inspectToolSchemas`、`resolveReasoningOutputMode`、`prepareExtraParams`、`createStreamFn`、`wrapStreamFn`、`resolveTransportTurnState`、`resolveWebSocketSessionPolicy`、`formatApiKey`、`refreshOAuth`、`buildAuthDoctorHint`、`matchesContextOverflowError`、`classifyFailoverReason`、`isCacheTtlEligible`、`buildMissingAuthMessage`、`suppressBuiltInModel`、`augmentModelCatalog`、`isBinaryThinking`、`supportsXHighThinking`、`resolveDefaultThinkingLevel`、`isModernModelRef`、`prepareRuntimeAuth`、`resolveUsageAuth`、`fetchUsageSnapshot`、`createEmbeddingProvider`、`buildReplayPolicy`、`sanitizeReplayHistory`、`validateReplayTurns`、`onModelSelected` +- 清单元数据:`providerAuthEnvVars` 用于在运行时加载之前,以低成本执行基于环境变量的提供商认证查找;`providerAuthAliases` 用于共享认证的提供商变体;`channelEnvVars` 用于在运行时加载之前,以低成本执行基于环境变量的渠道环境 / 设置查找;以及 `providerAuthChoices`,用于在运行时加载之前,以低成本提供新手引导 / 认证选项标签和 CLI 标志元数据 +- 配置阶段 hooks:`catalog` / 旧版 `discovery` 以及 `applyConfigDefaults` +- 运行时 hooks:`normalizeModelId`、`normalizeTransport`、`normalizeConfig`、`applyNativeStreamingUsageCompat`、`resolveConfigApiKey`、`resolveSyntheticAuth`、`resolveExternalAuthProfiles`、`shouldDeferSyntheticProfileAuth`、`resolveDynamicModel`、`prepareDynamicModel`、`normalizeResolvedModel`、`contributeResolvedModelCompat`、`capabilities`、`normalizeToolSchemas`、`inspectToolSchemas`、`resolveReasoningOutputMode`、`prepareExtraParams`、`createStreamFn`、`wrapStreamFn`、`resolveTransportTurnState`、`resolveWebSocketSessionPolicy`、`formatApiKey`、`refreshOAuth`、`buildAuthDoctorHint`、`matchesContextOverflowError`、`classifyFailoverReason`、`isCacheTtlEligible`、`buildMissingAuthMessage`、`suppressBuiltInModel`、`augmentModelCatalog`、`isBinaryThinking`、`supportsXHighThinking`、`resolveDefaultThinkingLevel`、`isModernModelRef`、`prepareRuntimeAuth`、`resolveUsageAuth`、`fetchUsageSnapshot`、`createEmbeddingProvider`、`buildReplayPolicy`、`sanitizeReplayHistory`、`validateReplayTurns`、`onModelSelected` -OpenClaw 仍然拥有通用智能体循环、故障切换、转录处理和工具策略。这些钩子是在无需实现完整自定义推理传输的情况下,扩展提供商特定行为的接口。 +OpenClaw 仍然拥有通用智能体循环、故障切换、转录处理和工具策略。这些 hooks 是提供商特定行为的扩展表面,因此无需实现整套自定义推理传输。 -当提供商具有基于环境变量的凭证,并且通用认证 / 状态 / 模型选择器路径需要在不加载插件运行时的前提下感知这些凭证时,请使用清单中的 `providerAuthEnvVars`。当一个 provider id 应复用另一个 provider id 的环境变量、认证配置文件、基于配置的认证和 API key 新手引导选项时,请使用清单中的 `providerAuthAliases`。当新手引导 / 认证选择 CLI 接口需要在不加载提供商运行时的情况下,了解该提供商的 choice id、分组标签和简单单 flag 认证接线时,请使用清单中的 `providerAuthChoices`。请将提供商运行时的 `envVars` 保留给面向操作员的提示信息,例如新手引导标签或 OAuth client-id / client-secret 设置变量。 +当提供商拥有基于环境变量的凭证,并且通用认证 / 状态 / 模型选择器路径需要在不加载插件运行时的情况下识别这些凭证时,请使用清单中的 `providerAuthEnvVars`。当一个 provider id 需要复用另一个 provider id 的环境变量、认证配置文件、基于配置的认证以及 API 密钥新手引导选项时,请使用清单中的 `providerAuthAliases`。当新手引导 / 认证选项 CLI 表面需要在不加载提供商运行时的情况下知道该提供商的选项 id、分组标签以及简单的单标志认证接线时,请使用清单中的 `providerAuthChoices`。请将提供商运行时 `envVars` 保留给面向操作者的提示,例如新手引导标签或 OAuth client-id / client-secret 设置环境变量。 -当某个渠道具有基于环境变量驱动的认证或设置,并且通用 shell 环境变量回退、配置 / 状态检查或设置提示需要在不加载渠道运行时的前提下感知这些信息时,请使用清单中的 `channelEnvVars`。 +当某个渠道拥有基于环境变量驱动的认证或设置,并且通用 shell 环境变量回退、配置 / 状态检查或设置提示需要在不加载渠道运行时的情况下感知这些信息时,请使用清单中的 `channelEnvVars`。 -### 钩子顺序和用法 +### Hook 顺序和用法 -对于模型 / 提供商插件,OpenClaw 会大致按以下顺序调用这些钩子。 +对于模型 / 提供商插件,OpenClaw 大致会按以下顺序调用 hooks。 “何时使用”这一列是快速决策指南。 -| # | 钩子 | 作用 | 何时使用 | +| # | Hook | 作用 | 何时使用 | | --- | --------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 | `catalog` | 在生成 `models.json` 期间,将提供商配置发布到 `models.providers` 中 | 当提供商拥有目录或 base URL 默认值时 | -| 2 | `applyConfigDefaults` | 在配置实体化期间,应用提供商自有的全局配置默认值 | 当默认值依赖认证模式、环境变量或提供商模型家族语义时 | -| -- | _(内置模型查找)_ | OpenClaw 会先尝试常规的注册表 / 目录路径 | _(这不是插件钩子)_ | -| 3 | `normalizeModelId` | 在查找前规范化旧版或预览版 model-id 别名 | 当提供商需要在规范模型解析前拥有别名清理逻辑时 | -| 4 | `normalizeTransport` | 在通用模型组装前,规范化提供商家族的 `api` / `baseUrl` | 当提供商需要为同一传输家族中的自定义 provider id 提供传输清理时 | -| 5 | `normalizeConfig` | 在运行时 / 提供商解析前,规范化 `models.providers.` | 当提供商需要将配置清理逻辑保留在插件内部时;内置的 Google 家族辅助工具也会为受支持的 Google 配置条目提供兜底 | -| 6 | `applyNativeStreamingUsageCompat` | 对配置提供商应用原生流式用量兼容性改写 | 当提供商需要基于端点驱动的原生流式用量元数据修复时 | -| 7 | `resolveConfigApiKey` | 在加载运行时认证前,为配置提供商解析环境变量标记认证 | 当提供商拥有自有的环境变量标记 API key 解析逻辑时;`amazon-bedrock` 在这里也有内置的 AWS 环境变量标记解析器 | -| 8 | `resolveSyntheticAuth` | 在不持久化明文的情况下暴露本地 / 自托管或基于配置的认证 | 当提供商可以使用 synthetic / 本地凭证标记运行时 | -| 9 | `resolveExternalAuthProfiles` | 叠加提供商自有的外部认证配置文件;CLI / 应用自有凭证的默认 `persistence` 为 `runtime-only` | 当提供商需要复用外部认证凭证,而不持久化复制的刷新令牌时 | -| 10 | `shouldDeferSyntheticProfileAuth` | 将已存储的 synthetic 配置文件占位符降级到环境变量 / 基于配置的认证之后 | 当提供商会存储 synthetic 占位配置文件,但这些占位符不应优先命中时 | -| 11 | `resolveDynamicModel` | 为尚未进入本地注册表的提供商自有模型 id 提供同步回退 | 当提供商接受任意上游模型 id 时 | -| 12 | `prepareDynamicModel` | 执行异步预热,然后再次运行 `resolveDynamicModel` | 当提供商在解析未知 id 之前需要网络元数据时 | -| 13 | `normalizeResolvedModel` | 在嵌入式 runner 使用解析后的模型之前做最终改写 | 当提供商需要进行传输改写,但仍然使用核心传输时 | -| 14 | `contributeResolvedModelCompat` | 为运行在另一种兼容传输之上的供应商模型提供兼容标志 | 当提供商能在不接管 provider 的情况下识别自己在代理传输上的模型时 | -| 15 | `capabilities` | 由共享核心逻辑使用的提供商自有转录 / 工具元数据 | 当提供商需要处理转录 / 提供商家族特有差异时 | -| 16 | `normalizeToolSchemas` | 在嵌入式 runner 看到工具 schema 之前进行规范化 | 当提供商需要进行传输家族级的 schema 清理时 | -| 17 | `inspectToolSchemas` | 在规范化后暴露提供商自有的 schema 诊断信息 | 当提供商希望给出关键字警告,而不需要让核心理解提供商特定规则时 | -| 18 | `resolveReasoningOutputMode` | 选择原生或带标签的推理输出契约 | 当提供商需要使用带标签的推理 / 最终输出,而不是原生字段时 | -| 19 | `prepareExtraParams` | 在通用流式选项包装器之前进行请求参数规范化 | 当提供商需要默认请求参数或按提供商清理参数时 | -| 20 | `createStreamFn` | 用自定义传输完全替换正常流路径 | 当提供商需要自定义线协议,而不仅仅是包装器时 | -| 21 | `wrapStreamFn` | 在应用通用包装器后,对流函数再做包装 | 当提供商需要请求头 / 请求体 / 模型兼容包装器,但不需要自定义传输时 | -| 22 | `resolveTransportTurnState` | 附加原生的逐轮传输头或元数据 | 当提供商希望通用传输发送提供商原生的轮次身份时 | +| 1 | `catalog` | 在生成 `models.json` 期间,将提供商配置发布到 `models.providers` 中 | 当提供商拥有一个目录,或拥有基础 `base URL` 默认值时 | +| 2 | `applyConfigDefaults` | 在配置物化期间应用由提供商拥有的全局配置默认值 | 当默认值依赖认证模式、环境或提供商模型族语义时 | +| -- | _(内置模型查找)_ | OpenClaw 会先尝试正常的注册表 / 目录路径 | _(这不是插件 hook)_ | +| 3 | `normalizeModelId` | 在查找前标准化旧版或预览版 model-id 别名 | 当提供商需要在规范模型解析前自行清理别名时 | +| 4 | `normalizeTransport` | 在通用模型组装前,标准化提供商家族的 `api` / `baseUrl` | 当同一传输家族中的自定义 provider id 需要由提供商自行完成传输清理时 | +| 5 | `normalizeConfig` | 在运行时 / 提供商解析前,标准化 `models.providers.` | 当提供商需要执行应与插件共置的配置清理时;内置 Google 家族辅助工具也会为受支持的 Google 配置项提供兜底 | +| 6 | `applyNativeStreamingUsageCompat` | 将原生分块流式传输用量兼容性重写应用到配置提供商 | 当提供商需要根据端点驱动修复原生分块流式传输用量元数据时 | +| 7 | `resolveConfigApiKey` | 在运行时认证加载前,为配置提供商解析环境标记认证 | 当提供商拥有由自身管理的环境标记 API 密钥解析逻辑时;`amazon-bedrock` 在此也有内置 AWS 环境标记解析器 | +| 8 | `resolveSyntheticAuth` | 在不持久化明文的前提下,暴露本地 / 自托管或基于配置的认证 | 当提供商可以使用合成 / 本地凭证标记运行时 | +| 9 | `resolveExternalAuthProfiles` | 叠加由提供商拥有的外部认证配置文件;默认 `persistence` 为 `runtime-only`,适用于 CLI / 应用自有凭证 | 当提供商需要复用外部认证凭证,而不持久化复制后的刷新令牌时 | +| 10 | `shouldDeferSyntheticProfileAuth` | 将已存储的合成配置文件占位符下调到基于环境 / 配置的认证之后 | 当提供商存储了合成占位配置文件,并且它们不应具有更高优先级时 | +| 11 | `resolveDynamicModel` | 为尚未存在于本地注册表中的提供商自有 model id 提供同步回退解析 | 当提供商接受任意上游 model id 时 | +| 12 | `prepareDynamicModel` | 执行异步预热,然后再次运行 `resolveDynamicModel` | 当提供商在解析未知 id 前需要网络元数据时 | +| 13 | `normalizeResolvedModel` | 在嵌入式 runner 使用已解析模型前进行最终重写 | 当提供商需要传输重写,但仍然使用核心传输时 | +| 14 | `contributeResolvedModelCompat` | 为在另一兼容传输后面的厂商模型贡献兼容标记 | 当提供商能够在代理传输上识别自己的模型,但又不接管该提供商时 | +| 15 | `capabilities` | 由提供商拥有、供共享核心逻辑使用的转录 / 工具元数据 | 当提供商需要处理转录 / 提供商家族的特殊行为时 | +| 16 | `normalizeToolSchemas` | 在嵌入式 runner 看到工具 schema 前将其标准化 | 当提供商需要进行传输家族级的 schema 清理时 | +| 17 | `inspectToolSchemas` | 在标准化后暴露由提供商拥有的 schema 诊断 | 当提供商希望给出关键字警告,而不需要让核心理解提供商特定规则时 | +| 18 | `resolveReasoningOutputMode` | 选择原生推理输出契约或带标签的推理输出契约 | 当提供商需要带标签的 reasoning / final 输出,而不是原生字段时 | +| 19 | `prepareExtraParams` | 在通用流选项包装器之前进行请求参数标准化 | 当提供商需要默认请求参数,或按提供商清理参数时 | +| 20 | `createStreamFn` | 用自定义传输完全替换正常流路径 | 当提供商需要自定义线路协议,而不只是一个包装器时 | +| 21 | `wrapStreamFn` | 在应用完通用包装器后对流函数再进行包装 | 当提供商需要请求头 / 请求体 / 模型兼容包装器,但不需要自定义传输时 | +| 22 | `resolveTransportTurnState` | 附加原生的逐轮传输头或元数据 | 当提供商希望通用传输发送提供商原生轮次标识时 | | 23 | `resolveWebSocketSessionPolicy` | 附加原生 WebSocket 头或会话冷却策略 | 当提供商希望通用 WS 传输调整会话头或回退策略时 | -| 24 | `formatApiKey` | 认证配置文件格式化器:将已存储配置文件转换为运行时 `apiKey` 字符串 | 当提供商存储了额外认证元数据,并需要自定义运行时 token 形态时 | -| 25 | `refreshOAuth` | 为自定义刷新端点或刷新失败策略覆盖 OAuth 刷新逻辑 | 当提供商不适配共享的 `pi-ai` 刷新器时 | -| 26 | `buildAuthDoctorHint` | 当 OAuth 刷新失败时追加修复提示 | 当提供商需要在刷新失败后提供自有的认证修复指导时 | -| 27 | `matchesContextOverflowError` | 提供商自有的上下文窗口溢出匹配器 | 当提供商存在通用启发式无法识别的原始溢出错误时 | -| 28 | `classifyFailoverReason` | 提供商自有的故障切换原因分类 | 当提供商可以将原始 API / 传输错误映射为限流 / 过载等类别时 | -| 29 | `isCacheTtlEligible` | 面向代理 / 回传提供商的提示词缓存策略 | 当提供商需要代理特定的缓存 TTL 门控时 | -| 30 | `buildMissingAuthMessage` | 替换通用的缺失认证恢复消息 | 当提供商需要提供商特定的缺失认证恢复提示时 | -| 31 | `suppressBuiltInModel` | 过时上游模型抑制,并可选提供面向用户的错误提示 | 当提供商需要隐藏过时的上游条目,或用供应商提示替换它们时 | -| 32 | `augmentModelCatalog` | 在发现后追加 synthetic / 最终目录条目 | 当提供商需要在 `models list` 和选择器中加入 synthetic 的前向兼容条目时 | -| 33 | `isBinaryThinking` | 面向 binary-thinking 提供商的开 / 关推理切换 | 当提供商只支持二元推理开 / 关时 | -| 34 | `supportsXHighThinking` | 为选定模型提供 `xhigh` 推理支持 | 当提供商只希望在部分模型上启用 `xhigh` 时 | -| 35 | `resolveDefaultThinkingLevel` | 为特定模型家族解析默认 `/think` 级别 | 当提供商拥有某个模型家族的默认 `/think` 策略时 | -| 36 | `isModernModelRef` | 用于实时配置文件筛选和 smoke 选择的现代模型匹配器 | 当提供商拥有实时 / smoke 首选模型匹配逻辑时 | -| 37 | `prepareRuntimeAuth` | 在推理前,将已配置凭证交换为真实运行时 token / key | 当提供商需要 token 交换或短生命周期请求凭证时 | -| 38 | `resolveUsageAuth` | 为 `/usage` 及相关状态接口解析用量 / 计费凭证 | 当提供商需要自定义用量 / 配额 token 解析,或需要不同的用量凭证时 | -| 39 | `fetchUsageSnapshot` | 在认证解析后获取并规范化提供商特定的用量 / 配额快照 | 当提供商需要提供商特定的用量端点或负载解析器时 | -| 40 | `createEmbeddingProvider` | 为 memory / search 构建提供商自有的嵌入适配器 | memory 嵌入行为应归属于提供商插件 | -| 41 | `buildReplayPolicy` | 返回一个控制该提供商转录处理方式的重放策略 | 当提供商需要自定义转录策略时(例如剥离 thinking block) | -| 42 | `sanitizeReplayHistory` | 在通用转录清理之后重写重放历史 | 当提供商需要在共享压缩辅助工具之外,再做提供商特定的重放改写时 | -| 43 | `validateReplayTurns` | 在嵌入式 runner 之前,对重放轮次做最终验证或重塑 | 当提供商传输在通用净化之后仍需要更严格的轮次验证时 | -| 44 | `onModelSelected` | 在模型被选中后运行提供商自有的副作用 | 当提供商需要在模型激活时记录遥测或维护提供商自有状态时 | +| 24 | `formatApiKey` | 认证配置文件格式化器:已存储配置文件会变成运行时 `apiKey` 字符串 | 当提供商存储了额外认证元数据,并且需要自定义运行时令牌形态时 | +| 25 | `refreshOAuth` | 针对自定义刷新端点或刷新失败策略覆盖 OAuth 刷新逻辑 | 当提供商不适配共享 `pi-ai` 刷新器时 | +| 26 | `buildAuthDoctorHint` | 在 OAuth 刷新失败时附加修复提示 | 当提供商需要在刷新失败后提供由自身拥有的认证修复指引时 | +| 27 | `matchesContextOverflowError` | 由提供商拥有的上下文窗口溢出匹配器 | 当提供商存在通用启发式无法识别的原始溢出错误时 | +| 28 | `classifyFailoverReason` | 由提供商拥有的故障切换原因分类 | 当提供商可以将原始 API / 传输错误映射为速率限制、过载等原因时 | +| 29 | `isCacheTtlEligible` | 适用于代理 / 回程提供商的提示词缓存 TTL 策略 | 当提供商需要按代理特性控制缓存 TTL 时 | +| 30 | `buildMissingAuthMessage` | 替换通用的认证缺失恢复消息 | 当提供商需要提供商特定的认证缺失恢复提示时 | +| 31 | `suppressBuiltInModel` | 抑制过时上游模型,并可附带面向用户的错误提示 | 当提供商需要隐藏过时的上游条目,或用厂商提示替换它们时 | +| 32 | `augmentModelCatalog` | 在发现之后追加合成 / 最终目录条目 | 当提供商需要在 `models list` 和选择器中加入合成的前向兼容条目时 | +| 33 | `isBinaryThinking` | 为二元 thinking 提供商提供开 / 关推理切换 | 当提供商只暴露二元的 thinking 开 / 关能力时 | +| 34 | `supportsXHighThinking` | 为选定模型提供 `xhigh` 推理支持 | 当提供商只希望部分模型支持 `xhigh` 时 | +| 35 | `resolveDefaultThinkingLevel` | 为特定模型族解析默认 `/think` 级别 | 当提供商拥有某个模型族的默认 `/think` 策略时 | +| 36 | `isModernModelRef` | 用于 live 配置文件过滤和 smoke 选择的现代模型匹配器 | 当提供商拥有 live / smoke 首选模型匹配逻辑时 | +| 37 | `prepareRuntimeAuth` | 在推理前,将已配置凭证交换为实际运行时令牌 / 密钥 | 当提供商需要进行令牌交换或生成短期请求凭证时 | +| 38 | `resolveUsageAuth` | 为 `/usage` 及相关状态表面解析用量 / 计费凭证 | 当提供商需要自定义用量 / 配额令牌解析,或需要使用不同的用量凭证时 | +| 39 | `fetchUsageSnapshot` | 在认证解析完成后抓取并标准化提供商特定的用量 / 配额快照 | 当提供商需要提供商特定的用量端点或负载解析器时 | +| 40 | `createEmbeddingProvider` | 为 memory / 搜索构建由提供商拥有的嵌入适配器 | memory 嵌入行为应归属于提供商插件 | +| 41 | `buildReplayPolicy` | 返回一个控制该提供商转录处理方式的重放策略 | 当提供商需要自定义转录策略时(例如去除 thinking 块) | +| 42 | `sanitizeReplayHistory` | 在通用转录清理之后重写重放历史 | 当提供商需要在共享压缩辅助工具之外,执行提供商特定的重放重写时 | +| 43 | `validateReplayTurns` | 在嵌入式 runner 之前,对重放轮次进行最终校验或重塑 | 当提供商传输在通用清理之后仍需要更严格的轮次校验时 | +| 44 | `onModelSelected` | 在模型被选中后运行由提供商拥有的副作用 | 当模型激活后,提供商需要遥测或维护由提供商拥有的状态时 | -`normalizeModelId`、`normalizeTransport` 和 `normalizeConfig` 会先检查已匹配的提供商插件,然后继续回退到其他具备相关钩子能力的提供商插件,直到某个插件实际修改了模型 id 或 transport / config。这样可以让别名 / 兼容型提供商 shim 正常工作,而无需调用方知道哪个内置插件拥有该改写逻辑。如果没有任何提供商钩子改写受支持的 Google 家族配置项,那么内置的 Google 配置规范化器仍会应用这类兼容性清理。 +`normalizeModelId`、`normalizeTransport` 和 `normalizeConfig` 会先检查已匹配的提供商插件,然后再依次回退到其他具备相关 hook 能力的提供商插件,直到其中某个插件实际更改了 model id 或 transport / config。这样可以让别名 / 兼容提供商 shim 继续工作,而无需调用方知道究竟是哪个内置插件拥有该重写逻辑。如果没有任何提供商 hook 重写某个受支持的 Google 家族配置项,那么内置的 Google 配置标准化器仍会执行该兼容性清理。 -如果提供商需要完全自定义的线协议或自定义请求执行器,那就是另一类扩展。这些钩子适用于仍运行在 OpenClaw 正常推理循环上的提供商行为。 +如果提供商需要完全自定义的线路协议或自定义请求执行器,那就是另一类扩展。这些 hooks 适用于仍然运行在 OpenClaw 正常推理循环上的提供商行为。 ### 提供商示例 @@ -627,29 +624,29 @@ api.registerProvider({ ### 内置示例 -- Anthropic 使用 `resolveDynamicModel`、`capabilities`、`buildAuthDoctorHint`、`resolveUsageAuth`、`fetchUsageSnapshot`、`isCacheTtlEligible`、`resolveDefaultThinkingLevel`、`applyConfigDefaults`、`isModernModelRef` 和 `wrapStreamFn`,因为它拥有 Claude 4.6 前向兼容、提供商家族提示、认证修复指引、用量端点集成、提示词缓存资格、支持认证感知的配置默认值、Claude 默认 / 自适应思考策略,以及面向 beta 头、`/fast` / `serviceTier` 和 `context1m` 的 Anthropic 特定流式整形逻辑。 -- Anthropic 的 Claude 特定流式辅助工具目前仍保留在该内置插件自己的公共 `api.ts` / `contract-api.ts` 接缝中。该包接口导出 `wrapAnthropicProviderStream`、`resolveAnthropicBetas`、`resolveAnthropicFastMode`、`resolveAnthropicServiceTier` 以及更底层的 Anthropic 包装器构建器,而不是为了某个提供商的 beta 头规则去扩展通用 SDK。 -- OpenAI 使用 `resolveDynamicModel`、`normalizeResolvedModel` 和 `capabilities`,以及 `buildMissingAuthMessage`、`suppressBuiltInModel`、`augmentModelCatalog`、`supportsXHighThinking` 和 `isModernModelRef`,因为它拥有 GPT-5.4 前向兼容、直接的 OpenAI `openai-completions` -> `openai-responses` 规范化、面向 Codex 的认证提示、Spark 抑制、合成 OpenAI 列表条目,以及 GPT-5 的 thinking / 实时模型策略;而 `openai-responses-defaults` 流家族拥有共享的原生 OpenAI Responses 包装器,用于 attribution 头、`/fast` / `serviceTier`、文本详细度、原生 Codex Web 搜索、reasoning 兼容负载整形以及 Responses 上下文管理。 -- OpenRouter 使用 `catalog` 以及 `resolveDynamicModel` 和 `prepareDynamicModel`,因为该提供商是透传型的,并且可能在 OpenClaw 的静态目录更新前就暴露新模型 id;它还使用 `capabilities`、`wrapStreamFn` 和 `isCacheTtlEligible`,以避免将提供商特定的请求头、路由元数据、reasoning 补丁和提示词缓存策略放入核心。它的重放策略来自 `passthrough-gemini` 家族,而 `openrouter-thinking` 流家族则拥有代理推理注入以及不受支持模型 / `auto` 跳过逻辑。 -- GitHub Copilot 使用 `catalog`、`auth`、`resolveDynamicModel` 和 `capabilities`,以及 `prepareRuntimeAuth` 和 `fetchUsageSnapshot`,因为它需要提供商自有的设备登录、模型回退行为、Claude 转录差异、GitHub token -> Copilot token 交换,以及提供商自有的用量端点。 -- OpenAI Codex 使用 `catalog`、`resolveDynamicModel`、`normalizeResolvedModel`、`refreshOAuth` 和 `augmentModelCatalog`,以及 `prepareExtraParams`、`resolveUsageAuth` 和 `fetchUsageSnapshot`,因为它虽然仍运行在核心 OpenAI 传输之上,但拥有自己的 transport / base URL 规范化、OAuth 刷新回退策略、默认传输选择、合成 Codex 目录条目以及 ChatGPT 用量端点集成;它与直接 OpenAI 共用同一个 `openai-responses-defaults` 流家族。 -- Google AI Studio 和 Gemini CLI OAuth 使用 `resolveDynamicModel`、`buildReplayPolicy`、`sanitizeReplayHistory`、`resolveReasoningOutputMode`、`wrapStreamFn` 和 `isModernModelRef`,因为 `google-gemini` 重放家族拥有 Gemini 3.1 前向兼容回退、原生 Gemini 重放验证、bootstrap 重放净化、带标签的推理输出模式以及现代模型匹配,而 `google-thinking` 流家族拥有 Gemini thinking 负载规范化;Gemini CLI OAuth 还使用 `formatApiKey`、`resolveUsageAuth` 和 `fetchUsageSnapshot` 来处理 token 格式化、token 解析和配额端点接线。 -- Anthropic Vertex 通过 `anthropic-by-model` 重放家族使用 `buildReplayPolicy`,从而让 Claude 特定的重放清理只限定在 Claude id 上,而不是作用于每个 `anthropic-messages` 传输。 -- Amazon Bedrock 使用 `buildReplayPolicy`、`matchesContextOverflowError`、`classifyFailoverReason` 和 `resolveDefaultThinkingLevel`,因为它拥有用于 Anthropic-on-Bedrock 流量的 Bedrock 特定限流 / 未就绪 / 上下文溢出错误分类;它的重放策略仍共享同一个仅限 Claude 的 `anthropic-by-model` 防护。 -- OpenRouter、Kilocode、Opencode 和 Opencode Go 通过 `passthrough-gemini` 重放家族使用 `buildReplayPolicy`,因为它们通过 OpenAI 兼容传输代理 Gemini 模型,并且需要 Gemini thought-signature 净化,而不需要原生 Gemini 重放验证或 bootstrap 改写。 -- MiniMax 通过 `hybrid-anthropic-openai` 重放家族使用 `buildReplayPolicy`,因为一个提供商同时拥有 Anthropic-message 和 OpenAI 兼容语义;它会在 Anthropic 一侧保持仅限 Claude 的 thinking block 丢弃,同时将 reasoning 输出模式改回原生,而 `minimax-fast-mode` 流家族则在共享流路径上拥有 fast-mode 模型改写逻辑。 -- Moonshot 使用 `catalog` 和 `wrapStreamFn`,因为它仍使用共享的 OpenAI 传输,但需要提供商自有的 thinking 负载规范化;`moonshot-thinking` 流家族将配置和 `/think` 状态映射到其原生的二元 thinking 负载上。 -- Kilocode 使用 `catalog`、`capabilities`、`wrapStreamFn` 和 `isCacheTtlEligible`,因为它需要提供商自有的请求头、reasoning 负载规范化、Gemini 转录提示和 Anthropic 缓存 TTL 门控;`kilocode-thinking` 流家族在共享代理流路径上保留 Kilo thinking 注入,同时跳过 `kilo/auto` 和其他不支持显式 reasoning 负载的代理模型 id。 -- Z.AI 使用 `resolveDynamicModel`、`prepareExtraParams`、`wrapStreamFn`、`isCacheTtlEligible`、`isBinaryThinking`、`isModernModelRef`、`resolveUsageAuth` 和 `fetchUsageSnapshot`,因为它拥有 GLM-5 回退、`tool_stream` 默认值、二元 thinking 用户体验、现代模型匹配,以及用量认证和配额拉取;`tool-stream-default-on` 流家族则将默认开启的 `tool_stream` 包装器从按提供商手写胶水逻辑中抽离出来。 -- xAI 使用 `normalizeResolvedModel`、`normalizeTransport`、`contributeResolvedModelCompat`、`prepareExtraParams`、`wrapStreamFn`、`resolveSyntheticAuth`、`resolveDynamicModel` 和 `isModernModelRef`,因为它拥有原生 xAI Responses 传输规范化、Grok fast-mode 别名改写、默认 `tool_stream`、严格工具 / reasoning 负载清理、面向插件自有工具的回退认证复用、前向兼容的 Grok 模型解析,以及提供商自有的兼容性补丁,例如 xAI 工具 schema 配置文件、不受支持的 schema 关键字、原生 `web_search` 和 HTML 实体工具调用参数解码。 -- Mistral、OpenCode Zen 和 OpenCode Go 仅使用 `capabilities`,以将转录 / 工具差异保留在核心之外。 -- 仅目录型的内置提供商,如 `byteplus`、`cloudflare-ai-gateway`、`huggingface`、`kimi-coding`、`nvidia`、`qianfan`、`synthetic`、`together`、`venice`、`vercel-ai-gateway` 和 `volcengine`,仅使用 `catalog`。 -- Qwen 为其文本提供商使用 `catalog`,同时为其多模态接口注册共享的 media-understanding 和 video-generation。 -- MiniMax 和 Xiaomi 使用 `catalog` 加上用量钩子,因为即使推理仍通过共享传输运行,它们的 `/usage` 行为仍归属于插件。 +- Anthropic 使用 `resolveDynamicModel`、`capabilities`、`buildAuthDoctorHint`、`resolveUsageAuth`、`fetchUsageSnapshot`、`isCacheTtlEligible`、`resolveDefaultThinkingLevel`、`applyConfigDefaults`、`isModernModelRef` 和 `wrapStreamFn`,因为它拥有 Claude 4.6 前向兼容、提供商家族提示、认证修复指导、用量端点集成、提示词缓存适用性、带认证感知的配置默认值、Claude 默认 / 自适应 thinking 策略,以及用于 beta headers、`/fast` / `serviceTier` 和 `context1m` 的 Anthropic 特定流塑形。 +- Anthropic 的 Claude 专用流辅助工具目前仍保留在该内置插件自己的公共 `api.ts` / `contract-api.ts` 接缝中。该包表面导出 `wrapAnthropicProviderStream`、`resolveAnthropicBetas`、`resolveAnthropicFastMode`、`resolveAnthropicServiceTier` 以及更底层的 Anthropic 包装器构建器,而不是为了某个提供商的 beta-header 规则去扩展通用 SDK。 +- OpenAI 使用 `resolveDynamicModel`、`normalizeResolvedModel` 和 `capabilities`,以及 `buildMissingAuthMessage`、`suppressBuiltInModel`、`augmentModelCatalog`、`supportsXHighThinking` 和 `isModernModelRef`,因为它拥有 GPT-5.4 前向兼容、直接的 OpenAI `openai-completions` -> `openai-responses` 标准化、Codex 感知认证提示、Spark 抑制、合成的 OpenAI 列表条目,以及 GPT-5 thinking / live 模型策略;`openai-responses-defaults` 流家族则拥有共享的原生 OpenAI Responses 包装器,用于 attribution headers、`/fast` / `serviceTier`、文本详细度、原生 Codex Web 搜索、reasoning 兼容负载塑形以及 Responses 上下文管理。 +- OpenRouter 使用 `catalog`、`resolveDynamicModel` 和 `prepareDynamicModel`,因为该提供商是透传型的,可能会在 OpenClaw 的静态目录更新之前暴露新的 model id;它还使用 `capabilities`、`wrapStreamFn` 和 `isCacheTtlEligible`,以便将提供商特定请求头、路由元数据、reasoning 补丁和提示词缓存策略保留在核心之外。它的重放策略来自 `passthrough-gemini` 家族,而 `openrouter-thinking` 流家族则拥有代理 reasoning 注入以及对不受支持模型 / `auto` 的跳过逻辑。 +- GitHub Copilot 使用 `catalog`、`auth`、`resolveDynamicModel` 和 `capabilities`,以及 `prepareRuntimeAuth` 和 `fetchUsageSnapshot`,因为它需要提供商自有的设备登录、模型回退行为、Claude 转录特殊行为、GitHub token -> Copilot token 交换,以及提供商自有的用量端点。 +- OpenAI Codex 使用 `catalog`、`resolveDynamicModel`、`normalizeResolvedModel`、`refreshOAuth` 和 `augmentModelCatalog`,以及 `prepareExtraParams`、`resolveUsageAuth` 和 `fetchUsageSnapshot`,因为它仍运行在核心 OpenAI 传输之上,但拥有自己的 transport / `base URL` 标准化、OAuth 刷新回退策略、默认传输选择、合成 Codex 目录条目,以及 ChatGPT 用量端点集成;它与直接 OpenAI 共享同一个 `openai-responses-defaults` 流家族。 +- Google AI Studio 和 Gemini CLI OAuth 使用 `resolveDynamicModel`、`buildReplayPolicy`、`sanitizeReplayHistory`、`resolveReasoningOutputMode`、`wrapStreamFn` 和 `isModernModelRef`,因为 `google-gemini` 重放家族拥有 Gemini 3.1 前向兼容回退、原生 Gemini 重放校验、bootstrap 重放清理、带标签的 reasoning 输出模式以及现代模型匹配,而 `google-thinking` 流家族拥有 Gemini thinking 负载标准化;Gemini CLI OAuth 还使用 `formatApiKey`、`resolveUsageAuth` 和 `fetchUsageSnapshot` 来处理令牌格式化、令牌解析和配额端点接线。 +- Anthropic Vertex 通过 `anthropic-by-model` 重放家族使用 `buildReplayPolicy`,从而让 Claude 专用的重放清理仅作用于 Claude id,而不是每一个 `anthropic-messages` 传输。 +- Amazon Bedrock 使用 `buildReplayPolicy`、`matchesContextOverflowError`、`classifyFailoverReason` 和 `resolveDefaultThinkingLevel`,因为它拥有适用于 Anthropic-on-Bedrock 流量的 Bedrock 特定限流 / 未就绪 / 上下文溢出错误分类;其重放策略仍共享同一个仅限 Claude 的 `anthropic-by-model` 保护。 +- OpenRouter、Kilocode、Opencode 和 Opencode Go 通过 `passthrough-gemini` 重放家族使用 `buildReplayPolicy`,因为它们通过 OpenAI 兼容传输代理 Gemini 模型,并且需要 Gemini thought-signature 清理,但不需要原生 Gemini 重放校验或 bootstrap 重写。 +- MiniMax 通过 `hybrid-anthropic-openai` 重放家族使用 `buildReplayPolicy`,因为一个提供商同时拥有 Anthropic-message 和 OpenAI 兼容语义;它在 Anthropic 侧保留仅限 Claude 的 thinking 块丢弃,同时将 reasoning 输出模式覆盖回原生,而 `minimax-fast-mode` 流家族则在共享流路径上拥有 fast-mode 模型重写。 +- Moonshot 使用 `catalog` 和 `wrapStreamFn`,因为它仍使用共享 OpenAI 传输,但需要提供商自有的 thinking 负载标准化;`moonshot-thinking` 流家族会将配置加上 `/think` 状态映射到其原生的二元 thinking 负载。 +- Kilocode 使用 `catalog`、`capabilities`、`wrapStreamFn` 和 `isCacheTtlEligible`,因为它需要提供商自有请求头、reasoning 负载标准化、Gemini 转录提示,以及 Anthropic 缓存 TTL 门控;`kilocode-thinking` 流家族会在共享代理流路径上保留 Kilo thinking 注入,同时跳过 `kilo/auto` 和其他不支持显式 reasoning 负载的代理 model id。 +- Z.AI 使用 `resolveDynamicModel`、`prepareExtraParams`、`wrapStreamFn`、`isCacheTtlEligible`、`isBinaryThinking`、`isModernModelRef`、`resolveUsageAuth` 和 `fetchUsageSnapshot`,因为它拥有 GLM-5 回退、`tool_stream` 默认值、二元 thinking 用户体验、现代模型匹配,以及用量认证和配额抓取;`tool-stream-default-on` 流家族则将默认开启的 `tool_stream` 包装器保留在每个提供商手写胶水代码之外。 +- xAI 使用 `normalizeResolvedModel`、`normalizeTransport`、`contributeResolvedModelCompat`、`prepareExtraParams`、`wrapStreamFn`、`resolveSyntheticAuth`、`resolveDynamicModel` 和 `isModernModelRef`,因为它拥有原生 xAI Responses 传输标准化、Grok fast-mode 别名重写、默认 `tool_stream`、strict-tool / reasoning 负载清理、为插件自有工具复用回退认证、前向兼容的 Grok 模型解析,以及由提供商拥有的兼容性补丁,例如 xAI 工具 schema 配置文件、不受支持的 schema 关键字、原生 `web_search` 和 HTML 实体工具调用参数解码。 +- Mistral、OpenCode Zen 和 OpenCode Go 仅使用 `capabilities`,以便将转录 / 工具特殊行为保留在核心之外。 +- 仅目录型内置提供商,例如 `byteplus`、`cloudflare-ai-gateway`、`huggingface`、`kimi-coding`、`nvidia`、`qianfan`、`synthetic`、`together`、`venice`、`vercel-ai-gateway` 和 `volcengine`,仅使用 `catalog`。 +- Qwen 对其文本提供商使用 `catalog`,同时针对其多模态表面使用共享的媒体理解和视频生成注册。 +- MiniMax 和 Xiaomi 使用 `catalog` 加上用量 hooks,因为即使推理仍通过共享传输运行,它们的 `/usage` 行为仍归插件所有。 ## 运行时辅助工具 -插件可通过 `api.runtime` 访问部分核心辅助工具。以 TTS 为例: +插件可以通过 `api.runtime` 访问一部分选定的核心辅助工具。以 TTS 为例: ```ts const clip = await api.runtime.tts.textToSpeech({ @@ -670,14 +667,14 @@ const voices = await api.runtime.tts.listVoices({ 说明: -- `textToSpeech` 返回用于文件 / 语音消息界面的标准核心 TTS 输出负载。 -- 使用核心 `messages.tts` 配置和提供商选择逻辑。 -- 返回 PCM 音频缓冲区 + 采样率。插件必须为各自提供商完成重采样 / 编码。 -- `listVoices` 对每个提供商来说都是可选的。可用于供应商自有的语音选择器或设置流程。 -- 语音列表可包含更丰富的元数据,例如语言区域、性别和人格标签,以支持提供商感知型选择器。 -- 当前 OpenAI 和 ElevenLabs 支持电话场景。Microsoft 不支持。 +- `textToSpeech` 返回普通核心 TTS 输出负载,适用于文件 / 语音便笺表面。 +- 使用核心 `messages.tts` 配置和提供商选择。 +- 返回 PCM 音频缓冲区 + 采样率。插件必须自行重新采样 / 编码以适配提供商。 +- `listVoices` 对每个提供商而言都是可选的。可将其用于厂商自有语音选择器或设置流程。 +- 语音列表可以包含更丰富的元数据,例如 locale、gender 和 personality 标签,以支持具备提供商感知能力的选择器。 +- 目前 OpenAI 和 ElevenLabs 支持电话场景。Microsoft 不支持。 -插件也可以通过 `api.registerSpeechProvider(...)` 注册语音提供商。 +插件还可以通过 `api.registerSpeechProvider(...)` 注册语音提供商。 ```ts api.registerSpeechProvider({ @@ -698,11 +695,11 @@ api.registerSpeechProvider({ 说明: - 将 TTS 策略、回退和回复投递保留在核心中。 -- 使用语音提供商来承载供应商自有的合成行为。 -- 旧版 Microsoft `edge` 输入会被规范化为 `microsoft` provider id。 -- 推荐的归属模型是面向公司的:随着 OpenClaw 增加这些能力契约,一个供应商插件可以同时拥有文本、语音、图像和未来的媒体提供商。 +- 使用语音提供商来承载厂商自有的合成行为。 +- 旧版 Microsoft `edge` 输入会被标准化为 `microsoft` provider id。 +- 首选的归属模型是面向公司的:随着 OpenClaw 增加这些能力契约,一个厂商插件可以同时拥有文本、语音、图像和未来的媒体提供商。 -对于图像 / 音频 / 视频理解,插件应注册一个类型化的 media-understanding 提供商,而不是一个通用的键 / 值包: +对于图像 / 音频 / 视频理解,插件会注册一个类型化的媒体理解提供商,而不是一个通用的键 / 值袋: ```ts api.registerMediaUnderstandingProvider({ @@ -717,14 +714,14 @@ api.registerMediaUnderstandingProvider({ 说明: - 将编排、回退、配置和渠道接线保留在核心中。 -- 将供应商行为保留在提供商插件中。 -- 增量扩展应保持类型化:新的可选方法、新的可选结果字段、新的可选能力。 +- 将厂商行为保留在提供商插件中。 +- 增量扩展应保持类型化:新增可选方法、新增可选结果字段、新增可选能力。 - 视频生成已经遵循相同模式: - 核心拥有能力契约和运行时辅助工具 - - 供应商插件注册 `api.registerVideoGenerationProvider(...)` + - 厂商插件注册 `api.registerVideoGenerationProvider(...)` - 功能 / 渠道插件消费 `api.runtime.videoGeneration.*` -对于 media-understanding 运行时辅助工具,插件可以调用: +对于媒体理解运行时辅助工具,插件可以调用: ```ts const image = await api.runtime.mediaUnderstanding.describeImageFile({ @@ -739,7 +736,7 @@ const video = await api.runtime.mediaUnderstanding.describeVideoFile({ }); ``` -对于音频转录,插件既可以使用 media-understanding 运行时,也可以使用旧版 STT 别名: +对于音频转写,插件既可以使用媒体理解运行时,也可以使用较旧的 STT 别名: ```ts const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ @@ -752,12 +749,12 @@ const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ 说明: -- `api.runtime.mediaUnderstanding.*` 是图像 / 音频 / 视频理解的首选共享接口。 -- 使用核心 media-understanding 音频配置(`tools.media.audio`)和提供商回退顺序。 -- 当未产生转录输出时返回 `{ text: undefined }`(例如输入被跳过 / 不受支持时)。 +- `api.runtime.mediaUnderstanding.*` 是图像 / 音频 / 视频理解的首选共享表面。 +- 使用核心媒体理解音频配置(`tools.media.audio`)和提供商回退顺序。 +- 当没有生成转写输出时,返回 `{ text: undefined }`(例如输入被跳过 / 不受支持)。 - `api.runtime.stt.transcribeAudioFile(...)` 仍保留为兼容性别名。 -插件也可以通过 `api.runtime.subagent` 启动后台子智能体运行: +插件还可以通过 `api.runtime.subagent` 启动后台子智能体运行: ```ts const result = await api.runtime.subagent.run({ @@ -771,13 +768,13 @@ const result = await api.runtime.subagent.run({ 说明: -- `provider` 和 `model` 是每次运行的可选覆盖项,不是持久化的会话变更。 -- OpenClaw 仅对可信调用方接受这些覆盖字段。 -- 对于插件自有的回退运行,操作员必须通过 `plugins.entries..subagent.allowModelOverride: true` 显式启用。 -- 使用 `plugins.entries..subagent.allowedModels` 将可信插件限制为特定的规范 `provider/model` 目标,或使用 `"*"` 以显式允许任意目标。 -- 不可信插件的子智能体运行仍可工作,但覆盖请求会被拒绝,而不是静默回退。 +- `provider` 和 `model` 是每次运行可选的覆盖项,不是持久化的会话变更。 +- OpenClaw 仅会对受信任调用方允许这些覆盖字段生效。 +- 对于插件自有回退运行,操作者必须通过 `plugins.entries..subagent.allowModelOverride: true` 显式选择启用。 +- 使用 `plugins.entries..subagent.allowedModels` 将受信任插件限制为特定的规范 `provider/model` 目标,或设置为 `"*"` 以显式允许任意目标。 +- 不受信任插件的子智能体运行仍然可用,但覆盖请求会被拒绝,而不是静默回退。 -对于 Web 搜索,插件可以消费共享运行时辅助工具,而不是深入到智能体工具接线中: +对于网页搜索,插件可以消费共享运行时辅助工具,而不是深入智能体工具接线: ```ts const providers = api.runtime.webSearch.listProviders({ @@ -793,14 +790,13 @@ const result = await api.runtime.webSearch.search({ }); ``` -插件也可以通过 -`api.registerWebSearchProvider(...)` 注册 Web 搜索提供商。 +插件还可以通过 `api.registerWebSearchProvider(...)` 注册网页搜索提供商。 说明: - 将提供商选择、凭证解析和共享请求语义保留在核心中。 -- 使用 Web 搜索提供商来承载供应商特定的搜索传输。 -- `api.runtime.webSearch.*` 是功能 / 渠道插件在需要搜索行为但不依赖智能体工具包装器时的首选共享接口。 +- 使用网页搜索提供商来承载厂商特定的搜索传输。 +- `api.runtime.webSearch.*` 是功能 / 渠道插件在不依赖智能体工具包装器的情况下需要搜索行为时的首选共享表面。 ### `api.runtime.imageGeneration` @@ -820,7 +816,7 @@ const providers = api.runtime.imageGeneration.listProviders({ ## Gateway 网关 HTTP 路由 -插件可以使用 `api.registerHttpRoute(...)` 暴露 HTTP 端点。 +插件可以通过 `api.registerHttpRoute(...)` 暴露 HTTP 端点。 ```ts api.registerHttpRoute({ @@ -838,159 +834,123 @@ api.registerHttpRoute({ 路由字段: - `path`:Gateway 网关 HTTP 服务器下的路由路径。 -- `auth`:必填。使用 `"gateway"` 表示要求常规 Gateway 网关认证,或使用 `"plugin"` 表示由插件自行管理认证 / webhook 校验。 -- `match`:可选。可为 `"exact"`(默认)或 `"prefix"`。 -- `replaceExisting`:可选。允许同一插件替换自身已有的路由注册。 +- `auth`:必填。使用 `"gateway"` 以要求普通 Gateway 网关认证,或使用 `"plugin"` 以执行插件管理的认证 / webhook 验证。 +- `match`:可选。`"exact"`(默认)或 `"prefix"`。 +- `replaceExisting`:可选。允许同一个插件替换其已有的路由注册。 - `handler`:当路由处理了请求时返回 `true`。 说明: -- `api.registerHttpHandler(...)` 已被移除,并会导致插件加载错误。请改用 `api.registerHttpRoute(...)`。 +- `api.registerHttpHandler(...)` 已移除,并且会导致插件加载错误。请改用 `api.registerHttpRoute(...)`。 - 插件路由必须显式声明 `auth`。 -- 精确的 `path + match` 冲突会被拒绝,除非设置 `replaceExisting: true`,且一个插件不能替换另一个插件的路由。 -- 具有不同 `auth` 级别的重叠路由会被拒绝。请仅在相同认证级别内使用 `exact` / `prefix` 回退链。 -- `auth: "plugin"` 路由**不会**自动接收操作员运行时作用域。它们用于插件自主管理的 webhook / 签名校验,而不是特权 Gateway 网关辅助调用。 -- `auth: "gateway"` 路由会在 Gateway 网关请求运行时作用域内运行,但该作用域被有意设置得较为保守: - - 共享密钥 bearer 认证(`gateway.auth.mode = "token"` / `"password"`)会将插件路由的运行时作用域固定为 `operator.write`,即使调用方发送了 `x-openclaw-scopes` - - 具备可信身份的 HTTP 模式(例如 `trusted-proxy`,或私有入口上的 `gateway.auth.mode = "none"`)仅在显式存在该头时才会接受 `x-openclaw-scopes` - - 如果这些具备身份信息的插件路由请求中缺少 `x-openclaw-scopes`,则运行时作用域会回退为 `operator.write` -- 实践规则:不要假设一个经过 gateway 认证的插件路由天然就是管理接口。如果你的路由需要仅管理员可用的行为,请要求具备身份信息的认证模式,并记录明确的 `x-openclaw-scopes` 头契约。 +- 除非设置了 `replaceExisting: true`,否则精确的 `path + match` 冲突会被拒绝,并且一个插件不能替换另一个插件的路由。 +- 使用不同 `auth` 级别的重叠路由会被拒绝。仅在相同认证级别内保留 `exact` / `prefix` 的回退链。 +- `auth: "plugin"` 路由**不会**自动接收操作者运行时作用域。它们用于插件管理的 webhook / 签名验证,而不是特权 Gateway 网关辅助调用。 +- `auth: "gateway"` 路由会运行在 Gateway 网关请求运行时作用域内,但该作用域是有意保守的: + - 共享密钥 bearer 认证(`gateway.auth.mode = "token"` / `"password"`)会将插件路由运行时作用域固定为 `operator.write`,即使调用方发送了 `x-openclaw-scopes` + - 带受信任身份的 HTTP 模式(例如 `trusted-proxy` 或私有入口上的 `gateway.auth.mode = "none"`)仅在明确存在该头时才会尊重 `x-openclaw-scopes` + - 如果这些带身份的插件路由请求中缺少 `x-openclaw-scopes`,运行时作用域将回退为 `operator.write` +- 实际规则:不要假设经过 gateway 认证的插件路由天然就是管理员表面。如果你的路由需要仅管理员可用的行为,请要求使用带身份的认证模式,并文档化明确的 `x-openclaw-scopes` 请求头契约。 ## 插件 SDK 导入路径 -在编写插件时,应使用 SDK 子路径,而不是整体式的 `openclaw/plugin-sdk` 导入: +在编写插件时,请使用 SDK 子路径,而不是单体的 `openclaw/plugin-sdk` 导入: - `openclaw/plugin-sdk/plugin-entry` 用于插件注册原语。 -- `openclaw/plugin-sdk/core` 用于通用的共享插件接口契约。 +- `openclaw/plugin-sdk/core` 用于通用共享的面向插件契约。 - `openclaw/plugin-sdk/config-schema` 用于根 `openclaw.json` Zod schema 导出(`OpenClawSchema`)。 -- 稳定的渠道原语,如 `openclaw/plugin-sdk/channel-setup`、 - `openclaw/plugin-sdk/setup-runtime`、 - `openclaw/plugin-sdk/setup-adapter-runtime`、 - `openclaw/plugin-sdk/setup-tools`、 - `openclaw/plugin-sdk/channel-pairing`、 - `openclaw/plugin-sdk/channel-contract`、 - `openclaw/plugin-sdk/channel-feedback`、 - `openclaw/plugin-sdk/channel-inbound`、 - `openclaw/plugin-sdk/channel-lifecycle`、 - `openclaw/plugin-sdk/channel-reply-pipeline`、 - `openclaw/plugin-sdk/command-auth`、 - `openclaw/plugin-sdk/secret-input` 和 - `openclaw/plugin-sdk/webhook-ingress`,用于共享的设置 / 认证 / 回复 / webhook 接线。`channel-inbound` 是去抖、提及匹配、入站提及策略辅助工具、信封格式化以及入站信封上下文辅助工具的共享归属位置。`channel-setup` 是可选安装设置的窄接缝。`setup-runtime` 是 `setupEntry` / 延迟启动所使用的运行时安全设置接口,其中包括导入安全的设置补丁适配器。`setup-adapter-runtime` 是具备环境变量感知能力的账户设置适配器接缝。`setup-tools` 是小型 CLI / 归档 / 文档辅助工具接缝(`formatCliCommand`、`detectBinary`、`extractArchive`、`resolveBrewExecutable`、`formatDocsLink`、`CONFIG_DIR`)。 -- 领域子路径,如 `openclaw/plugin-sdk/channel-config-helpers`、 - `openclaw/plugin-sdk/allow-from`、 - `openclaw/plugin-sdk/channel-config-schema`、 - `openclaw/plugin-sdk/telegram-command-config`、 - `openclaw/plugin-sdk/channel-policy`、 - `openclaw/plugin-sdk/approval-gateway-runtime`、 - `openclaw/plugin-sdk/approval-handler-adapter-runtime`、 - `openclaw/plugin-sdk/approval-handler-runtime`、 - `openclaw/plugin-sdk/approval-runtime`、 - `openclaw/plugin-sdk/config-runtime`、 - `openclaw/plugin-sdk/infra-runtime`、 - `openclaw/plugin-sdk/agent-runtime`、 - `openclaw/plugin-sdk/lazy-runtime`、 - `openclaw/plugin-sdk/reply-history`、 - `openclaw/plugin-sdk/routing`、 - `openclaw/plugin-sdk/status-helpers`、 - `openclaw/plugin-sdk/text-runtime`、 - `openclaw/plugin-sdk/runtime-store` 和 - `openclaw/plugin-sdk/directory-runtime`,用于共享运行时 / 配置辅助工具。`telegram-command-config` 是 Telegram 自定义命令规范化 / 校验的窄公共接缝,即使内置 Telegram 契约接口暂时不可用,它也会保持可用。`text-runtime` 是共享的文本 / Markdown / 日志接口,包括面向助手可见文本剥离、Markdown 渲染 / 分块辅助工具、脱敏辅助工具、指令标签辅助工具和安全文本工具。 -- 审批特定的渠道接缝应优先使用插件上的单一 `approvalCapability` 契约。然后,核心会通过这一能力来读取审批认证、投递、渲染、原生路由和惰性原生处理器行为,而不是将审批行为混入不相关的插件字段中。 -- `openclaw/plugin-sdk/channel-runtime` 已被弃用,仅作为旧版插件的兼容性 shim 保留。新代码应改为导入更窄的通用原语,仓库代码也不应新增对该 shim 的导入。 -- 内置扩展内部实现仍然是私有的。外部插件应仅使用 `openclaw/plugin-sdk/*` 子路径。OpenClaw 核心 / 测试代码可以使用插件包根目录下的仓库公共入口点,例如 `index.js`、`api.js`、`runtime-api.js`、`setup-entry.js` 以及诸如 `login-qr-api.js` 之类的窄范围文件。绝不要从核心或其他扩展中导入某个插件包的 `src/*`。 +- 稳定的渠道原语,例如 `openclaw/plugin-sdk/channel-setup`、`openclaw/plugin-sdk/setup-runtime`、`openclaw/plugin-sdk/setup-adapter-runtime`、`openclaw/plugin-sdk/setup-tools`、`openclaw/plugin-sdk/channel-pairing`、`openclaw/plugin-sdk/channel-contract`、`openclaw/plugin-sdk/channel-feedback`、`openclaw/plugin-sdk/channel-inbound`、`openclaw/plugin-sdk/channel-lifecycle`、`openclaw/plugin-sdk/channel-reply-pipeline`、`openclaw/plugin-sdk/command-auth`、`openclaw/plugin-sdk/secret-input` 和 `openclaw/plugin-sdk/webhook-ingress`,用于共享设置 / 认证 / 回复 / webhook 接线。`channel-inbound` 是 debounce、mention 匹配、入站 mention 策略辅助工具、envelope 格式化以及入站 envelope 上下文辅助工具的共享归属地。`channel-setup` 是狭窄的可选安装设置接缝。`setup-runtime` 是供 `setupEntry` / 延迟启动使用的运行时安全设置表面,其中包括可安全导入的设置补丁适配器。`setup-adapter-runtime` 是具备环境感知能力的账户设置适配器接缝。`setup-tools` 是小型 CLI / archive / 文档辅助接缝(`formatCliCommand`、`detectBinary`、`extractArchive`、`resolveBrewExecutable`、`formatDocsLink`、`CONFIG_DIR`)。 +- 领域子路径,例如 `openclaw/plugin-sdk/channel-config-helpers`、`openclaw/plugin-sdk/allow-from`、`openclaw/plugin-sdk/channel-config-schema`、`openclaw/plugin-sdk/telegram-command-config`、`openclaw/plugin-sdk/channel-policy`、`openclaw/plugin-sdk/approval-gateway-runtime`、`openclaw/plugin-sdk/approval-handler-adapter-runtime`、`openclaw/plugin-sdk/approval-handler-runtime`、`openclaw/plugin-sdk/approval-runtime`、`openclaw/plugin-sdk/config-runtime`、`openclaw/plugin-sdk/infra-runtime`、`openclaw/plugin-sdk/agent-runtime`、`openclaw/plugin-sdk/lazy-runtime`、`openclaw/plugin-sdk/reply-history`、`openclaw/plugin-sdk/routing`、`openclaw/plugin-sdk/status-helpers`、`openclaw/plugin-sdk/text-runtime`、`openclaw/plugin-sdk/runtime-store` 和 `openclaw/plugin-sdk/directory-runtime`,用于共享运行时 / 配置辅助工具。`telegram-command-config` 是 Telegram 自定义命令标准化 / 校验的狭窄公共接缝,即使内置 Telegram 契约表面暂时不可用,它也会保持可用。`text-runtime` 是共享的文本 / Markdown / 日志接缝,包括对 assistant 可见文本的剥离、Markdown 渲染 / 分块辅助工具、脱敏辅助工具、directive 标签辅助工具以及安全文本工具。 +- 审批专用渠道接缝应优先使用插件上的单一 `approvalCapability` 契约。这样,核心就可以通过这一项能力来读取审批认证、投递、渲染、原生路由和延迟原生处理器行为,而不是将审批行为混入无关的插件字段中。 +- `openclaw/plugin-sdk/channel-runtime` 已弃用,仅作为旧版插件的兼容 shim 保留。新代码应改用更狭窄的通用原语导入,仓库代码也不应再新增对该 shim 的导入。 +- 内置扩展内部实现仍然是私有的。外部插件只能使用 `openclaw/plugin-sdk/*` 子路径。OpenClaw 核心 / 测试代码可以使用某个插件包根目录下的仓库公共入口点,例如 `index.js`、`api.js`、`runtime-api.js`、`setup-entry.js` 以及像 `login-qr-api.js` 这样的窄范围文件。绝不要从核心或另一个扩展中导入某个插件包的 `src/*`。 - 仓库入口点拆分: - `/api.js` 是辅助工具 / 类型 barrel, - `/runtime-api.js` 是仅运行时 barrel, - `/index.js` 是内置插件入口, + `/api.js` 是辅助工具 / 类型 barrel, + `/runtime-api.js` 是仅运行时 barrel, + `/index.js` 是内置插件入口, `/setup-entry.js` 是设置插件入口。 - 当前内置提供商示例: - - Anthropic 使用 `api.js` / `contract-api.js` 暴露 Claude 流式辅助工具,例如 `wrapAnthropicProviderStream`、beta 头辅助工具以及 `service_tier` 解析。 - - OpenAI 使用 `api.js` 暴露提供商构建器、默认模型辅助工具和实时提供商构建器。 - - OpenRouter 使用 `api.js` 暴露其提供商构建器以及新手引导 / 配置辅助工具,而 `register.runtime.js` 仍可为仓库本地用途重新导出通用 `plugin-sdk/provider-stream` 辅助工具。 -- 通过 facade 加载的公共入口点在存在活动运行时配置快照时优先使用该快照;当 OpenClaw 尚未提供运行时快照时,则回退到磁盘上已解析的配置文件。 -- 通用共享原语仍然是首选的公共插件 SDK 契约。目前仍存在一小组保留的、带有内置渠道品牌的辅助接缝,用于兼容性。应将它们视为内置维护 / 兼容性接缝,而不是新的第三方导入目标;新的跨渠道契约仍应落在通用 `plugin-sdk/*` 子路径或插件本地 `api.js` / `runtime-api.js` barrel 上。 + - Anthropic 使用 `api.js` / `contract-api.js` 提供 Claude 流辅助工具,例如 `wrapAnthropicProviderStream`、beta-header 辅助工具以及 `service_tier` 解析。 + - OpenAI 使用 `api.js` 提供 provider 构建器、默认模型辅助工具和实时提供商构建器。 + - OpenRouter 使用 `api.js` 提供其 provider 构建器以及新手引导 / 配置辅助工具,而 `register.runtime.js` 仍可为仓库内本地使用重新导出通用 `plugin-sdk/provider-stream` 辅助工具。 +- 由 facade 加载的公共入口点会优先使用当前激活的运行时配置快照;如果 OpenClaw 尚未提供运行时快照,则回退为磁盘上的已解析配置文件。 +- 通用共享原语仍然是首选的公共 SDK 契约。当前仍存在一小组保留的、带内置渠道品牌的辅助接缝,作为兼容性用途。应将它们视为内置维护 / 兼容性接缝,而不是新的第三方导入目标;新的跨渠道契约仍应落在通用 `plugin-sdk/*` 子路径或插件本地 `api.js` / `runtime-api.js` barrel 上。 兼容性说明: - 新代码应避免使用根级 `openclaw/plugin-sdk` barrel。 -- 优先使用狭窄且稳定的原语。较新的 setup / pairing / reply / feedback / contract / inbound / threading / command / secret-input / webhook / infra / allowlist / status / message-tool 子路径,才是新的内置和外部插件开发的预期契约。 - 目标解析 / 匹配应放在 `openclaw/plugin-sdk/channel-targets`。 - message 动作 gate 和 reaction message-id 辅助工具应放在 - `openclaw/plugin-sdk/channel-actions`。 -- 内置扩展特定的辅助 barrel 默认并不稳定。如果某个辅助工具仅被某个内置扩展需要,请将其保留在该扩展本地的 `api.js` 或 `runtime-api.js` 接缝之后,而不是提升到 `openclaw/plugin-sdk/` 中。 -- 新的共享辅助工具接缝应当是通用的,而不是带渠道品牌的。共享目标解析应放在 `openclaw/plugin-sdk/channel-targets`;渠道特定内部实现应保留在归属插件自己的本地 `api.js` 或 `runtime-api.js` 接缝之后。 -- 像 `image-generation`、`media-understanding` 和 `speech` 这样的能力特定子路径之所以存在,是因为当前内置 / 原生插件正在使用它们。但它们的存在本身并不意味着其中每个已导出的辅助工具都是长期冻结的外部契约。 +- 优先使用狭窄且稳定的原语。较新的 setup / pairing / reply / feedback / contract / inbound / threading / command / secret-input / webhook / infra / allowlist / status / message-tool 子路径,是新的内置和外部插件工作的预期契约。 + 目标解析 / 匹配应归属于 `openclaw/plugin-sdk/channel-targets`。 + 消息动作 gate 和 reaction message-id 辅助工具应归属于 `openclaw/plugin-sdk/channel-actions`。 +- 内置扩展专用的辅助 barrel 默认并不稳定。如果某个辅助工具仅由某个内置扩展需要,应将其保留在该扩展本地的 `api.js` 或 `runtime-api.js` 接缝之后,而不是提升到 `openclaw/plugin-sdk/` 中。 +- 新的共享辅助接缝应当是通用的,而不是带渠道品牌的。共享目标解析应归属于 `openclaw/plugin-sdk/channel-targets`;渠道特定内部实现应保留在归属插件本地的 `api.js` 或 `runtime-api.js` 接缝之后。 +- `image-generation`、`media-understanding` 和 `speech` 这类能力专用子路径之所以存在,是因为内置 / 原生插件今天就在使用它们。它们的存在本身并不意味着每个导出的辅助工具都是长期冻结的外部契约。 -## message 工具 schema +## 消息工具 schema -插件应拥有渠道特定的 `describeMessageTool(...)` schema 扩展。请将提供商特定字段保留在插件中,而不是放入共享核心。 +插件应拥有渠道特定的 `describeMessageTool(...)` schema 贡献。请将提供商特定字段保留在插件中,而不是放进共享核心。 -对于共享的可移植 schema 片段,请复用通过 -`openclaw/plugin-sdk/channel-actions` 导出的通用辅助工具: +对于共享的可移植 schema 片段,请复用通过 `openclaw/plugin-sdk/channel-actions` 导出的通用辅助工具: -- `createMessageToolButtonsSchema()` 用于按钮网格风格的负载 +- `createMessageToolButtonsSchema()` 用于按钮网格风格负载 - `createMessageToolCardSchema()` 用于结构化卡片负载 -如果某种 schema 形态只对单个提供商有意义,请在该插件自己的源码中定义它,而不是将其提升到共享 SDK 中。 +如果某个 schema 形态只对一个提供商有意义,就应将其定义在该插件自己的源码中,而不是提升到共享 SDK 中。 ## 渠道目标解析 -渠道插件应拥有渠道特定的目标语义。请保持共享出站宿主的通用性,并通过消息适配器接口处理提供商规则: +渠道插件应拥有渠道特定的目标语义。请保持共享出站宿主为通用实现,并使用消息适配器表面承载提供商规则: -- `messaging.inferTargetChatType({ to })` 决定规范化目标在目录查找前应被视为 `direct`、`group` 还是 `channel` -- `messaging.targetResolver.looksLikeId(raw, normalized)` 告诉核心,某个输入是否应跳过目录搜索,直接进入类 id 解析 -- `messaging.targetResolver.resolveTarget(...)` 是核心在规范化后或目录未命中后,进行最终提供商自有解析时的插件回退路径 -- `messaging.resolveOutboundSessionRoute(...)` 在目标解析完成后,负责构建提供商特定的会话路由 +- `messaging.inferTargetChatType({ to })` 用于在目录查找前,决定一个标准化目标应被视为 `direct`、`group` 还是 `channel` +- `messaging.targetResolver.looksLikeId(raw, normalized)` 用于告知核心某个输入是否应跳过目录搜索,直接进入类似 id 的解析 +- `messaging.targetResolver.resolveTarget(...)` 是核心在标准化之后或目录未命中之后,需要进行最终提供商自有解析时的插件回退路径 +- `messaging.resolveOutboundSessionRoute(...)` 用于在目标解析完成后,承载提供商特定的 session 路由构造 -推荐拆分方式: +建议的拆分方式: -- 对于应在搜索 peers / groups 之前发生的分类决策,使用 `inferTargetChatType` +- 对于应在搜索 peers / groups 之前做出的类别决策,使用 `inferTargetChatType` - 对于“将其视为显式 / 原生目标 id”的检查,使用 `looksLikeId` -- 对于提供商特定的规范化回退,使用 `resolveTarget`,而不是做广泛的目录搜索 -- 将聊天 id、线程 id、JID、handle 和 room id 这类提供商原生 id 保留在 `target` 值或提供商特定参数中,而不是放在通用 SDK 字段里 +- 对于提供商特定的标准化回退,使用 `resolveTarget`,而不是用于宽泛的目录搜索 +- 将提供商原生 id,例如 chat id、thread id、JID、handle 和 room id,保留在 `target` 值或提供商特定参数中,而不是放在通用 SDK 字段里 ## 基于配置的目录 -如果插件从配置派生目录条目,应将这部分逻辑保留在插件中,并复用 -`openclaw/plugin-sdk/directory-runtime` 中的共享辅助工具。 +如果插件需要从配置派生目录条目,应将这部分逻辑保留在插件中,并复用来自 `openclaw/plugin-sdk/directory-runtime` 的共享辅助工具。 -当某个渠道需要基于配置的 peers / groups 时,请使用这种方式,例如: +当渠道需要基于配置的 peers / groups 时,可以使用这一方式,例如: -- 由 allowlist 驱动的私信 peers -- 已配置的渠道 / 群组映射 -- 账户作用域内的静态目录回退 +- 基于 allowlist 的私信 peers +- 已配置的 channel / group 映射 +- 按账户作用域的静态目录回退 `directory-runtime` 中的共享辅助工具只处理通用操作: - 查询过滤 -- limit 应用 -- 去重 / 规范化辅助工具 +- 应用限制 +- 去重 / 标准化辅助工具 - 构建 `ChannelDirectoryEntry[]` -渠道特定的账户检查和 id 规范化应保留在插件实现中。 +渠道特定的账户检查和 id 标准化应保留在插件实现中。 ## 提供商目录 -提供商插件可以通过 -`registerProvider({ catalog: { run(...) { ... } } })` -为推理定义模型目录。 +提供商插件可以通过 `registerProvider({ catalog: { run(...) { ... } } })` 为推理定义模型目录。 -`catalog.run(...)` 返回的形态与 OpenClaw 写入 -`models.providers` 的内容相同: +`catalog.run(...)` 返回与 OpenClaw 写入 `models.providers` 相同的结构: - `{ provider }` 表示一个提供商条目 - `{ providers }` 表示多个提供商条目 -当插件拥有提供商特定的模型 id、base URL 默认值或受认证控制的模型元数据时,请使用 `catalog`。 +当插件拥有提供商特定 model id、基础 `base URL` 默认值,或受认证控制的模型元数据时,请使用 `catalog`。 `catalog.order` 控制插件目录相对于 OpenClaw 内置隐式提供商的合并时机: -- `simple`:普通 API key 或基于环境变量的提供商 -- `profile`:当存在认证配置文件时出现的提供商 -- `paired`:合成多个相关提供商条目的提供商 +- `simple`:普通 API 密钥或基于环境变量的提供商 +- `profile`:当认证配置文件存在时出现的提供商 +- `paired`:会合成多个相关提供商条目的提供商 - `late`:最后一轮,在其他隐式提供商之后 -发生键冲突时,较晚的提供商会胜出,因此插件可以有意覆盖拥有相同 provider id 的内置提供商条目。 +在 key 冲突时,较晚的提供商会获胜,因此插件可以有意用相同 provider id 覆盖内置提供商条目。 兼容性: @@ -999,32 +959,30 @@ api.registerHttpRoute({ ## 只读渠道检查 -如果你的插件注册了某个渠道,请优先在 `resolveAccount(...)` 旁边实现 -`plugin.config.inspectAccount(cfg, accountId)`。 +如果你的插件注册了一个渠道,建议在实现 `resolveAccount(...)` 的同时实现 `plugin.config.inspectAccount(cfg, accountId)`。 -原因如下: +原因: -- `resolveAccount(...)` 是运行时路径。它可以假设凭证已被完整实体化,并且在缺少必需密钥时快速失败。 -- 诸如 `openclaw status`、`openclaw status --all`、 - `openclaw channels status`、`openclaw channels resolve` 以及 Doctor / 配置修复流程等只读命令路径,不应只是为了描述配置就去实体化运行时凭证。 +- `resolveAccount(...)` 是运行时路径。它可以假定凭证已完全物化,并在缺少必要 secret 时快速失败。 +- 诸如 `openclaw status`、`openclaw status --all`、`openclaw channels status`、`openclaw channels resolve` 以及 Doctor / 配置修复流程等只读命令路径,不应为了描述配置而必须物化运行时凭证。 推荐的 `inspectAccount(...)` 行为: -- 仅返回描述性的账户状态。 +- 只返回描述性的账户状态。 - 保留 `enabled` 和 `configured`。 - 在相关时包含凭证来源 / 状态字段,例如: - `tokenSource`、`tokenStatus` - `botTokenSource`、`botTokenStatus` - `appTokenSource`、`appTokenStatus` - `signingSecretSource`、`signingSecretStatus` -- 你不需要为了报告只读可用性而返回原始 token 值。返回 `tokenStatus: "available"`(以及对应的来源字段)就足以支持状态类命令。 +- 为了报告只读可用性,你不需要返回原始令牌值。返回 `tokenStatus: "available"`(以及匹配的来源字段)就足够支撑状态类命令。 - 当凭证通过 SecretRef 配置,但在当前命令路径中不可用时,请使用 `configured_unavailable`。 -这样,只读命令就能报告“已配置,但在当前命令路径中不可用”,而不会崩溃或错误地把该账户报告为未配置。 +这样可以让只读命令报告“已配置,但在当前命令路径中不可用”,而不是崩溃或误报为未配置。 ## 包集合 -插件目录可以包含一个带有 `openclaw.extensions` 的 `package.json`: +一个插件目录可以包含带有 `openclaw.extensions` 的 `package.json`: ```json { @@ -1036,40 +994,37 @@ api.registerHttpRoute({ } ``` -每个条目都会变成一个插件。如果该包列出了多个扩展,则插件 id 会变成 `name/`。 +每个条目都会变成一个插件。如果该包集合列出了多个扩展,插件 id 将变为 `name/`。 -如果你的插件导入了 npm 依赖,请在该目录中安装它们,以确保 `node_modules` 可用(`npm install` / `pnpm install`)。 +如果你的插件导入 npm 依赖,请在该目录中安装它们,以便 `node_modules` 可用(`npm install` / `pnpm install`)。 -安全护栏:每个 `openclaw.extensions` 条目在解析符号链接后,都必须仍位于插件目录内部。任何逃逸出包目录的条目都会被拒绝。 +安全护栏:每个 `openclaw.extensions` 条目在解析符号链接后都必须仍位于插件目录内部。任何逃逸出包目录的条目都会被拒绝。 -安全说明:`openclaw plugins install` 会通过 -`npm install --omit=dev --ignore-scripts` -安装插件依赖(不运行生命周期脚本,运行时也不安装开发依赖)。请保持插件依赖树为“纯 JS / TS”,并避免需要 `postinstall` 构建的包。 +安全说明:`openclaw plugins install` 会使用 `npm install --omit=dev --ignore-scripts` 安装插件依赖(不运行生命周期脚本,运行时也不安装开发依赖)。请保持插件依赖树为“纯 JS / TS”,并避免需要 `postinstall` 构建的包。 -可选:`openclaw.setupEntry` 可以指向一个轻量级、仅用于设置的模块。当 OpenClaw 需要为一个已禁用的渠道插件提供设置接口,或当某个渠道插件已启用但尚未配置时,它会加载 `setupEntry`,而不是完整插件入口。这样,当你的主插件入口还会接线工具、钩子或其他仅运行时代码时,就能让启动和设置过程更轻量。 +可选:`openclaw.setupEntry` 可以指向一个轻量的仅设置模块。当 OpenClaw 需要为一个已禁用的渠道插件提供设置表面,或者某个渠道插件已启用但尚未配置时,它会加载 `setupEntry`,而不是完整插件入口。这样,当你的主插件入口还会接线工具、hooks 或其他仅运行时代码时,启动和设置过程就会更轻量。 -可选:`openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen` -可以让某个渠道插件在 Gateway 网关监听前的启动阶段,即使该渠道已经完成配置,也仍走同样的 `setupEntry` 路径。 +可选:`openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen` 可以让某个渠道插件在 Gateway 网关的 pre-listen 启动阶段,即使渠道已经配置完成,也走同样的 `setupEntry` 路径。 -只有当 `setupEntry` 完整覆盖了 Gateway 网关开始监听前必须存在的启动接口时,才应使用它。实际上,这意味着设置入口必须注册启动所依赖的每一项渠道自有能力,例如: +仅当 `setupEntry` 能够完全覆盖网关开始监听前必须存在的启动表面时,才应使用这一选项。实践中,这意味着设置入口必须注册所有启动所依赖的渠道自有能力,例如: - 渠道注册本身 -- 任何必须在 Gateway 网关开始监听前可用的 HTTP 路由 -- 任何必须在同一时间窗口内存在的 Gateway 网关方法、工具或服务 +- 所有必须在 Gateway 网关开始监听前可用的 HTTP 路由 +- 所有在同一时间窗口内必须存在的 gateway 方法、工具或服务 -如果完整入口仍拥有任何必需的启动能力,请不要启用此标志。应保持插件使用默认行为,让 OpenClaw 在启动期间加载完整入口。 +如果你的完整入口仍然拥有任何必需的启动能力,就不要启用此标志。请保持插件使用默认行为,让 OpenClaw 在启动期间加载完整入口。 -内置渠道还可以发布仅设置用的契约接口辅助工具,供核心在完整渠道运行时尚未加载前查询。当前的设置提升接口包括: +内置渠道还可以发布仅设置的契约表面辅助工具,以便核心在完整渠道运行时加载前进行查询。当前的设置提升表面是: - `singleAccountKeysToMove` - `namedAccountPromotionKeys` - `resolveSingleAccountPromotionTarget(...)` -当核心需要在不加载完整插件入口的情况下,将旧版单账户渠道配置提升为 `channels..accounts.*` 时,会使用这组接口。Matrix 是当前的内置示例:当已存在命名账户时,它只会将认证 / bootstrap 键移动到提升后的命名账户中,并且能够保留一个已配置但非规范的默认账户键,而不是总是创建 `accounts.default`。 +当核心需要在不加载完整插件入口的情况下,将旧版单账户渠道配置提升到 `channels..accounts.*` 时,就会使用这一表面。Matrix 是当前的内置示例:当已存在命名账户时,它只会将认证 / bootstrap 键移动到一个命名的提升账户中,并且它可以保留一个已配置但非规范默认账户键,而不是总是创建 `accounts.default`。 -这些设置补丁适配器使内置契约接口发现保持惰性。导入时保持轻量;提升接口只会在首次使用时加载,而不会在模块导入时重新进入内置渠道启动流程。 +这些设置补丁适配器让内置契约表面的发现保持惰性。导入时仍然轻量;提升表面只会在首次使用时加载,而不会在模块导入时重新进入内置渠道启动流程。 -当这些启动接口包含 Gateway 网关 RPC 方法时,请将它们保留在插件特定前缀下。核心管理命名空间(`config.*`、`exec.approvals.*`、`wizard.*`、`update.*`)仍然是保留的,并且始终解析为 `operator.admin`,即使某个插件请求更窄的作用域也是如此。 +当这些启动表面包含 Gateway 网关 RPC 方法时,请将其保留在插件专用前缀下。核心管理命名空间(`config.*`、`exec.approvals.*`、`wizard.*`、`update.*`)仍然是保留的,并且始终解析为 `operator.admin`,即使某个插件请求了更窄的作用域。 示例: @@ -1088,7 +1043,7 @@ api.registerHttpRoute({ ### 渠道目录元数据 -渠道插件可以通过 `openclaw.channel` 声明设置 / 发现元数据,并通过 `openclaw.install` 声明安装提示。这样可以让核心目录保持无数据化。 +渠道插件可以通过 `openclaw.channel` 声明设置 / 发现元数据,并通过 `openclaw.install` 声明安装提示。这样可以让核心目录保持无数据状态。 示例: @@ -1116,33 +1071,34 @@ api.registerHttpRoute({ } ``` -除了最小示例外,实用的 `openclaw.channel` 字段还包括: +除了最小示例之外,其他有用的 `openclaw.channel` 字段包括: -- `detailLabel`:用于更丰富目录 / 状态界面的次级标签 -- `docsLabel`:覆盖文档链接的显示文本 -- `preferOver`:此目录条目应优先于的低优先级插件 / 渠道 id -- `selectionDocsPrefix`、`selectionDocsOmitLabel`、`selectionExtras`:选择界面的文案控制 -- `markdownCapable`:将该渠道标记为支持 Markdown,以供出站格式决策使用 -- `exposure.configured`:设置为 `false` 时,在已配置渠道列表界面中隐藏该渠道 +- `detailLabel`:用于更丰富目录 / 状态表面的次级标签 +- `docsLabel`:覆盖文档链接文本 +- `preferOver`:该目录条目应优先于的低优先级插件 / 渠道 id +- `selectionDocsPrefix`、`selectionDocsOmitLabel`、`selectionExtras`:选择表面文案控制 +- `markdownCapable`:将该渠道标记为支持 Markdown,以便用于出站格式化决策 +- `exposure.configured`:设置为 `false` 时,在已配置渠道列表表面中隐藏该渠道 - `exposure.setup`:设置为 `false` 时,在交互式设置 / 配置选择器中隐藏该渠道 -- `exposure.docs`:在文档导航界面中将该渠道标记为内部 / 私有 -- `showConfigured` / `showInSetup`:旧版别名,出于兼容性仍可接受;优先使用 `exposure` -- `quickstartAllowFrom`:让该渠道接入标准快速开始 `allowFrom` 流程 -- `forceAccountBinding`:即使只有一个账户,也要求显式账户绑定 -- `preferSessionLookupForAnnounceTarget`:在解析公告目标时优先使用会话查找 +- `exposure.docs`:将该渠道标记为内部 / 私有,以用于文档导航表面 +- `showConfigured` / `showInSetup`:仍接受的旧版别名,仅用于兼容;优先使用 `exposure` +- `quickstartAllowFrom`:将该渠道加入标准快速开始 `allowFrom` 流程 +- `forceAccountBinding`:即使只存在一个账户,也要求显式账户绑定 +- `preferSessionLookupForAnnounceTarget`:在解析公告目标时优先使用 session 查找 -OpenClaw 还可以合并**外部渠道目录**(例如某个 MPM 注册表导出)。你可以将 JSON 文件放到以下任一路径: +OpenClaw 还可以合并**外部渠道目录**(例如某个 MPM 注册表导出)。将 JSON 文件放到以下任一位置即可: -或者将 `OPENCLAW_PLUGIN_CATALOG_PATHS`(或 `OPENCLAW_MPM_CATALOG_PATHS`)指向一个或多个 JSON 文件(使用逗号 / 分号 / `PATH` 分隔)。每个文件应包含 -`{ "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }`。解析器也接受 `"packages"` 或 `"plugins"` 作为 `"entries"` 键的旧版别名。 +- `~/.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"` 键的旧版别名。 ## 上下文引擎插件 -上下文引擎插件拥有会话上下文编排,包括摄取、组装和压缩。请在你的插件中通过 -`api.registerContextEngine(id, factory)` 注册它们,然后通过 -`plugins.slots.contextEngine` 选择活动引擎。 +上下文引擎插件拥有用于摄取、组装和压缩的会话上下文编排。通过 `api.registerContextEngine(id, factory)` 在你的插件中注册它们,然后通过 `plugins.slots.contextEngine` 选择活动引擎。 -当你的插件需要替换或扩展默认上下文流水线,而不仅仅是增加 memory 搜索或钩子时,请使用这种方式。 +当你的插件需要替换或扩展默认上下文流水线,而不仅仅是增加 memory 搜索或 hooks 时,请使用此机制。 ```ts import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core"; @@ -1170,7 +1126,7 @@ export default function (api) { } ``` -如果你的引擎**不**拥有压缩算法,请保留 `compact()` 的实现,并显式委托它: +如果你的引擎**不**拥有压缩算法,请保留 `compact()` 实现,并显式委托它: ```ts import { @@ -1207,37 +1163,37 @@ export default function (api) { ## 添加新能力 -当插件需要当前 API 无法覆盖的行为时,不要通过私有内部接入绕过插件系统。应添加缺失的能力。 +当插件需要当前 API 无法容纳的行为时,不要通过私有直连绕过插件系统。应当添加缺失的能力。 推荐顺序: 1. 定义核心契约 - 确定哪些共享行为应由核心拥有:策略、回退、配置合并、生命周期、面向渠道的语义,以及运行时辅助工具的形态。 -2. 添加类型化的插件注册 / 运行时接口 - 以最小且有用的类型化能力接口扩展 `OpenClawPluginApi` 和 / 或 `api.runtime`。 -3. 接入核心 + 渠道 / 功能消费者 - 渠道和功能插件应通过核心消费新能力,而不是直接导入某个供应商实现。 -4. 注册供应商实现 - 然后由供应商插件针对该能力注册它们的后端实现。 + 决定哪些共享行为应由核心拥有:策略、回退、配置合并、生命周期、面向渠道的语义,以及运行时辅助工具的形态。 +2. 添加类型化的插件注册 / 运行时表面 + 以最小但有用的类型化能力表面扩展 `OpenClawPluginApi` 和 / 或 `api.runtime`。 +3. 对接核心 + 渠道 / 功能消费者 + 渠道和功能插件应通过核心消费新能力,而不是直接导入某个厂商实现。 +4. 注册厂商实现 + 然后由厂商插件针对该能力注册它们的后端。 5. 添加契约覆盖 - 添加测试,以便归属和注册形态能长期保持清晰。 + 添加测试,以便归属关系和注册形态能长期保持明确。 -这就是 OpenClaw 在保持明确立场的同时,又不会被硬编码到某个提供商世界观中的方式。具体文件检查清单和完整示例,请参见[能力扩展手册](/zh-CN/plugins/architecture)。 +这正是 OpenClaw 保持有主见、但又不被某个提供商世界观硬编码绑死的方式。具体的文件清单和完整示例,请参见[能力扩展手册](/zh-CN/plugins/architecture)。 -### 能力检查清单 +### 能力清单 -当你添加一个新能力时,实现通常应同时覆盖以下接口: +当你添加一个新能力时,实现通常应一起覆盖以下表面: - `src//types.ts` 中的核心契约类型 - `src//runtime.ts` 中的核心 runner / 运行时辅助工具 -- `src/plugins/types.ts` 中的插件 API 注册接口 +- `src/plugins/types.ts` 中的插件 API 注册表面 - `src/plugins/registry.ts` 中的插件注册表接线 -- 当功能 / 渠道插件需要消费该能力时,位于 `src/plugins/runtime/*` 中的插件运行时暴露接口 +- 当功能 / 渠道插件需要消费它时,`src/plugins/runtime/*` 中的插件运行时暴露 - `src/test-utils/plugin-registration.ts` 中的捕获 / 测试辅助工具 - `src/plugins/contracts/registry.ts` 中的归属 / 契约断言 -- `docs/` 中面向操作员 / 插件的文档 +- `docs/` 中的操作者 / 插件文档 -如果其中某个接口缺失,通常意味着该能力尚未被完整集成。 +如果这些表面中有某一项缺失,通常说明该能力尚未被完整集成。 ### 能力模板 @@ -1273,9 +1229,9 @@ const clip = await api.runtime.videoGeneration.generate({ expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]); ``` -这样规则就保持简单: +这样规则就很清晰: - 核心拥有能力契约 + 编排 -- 供应商插件拥有供应商实现 +- 厂商插件拥有厂商实现 - 功能 / 渠道插件消费运行时辅助工具 -- 契约测试让归属保持清晰 +- 契约测试让归属关系保持明确 diff --git a/docs/zh-CN/plugins/sdk-channel-plugins.md b/docs/zh-CN/plugins/sdk-channel-plugins.md index df5bed012..986c24e03 100644 --- a/docs/zh-CN/plugins/sdk-channel-plugins.md +++ b/docs/zh-CN/plugins/sdk-channel-plugins.md @@ -2,25 +2,25 @@ read_when: - 你正在构建一个新的消息渠道插件 - 你想将 OpenClaw 连接到一个消息平台 - - 你需要了解 `ChannelPlugin` 适配器接口 surface + - 你需要理解 `ChannelPlugin` 适配器接口。 sidebarTitle: Channel Plugins summary: 为 OpenClaw 构建消息渠道插件的分步指南 title: 构建渠道插件 x-i18n: - generated_at: "2026-04-10T20:41:22Z" + generated_at: "2026-04-14T16:24:20Z" model: gpt-5.4 provider: openai - source_hash: 8a026e924f9ae8a3ddd46287674443bcfccb0247be504261522b078e1f440aef + source_hash: a7f4c746fe3163a8880e14c433f4db4a1475535d91716a53fb879551d8d62f65 source_path: plugins/sdk-channel-plugins.md workflow: 15 --- # 构建渠道插件 -本指南将带你构建一个将 OpenClaw 连接到消息平台的渠道插件。完成后,你将拥有一个可工作的渠道,具备私信安全、配对、回复线程处理以及出站消息能力。 +本指南将带你构建一个把 OpenClaw 连接到消息平台的渠道插件。完成后,你将拥有一个可正常工作的渠道,具备私信安全、配对、回复线程以及出站消息能力。 - 如果你之前还没有构建过任何 OpenClaw 插件,请先阅读 + 如果你之前没有构建过任何 OpenClaw 插件,请先阅读 [入门指南](/zh-CN/plugins/building-plugins),了解基础包结构和清单设置。 @@ -28,57 +28,61 @@ x-i18n: 渠道插件不需要自己的发送/编辑/响应工具。OpenClaw 在核心中保留了一个共享的 `message` 工具。你的插件负责: -- **配置** — 账号解析和设置向导 -- **安全** — 私信策略和允许列表 -- **配对** — 私信批准流程 -- **会话语法** — 提供商特定的会话 id 如何映射到基础聊天、线程 id 和父级回退 -- **出站** — 向平台发送文本、媒体和投票 -- **线程处理** — 回复如何在线程中组织 +- **配置** —— 账户解析和设置向导 +- **安全** —— 私信策略和允许列表 +- **配对** —— 私信批准流程 +- **会话语法** —— 提供商特定的会话 id 如何映射到基础聊天、线程 id 和父级回退 +- **出站** —— 向平台发送文本、媒体和投票 +- **线程** —— 回复如何建立线程 核心负责共享的消息工具、提示词接线、外层会话键形状、通用 `:thread:` 记录以及分发。 -如果你的平台在会话 id 中存储了额外作用域,请将该解析逻辑保留在插件中,并使用 `messaging.resolveSessionConversation(...)`。这是将 `rawId` 映射为基础会话 id、可选线程 id、显式 `baseConversationId` 以及任意 `parentConversationCandidates` 的规范钩子。 -当你返回 `parentConversationCandidates` 时,请按从最窄父级到最宽泛/基础会话的顺序排列。 +如果你的渠道为消息工具添加了携带媒体来源的参数,请通过 `describeMessageTool(...).mediaSourceParams` 暴露这些参数名。核心使用这个显式列表来进行沙箱路径规范化和出站媒体访问策略处理,因此插件不需要为提供商特定的头像、附件或封面图片参数添加共享核心特例。 +更推荐返回一个按 action 键控的映射,例如 +`{ "set-profile": ["avatarUrl", "avatarPath"] }`,这样不相关的 action 就不会继承另一个 action 的媒体参数。对于确实要在所有暴露 action 之间共享的参数,扁平数组同样可用。 -需要在渠道注册表启动之前执行相同解析的内置插件,也可以公开一个顶层 `session-key-api.ts` 文件,并提供匹配的 `resolveSessionConversation(...)` 导出。只有在运行时插件注册表尚不可用时,核心才会使用这个可安全用于引导的接口。 +如果你的平台会在会话 id 中存储额外作用域,请把该解析逻辑保留在插件中,通过 `messaging.resolveSessionConversation(...)` 处理。这是将 `rawId` 映射为基础会话 id、可选线程 id、显式 `baseConversationId` 以及任意 `parentConversationCandidates` 的规范钩子。 +当你返回 `parentConversationCandidates` 时,请按从最窄父级到最宽/基础会话的顺序排列。 -当插件只需要在通用/原始 id 之上提供父级回退时,`messaging.resolveParentConversationCandidates(...)` 仍然可作为旧版兼容回退方案使用。如果两个钩子都存在,核心会优先使用 `resolveSessionConversation(...).parentConversationCandidates`,只有在规范钩子省略它们时,才会回退到 `resolveParentConversationCandidates(...)`。 +需要在渠道注册表启动前完成相同解析的内置插件,也可以暴露一个顶层 `session-key-api.ts` 文件,并导出匹配的 `resolveSessionConversation(...)`。只有在运行时插件注册表尚不可用时,核心才会使用这个可安全引导的接口。 -## 批准和渠道能力 +当插件只需要在通用/原始 id 之上补充父级回退时,`messaging.resolveParentConversationCandidates(...)` 仍可作为旧版兼容回退使用。如果两个钩子都存在,核心会优先使用 `resolveSessionConversation(...).parentConversationCandidates`,只有在规范钩子省略它们时,才会回退到 `resolveParentConversationCandidates(...)`。 + +## 批准与渠道能力 大多数渠道插件不需要特定于批准的代码。 -- 核心负责同一聊天中的 `/approve`、共享的批准按钮负载以及通用回退投递。 -- 当渠道需要特定于批准的行为时,优先在渠道插件上使用单个 `approvalCapability` 对象。 -- `ChannelPlugin.approvals` 已移除。请将批准投递/原生/渲染/鉴权相关信息放到 `approvalCapability` 中。 -- `plugin.auth` 仅用于登录/登出;核心不再从该对象读取批准鉴权钩子。 -- `approvalCapability.authorizeActorAction` 和 `approvalCapability.getActionAvailabilityState` 是规范的批准鉴权接口。 -- 对于同一聊天中的批准鉴权可用性,请使用 `approvalCapability.getActionAvailabilityState`。 -- 如果你的渠道公开原生 exec 批准,请在发起界面/原生客户端状态与同一聊天中的批准鉴权不同时使用 `approvalCapability.getExecInitiatingSurfaceState`。核心使用这个特定于 exec 的钩子来区分 `enabled` 与 `disabled`、判断发起渠道是否支持原生 exec 批准,并在原生客户端回退指南中包含该渠道。`createApproverRestrictedNativeApprovalCapability(...)` 为常见情况提供了这一能力。 -- 对于渠道特定的负载生命周期行为,例如隐藏重复的本地批准提示,或在投递前发送输入中指示器,请使用 `outbound.shouldSuppressLocalPayloadPrompt` 或 `outbound.beforeDeliverPayload`。 -- 仅在原生批准路由或回退抑制时使用 `approvalCapability.delivery`。 -- 对于渠道自有的原生批准事实,请使用 `approvalCapability.nativeRuntime`。在高频渠道入口点上,请通过 `createLazyChannelApprovalNativeRuntimeAdapter(...)` 保持其惰性加载;这样它可以按需导入你的运行时模块,同时仍让核心组装批准生命周期。 -- 仅当渠道确实需要自定义批准负载而不是共享渲染器时,才使用 `approvalCapability.render`。 -- 当渠道希望禁用路径回复能够说明启用原生 exec 批准所需的精确配置项时,请使用 `approvalCapability.describeExecApprovalSetup`。该钩子接收 `{ channel, channelLabel, accountId }`;命名账号渠道应渲染带账号作用域的路径,例如 `channels..accounts..execApprovals.*`,而不是顶层默认值。 -- 如果渠道可以从现有配置中推断出稳定的 owner 类似私信身份,请使用来自 `openclaw/plugin-sdk/approval-runtime` 的 `createResolvedApproverActionAuthAdapter`,以在不添加特定于批准的核心逻辑的情况下限制同一聊天中的 `/approve`。 -- 如果渠道需要原生批准投递,请让渠道代码专注于目标规范化以及传输/呈现事实。请使用来自 `openclaw/plugin-sdk/approval-runtime` 的 `createChannelExecApprovalProfile`、`createChannelNativeOriginTargetResolver`、`createChannelApproverDmTargetResolver` 和 `createApproverRestrictedNativeApprovalCapability`。将渠道特定事实放在 `approvalCapability.nativeRuntime` 之后,理想情况下通过 `createChannelApprovalNativeRuntimeAdapter(...)` 或 `createLazyChannelApprovalNativeRuntimeAdapter(...)`,这样核心就可以组装处理器并负责请求过滤、路由、去重、过期、Gateway 网关订阅以及“已路由到其他位置”的通知。`nativeRuntime` 被拆分为几个更小的接口: -- `availability` — 账号是否已配置,以及请求是否应被处理 -- `presentation` — 将共享批准视图模型映射为待处理/已解决/已过期的原生负载或最终操作 -- `transport` — 准备目标,以及发送/更新/删除原生批准消息 -- `interactions` — 原生按钮或响应的可选绑定/解绑/清除操作钩子 -- `observe` — 可选的投递诊断钩子 -- 如果渠道需要运行时自有对象,例如客户端、令牌、Bolt 应用或 webhook 接收器,请通过 `openclaw/plugin-sdk/channel-runtime-context` 注册它们。通用运行时上下文注册表让核心能够从渠道启动状态中引导基于能力的处理器,而无需添加特定于批准的包装胶水代码。 -- 仅当基于能力的接口还不够有表达力时,才使用更底层的 `createChannelApprovalHandler` 或 `createChannelNativeApprovalRuntime`。 -- 原生批准渠道必须通过这些辅助工具同时路由 `accountId` 和 `approvalKind`。`accountId` 使多账号批准策略保持在正确的机器人账号作用域内,而 `approvalKind` 让渠道在不在核心中编写硬编码分支的情况下获得 exec 与插件批准行为。 -- 现在连批准重路由通知也由核心负责。渠道插件不应再从 `createChannelNativeApprovalRuntime` 发送自己的“批准已转到私信/其他渠道”后续消息;相反,应通过共享批准能力辅助工具公开准确的来源和 approver 私信路由,并让核心在向发起聊天回发任何通知前聚合实际投递结果。 -- 端到端保留已投递的批准 id 类型。原生客户端不应根据渠道本地状态猜测或重写 exec 与插件批准路由。 -- 不同的批准类型可以有意公开不同的原生界面。 - 当前的内置示例: - - Slack 对 exec 和插件 id 都保留了原生批准路由能力。 - - Matrix 对 exec 和插件批准保留相同的原生私信/渠道路由和 reaction 交互体验,同时仍允许鉴权按批准类型不同。 -- `createApproverRestrictedNativeApprovalAdapter` 仍然作为兼容包装器存在,但新代码应优先使用能力构建器,并在插件上公开 `approvalCapability`。 +- 核心负责同一聊天中的 `/approve`、共享的批准按钮负载,以及通用回退投递。 +- 当渠道需要特定于批准的行为时,优先在渠道插件上使用一个 `approvalCapability` 对象。 +- `ChannelPlugin.approvals` 已移除。请将批准投递/原生/渲染/认证相关信息放到 `approvalCapability` 上。 +- `plugin.auth` 仅用于登录/登出;核心不再从该对象读取批准认证钩子。 +- `approvalCapability.authorizeActorAction` 和 `approvalCapability.getActionAvailabilityState` 是规范的批准认证接口。 +- 对于同一聊天中的批准认证可用性,请使用 `approvalCapability.getActionAvailabilityState`。 +- 如果你的渠道暴露原生 exec 批准,请在其与同一聊天批准认证不同时使用 `approvalCapability.getExecInitiatingSurfaceState` 来表示发起界面/原生客户端状态。核心使用这个 exec 专用钩子来区分 `enabled` 与 `disabled`、判断发起渠道是否支持原生 exec 批准,并在原生客户端回退指引中包含该渠道。`createApproverRestrictedNativeApprovalCapability(...)` 可为常见情况完成这部分逻辑。 +- 对于特定于渠道的负载生命周期行为,例如隐藏重复的本地批准提示或在投递前发送输入中指示器,请使用 `outbound.shouldSuppressLocalPayloadPrompt` 或 `outbound.beforeDeliverPayload`。 +- 仅在进行原生批准路由或抑制回退时使用 `approvalCapability.delivery`。 +- 对于渠道自有的原生批准事实,请使用 `approvalCapability.nativeRuntime`。在高频渠道入口点中,使用 `createLazyChannelApprovalNativeRuntimeAdapter(...)` 保持其惰性加载;这样可以按需导入你的运行时模块,同时仍允许核心组装批准生命周期。 +- 只有当渠道确实需要自定义批准负载而不是共享渲染器时,才使用 `approvalCapability.render`。 +- 如果渠道希望在禁用路径的回复中说明启用原生 exec 批准所需的精确配置项,请使用 `approvalCapability.describeExecApprovalSetup`。该钩子接收 `{ channel, channelLabel, accountId }`;具名账户渠道应渲染带账户作用域的路径,例如 `channels..accounts..execApprovals.*`,而不是顶层默认值。 +- 如果渠道可以从现有配置中推断出稳定的类似所有者的私信身份,请使用 `openclaw/plugin-sdk/approval-runtime` 中的 `createResolvedApproverActionAuthAdapter`,以限制同一聊天中的 `/approve`,而无需添加特定于批准的核心逻辑。 +- 如果渠道需要原生批准投递,请让渠道代码专注于目标规范化以及传输/展示事实。使用 `openclaw/plugin-sdk/approval-runtime` 中的 `createChannelExecApprovalProfile`、`createChannelNativeOriginTargetResolver`、`createChannelApproverDmTargetResolver` 和 `createApproverRestrictedNativeApprovalCapability`。将特定于渠道的事实放在 `approvalCapability.nativeRuntime` 后面,理想情况下通过 `createChannelApprovalNativeRuntimeAdapter(...)` 或 `createLazyChannelApprovalNativeRuntimeAdapter(...)`,这样核心就能组装处理器,并负责请求过滤、路由、去重、过期、Gateway 网关订阅以及“已路由到其他地方”通知。`nativeRuntime` 被拆分为几个更小的接口: +- `availability` —— 账户是否已配置,以及请求是否应被处理 +- `presentation` —— 将共享的批准视图模型映射为待处理/已解决/已过期的原生负载或最终动作 +- `transport` —— 准备目标,并发送/更新/删除原生批准消息 +- `interactions` —— 原生按钮或响应的可选 bind/unbind/clear-action 钩子 +- `observe` —— 可选的投递诊断钩子 +- 如果渠道需要运行时自有对象,例如客户端、令牌、Bolt 应用或 webhook 接收器,请通过 `openclaw/plugin-sdk/channel-runtime-context` 注册它们。通用 runtime-context 注册表让核心可以从渠道启动状态引导基于能力的处理器,而无需添加特定于批准的包装胶水代码。 +- 仅当基于能力的接口还不足以表达需求时,才使用更底层的 `createChannelApprovalHandler` 或 `createChannelNativeApprovalRuntime`。 +- 原生批准渠道必须通过这些辅助函数同时路由 `accountId` 和 `approvalKind`。`accountId` 使多账户批准策略保持在正确的机器人账户作用域内,而 `approvalKind` 则使 exec 与插件批准行为能够在渠道中可用,而无需在核心中写死分支。 +- 核心现在也负责批准重路由通知。渠道插件不应再从 `createChannelNativeApprovalRuntime` 发送自己的“批准已转到私信/另一个渠道”后续消息;应通过共享批准能力辅助函数暴露准确的来源 + 批准者私信路由,并让核心在向发起聊天发送任何通知前先汇总实际投递情况。 +- 在端到端流程中保留已投递批准 id 的种类。原生客户端不应根据渠道本地状态去猜测或重写 exec 与插件批准的路由。 +- 不同批准类型可以有意暴露不同的原生界面。 + 当前内置示例: + - Slack 对 exec 和插件 id 都保留原生批准路由能力。 + - Matrix 对 exec 和插件批准保持相同的原生私信/渠道路由与响应 UX,同时仍允许认证因批准类型而异。 +- `createApproverRestrictedNativeApprovalAdapter` 仍然存在,作为兼容性包装器,但新代码应优先使用能力构建器,并在插件上暴露 `approvalCapability`。 -对于高频渠道入口点,如果你只需要该系列中的某一部分,请优先使用更窄的运行时子路径: +对于高频渠道入口点,如果你只需要这个家族中的某一部分,请优先使用更窄的运行时子路径: - `openclaw/plugin-sdk/approval-auth-runtime` - `openclaw/plugin-sdk/approval-client-runtime` @@ -90,66 +94,65 @@ x-i18n: - `openclaw/plugin-sdk/approval-reply-runtime` - `openclaw/plugin-sdk/channel-runtime-context` -同样,如果你不需要更宽泛的总接口,请优先使用 `openclaw/plugin-sdk/setup-runtime`、`openclaw/plugin-sdk/setup-adapter-runtime`、`openclaw/plugin-sdk/reply-runtime`、`openclaw/plugin-sdk/reply-dispatch-runtime`、`openclaw/plugin-sdk/reply-reference` 和 `openclaw/plugin-sdk/reply-chunking`。 +同样地,当你不需要更宽泛的总入口接口时,请优先使用 `openclaw/plugin-sdk/setup-runtime`、 +`openclaw/plugin-sdk/setup-adapter-runtime`、 +`openclaw/plugin-sdk/reply-runtime`、 +`openclaw/plugin-sdk/reply-dispatch-runtime`、 +`openclaw/plugin-sdk/reply-reference` 和 +`openclaw/plugin-sdk/reply-chunking`。 对于设置,具体如下: -- `openclaw/plugin-sdk/setup-runtime` 包含运行时安全的设置辅助工具: - 可安全导入的设置补丁适配器(`createPatchedAccountSetupAdapter`、 +- `openclaw/plugin-sdk/setup-runtime` 覆盖运行时安全的设置辅助工具: + 导入安全的设置补丁适配器(`createPatchedAccountSetupAdapter`、 `createEnvPatchedAccountSetupAdapter`、 `createSetupInputPresenceValidator`)、查找说明输出、 `promptResolvedAllowFrom`、`splitSetupEntries` 以及委托式 setup-proxy 构建器 -- `openclaw/plugin-sdk/setup-adapter-runtime` 是用于 `createEnvPatchedAccountSetupAdapter` 的窄型、环境感知适配器接口 -- `openclaw/plugin-sdk/channel-setup` 包含可选安装设置构建器以及一些设置安全的原语: +- `openclaw/plugin-sdk/setup-adapter-runtime` 是用于 `createEnvPatchedAccountSetupAdapter` 的窄范围、环境感知适配器接口 +- `openclaw/plugin-sdk/channel-setup` 覆盖可选安装的设置构建器以及一些设置安全原语: `createOptionalChannelSetupSurface`、`createOptionalChannelSetupAdapter`、 -如果你的渠道支持由环境驱动的设置或鉴权,并且通用启动/配置流程需要在运行时加载前知道这些环境变量名称,请在插件清单中使用 `channelEnvVars` 声明它们。请将渠道运行时 `envVars` 或本地常量仅保留给面向运维者的文案。 +如果你的渠道支持由环境变量驱动的设置或认证,并且通用启动/配置流程需要在运行时加载前知道这些环境变量名称,请在插件清单中用 `channelEnvVars` 声明它们。将渠道运行时 `envVars` 或本地常量保留给面向操作者的文案使用。 `createOptionalChannelSetupWizard`、`DEFAULT_ACCOUNT_ID`、 `createTopLevelChannelDmPolicy`、`setSetupChannelEnabled` 和 `splitSetupEntries` -- 只有在你还需要更重型的共享设置/配置辅助工具时,才使用更宽泛的 `openclaw/plugin-sdk/setup` 接口,例如 +- 只有当你还需要更重的共享设置/配置辅助工具时,才使用更宽泛的 `openclaw/plugin-sdk/setup` 接口,例如 `moveSingleAccountChannelSectionToDefaultAccount(...)` -如果你的渠道只想在设置界面中提示“请先安装此插件”,优先使用 `createOptionalChannelSetupSurface(...)`。生成的适配器/向导会在配置写入和最终完成时默认关闭,并在校验、完成和文档链接文案中复用同一条“需要安装”的消息。 +如果你的渠道只想在设置界面中提示“请先安装此插件”,优先使用 `createOptionalChannelSetupSurface(...)`。生成的 adapter/wizard 会在配置写入和最终化时默认关闭,并且会在验证、最终化以及文档链接文案中复用同一条“需要安装”消息。 -对于其他高频渠道路径,请优先使用窄型辅助工具,而不是更宽泛的旧接口: +对于其他高频渠道路径,请优先使用窄接口辅助工具,而不是更宽泛的旧版接口: - `openclaw/plugin-sdk/account-core`、 `openclaw/plugin-sdk/account-id`、 `openclaw/plugin-sdk/account-resolution` 和 - `openclaw/plugin-sdk/account-helpers`,用于多账号配置和 - 默认账号回退 + `openclaw/plugin-sdk/account-helpers`,用于多账户配置和默认账户回退 - `openclaw/plugin-sdk/inbound-envelope` 和 - `openclaw/plugin-sdk/inbound-reply-dispatch`,用于入站路由/信封以及 - record-and-dispatch 接线 + `openclaw/plugin-sdk/inbound-reply-dispatch`,用于入站路由/信封以及记录并分发接线 - `openclaw/plugin-sdk/messaging-targets`,用于目标解析/匹配 - `openclaw/plugin-sdk/outbound-media` 和 - `openclaw/plugin-sdk/outbound-runtime`,用于媒体加载以及出站 - 身份/发送委托 -- `openclaw/plugin-sdk/thread-bindings-runtime`,用于线程绑定生命周期 - 和适配器注册 -- 仅当仍然需要旧版智能体/媒体负载字段布局时,才使用 `openclaw/plugin-sdk/agent-media-payload` -- `openclaw/plugin-sdk/telegram-command-config`,用于 Telegram 自定义命令 - 规范化、重复/冲突校验,以及一个回退稳定的命令 - 配置契约 + `openclaw/plugin-sdk/outbound-runtime`,用于媒体加载以及出站身份/发送委托 +- `openclaw/plugin-sdk/thread-bindings-runtime`,用于线程绑定生命周期和 adapter 注册 +- `openclaw/plugin-sdk/agent-media-payload`,仅在仍需要旧版智能体/媒体负载字段布局时使用 +- `openclaw/plugin-sdk/telegram-command-config`,用于 Telegram 自定义命令规范化、重复/冲突校验,以及具备稳定回退行为的命令配置契约 -仅鉴权渠道通常可以停留在默认路径:核心处理批准,而插件只公开出站/鉴权能力。像 Matrix、Slack、Telegram 和自定义聊天传输这样的原生批准渠道,应使用共享的原生辅助工具,而不是自行实现批准生命周期。 +仅认证渠道通常可以停留在默认路径:核心负责批准,而插件只需暴露出站/认证能力。像 Matrix、Slack、Telegram 以及自定义聊天传输这类原生批准渠道,应使用共享的原生辅助工具,而不是自行实现批准生命周期。 ## 入站提及策略 请将入站提及处理拆分为两层: - 插件自有的证据收集 -- 共享策略评估 +- 共享的策略评估 共享层请使用 `openclaw/plugin-sdk/channel-inbound`。 适合放在插件本地逻辑中的内容: -- 回复给机器人的检测 -- 引用机器人的检测 +- 回复机器人检测 +- 引用机器人检测 - 线程参与检查 - 服务/系统消息排除 - 用于证明机器人参与的平台原生缓存 @@ -160,12 +163,12 @@ x-i18n: - 显式提及结果 - 隐式提及允许列表 - 命令绕过 -- 最终跳过决策 +- 最终跳过决定 推荐流程: 1. 计算本地提及事实。 -2. 将这些事实传入 `resolveInboundMentionDecision({ facts, policy })`。 +2. 将这些事实传给 `resolveInboundMentionDecision({ facts, policy })`。 3. 在你的入站门控中使用 `decision.effectiveWasMentioned`、`decision.shouldBypassMention` 和 `decision.shouldSkip`。 ```typescript @@ -205,7 +208,7 @@ const decision = resolveInboundMentionDecision({ if (decision.shouldSkip) return; ``` -`api.runtime.channel.mentions` 为已经依赖运行时注入的内置渠道插件公开了同样的共享提及辅助工具: +`api.runtime.channel.mentions` 为已经依赖运行时注入的内置渠道插件暴露了同一组共享提及辅助工具: - `buildMentionRegexes` - `matchesMentionPatterns` @@ -222,9 +225,8 @@ if (decision.shouldSkip) return; - 创建标准插件文件。`package.json` 中的 `channel` 字段 - 让它成为一个渠道插件。有关完整的包元数据接口, - 请参阅 [插件设置和配置](/zh-CN/plugins/sdk-setup#openclaw-channel): + 创建标准插件文件。`package.json` 中的 `channel` 字段会将其标记为渠道插件。完整的包元数据接口请参见 + [插件设置和配置](/zh-CN/plugins/sdk-setup#openclaw-channel): ```json package.json @@ -238,7 +240,7 @@ if (decision.shouldSkip) return; "channel": { "id": "acme-chat", "label": "Acme Chat", - "blurb": "将 OpenClaw 连接到 Acme Chat。" + "blurb": "Connect OpenClaw to Acme Chat." } } } @@ -250,7 +252,7 @@ if (decision.shouldSkip) return; "kind": "channel", "channels": ["acme-chat"], "name": "Acme Chat", - "description": "Acme Chat 渠道插件", + "description": "Acme Chat channel plugin", "configSchema": { "type": "object", "additionalProperties": false, @@ -274,8 +276,7 @@ if (decision.shouldSkip) return; - `ChannelPlugin` 接口具有许多可选的适配器接口。先从 - 最小集合开始——`id` 和 `setup`——然后按需添加适配器。 + `ChannelPlugin` 接口有许多可选适配器接口。先从最小集合开始——`id` 和 `setup`——然后按需添加适配器。 创建 `src/channel.ts`: @@ -285,7 +286,7 @@ if (decision.shouldSkip) return; createChannelPluginBase, } from "openclaw/plugin-sdk/channel-core"; import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core"; - import { acmeChatApi } from "./client.js"; // 你的平台 API 客户端 + import { acmeChatApi } from "./client.js"; // your platform API client type ResolvedAccount = { accountId: string | null; @@ -326,7 +327,7 @@ if (decision.shouldSkip) return; }, }), - // 私信安全:谁可以给机器人发消息 + // DM security: who can message the bot security: { dm: { channelKey: "acme-chat", @@ -336,21 +337,21 @@ if (decision.shouldSkip) return; }, }, - // 配对:新私信联系人的批准流程 + // Pairing: approval flow for new DM contacts pairing: { text: { - idLabel: "Acme Chat 用户名", - message: "发送此代码以验证你的身份:", + idLabel: "Acme Chat username", + message: "Send this code to verify your identity:", notify: async ({ target, code }) => { await acmeChatApi.sendDm(target, `Pairing code: ${code}`); }, }, }, - // 线程处理:回复如何投递 + // Threading: how replies are delivered threading: { topLevelReplyToMode: "reply" }, - // 出站:向平台发送消息 + // Outbound: send messages to the platform outbound: { attachedResults: { sendText: async (params) => { @@ -371,17 +372,16 @@ if (decision.shouldSkip) return; ``` - 你无需手动实现底层适配器接口, - 而是传入声明式选项,由构建器负责组合它们: + 你无需手动实现底层适配器接口,而是传入声明式选项,由构建器负责组合它们: | 选项 | 接线内容 | | --- | --- | - | `security.dm` | 根据配置字段生成带作用域的私信安全解析器 | - | `pairing.text` | 基于文本代码交换的私信配对流程 | - | `threading` | reply-to 模式解析器(固定、账号作用域或自定义) | - | `outbound.attachedResults` | 返回结果元数据(消息 ID)的发送函数 | + | `security.dm` | 从配置字段解析带作用域的私信安全解析器 | + | `pairing.text` | 基于文本和验证码交换的私信配对流程 | + | `threading` | 回复模式解析器(固定、账户作用域或自定义) | + | `outbound.attachedResults` | 返回结果元数据的发送函数(消息 ID) | - 如果你需要完全控制,也可以直接传入原始适配器对象,而不是声明式选项。 + 如果你需要完全控制,也可以直接传入原始适配器对象,而不是这些声明式选项。 @@ -396,20 +396,20 @@ if (decision.shouldSkip) return; export default defineChannelPluginEntry({ id: "acme-chat", name: "Acme Chat", - description: "Acme Chat 渠道插件", + description: "Acme Chat channel plugin", plugin: acmeChatPlugin, registerCliMetadata(api) { api.registerCli( ({ program }) => { program .command("acme-chat") - .description("Acme Chat 管理"); + .description("Acme Chat management"); }, { descriptors: [ { name: "acme-chat", - description: "Acme Chat 管理", + description: "Acme Chat management", hasSubcommands: false, }, ], @@ -422,21 +422,16 @@ if (decision.shouldSkip) return; }); ``` - 将渠道自有的 CLI 描述符放在 `registerCliMetadata(...)` 中,这样 OpenClaw - 就可以在不激活完整渠道运行时的情况下在根帮助中显示它们, - 同时正常的完整加载仍会使用同一组描述符进行真实命令 - 注册。请将 `registerFull(...)` 保留给仅运行时的工作。 - 如果 `registerFull(...)` 注册 Gateway 网关 RPC 方法,请使用 - 插件特定前缀。核心管理命名空间(`config.*`、 - `exec.approvals.*`、`wizard.*`、`update.*`)保持保留,并且始终 - 解析为 `operator.admin`。 - `defineChannelPluginEntry` 会自动处理注册模式拆分。有关全部 - 选项,请参阅 [入口点](/zh-CN/plugins/sdk-entrypoints#definechannelpluginentry)。 + 将渠道自有的 CLI 描述符放在 `registerCliMetadata(...)` 中,这样 OpenClaw 就可以在不激活完整渠道运行时的情况下,在根帮助中展示它们;而正常的完整加载仍会拾取相同的描述符来进行真实命令注册。`registerFull(...)` 应保留给仅运行时需要的工作。 + 如果 `registerFull(...)` 注册 Gateway 网关 RPC 方法,请使用插件专属前缀。核心管理命名空间(`config.*`、 + `exec.approvals.*`、`wizard.*`、`update.*`)保留给核心使用,并始终解析到 `operator.admin`。 + `defineChannelPluginEntry` 会自动处理注册模式拆分。所有选项请参见 + [入口点](/zh-CN/plugins/sdk-entrypoints#definechannelpluginentry)。 - 创建 `setup-entry.ts`,用于在新手引导期间进行轻量加载: + 创建 `setup-entry.ts`,用于新手引导期间的轻量加载: ```typescript setup-entry.ts import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core"; @@ -445,27 +440,26 @@ if (decision.shouldSkip) return; export default defineSetupPluginEntry(acmeChatPlugin); ``` - 当渠道被禁用或尚未配置时,OpenClaw 会加载它而不是完整入口。 - 这样可以避免在设置流程期间拉入重量级运行时代码。 - 详见 [设置和配置](/zh-CN/plugins/sdk-setup#setup-entry)。 + 当渠道被禁用或尚未配置时,OpenClaw 会加载这个入口,而不是完整入口。 + 这样可以避免在设置流程中拉入较重的运行时代码。详见 + [设置和配置](/zh-CN/plugins/sdk-setup#setup-entry)。 - 你的插件需要从平台接收消息并将其转发给 - OpenClaw。典型模式是一个 webhook:它验证请求,然后通过你的渠道入站处理器进行分发: + 你的插件需要从平台接收消息并将其转发给 OpenClaw。典型模式是使用一个 webhook,对请求进行校验,并通过你渠道的入站处理器进行分发: ```typescript registerFull(api) { api.registerHttpRoute({ path: "/acme-chat/webhook", - auth: "plugin", // 插件管理的鉴权(请自行验证签名) + auth: "plugin", // plugin-managed auth (verify signatures yourself) handler: async (req, res) => { const event = parseWebhookPayload(req); - // 你的入站处理器将消息分发到 OpenClaw。 - // 具体接线方式取决于你的平台 SDK — - // 可在内置的 Microsoft Teams 或 Google Chat 插件包中查看真实示例。 + // Your inbound handler dispatches the message to OpenClaw. + // The exact wiring depends on your platform SDK — + // see a real example in the bundled Microsoft Teams or Google Chat plugin package. await handleAcmeChatInbound(api, event); res.statusCode = 200; @@ -477,9 +471,8 @@ if (decision.shouldSkip) return; ``` - 入站消息处理是渠道特定的。每个渠道插件都负责 - 自己的入站管线。请查看内置渠道插件 - (例如 Microsoft Teams 或 Google Chat 插件包)中的真实模式。 + 入站消息处理是渠道特定的。每个渠道插件都负责自己的入站流水线。请查看内置渠道插件 + (例如 Microsoft Teams 或 Google Chat 插件包)了解真实模式。 @@ -493,7 +486,7 @@ if (decision.shouldSkip) return; import { acmeChatPlugin } from "./channel.js"; describe("acme-chat plugin", () => { - it("从配置解析账号", () => { + it("resolves account from config", () => { const cfg = { channels: { "acme-chat": { token: "test-token", allowFrom: ["user1"] }, @@ -503,7 +496,7 @@ if (decision.shouldSkip) return; expect(account.token).toBe("test-token"); }); - it("在不实体化密钥的情况下检查账号", () => { + it("inspects account without materializing secrets", () => { const cfg = { channels: { "acme-chat": { token: "test-token" } }, } as any; @@ -512,7 +505,7 @@ if (decision.shouldSkip) return; expect(result.tokenStatus).toBe("available"); }); - it("报告缺失的配置", () => { + it("reports missing config", () => { const cfg = { channels: {} } as any; const result = acmeChatPlugin.setup!.inspectAccount!(cfg, undefined); expect(result.configured).toBe(false); @@ -524,55 +517,53 @@ if (decision.shouldSkip) return; pnpm test -- /acme-chat/ ``` - 有关共享测试辅助工具,请参阅 [测试](/zh-CN/plugins/sdk-testing)。 + 关于共享测试辅助工具,请参见 [测试](/zh-CN/plugins/sdk-testing)。 ## 文件结构 -```text +``` /acme-chat/ -├── package.json # `openclaw.channel` 元数据 +├── package.json # openclaw.channel 元数据 ├── openclaw.plugin.json # 带配置 schema 的清单 -├── index.ts # `defineChannelPluginEntry` -├── setup-entry.ts # `defineSetupPluginEntry` +├── index.ts # defineChannelPluginEntry +├── setup-entry.ts # defineSetupPluginEntry ├── api.ts # 公共导出(可选) ├── runtime-api.ts # 内部运行时导出(可选) └── src/ - ├── channel.ts # 通过 `createChatChannelPlugin` 创建的 `ChannelPlugin` + ├── channel.ts # 通过 createChatChannelPlugin 创建的 ChannelPlugin ├── channel.test.ts # 测试 ├── client.ts # 平台 API 客户端 - └── runtime.ts # 运行时存储(如有需要) + └── runtime.ts # 运行时存储(如需要) ``` ## 高级主题 - - 固定、账号作用域或自定义回复模式 + + 固定、账户作用域或自定义回复模式 - `describeMessageTool` 和 action 发现 + describeMessageTool 和 action 发现 - `inferTargetChatType`、`looksLikeId`、`resolveTarget` + inferTargetChatType、looksLikeId、resolveTarget - 通过 `api.runtime` 使用 TTS、STT、媒体、子智能体 + 通过 api.runtime 使用 TTS、STT、媒体、子智能体 -一些内置辅助工具接口仍然存在,用于内置插件的维护和兼容性。 -对于新的渠道插件,它们不是推荐模式; -除非你正在直接维护该内置插件家族,否则请优先使用通用 SDK -接口中的 channel/setup/reply/runtime 子路径。 +一些内置辅助接口仍然保留,用于内置插件维护和兼容性。 +对于新的渠道插件,它们并不是推荐模式;除非你正在直接维护该内置插件家族,否则请优先使用通用 SDK 接口中的 channel/setup/reply/runtime 子路径。 ## 后续步骤 -- [提供商插件](/zh-CN/plugins/sdk-provider-plugins) — 如果你的插件也提供模型 -- [插件 SDK 概览](/zh-CN/plugins/sdk-overview) — 完整的子路径导入参考 -- [插件 SDK 测试](/zh-CN/plugins/sdk-testing) — 测试工具和契约测试 -- [插件清单](/zh-CN/plugins/manifest) — 完整的清单 schema +- [提供商插件](/zh-CN/plugins/sdk-provider-plugins) —— 如果你的插件也提供模型 +- [SDK 概览](/zh-CN/plugins/sdk-overview) —— 完整子路径导入参考 +- [插件 SDK 测试](/zh-CN/plugins/sdk-testing) —— 测试工具和契约测试 +- [插件清单](/zh-CN/plugins/manifest) —— 完整清单 schema