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

扩展能力的工程化关键不是“能不能加功能”，而是“扩展是否可控、可审计、可回滚”。OpenClaw 当前仍以插件体系作为扩展主入口：原生插件可以引入新的工具、渠道、模型供应商或运行时组件，并通过显式启用与白名单机制收敛风险；同时系统也能发现并导入兼容的 Codex、Claude、Cursor 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`、可选 `uiHints` 等元数据。系统在渲染配置表单或首轮校验时，**无需执行第三方插件代码**，直接通过清单文件进行 JSON Schema 验证。这避免了未经审查的代码自动执行的风险。同时也要注意，最新版本并不只有这一种插件格式；对兼容 bundle，OpenClaw 会识别 `.codex-plugin/plugin.json`、`.claude-plugin/plugin.json`、`.cursor-plugin/plugin.json` 等布局，并按兼容层规则导入可承接的能力。

如果插件通过 npm 包、压缩包等形式分发，当前版本还要求在 `package.json` 的 `openclaw.extensions` 中声明运行时入口列表。分工明确：`openclaw.plugin.json` 负责“这是什么插件、怎么配”的元数据声明，`package.json#openclaw.extensions` 负责“运行时代码入口在哪里”的打包指引。

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

1. 插件版本进入版本控制与发布流程。
2. 插件的 `openclaw.plugin.json` 严格约束参数，不合规清单在 Gateway 启动期直接拒载。
3. 打包分发时显式维护 `package.json` 中的 `openclaw.extensions`，避免安装后找不到入口。
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: {
            'admin_id_001': { alsoAllow: ['jira.create*'] },
          },
        },
      },
    },
  },
}
```

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

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

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

**钩子分类总览**：

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

| 钩子名称                   | 执行模式 | 典型用途         |
| ---------------------- | ---- | ------------ |
| `before_model_resolve` | 修改型  | 覆盖模型/供应商选择   |
| `before_prompt_build`  | 修改型  | 注入上下文与系统提示词  |
| `llm_input`            | 观测型  | 审计 LLM 精确输入  |
| `llm_output`           | 观测型  | 审计 LLM 精确输出  |
| `agent_end`            | 观测型  | 分析已完成会话      |
| `before_compaction`    | 修改型  | 干预消息压缩过程（前置） |
| `after_compaction`     | 修改型  | 干预消息压缩过程（后置） |

### 消息处理钩子

| 钩子名称                   | 执行模式 | 典型用途             |
| ---------------------- | ---- | ---------------- |
| `message_received`     | 观测型  | 消息到达后的旁路处理（即发即忘） |
| `message_sending`      | 修改型  | 修改或取消待发消息        |
| `message_sent`         | 观测型  | 消息发送后的确认         |
| `before_message_write` | 同步型  | JSONL 写入前的同步阻断   |

### 工具调用钩子

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

### 子智能体钩子

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

### 网关生命周期钩子

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

**四种执行模式**：

* **观测型**（Void Hooks）：**并行执行**，不能修改状态，用于审计与旁路处理（如 `llm_input`、`message_sent`）。
* **修改型**（Modifying Hooks）：**按优先级顺序执行**（高优先级先执行），每个钩子的修改结果流入下一个钩子，最终合并应用（如 `before_tool_call`、`message_sending`）。
* **顺序型**：与修改型类似按优先级链式执行，但专用于子智能体路由场景——如 `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` 合并参数与阻断状态；`message_sending` 首个取消操作获胜。

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

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

1. **发现**：按 `plugins.load.paths`、workspace、global、bundled 的顺序扫描候选插件；同一插件 ID 首个命中生效。
2. **校验**：读取 `openclaw.plugin.json` 清单，验证配置是否符合 JSON Schema。
3. **入口解析**：按插件目录或 `package.json#openclaw.extensions` 解析运行时入口，并执行边界检查。
4. **注册**：同步调用插件的 `register(api)` 函数，插件通过 API 注册工具、钩子、渠道、服务、HTTP 路由等。
5. **激活**：所有插件注册完成后，构建 `PluginRegistry`，初始化全局 Hook Runner。
6. **运行**：在每个钩子点，系统通过 Hook Runner 调度已注册的处理器。

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

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

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

## 12.1.5 插件市场与 Bundle 兼容

除了手动在 `load.paths` 中指定本地路径外，OpenClaw 还支持从\*\*插件市场（Marketplace）\*\*一键安装，也支持直接安装本地路径、压缩包与 npm 规格。对原生插件来说，最核心的识别文件仍是 `openclaw.plugin.json`；对兼容 bundle 来说，OpenClaw 会额外识别 `.codex-plugin/plugin.json`、`.claude-plugin/plugin.json`、`.cursor-plugin/plugin.json` 等入口。

当前官方 CLI 支持多种市场来源：Claude 已知 marketplace 名称、本地 marketplace 根目录或 `marketplace.json`、GitHub 仓库简写，以及 git URL。写书时不应把某一种来源写成唯一官方形态。

安装方式：

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

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

# 检查已安装插件的更新
openclaw plugins update
```

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

> \[!NOTE] Bundle 兼容性是选择性的：技能文件（如 `SKILL.md`）通常最容易映射；但涉及特定框架 API、运行时命令模型或平台专属 hooks 的部分，可能只会被部分加载，甚至被安全策略直接跳过。建议安装后通过 `openclaw plugins doctor` 与 `openclaw plugins list` 验证映射结果，确认哪些能力已成功加载、哪些被跳过。

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

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

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

```bash
openclaw plugins list
openclaw plugins inspect my-plugin
openclaw plugins doctor
openclaw status --deep
```

当插件加载失败时，优先看 `plugins doctor`、`plugins inspect` 与 `status --deep` 输出；当插件加载成功但行为异常时，再结合结构化日志按 `traceId` 回放单次链路。

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET 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?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
