docs/plugins/compatibility.md
OpenClaw keeps older plugin contracts wired through named compatibility adapters before removing them. This protects existing bundled and external plugins while the SDK, manifest, setup, config, and agent runtime contracts evolve.
Plugin compatibility contracts are tracked in the core registry at
src/plugins/compat/registry.ts. Each record has:
active, deprecated, removal-pending, or removedsdk, config, setup, channel, provider, plugin-execution,
agent-runtime, or coreThe registry is the source for maintainer planning and future plugin inspector checks. If a plugin-facing behavior changes, add or update the compatibility record in the same change that adds the adapter.
Doctor repair and migration compatibility is tracked separately at
src/commands/doctor/shared/deprecation-compat.ts. Those records cover old
config shapes, install-ledger layouts, and repair shims that may need to
stay available after the runtime compatibility path is removed.
Release sweeps should check both registries. Do not delete a doctor migration just because the matching runtime or config compatibility record expired; first verify there is no supported upgrade path that still needs the repair. Revalidate each replacement annotation during release planning too, since plugin ownership and config footprint can change as providers and channels move out of core.
OpenClaw should not remove a documented plugin contract in the same release that introduces its replacement. Migration sequence:
Deprecated records must include a warning start date, replacement, docs
link, and a final removal date no more than three months after the warning
starts. Do not add a deprecated compatibility path with an open-ended
removal window unless maintainers explicitly decide it is permanent
compatibility and mark it active instead.
The registry currently tracks around 70 compatibility codes across these areas. New plugin code should use the replacement in each area and in the specific migration guide; existing plugins can keep using a compatibility path until docs, diagnostics, and release notes announce a removal window.
openclaw/plugin-sdk/compatbefore_agent_startapi.on("deactivate", ...) cleanup hook names while plugins
migrate to gateway_stopactivate(api) plugin entrypoints while plugins migrate to
register(api)openclaw/extension-api,
openclaw/plugin-sdk/channel-runtime, openclaw/plugin-sdk/command-auth
status builders, openclaw/plugin-sdk/test-utils (replaced by focused
openclaw/plugin-sdk/* test subpaths), and the ClawdbotConfig /
OpenClawSchemaType type aliasesapi.runtime.taskFlow,
api.runtime.subagent.getSession, api.runtime.stt, and deprecated
api.runtime.config.loadConfig() / api.runtime.config.writeConfigFile(...)WebInboundMessage flat callback fields (see below)WebInboundMessage top-level admission fields (see below)registerMemoryCapabilityapi.registerEmbeddingProvider(...) and
contracts.embeddingProvidersopenclaw/plugin-sdk/channel-routesetup-api runtime fallback while setup descriptors move to cold
setup.requiresRuntime: false metadatadiscovery hooks while provider catalog hooks move to
catalog.run(...)showConfigured / showInSetup metadata while channel packages
move to openclaw.channel.exposureagentRuntimechannelConfigs metadata landsopenclaw plugins registry --refresh
and openclaw doctor --fixplugins.entries.<plugin>.configplugins.installs authored config and bundled plugin load-path
aliases while install metadata moves into the state-managed plugin ledgerWhatsApp runtime callbacks deliver WebInboundMessage: the canonical
nested event, payload, quote, group, and platform contexts plus
deprecated flat aliases for the shipped callback fields. New callback code
should read the nested contexts. Code that constructs clean nested callback
messages can use WebInboundCallbackMessage; compatibility listeners that
still inject old flat test or plugin messages should use
LegacyFlatWebInboundMessage or WebInboundMessageInput.
The flat aliases remain available until 2026-08-30; that window applies
only to flat alias access, not to the nested shape, which is the canonical
runtime contract. Each flat alias's TypeScript @deprecated annotation
names its exact nested replacement. Common examples:
id, timestamp, and isBatched move under event.body, mediaPath, mediaType, mediaFileName, mediaUrl, location,
and untrustedStructuredContext move under payload.to, chatId, sender/self fields, sendComposing, reply(...), and
sendMedia(...) move under platform.replyTo* fields move under quote; group subject/participant/mention
fields move under group.payload.untrustedStructuredContext is extracted from inbound provider
payloads. Plugins should inspect label, source, and type before
treating its payload as authoritative.
Accepted WhatsApp callback messages carry admission, a public-safe
envelope for the access-control decision that admitted the message. New
callback code should read admission facts from msg.admission instead of
the older top-level admission fields.
The top-level fields remain available until 2026-08-30. Each field's
TypeScript @deprecated annotation names its replacement:
from and conversationId move to admission.conversation.id.accountId moves to admission.accountId.accessControlPassed is a derived compatibility view of
admission.ingress.decision === "allow"; on messages that already carry
admission, writing the legacy boolean does not rewrite the ingress
graph.chatType moves to admission.conversation.kind.The plugin inspector should live outside the core OpenClaw repo as a separate package/repository backed by the versioned compatibility and manifest contracts. The day-one CLI should be:
openclaw-plugin-inspector ./my-plugin
It should emit manifest/schema validation, the contract compatibility
version being checked, install/source metadata checks, cold-path import
checks, and deprecation/compatibility warnings. Use --json for stable
machine-readable output in CI annotations. OpenClaw core should expose
contracts and fixtures the inspector can consume, but should not publish the
inspector binary from the main openclaw package.
Use Crabbox-backed Blacksmith Testbox for the installable-package acceptance lane when validating the external inspector against OpenClaw plugin packages. Run it from a clean OpenClaw checkout after the package is built:
pnpm crabbox:run -- --provider blacksmith-testbox --timing-json --shell -- "pnpm install && pnpm build && npm exec --yes @openclaw/[email protected] -- ./extensions/telegram --json"
pnpm crabbox:run -- --provider blacksmith-testbox --timing-json --shell -- "npm exec --yes @openclaw/[email protected] -- ./extensions/discord --json"
pnpm crabbox:run -- --provider blacksmith-testbox --timing-json --shell -- "npm exec --yes @openclaw/[email protected] -- <clawhub-plugin-dir> --json"
Keep this lane opt-in for maintainers, since it installs an external npm package and may inspect plugin packages cloned outside the repo. The local repo guards cover the SDK export map, compatibility registry metadata, deprecated SDK-import burn-down, and bundled extension import boundaries; Testbox inspector proof covers the package as external plugin authors consume it.
Release notes should include upcoming plugin deprecations with target dates
and links to migration docs, before a compatibility path moves to
removal-pending or removed.