跳转至

CLI 后端插件

CLI 后端插件允许 OpenClaw 将本地 AI CLI 作为文本推理后端进行调用。该后端会以 provider 前缀的形式出现在模型引用中:

acme-cli/acme-large

当上游集成已经以本地命令形式暴露、CLI 拥有本地登录状态,或在 API 提供方不可用时作为回退方案时,使用 CLI 后端。

Info

如果上游服务暴露了普通的 HTTP 模型 API,请改为编写 provider 插件。如果上游运行时拥有完整的 agent 会话、工具事件、压缩或后台任务状态,请使用 agent harness。

插件负责的内容

CLI 后端插件有三个契约:

契约 文件 用途
包入口 package.json 将 OpenClaw 指向插件运行时模块
清单所有权 openclaw.plugin.json 在运行时加载前声明后端 id
运行时注册 index.ts 使用命令默认值调用 api.registerCliBackend(...)

清单是发现元数据:它不会执行 CLI,也不会注册运行时行为。运行时行为从插件入口调用 api.registerCliBackend(...) 时开始。

最小后端插件

1. 创建包元数据

```json package.json { "name": "@acme/openclaw-acme-cli", "version": "1.0.0", "type": "module", "openclaw": { "extensions": ["./index.ts"], "compat": { "pluginApi": ">=2026.3.24-beta.2", "minGatewayVersion": "2026.3.24-beta.2" }, "build": { "openclawVersion": "2026.3.24-beta.2", "pluginSdkVersion": "2026.3.24-beta.2" } }, "dependencies": { "openclaw": "^2026.3.24" }, "devDependencies": { "typescript": "^5.9.0" } }

发布的包必须附带已构建的 JavaScript 运行时文件。如果你的源入口是 `./src/index.ts`,请添加 `openclaw.runtimeExtensions` 指向已构建的 JavaScript 对应文件。参见[入口点](sdk-entrypoints.md)。

**2. 声明后端所有权**

```json openclaw.plugin.json
{
  "id": "acme-cli",
  "name": "Acme CLI",
  "description": "Run Acme's local AI CLI through OpenClaw",
  "cliBackends": ["acme-cli"],
  "setup": {
    "cliBackends": ["acme-cli"],
    "requiresRuntime": false
  },
  "activation": {
    "onStartup": false
  },
  "configSchema": {
    "type": "object",
    "additionalProperties": false
  }
}

cliBackends 是运行时所有权列表;当模型选择或 agentRuntime.id 提到 acme-cli 时,它可让 OpenClaw 自动加载插件。

setup.cliBackends 是描述符优先的设置界面。当模型发现、引导或状态需要在不加载插件运行时的情况下识别后端时,请添加它。仅当这些静态描述符足以完成设置时,使用 requiresRuntime: false。

3. 注册后端

```typescript index.ts import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";

function buildAcmeCliBackend(): Parameters[0] { return { id: "acme-cli", liveTest: { defaultModelRef: "acme-cli/acme-large", defaultImageProbe: false, defaultMcpProbe: false, docker: { npmPackage: "@acme/acme-cli", binaryName: "acme", }, }, config: { command: "acme", args: ["chat", "--output-format", "stream-json", "--prompt", "{prompt}"], resumeArgs: [ "chat", "--resume", "{sessionId}", "--output-format", "stream-json", "--prompt", "{prompt}", ], output: "jsonl", resumeOutput: "jsonl", jsonlDialect: "gemini-stream-json", input: "arg", modelArg: "--model", modelAliases: { large: "acme-large-2026", fast: "acme-fast-2026", }, sessionArgs: ["--session", "{sessionId}"], sessionMode: "existing", sessionIdFields: ["session_id", "conversation_id"], systemPromptFileArg: "--system-file", systemPromptWhen: "first", imageArg: "--image", imageMode: "repeat", imagePathScope: "workspace", serialize: true, }, }; }

export default definePluginEntry({ id: "acme-cli", name: "Acme CLI", description: "Run Acme's local AI CLI through OpenClaw", register(api) { api.registerCliBackend(buildAcmeCliBackend()); }, });

后端 id 必须与清单中的 `cliBackends` 条目匹配。已注册的适配器是权威的插件代码;OpenClaw 配置选择后端,但不会重写其命令契约。

## 配置结构 {#config-shape}

`CliBackendConfig` 描述 OpenClaw 应如何启动和解析 CLI。上面的示例有意使用与内置 `google-gemini-cli` 适配器相同的 command、resume、JSONL、model-alias、session 和 image 字段:

| 字段                                                     | 用途                                                                               |
| --------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `command`                                                 | 二进制名称或绝对命令路径                                              |
| `args`                                                    | 新运行的基础 argv                                                          |
| `resumeArgs`                                              | 恢复会话的替代 argv;支持 `{sessionId}`                       |
| `output` / `resumeOutput`                                 | 解析器:`json`、`jsonl` 或 `text`                                                |
| `jsonlDialect`                                            | JSONL 事件方言:`claude-stream-json` 或 `gemini-stream-json`                 |


| 字段 | 用途 |
| --- | --- |
| `liveSession` | 长生命周期 CLI 进程模式(`claude-stdio`) |
| `input` | 提示词传输:`arg` 或 `stdin` |
| `maxPromptArgChars` | `arg` 模式下回退到 stdin 之前的最大提示词长度 |
| `env` / `clearEnv` | 要注入的额外环境变量,或启动前要移除的名称 |
| `modelArg` | 用于模型 id 之前的标志 |
| `modelAliases` | 将 OpenClaw 模型 id 映射到 CLI 原生 id |
| `sessionArgs` | 如何使用 `{sessionId}` 传递会话 id |
| `sessionMode` | `always`、`existing` 或 `none` |
| `sessionIdFields` | OpenClaw 从 CLI 输出中读取的 JSON 字段 |
| `systemPromptArg` / `systemPromptFileArg` | 系统提示词传输 |
| `systemPromptFileConfigArg` / `systemPromptFileConfigKey` | 系统提示词文件的配置覆盖传输方式(例如 `-c`) |
| `systemPromptMode` | `append` 或 `replace` |
| `systemPromptWhen` | `first`、`always` 或 `never` |
| `imageArg` / `imageMode` | 图像路径标志以及传递多个图像的方式(`repeat` 或 `list`) |
| `imagePathScope` | 交接前暂存图像文件的位置:`temp` 或 `workspace` |
| `serialize` | 保持同一后端运行有序 |
| `reseedFromRawTranscriptWhenUncompacted` | 选择启用压缩前的有界原始转录重播种,以实现安全的会话重置 |
| `freshSessionRecovery` | 可恢复的已恢复会话失败后的全新恢复策略 |
| `reliability.watchdog` | 无输出超时调优,针对全新运行与恢复运行分别设置 |

`claude-stream-json` 不仅仅是一种解析器选择:它声明后端的 `result` 记录承载 Claude Code 的终止语义,包括 `terminal_reason`。一个无回复的 `result`,如果其 `terminal_reason` 为 `hook_stopped`、`stop_hook_prevented`、`aborted_tools`、`aborted_streaming`、`budget_exhausted` 或 `max_turns`,则表示一次已记录的回合停止:OpenClaw 会向用户报告该原因,并且不会在回退模型上重放该回合,因为后端的工具操作可能已经执行。

省略 `reliability.watchdog` 以继承标准配置,包括用于 cron 和显式超时的更长恢复运行预算。仅当后端有意需要自己的看门狗策略时,才设置它。

恢复重试始终保持在操作员配置的 `timeoutMs` 之内:经过时间按单调方式测量,因此 NTP 校正或手动时钟更改既不能缩短仍有预算的重试,也不能让挂起的 CLI 存活超过其超时时间。

`freshSessionRecovery` 是由后端拥有的兼容性契约:

- 将其保留为未定义,或将其设置为 `"replace-binding"`,以保留传统的清除并重新播种行为。当失败符合恢复条件时,OpenClaw 会清除已持久化的绑定,并使用全新会话重试。
- 将其设置为 `"invalidated-only"`,以抑制全新替换,除非规范的失效谓词证明旧会话已失效。只有 `session_expired` 会这样做。

请根据 CLI 或 SDK 会话契约选择值,而不是根据提供商 id 或宽泛的错误类别选择。内置的 Anthropic 后端使用 `"invalidated-only"`;其原生会话契约不会将非过期失败视为对话无法再恢复的证明。

优先选择与 CLI 匹配的最小静态配置。仅为真正属于后端的行为添加插件回调。

## 高级后端钩子 {#advanced-backend-hooks}

`CliBackendPlugin` 还可以定义:

| 钩子 | 用途 |
| --- | --- |
| `normalizeConfig(config, context)` | 使用运行时上下文规范化已注册的静态适配器 |
| `resolveExecutionArgs(ctx)` | 添加请求范围标志,例如思考力度或侧边问题隔离 |
| `prepareExecution(ctx)` | 在启动前创建临时身份验证、配置或环境桥接 |
| `transformSystemPrompt(ctx)` | 应用最终的 CLI 特定系统提示词转换 |
| `textTransforms` | 双向提示词/输出替换 |
| `defaultAuthProfileId` | 优先使用特定的 OpenClaw 身份验证配置 |
| `authEpochMode` | 决定身份验证更改如何使已存储的 CLI 会话失效 |


| 钩子                               | 用途                                                                         |
| ---------------------------------- | --------------------------------------------------------------------------- |
| `nativeToolMode`                   | 声明原生工具是缺失、始终开启,还是可由宿主选择                                |
| `toolAvailabilityEnforcement`      | 声明是否在 argv 或执行准备阶段强制精确的工具上限                             |
| `projectNativeToolAuthority`       | 将观察到的原生工具列表映射为用于 cron 上限的规范能力                         |
| `sideQuestionToolMode`             | 声明 `/btw` 侧边问题中禁用的原生工具                                         |
| `bundleMcp` / `bundleMcpMode`      | 选择加入 OpenClaw 的环回 MCP 工具桥接                                        |
| `ownsNativeCompaction`             | 后端拥有其自身的自动压缩 - OpenClaw 会延迟处理                               |
| `manualCompaction`                 | 原子命令、传输和确认回执契约                                                 |
| `subscriptionAuthDispatch`         | 选择加入的、基于订阅凭据的嵌入式运行通过此后端执行                           |
| `runtimeArtifact`                  | 将脚本启动器绑定到其完整的捆绑包树                                           |

保持这些钩子由提供方拥有。当后端钩子可以表达该行为时,不要向核心添加 CLI 特定分支。

`prepareExecution(ctx)` 接收 `ctx.contextTokenBudget`,即为本次运行选定的有效 Token 限制。拥有原生压缩的后端可以将该预算映射到其 CLI 特定的启动契约中。它还接收可选的有效 `ctx.thinkingLevel`:`off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`adaptive` 或 `max`。当所选级别必须通过启动环境或分阶段配置应用时,使用该字段;同一字段也可供 `resolveExecutionArgs(ctx)` 用于原生 CLI 标志。

`resolveExecutionArgs(ctx)` 还接收可选的 `ctx.fastMode`,即本次调用的有效布尔值。核心在 CLI 和进程范围准入以及后端准备之后解析自动模式,因此已耗时的等待会计入其截止时间。显式开启和关闭保持不变。该字段遵循会话、代理和模型的快速模式设置;后端可以将其映射到其原生参数或忽略它。派生的进程会保留该调用的决定;它不会收到原始 `"auto"`。

`prepareExecution(ctx)` 还可以返回可选的 `execute` 传输,当后端拥有已安装 CLI 的协议或 SDK 集成时。该传输接收精确准备好的命令、参数、可选 `argv0`、环境、Prompt、会话和工具可用性;它产生后端现有的结构化流记录。在构造 CLI 调用时,保留准备好的命令、`argv0` 以及 `args` 中的解释器或脚本前缀。`argv0` 保留 PATH 垫片的调用名称。可选的 `promptContext.prependContext` 和 `promptContext.appendContext` 是私有 Prompt 构建附加内容和有界保存的会话笔记,与普通 `prompt` 分开。保存的笔记是被引用的参考数据,可能在恢复的回合中重复出现;它们并不断言某个原生回合之前已消费过它们。通过原生运行时的私有上下文机制传输它们;切勿将其记录为操作员编写的输入。OpenClaw 的策略和观察钩子仍会接收完整的逻辑 Prompt。原生工具操作必须使用提供的、与运行绑定的 `requestToolPermission` 回调,而不是创建独立的审批权限。OpenClaw 保留取消、看门狗、会话策略和 MCP 授权所有权。配对节点执行和手动压缩继续通过现有的宿主管理进程路径进行。

可复用传输接收与运行绑定的 `liveSession` 能力。在创建初始或替换进程之前,除非当前进程可复用,否则请等待 `liveSession.restart()`。这会合并前一个进程退出及其宿主拥有的资源清理;仅等待子进程退出是不够的。当调用方被撤销或必须保留确切的实时代际时,宿主会拒绝重启。`register()` 仍然是同步准入检查,并在清理未解决时拒绝替换。

`runtimeArtifact` 由插件拥有。仅当实时推理回合铸造或重新验证已验证的设置权限时才会参考它;普通 CLI 运行不需要它。没有此声明的后端无法铸造已验证的 CLI 设置权限。`bundled-package-tree` 声明会指定确切的 `package.json` 所有者,并要求包入口点即为该命令。OpenClaw 会对有界的完整已安装包树(包括嵌套依赖)进行哈希,并在重定向符号链接、声明包之外的启动器、必需的外部依赖声明、超大树和未知脚本的情况下失败关闭。仅当该树包含完整的推理实现时才声明此项;可选工具集成不会使外部实现图安全。

在 Windows 上,受支持的 JavaScript 入口点通过从 `PATH` 中选择的已验证 Node 可执行文件运行。显式脚本路径不需要其后缀出现在 `PATHEXT` 中;裸命令查找仍遵循 `PATH` 和 `PATHEXT`。

如果同一后端还附带一个自包含的原生可执行文件,请在 `nativeExecutableNames` 中列出其规范基本名称。其他原生命令仍保持未验证状态。

`ctx.executionMode` 在普通回合中为 `"agent"`,在临时 `/btw` 调用中为 `"side-question"`。当 CLI 需要不同的一次性标志时,请使用它,例如为 BTW 禁用原生工具、会话持久化或恢复行为。如果后端通常具有 `nativeToolMode: "always-on"`,但其侧边问题 argv 可靠地禁用了这些工具,也请设置 `sideQuestionToolMode: "disabled"`;否则,当 BTW 需要无工具的 CLI 运行时,OpenClaw 会失败关闭。

仅当后端能够为单个运行禁用所有后端原生工具时,才设置 `nativeToolMode: "selectable"`。受限运行接收规范契约:`ctx.toolAvailability.native` 是精确的后端原生列表,`ctx.toolAvailability.openClaw` 是精确的 OpenClaw 工具名称列表。宿主会独立地将生成的 MCP 配置和授权限制到该 OpenClaw 列表;插件不得在核心中转换它,也不得添加传输前缀。



Declare how the backend enforces that contract:

- `toolAvailabilityEnforcement: "execution-args"` requires
  `resolveExecutionArgs`. The hook must replace conflicting tool flags, disable
  customization surfaces that can execute outside the selected tools, and
  return enforcing argv for both fresh and resumed runs.
- `toolAvailabilityEnforcement: "prepare-execution"` requires
  `prepareExecution`. The hook must stage an exact per-run policy and return
  `toolAvailabilityEnforced: true`; missing acknowledgement fails closed and
  OpenClaw cleans up the staged resources before launch.

Runtime caps such as cron `toolsAllow` are normalized and group-expanded by
OpenClaw before this contract is built. Native tools are disabled, and a
backend without a complete declared enforcement path fails before execution.

Rooted runs such as [Skill Workshop reviews](../tools/skill-workshop.md) also require
`isolatesInstructionsWithExactTools: true` on the backend registration. Declare
this optional capability only when exact-tool execution suppresses ambient
instruction files, skills, hooks, and plugins for both fresh and resumed runs.
The host-prepared instruction snapshot must remain authoritative. OpenClaw
rejects rooted runs when this declaration is absent, even if the backend can
enforce exact tools. Existing non-rooted runs do not require this field.

The bundled Claude CLI backend declares this capability. Rooted execution
disables its native tools and serves the selected OpenClaw tools through the
host-owned MCP grant, which retains the root, filesystem policy, and configured
sandbox. The declaration does not grant filesystem or approval authority to
the backend.

A backend whose native tools are model-callable may declare
`projectNativeToolAuthority(nativeTools)` so that automations created from its
sessions keep the creator's native capabilities. For Claude stream-JSON, the
input is the parent turn's `system/init.tools` list, intersected with
`toolAvailability.native` when a host selection exists. Managed native settings
can remove tools after CLI argument selection, so defaults are never inferred.
Each turn starts with pending authority: MCP discovery remains available, but
tool calls reject visibly until initialization supplies the list. Warm turns
cannot borrow a previous turn's snapshot. Return only canonical names from the
core vocabulary (`read`, `write`, `edit`, `apply_patch`, `exec`, `process`,
`web_search`, `web_fetch`), each derived from a native tool the host enforces
through this contract. Core validates the result before updating the active
loopback grant and again at final creator-cap capture; any other name fails the
turn. Updating the snapshot invalidates earlier cached tool projections.
Project only equivalent capabilities: Claude's `Glob` locates paths and
`NotebookEdit` edits notebook cells, so neither grants general `read` or `edit`.
The native list contains tool names, not permission-rule patterns.
Codex native code mode projects `read` and `exec` after OpenClaw explicitly
requests the shell and rejects managed requirements or legacy managed settings
that disable it. The effective setting and its source are checked at each
preflight; a user-local shell disable is overridden for native mode, while a
managed denial rejects before capture. It never infers `write`, `edit`,
`apply_patch`, or `process`. The pinned Codex registry has
no shell-disabled models; a custom model that disables its shell remains an
unobservable exception because Codex does not expose that model capability.

Previously saved empty automation caps remain restricted. Recreate the job or
explicitly edit its tools from a fresh authorized creator turn; an old empty cap
cannot safely be distinguished from an intentional denial.

### `parseJsonlEvent`: provider-specific JSONL streams

Set `parseJsonlEvent` when a backend emits line-delimited JSON that does not
match the built-in Claude, Codex, or Gemini dialects. The hook receives one raw
line plus the resolved backend id and config, and returns one normalized event,
multiple events, or `null` to let the built-in parser try the line.

Supported events are incremental assistant text, incremental thinking, native
tool start/result display, session ids, and terminal results. Terminal results
may include final text, usage, an error, and a successor session id. Session ids
reported by either event shape participate in resumed-session and fork
persistence.

Lifecycle events are intentionally separate from this return union so existing
plugins can continue to match it exhaustively. Use `parseJsonlLifecycleEvent`
for backend-owned lifecycle records instead.

Tool events describe work the backend already performed. OpenClaw renders and
summarizes them, but does not treat them as host tool execution, trusted
diagnostics, loopback correlation, or message-delivery evidence.

### `parseJsonlLifecycleEvent`: provider-native lifecycle records

Set `parseJsonlLifecycleEvent` when a backend emits JSONL records for lifecycle
state that is independent of assistant text, tools, sessions, and terminal
results. The hook receives the same line and context as `parseJsonlEvent` and is
tried first. Returning a lifecycle event consumes that line; returning `null`
lets the source-compatible `parseJsonlEvent` hook or built-in parser handle it.

The current lifecycle contract supports native compaction start and end records.
An end record includes `completed` so channels can distinguish successful and
incomplete compaction without inferring an outcome from later messages.

### `ownsNativeCompaction`: opting out of OpenClaw compaction

If your backend runs an agent that compacts its **own** transcript, set
`ownsNativeCompaction: true` so OpenClaw's safeguard summarizer never runs
against its sessions - automatic CLI compaction defers to the backend and the
turn proceeds. `claude-cli` declares it because Claude Code compacts
internally with no harness endpoint. It also declares
`manualCompaction`, so an explicit OpenClaw `/compact` resumes the
bound Claude Code session and invokes its native `/compact` command without
recording a conversation turn. Native-harness sessions such as Codex keep
routing to their harness compaction endpoint instead.



**仅当以下条件全部成立时才声明它**,否则延迟的
超预算会话可能持续超预算或失效(OpenClaw 不再会
兜底处理它):

- 后端在接近其上下文窗口时,能够可靠地压缩或限制自身的对话记录;
- 它持久化可恢复会话,使压缩后的状态在轮次间保留(例如 `--resume` / `--session-id`);
- 它不是原生 harness 压缩会话——匹配 `agentHarnessId` 的会话会改为路由到 harness 端点。

如果后端支持就地手动命令,请连同所有权标志一起声明:

```typescript
manualCompaction: {
  buildPrompt: (instructions) =>
    instructions ? `/compact ${instructions}` : "/compact",
  input: "arg",
  validateOutput: (rawOutput) =>
    rawOutput.includes('"type":"compaction_complete"')
      ? { ok: true }
      : { ok: false, reason: "CLI did not confirm compaction." },
},

构建器会接收可选的 /compact 指令。校验器会接收有界的原始进程输出,并且必须要求后端拥有的正向确认;仅退出码为零不足以证明压缩完成。不要为会创建独立会话或需要普通模型轮次的命令声明此能力。

MCP 工具桥

CLI 后端默认不会接收 OpenClaw 工具。如果 CLI 可以消费 MCP 配置,请显式选择加入:

return {
  id: "acme-cli",
  bundleMcp: true,
  bundleMcpMode: "codex-config-overrides",
  config: {
    command: "acme",
    args: ["chat", "--json"],
    output: "json",
  },
};

支持的桥接模式:

模式 用途
claude-config-file 接受 MCP 配置文件的 CLI
codex-config-overrides 接受 argv 上配置覆盖的 CLI
gemini-system-settings 从其系统设置目录读取 MCP 设置的 CLI

仅当 CLI 确实能够消费该桥接时才启用它。如果 CLI 拥有 自己的内置工具层且无法禁用,请设置 nativeToolMode: "always-on",以便在调用方要求不使用原生工具时,OpenClaw 可以失败关闭。如果它可以在每次运行时禁用所有原生工具,请使用 "selectable" 并遵循上述 resolveExecutionArgs 约定。

选择后端

用户通过其模型引用前缀选择独立后端。声明了规范 modelProvider 的后端,也可以通过该 提供商模型的 agentRuntime.id 进行选择。适配器机制仍保留在插件中:

{
  agents: {
    defaults: {
      model: {
        primary: "openai/gpt-6-astra",
        fallbacks: ["acme-cli/large"],
      },
    },
  },
}

将凭据放在 OpenClaw 认证配置或插件拥有的配置中。确保已注册的命令位于网关服务的 PATH 中;需要不同路径或 argv 的部署应修改或包装插件注册。

验证

对于捆绑插件,请围绕构建器和 setup 注册添加聚焦测试,然后运行该插件的目标测试通道:

pnpm test extensions/acme-cli

对于本地或已安装插件,请验证发现机制和一次真实模型运行:

openclaw plugins inspect acme-cli --runtime --json
openclaw agent --message "reply exactly: backend ok" --model acme-cli/acme-large

如果后端支持图像或 MCP,请添加一次实时冒烟测试,使用真实 CLI 证明这些路径。不要依赖静态检查来验证提示、图像、MCP 或会话恢复行为。

检查清单

Success

已发布的包中,package.json 包含 openclaw.extensions 和已构建的运行时条目

Success

openclaw.plugin.json 声明了 cliBackends 和显式的 activation.onStartup

Success

当 setup/模型发现需要在冷启动时看到后端时,setup.cliBackends 存在

Success

api.registerCliBackend(...) 使用与清单相同的后端 id

Success

后端模型前缀或模型范围的 agentRuntime.id 选择该注册

Success

会话、系统提示、图像和输出解析器设置与真实 CLI 约定一致

Success

目标测试和至少一次实时 CLI 冒烟测试证明后端路径

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw