> For the complete documentation index, see [llms.txt](https://yeasy.gitbook.io/openclaw_guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yeasy.gitbook.io/openclaw_guide/di-san-bu-fen-shi-xian-yuan-li-yu-gong-cheng-luo-di/12_extension_engineering/12.1_plugin_architecture.md).

# 12.1 插件开发体系：自定义扩展的工程机制

扩展能力的工程化关键不是“能不能加功能”，而是“扩展是否可控、可审计、可回滚”。OpenClaw 当前仍以插件体系作为扩展主入口：原生插件可以引入新的工具、渠道、模型供应商或运行时组件，并通过显式启用与白名单机制收敛风险；同时系统也能发现并导入兼容的 Codex、Claude、Cursor bundle，其中 Claude 支持默认目录布局的 manifestless bundle。需要特别区分的是：用户侧配置与 CLI 仍统一使用 `plugins.*` / `openclaw plugins ...`，而源码与发行产物中常会看到 `extensions/` 目录这一实现形态。本节基于最新官方文档与源码说明原生插件的核心结构、兼容 bundle 的边界，以及一组可用于验收的命令。

## 12.1.1 插件是什么：长驻进程内的 TypeScript/JavaScript 模块

与仅提供文本提示思路的技能（Skills）截然不同，**原生插件本质是运行在 Gateway 进程内的 TypeScript/JavaScript 扩展模块**。它们通过运行时加载器动态装入，可直接挂载自定义 Agent tools、注册聊天渠道、实现后台轮询服务，甚至接管特定 Hook 或 HTTP/RPC 能力。

官方文档对原生插件的定位、能力范围与加载方式给出了完整说明。其中最核心的安全与工程设计在于引入了 **`openclaw.plugin.json`** 清单文件。它声明插件 `id`、`configSchema`、`contracts.tools`、可选 `uiHints` 等元数据；运行时 `api.registerTool()` 注册的工具应与 manifest 中声明的工具所有权保持一致。系统在渲染配置表单或首轮校验时，**无需执行第三方插件代码**，直接通过清单文件进行 JSON Schema 验证。这避免了未经审查的代码自动执行的风险。同时也要注意，最新版本并不只有这一种插件格式；对兼容 bundle，OpenClaw 会识别 `.codex-plugin/plugin.json`、`.claude-plugin/plugin.json`、`.cursor-plugin/plugin.json` 以及 Claude manifestless 默认布局，并按兼容层规则导入可承接的能力。

如果插件通过 npm 包、压缩包等形式分发，发布包应在 `package.json` 的 `openclaw.extensions` 中声明插件入口列表；发布包的运行时文件路径可用 `openclaw.runtimeExtensions` 指向构建后的 JavaScript，并会在显式声明后受到校验。外部 ClawHub code plugin 还应维护 `openclaw.compat.pluginApi` 与 `openclaw.build.openclawVersion` 等兼容/构建元数据；`pluginSdkVersion`、`minHostVersion` 则更多是兼容提示，不能替代运行时校验。分工明确：`openclaw.plugin.json` 负责“这是什么插件、怎么配”的元数据声明，`package.json#openclaw.extensions` / `package.json#openclaw.runtimeExtensions` 负责“源码入口与发布运行时代码入口在哪里”的打包指引。

工程上建议把插件当作严肃的代码级系统依赖对待：

1. 插件版本进入版本控制与发布流程。
2. 插件的 `openclaw.plugin.json` 严格约束参数，不合规清单在 Gateway 启动期直接拒载。
3. 打包分发时维护 `package.json` 中的 `openclaw.extensions`，并在发布产物路径不同于源码入口时维护 `openclaw.runtimeExtensions`，避免安装后找不到入口。
4. 插件的工具与副作用被工具策略与白名单彻底约束绑定。

## 12.1.2 配置与启用：entries、enabled、config 与 load.paths

插件的启用通过 `plugins.entries` 声明。这里应优先把 **manifest 中的插件 ID** 视为权威键，而不是想当然地把 npm 包名当作最终配置键。即便源码目录常命名为 `extensions/<id>/`，对用户稳定暴露的治理接口仍是 `plugins.entries.<id>`。每个条目包含启用开关与插件自定义配置；CLI 层的 `openclaw plugins enable <id>` / `disable <id>` 本质上也是切换 `plugins.entries.<id>.enabled`。官方文档还提供了 `allow`、`deny`、`load.paths` 等字段的完整写法：<https://docs.openclaw.ai/tools/plugin>。需要补充的是：workspace 来源插件默认关闭，而 bundled plugin 遵循内建 default-on 集合，不应把“所有插件默认禁用”写成绝对规则。

下面示例展示了完整骨架：白名单准入 + 本地路径加载 + 启用插件并传入配置。

```jsonc
{
  plugins: {
    enabled: true,
    allow: ['my-plugin'],           // 白名单：仅允许列出的插件加载
    deny: ['untrusted-plugin'],     // 黑名单：deny 优先于 allow
    load: {
      paths: ['~/Projects/my-plugin'], // 本地开发中的插件目录
    },
    entries: {
      'my-plugin': {
        enabled: true,
        config: {
          // 插件自定义配置
        },
      },
    },
  },
}
```

为了降低供应链风险，建议配合白名单机制只允许经过审计的插件生效。

## 12.1.3 安全边界：插件白名单与工具策略协同

插件引入的是能力，能力的使用边界仍应由确定性的策略系统兜底。官方插件文档提供了 `plugins.allow` 与 `plugins.deny` 的白名单机制；官方工具文档提供了工具的允许、拒绝与分层策略机制，并强调拒绝规则优先：

* 插件白名单：<https://docs.openclaw.ai/tools/plugin>
* 工具治理：<https://docs.openclaw.ai/tools>

实践中建议采用“双层收敛”：

1. 先用 `plugins.allow` 收敛可加载插件集合。
2. 再用工具策略把插件提供的工具按风险分组，并在默认档案中拒绝高风险组。

进而可以将“扩展可用性”与“扩展可执行性”拆开管理，避免某个插件被安装后立即具备不受控副作用。

**具体例子：Jira 插件的灰度上线流程**

假设团队开发了一个 Jira 集成插件 `jira-sync`（npm 包名 `@yourteam/jira-sync`），能让智能体查询和创建工单。安全的上线路径如下：

1. **阶段一：白名单准入** ——只在 `plugins.allow` 中添加该插件，但在 `tools.deny` 中拒绝其写入工具，先只允许查询功能：

```jsonc
{
  plugins: {
    allow: ['jira-sync'],
    entries: { 'jira-sync': { enabled: true } },
  },
  tools: {
    deny: ['jira.create*', 'jira.update*', 'jira.delete*'],
  },
}
```

2. **阶段二：小范围放开写入** ——在内部 Telegram 群中，通过 `toolsBySender` 只允许管理员触发创建工单：

```jsonc
{
  channels: {
    telegram: {
      groups: {
        '*': {
          toolsBySender: {
            'id:admin_id_001': { alsoAllow: ['jira.create*'] },
          },
        },
      },
    },
  },
}
```

3. **阶段三：全量放开** ——在内部验证 2 周无异常后，移除 `tools.deny` 中的 Jira 写入限制，但保留审计日志与幂等键。

## 12.1.4 Hook 架构：插件的核心运行机制

插件的真正力量不在于“注册工具”，而在于 **Hook（钩子）系统**——插件通过在执行管线的关键节点注册钩子函数来拦截、修改或观测系统行为。书中这一节讨论的是**原生 OpenClaw 插件的 Hook 运行时**；兼容 bundle 中的 hooks 只会映射到 OpenClaw 可以安全承接的那一部分，不应把 bundle 文档里的任意钩子语义直接等同为 OpenClaw 原生运行时。

**常用钩子摘要**（不是完整列表；以当前 `docs/plugins/hooks` 和类型定义为准）：

### 智能体生命周期钩子

| 钩子名称                                      | 执行模式    | 典型用途                              |
| ----------------------------------------- | ------- | --------------------------------- |
| `before_model_resolve`                    | 修改型     | 覆盖模型/供应商选择                        |
| `agent_turn_prepare`                      | 修改型     | 在一次 agent turn 前准备上下文             |
| `before_prompt_build`                     | 修改型     | 注入上下文与系统提示词                       |
| `before_agent_reply`                      | claim 型 | 可返回 `{ handled: true }` 合成或静默本轮回复 |
| `before_agent_finalize`                   | 修改/检查型  | 最终回答检查、补充处理或终止前清理                 |
| `model_call_started` / `model_call_ended` | 观测型     | 观测模型调用生命周期                        |
| `llm_input`                               | 观测型     | 审计 LLM 精确输入                       |
| `llm_output`                              | 观测型     | 审计 LLM 精确输出                       |
| `agent_end`                               | 观测型     | 分析已完成会话                           |
| `before_compaction`                       | 观测型     | 观测或标注消息压缩过程（前置）                   |
| `after_compaction`                        | 观测型     | 观测或标注消息压缩过程（后置）                   |

### 消息处理钩子

| 钩子名称                   | 执行模式                 | 典型用途                                                          |
| ---------------------- | -------------------- | ------------------------------------------------------------- |
| `message_received`     | 观测型                  | 消息到达后的旁路处理（即发即忘）                                              |
| `inbound_claim`        | claim 型              | 让插件按 first-claim-wins 规则接管入站消息                                |
| `message_sending`      | 修改型                  | 修改或取消待发消息                                                     |
| `reply_dispatch`       | claim / dispatch 决策型 | 决定是否接管或改写回复投递目标；需要重写 outbound dispatch 时优先看 `before_dispatch` |
| `message_sent`         | 观测型                  | 消息发送后的确认                                                      |
| `before_message_write` | 同步型                  | JSONL 写入前的同步阻断                                                |

### 工具调用钩子

| 钩子名称                  | 执行模式 | 典型用途      |
| --------------------- | ---- | --------- |
| `before_tool_call`    | 修改型  | 拦截或修改工具参数 |
| `after_tool_call`     | 观测型  | 观测工具执行结果  |
| `tool_result_persist` | 同步型  | 链式修改持久化消息 |

### 子智能体钩子

| 钩子名称                       | 执行模式 | 典型用途   |
| -------------------------- | ---- | ------ |
| `subagent_spawning`        | 顺序型  | 配置线程绑定 |
| `subagent_delivery_target` | 顺序型  | 解析路由目标 |

### 网关生命周期钩子

| 钩子名称            | 执行模式 | 典型用途    |
| --------------- | ---- | ------- |
| `gateway_start` | 观测型  | 网关启动时触发 |
| `gateway_stop`  | 观测型  | 网关停止时触发 |

非 bundled 插件若需要读取完整 conversation 内容，通常还需要显式配置 `plugins.entries.<id>.hooks.allowConversationAccess=true`。涉及终端、审批或安装前控制的 hooks（如 `before_install`）也有各自门控，不能只看函数名判断权限。

**五种执行模式**：

* **观测型**（Void Hooks）：**并行执行**，不能修改状态，用于审计与旁路处理（如 `llm_input`、`message_sent`）。
* **修改型**（Modifying Hooks）：**按优先级顺序执行**（高优先级先执行），但并非所有修改结果都会作为下一钩子的输入；`before_tool_call` / `message_sending` 等会把多个 handler 的返回合并应用，只有 `tool_result_persist`、`before_message_write` 等特定同步 hooks 才是真正链式传递。
* **claim 型**：按优先级执行，首个返回 `handled: true` 的 handler 获胜并短路后续 handler。`inbound_claim` 和 `before_agent_reply` 都属于这类语义；不要用它们实现“多个插件依次改写最终答案”的需求。
* **顺序型**：与修改型类似按优先级链式执行，但专用于子智能体路由场景——如 `subagent_spawning`（配置线程绑定）和 `subagent_delivery_target`（解析路由目标），每个钩子可以补充或覆盖路由决策。
* **同步型**：在热路径上同步执行，系统会检测并拒绝意外的异步处理器。包括 `tool_result_persist`（链式修改持久化消息）和 `before_message_write`（JSONL 写入前的同步阻断）。

**全局 Hook Runner**：Gateway 启动时创建单例 Hook Runner，所有执行路径通过它统一调度。多数观测型钩子失败后会记录日志并继续运行；但 `before_tool_call` 这类守卫钩子可以 fail-closed，在必要时阻断被保护的操作。

**插件注册钩子的方式**：

```typescript
// 推荐：类型化 API
api.on("before_tool_call", (event, ctx) => {
  if (event.toolName === "dangerous_tool") {
    return { block: true, blockReason: "该工具在此会话中被禁止" };
  }
  return { params: { ...event.params, audited: true } };
}, { priority: 10 });

// 遗留：无类型 API
api.registerHook("hook_name", handler, { entry, register: true });
```

**结果合并策略**因钩子类型而异：`before_model_resolve` 采用首个非空结果获胜；`before_prompt_build` 拼接上下文与系统提示词；`before_tool_call` 合并参数与阻断状态但不会把上一个 handler 的修改作为下一个 handler 的输入；`message_sending` 首个取消操作获胜。

**渠道插件**是一种特殊插件类型，通过 `api.registerChannel()` 注册，可提供 15+ 种适配器接口（配置、网关、出站、消息处理等）。渠道插件深度集成子智能体路由——通过 `subagent_spawning` 钩子配置每会话的线程绑定，通过 `subagent_delivery_target` 钩子解析消息路由目标。

**插件加载的完整生命周期**：

1. **发现**：从 `plugins.load.paths`、workspace plugins、global plugins、bundled plugins 等来源收集候选插件；同 ID 的 workspace 插件在显式启用或 allowlist 后可有意 shadow bundled copy，duplicate 诊断用于提示来源冲突。
2. **校验**：读取 `openclaw.plugin.json` 清单，验证配置是否符合 JSON Schema。
3. **入口解析**：按插件目录或 `package.json#openclaw.extensions` / `package.json#openclaw.runtimeExtensions` 解析入口，并执行边界检查。
4. **注册**：同步调用插件的 `register(api)` 函数，插件通过 API 注册工具、钩子、渠道、服务、HTTP 路由等。
5. **激活**：所有插件注册完成后，构建 `PluginRegistry`，初始化全局 Hook Runner。
6. **运行**：在每个钩子点，系统通过 Hook Runner 调度已注册的处理器。

对应的生命周期可以简化为下面这张图：

```mermaid
flowchart LR
  D["发现候选插件"] --> V["读取 Manifest<br/>Schema 校验"]
  V --> L["原生 loader / require<br/>Jiti 仅作 TS fallback"]
  L --> R["register(api) 注册工具/钩子/渠道"]
  R --> P["构建 PluginRegistry"]
  P --> H["初始化 Hook Runner"]
  H --> X["运行期调度"]
```

图 12-1：插件从发现到运行期调度的生命周期

## 12.1.5 插件市场与 Bundle 兼容

除了手动在 `load.paths` 中指定本地路径外，OpenClaw 还支持从 ClawHub / marketplace 来源一键安装，也支持直接安装本地路径、压缩包与 npm 规格。ClawHub 是 OpenClaw 发行与发现的主路径；`--marketplace` 更适合 Claude-compatible marketplace 或 bundle 来源。对原生插件来说，最核心的识别文件仍是 `openclaw.plugin.json`；对兼容 bundle 来说，OpenClaw 会额外识别 `.codex-plugin/plugin.json`、`.claude-plugin/plugin.json`、`.cursor-plugin/plugin.json` 与 Claude manifestless 默认布局等入口。

当前官方 CLI 支持多种市场来源：Claude 已知 marketplace 名称、本地 marketplace 根目录或 `marketplace.json`、GitHub 仓库简写，以及 git URL。远程 marketplace 中的插件条目必须留在克隆出来的 marketplace 仓库内，并使用相对路径来源；不应把 HTTP(S)、绝对路径或二次 git 源写进远程 manifest。`--pin` 只适用于 npm 安装，不适用于 `--marketplace`。依赖安装发生在显式 install / update / doctor repair 流程中，而不是普通运行时隐式执行。写书时不应把某一种来源写成唯一官方形态。

安装方式：

```bash
# 从市场安装指定插件
openclaw plugins install <插件名>@<市场名>

# 列出市场中所有可用插件
openclaw plugins marketplace list <市场名>

# 更新已追踪插件
openclaw plugins update <id-or-npm-spec>
openclaw plugins update --all
```

此外，OpenClaw 可以发现并安装兼容的 **Codex、Claude 和 Cursor bundle**。系统会尝试把 bundle 中的技能、命令、settings 以及部分 hooks/agents 信息映射到 OpenClaw 可承接的能力面，但这种映射是“尽力兼容”而不是“逐字段等价”。根据当前官方插件 CLI 文档，已经明确说明会在 `inspect` / `doctor` 输出里展示被识别出的 bundle 能力，其中并非所有能力都会自动接入运行时执行。

> \[!NOTE] Bundle 兼容性是选择性的：技能文件（如 `SKILL.md`）通常最容易映射；但涉及特定框架 API、运行时命令模型或平台专属 hooks 的部分，可能只会被部分加载，甚至被安全策略直接跳过。建议安装后通过 `openclaw plugins inspect <id>` 查看识别出的 bundle capabilities，再用 `openclaw plugins doctor` 排查加载错误、manifest/discovery 诊断与兼容性提示。

## 12.1.6 验收与排障：用插件诊断与状态深查做成完整流程

插件相关问题不应靠“对话试出来”。官方 CLI 提供了插件列表与诊断命令，可用于上线前验收与线上排障：<https://docs.openclaw.ai/cli/plugins>。

建议把以下命令固定为插件变更后的最小验收集：

```bash
openclaw plugins list
openclaw plugins inspect my-plugin
openclaw plugins inspect my-plugin --runtime --json
openclaw plugins doctor
openclaw gateway status --deep --require-rpc
```

当插件加载失败时，优先看 `plugins doctor`、`plugins inspect` 与 `gateway status --deep --require-rpc` 输出；当插件加载成功但行为异常时，再结合 `inspect --runtime --json` 和结构化日志按 `traceId` 回放单次链路。`openclaw plugins enable <id>` / `openclaw plugins disable <id>` 只更新配置并刷新 cold registry；要让已运行 Gateway 的 live runtime surfaces 生效，应显式重启或重载 Gateway，并用 `openclaw plugins inspect <id> --runtime --json` 与 `openclaw gateway status --deep --require-rpc` 复验。

```bash
openclaw logs --follow --json
```
