Skip to content

Plugin SDK Overview

The plugin SDK is the typed contract between plugins and core. This page is the reference for what to import and what you can register.

Always import from a specific subpath:

import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";

Each subpath is a small, self-contained module. This keeps startup fast and prevents circular dependency issues.

The most commonly used subpaths, grouped by purpose. The full list of 100+ subpaths is in scripts/lib/plugin-sdk-entrypoints.json.

SubpathKey exports
plugin-sdk/plugin-entrydefinePluginEntry
plugin-sdk/coredefineChannelPluginEntry, createChatChannelPlugin, createChannelPluginBase, defineSetupPluginEntry, buildChannelConfigSchema
Channel subpaths
SubpathKey exports
plugin-sdk/channel-setupcreateOptionalChannelSetupSurface
plugin-sdk/channel-pairingcreateChannelPairingController
plugin-sdk/channel-reply-pipelinecreateChannelReplyPipeline
plugin-sdk/channel-config-helperscreateHybridChannelConfigAdapter
plugin-sdk/channel-config-schemaChannel config schema types
plugin-sdk/channel-policyresolveChannelGroupRequireMention
plugin-sdk/channel-lifecyclecreateAccountStatusSink
plugin-sdk/channel-inboundDebounce, mention matching, envelope helpers
plugin-sdk/channel-send-resultReply result types
plugin-sdk/channel-actionscreateMessageToolButtonsSchema, createMessageToolCardSchema
plugin-sdk/channel-targetsTarget parsing/matching helpers
plugin-sdk/channel-contractChannel contract types
plugin-sdk/channel-feedbackFeedback/reaction wiring
Provider subpaths
SubpathKey exports
plugin-sdk/cli-backendCLI backend defaults + watchdog constants
plugin-sdk/provider-authcreateProviderApiKeyAuthMethod, ensureApiKeyFromOptionEnvOrPrompt, upsertAuthProfile
plugin-sdk/provider-model-sharednormalizeModelCompat
plugin-sdk/provider-catalog-sharedfindCatalogTemplate, buildSingleProviderApiKeyCatalog
plugin-sdk/provider-usagefetchClaudeUsage and similar
plugin-sdk/provider-streamStream wrapper types
plugin-sdk/provider-onboardOnboarding config patch helpers
plugin-sdk/global-singletonProcess-local singleton/map/cache helpers
Auth and security subpaths
SubpathKey exports
plugin-sdk/command-authresolveControlCommandGate
plugin-sdk/allow-fromformatAllowFromLowercase
plugin-sdk/secret-inputSecret input parsing helpers
plugin-sdk/webhook-ingressWebhook request/target helpers
plugin-sdk/webhook-request-guardsRequest body size/timeout helpers
Runtime and storage subpaths
SubpathKey exports
plugin-sdk/runtime-storecreatePluginRuntimeStore
plugin-sdk/config-runtimeConfig load/write helpers
plugin-sdk/approval-runtimeExec/plugin approval helpers, approval-capability builders, auth/profile helpers, native routing/runtime helpers
plugin-sdk/infra-runtimeSystem event/heartbeat helpers
plugin-sdk/collection-runtimeSmall bounded cache helpers
plugin-sdk/diagnostic-runtimeDiagnostic flag and event helpers
plugin-sdk/error-runtimeError graph and formatting helpers
plugin-sdk/fetch-runtimeWrapped fetch, proxy, and pinned lookup helpers
plugin-sdk/host-runtimeHostname and SCP host normalization helpers
plugin-sdk/retry-runtimeRetry config and retry runner helpers
plugin-sdk/agent-runtimeAgent dir/identity/workspace helpers
plugin-sdk/directory-runtimeConfig-backed directory query/dedup
plugin-sdk/keyed-async-queueKeyedAsyncQueue
Capability and testing subpaths
SubpathKey exports
plugin-sdk/image-generationImage generation provider types
plugin-sdk/media-understandingMedia understanding provider types
plugin-sdk/speechSpeech provider types
plugin-sdk/testinginstallCommonResolveTargetErrorCases, shouldAckReaction

The register(api) callback receives an OpenClawPluginApi object with these methods:

MethodWhat it registers
api.registerProvider(...)Text inference (LLM)
api.registerCliBackend(...)Local CLI inference backend
api.registerChannel(...)Messaging channel
api.registerSpeechProvider(...)Text-to-speech / STT synthesis
api.registerMediaUnderstandingProvider(...)Image/audio/video analysis
api.registerImageGenerationProvider(...)Image generation
api.registerWebSearchProvider(...)Web search
MethodWhat it registers
api.registerTool(tool, opts?)Agent tool (required or { optional: true })
api.registerCommand(def)Custom command (bypasses the LLM)
MethodWhat it registers
api.registerHook(events, handler, opts?)Event hook
api.registerHttpRoute(params)Gateway HTTP endpoint
api.registerGatewayMethod(name, handler)Gateway RPC method
api.registerCli(registrar, opts?)CLI subcommand
api.registerService(service)Background service
api.registerInteractiveHandler(registration)Interactive handler

api.registerCli(registrar, opts?) accepts two kinds of top-level metadata:

  • commands: explicit command roots owned by the registrar
  • descriptors: parse-time command descriptors used for root CLI help, routing, and lazy plugin CLI registration

If you want a plugin command to stay lazy-loaded in the normal root CLI path, provide descriptors that cover every top-level command root exposed by that registrar.

api.registerCli(
async ({ program }) => {
const { registerMatrixCli } = await import("./src/cli.js");
registerMatrixCli({ program });
},
{
descriptors: [
{
name: "matrix",
description: "Manage Matrix accounts, verification, devices, and profile state",
hasSubcommands: true,
},
],
},
);

Use commands by itself only when you do not need lazy root CLI registration. That eager compatibility path remains supported, but it does not install descriptor-backed placeholders for parse-time lazy loading.

api.registerCliBackend(...) lets a plugin own the default config for a local AI CLI backend such as claude-cli or codex-cli.

  • The backend id becomes the provider prefix in model refs like claude-cli/opus.
  • The backend config uses the same shape as agents.defaults.cliBackends.<id>.
  • User config still wins. OpenClaw merges agents.defaults.cliBackends.<id> over the plugin default before running the CLI.
  • Use normalizeConfig when a backend needs compatibility rewrites after merge (for example normalizing old flag shapes).
MethodWhat it registers
api.registerContextEngine(id, factory)Context engine (one active at a time)
api.registerMemoryPromptSection(builder)Memory prompt section builder
api.registerMemoryFlushPlan(resolver)Memory flush plan resolver
api.registerMemoryRuntime(runtime)Memory runtime adapter
MethodWhat it registers
api.registerMemoryEmbeddingProvider(adapter)Memory embedding adapter for the active plugin
  • registerMemoryPromptSection, registerMemoryFlushPlan, and registerMemoryRuntime are exclusive to memory plugins.
  • registerMemoryEmbeddingProvider lets the active memory plugin register one or more embedding adapter ids (for example openai, gemini, or a custom plugin-defined id).
  • User config such as agents.defaults.memorySearch.provider and agents.defaults.memorySearch.fallback resolves against those registered adapter ids.
MethodWhat it does
api.on(hookName, handler, opts?)Typed lifecycle hook
api.onConversationBindingResolved(handler)Conversation binding callback
  • before_tool_call: returning { block: true } is terminal. Once any handler sets it, lower-priority handlers are skipped.
  • before_tool_call: returning { block: false } is treated as no decision (same as omitting block), not as an override.
  • before_install: returning { block: true } is terminal. Once any handler sets it, lower-priority handlers are skipped.
  • before_install: returning { block: false } is treated as no decision (same as omitting block), not as an override.
  • message_sending: returning { cancel: true } is terminal. Once any handler sets it, lower-priority handlers are skipped.
  • message_sending: returning { cancel: false } is treated as no decision (same as omitting cancel), not as an override.
FieldTypeDescription
api.idstringPlugin id
api.namestringDisplay name
api.versionstring?Plugin version (optional)
api.descriptionstring?Plugin description (optional)
api.sourcestringPlugin source path
api.rootDirstring?Plugin root directory (optional)
api.configOpenClawConfigCurrent config snapshot
api.pluginConfigRecord<string, unknown>Plugin-specific config from plugins.entries.<id>.config
api.runtimePluginRuntimeRuntime helpers
api.loggerPluginLoggerScoped logger (debug, info, warn, error)
api.registrationModePluginRegistrationMode"full", "setup-only", "setup-runtime", or "cli-metadata"
api.resolvePath(input)(string) => stringResolve path relative to plugin root

Within your plugin, use local barrel files for internal imports:

my-plugin/
api.ts # Public exports for external consumers
runtime-api.ts # Internal-only runtime exports
index.ts # Plugin entry point
setup-entry.ts # Lightweight setup-only entry (optional)