If the upstream service exposes a normal HTTP model API, write a
provider plugin instead. If the upstream
runtime owns complete agent sessions, tool events, compaction, or background
task state, use an agent harness.
What the plugin owns
A CLI backend plugin has three contracts:| Contract | File | Purpose |
|---|---|---|
| Package entry | package.json | Points OpenClaw at the plugin runtime module |
| Manifest ownership | openclaw.plugin.json | Declares the backend id before runtime loads |
| Runtime registration | index.ts | Calls api.registerCliBackend(...) with command defaults |
api.registerCliBackend(...).
Minimal backend plugin
Create package metadata
package.json
./src/index.ts, add openclaw.runtimeExtensions pointing at the
built JavaScript peer. See Entry points.Declare backend ownership
openclaw.plugin.json
cliBackends is the runtime ownership list; it lets OpenClaw auto-load the
plugin when config or model selection mentions acme-cli/....setup.cliBackends is the descriptor-first setup surface. Add it when
model discovery, onboarding, or status should recognize the backend
without loading plugin runtime. Use requiresRuntime: false only when
those static descriptors are enough for setup.Config shape
CliBackendConfig describes how OpenClaw should launch and parse the CLI:
| Field | Use |
|---|---|
command | Binary name or absolute command path |
args | Base argv for fresh runs |
resumeArgs | Alternate argv for resumed sessions; supports {sessionId} |
output / resumeOutput | Parser: json, jsonl, or text |
jsonlDialect | JSONL event dialect: claude-stream-json or gemini-stream-json |
liveSession | Long-lived CLI process mode (claude-stdio) |
input | Prompt transport: arg or stdin |
maxPromptArgChars | Max prompt length for arg mode before falling back to stdin |
env / clearEnv | Extra env vars to inject, or names to strip before launch |
modelArg | Flag used before the model id |
modelAliases | Map OpenClaw model ids to CLI-native ids |
sessionArg / sessionArgs | How to pass a session id |
sessionMode | always, existing, or none |
sessionIdFields | JSON fields OpenClaw reads from CLI output |
systemPromptArg / systemPromptFileArg | System prompt transport |
systemPromptFileConfigArg / systemPromptFileConfigKey | Config-override transport for a system prompt file (for example -c) |
systemPromptMode | append or replace |
systemPromptWhen | first, always, or never |
imageArg / imageMode | Image path flag and how to pass multiple images (repeat or list) |
imagePathScope | Where staged image files live before handoff: temp or workspace |
serialize | Keep same-backend runs ordered |
reseedFromRawTranscriptWhenUncompacted | Opt in to bounded raw-transcript reseed before compaction for safe session resets |
reliability.outputLimits | Max raw JSONL chars/lines retained for one live CLI turn (live-session backends) |
reliability.watchdog | No-output timeout tuning, separate for fresh vs resumed runs |
Advanced backend hooks
CliBackendPlugin can also define:
| Hook | Use |
|---|---|
normalizeConfig(config, context) | Rewrite legacy user config after merge |
resolveExecutionArgs(ctx) | Add request-scoped flags such as thinking effort or side-question isolation |
prepareExecution(ctx) | Create temporary auth or config bridges before launch |
transformSystemPrompt(ctx) | Apply a final CLI-specific system prompt transform |
textTransforms | Bidirectional prompt/output replacements |
defaultAuthProfileId | Prefer a specific OpenClaw auth profile |
authEpochMode | Decide how auth changes invalidate stored CLI sessions |
nativeToolMode | Declare whether the CLI has always-on native tools |
sideQuestionToolMode | Declare disabled native tools for /btw side questions |
bundleMcp / bundleMcpMode | Opt into OpenClaw’s loopback MCP tool bridge |
ownsNativeCompaction | Backend owns its own compaction - OpenClaw defers |
ctx.executionMode is "agent" for normal turns and "side-question" for
ephemeral /btw calls. Use it when the CLI needs different one-shot flags,
such as disabling native tools, session persistence, or resume behavior for
BTW. If a backend normally has nativeToolMode: "always-on" but its
side-question argv reliably disables those tools, also set
sideQuestionToolMode: "disabled"; otherwise OpenClaw fails closed when BTW
requires a no-tools CLI run.
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 - the CLI compaction lifecycle returns a no-op and the
turn proceeds. claude-cli declares it because Claude Code compacts
internally with no harness endpoint. Native-harness sessions such as Codex
keep routing to their harness compaction endpoint instead.
Only declare it when all of the following hold, or a deferred
over-budget session can stay over budget or go stale (OpenClaw no longer
rescues it):
- the backend reliably compacts or bounds its own transcript as it nears its window;
- it persists a resumable session so the compacted state survives turns
(for example
--resume/--session-id); - it is not a native-harness compaction session - matching
agentHarnessIdsessions route to the harness endpoint instead.
MCP tool bridge
CLI backends do not receive OpenClaw tools by default. If the CLI can consume an MCP configuration, opt in explicitly:| Mode | Use |
|---|---|
claude-config-file | CLIs that accept an MCP config file |
codex-config-overrides | CLIs that accept config overrides on argv |
gemini-system-settings | CLIs that read MCP settings from their system settings directory |
nativeToolMode: "always-on" so OpenClaw can fail closed when a caller requires no native
tools.
User configuration
Users can override any backend default:command when the binary is outside PATH.
Verification
For bundled plugins, add a focused test around the builder and setup registration, then run the plugin’s targeted test lane:Checklist
package.json has openclaw.extensions and built runtime entries for published packagesopenclaw.plugin.json declares cliBackends and intentional activation.onStartupsetup.cliBackends is present when setup/model discovery should see the backend coldapi.registerCliBackend(...) uses the same backend id as the manifestUser overrides under
agents.defaults.cliBackends.<id> still winSession, system prompt, image, and output parser settings match the real CLI contract
Targeted tests and at least one live CLI smoke prove the backend path
Related
- CLI backends - user configuration and runtime behavior
- Building plugins - package and manifest basics
- Plugin SDK overview - registration API reference
- Plugin manifest -
cliBackendsand setup descriptors - Agent harness - full external agent runtimes