Plugin Compatibility Contracts, Deprecation Metadata, and Migration Expectations
This page explains how OpenClaw preserves older plugin contracts through compatibility adapters. Plugin maintainers and developers will find details on the compatibility registry, statuses, and replacement guidance.
Read this when
- You maintain an OpenClaw plugin
- You see a plugin compatibility warning
- You are planning a plugin SDK or manifest migration
OpenClaw preserves older plugin contracts by routing them through named compatibility adapters before they are retired. This safeguards existing bundled and external plugins while the SDK, manifest, setup, config, and agent runtime contracts continue to change.
Compatibility registry
The core registry at src/plugins/compat/registry.ts tracks plugin compatibility contracts. Each entry contains:
- a stable compatibility code
- one of these statuses:
active,deprecated,removal-pending, orremoved - an owner from:
sdk,config,setup,channel,provider,plugin-execution,agent-runtime, orcore - introduction and deprecation dates where applicable
- a precise removal date once the owning maintainer approves it; if
removeAfteris absent, a deprecated surface cannot be removed - replacement guidance
- docs, diagnostics, and tests covering both old and new behavior
Maintainers use the registry for planning, and future plugin inspector checks will rely on it. When a plugin-facing behavior changes, the same change that adds the adapter must also add or update the compatibility record.
Doctor repair and migration compatibility are tracked separately at src/commands/doctor/shared/deprecation-compat.ts. Those records cover legacy config shapes, install-ledger layouts, and repair shims that might need to remain available after the runtime compatibility path is removed.
Release sweeps must inspect both registries. Do not delete a doctor migration solely because the matching runtime or config compatibility record has expired. First confirm that no supported upgrade path still requires the repair. Also revalidate each replacement annotation during release planning, because plugin ownership and config footprint can shift as providers and channels move out of core.
Deprecation policy
OpenClaw must not remove a documented plugin contract in the same release that introduces its replacement. Follow this migration sequence:
- Add the new contract.
- Keep the old behavior routed through a named compatibility adapter.
- Emit diagnostics or warnings when plugin authors can take action.
- Document the replacement and timeline.
- Test both old and new paths.
- Wait through the announced migration window.
- Remove only with explicit breaking-release approval.
Deprecated records must include a warning start date, a replacement, a docs link, and a final removal date no more than three months after the warning begins. Do not add a deprecated compatibility path with an open-ended removal window unless maintainers explicitly decide it is permanent compatibility and label it active instead.
Current compatibility areas
The July 2026 sweep removed expired root SDK, manifest, provider, runtime, registry-flag, and plugin-owned web-config aliases. Doctor migrations remain tracked separately so supported upgrade paths can still repair old config.
The remaining dated compatibility areas are:
- the August and September SDK subpath windows listed in the migration guide
api.on("deactivate", ...)andapi.on("subagent_spawning", ...)hook aliases- memory-specific embedding registration and the beta.5 session-store bridge
- WhatsApp inbound callback aliases described below
- explicit channel target parsing and
openclaw/plugin-sdk/messaging-targets - embedded Pi agent aliases
- the shipped agent-harness SDK aliases, whose removal awaits a new externally documented migration decision
Active, undated registry records cover supported behavior rather than removal debt, including activation hints, plugin capture, bundled plugin enablement, and the generated channel-config fallback.
WhatsApp inbound callback flat aliases
WhatsApp 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, andisBatchedmove underevent.body,mediaPath,mediaType,mediaFileName,mediaUrl,location, anduntrustedStructuredContextmove underpayload.to,chatId, sender/self fields,sendComposing,reply(...), andsendMedia(...)move underplatform.replyTo*fields move underquote. Group subject, participant, and mention fields move undergroup.
payload.untrustedStructuredContext is extracted from inbound provider payloads. Plugins should inspect label, source, and type before treating its payload as authoritative.
WhatsApp inbound admission fields
Accepted WhatsApp callback messages include admission, a publicly safe wrapper that contains the access control decision which permitted the message. For new callback implementations, read admission details from msg.admission rather than the earlier top-level admission fields.
The top-level fields will remain accessible until 2026-08-30. Each field has a TypeScript @deprecated annotation indicating its replacement:
fromandconversationIdare relocated toadmission.conversation.id.accountIdis relocated toadmission.accountId.accessControlPassedprovides a derived compatibility view ofadmission.ingress.decision === "allow"; for messages that already containadmission, writing the legacy boolean does not modify the ingress graph.chatTypeis relocated toadmission.conversation.kind.
Plugin inspector package
The plugin inspector should reside outside the primary OpenClaw repository as a separate package or repository, relying on the versioned compatibility and manifest contracts. The initial CLI should be:
openclaw-plugin-inspector ./my-plugin
It should output manifest and schema validation, the compatibility contract version under review, install and source metadata checks, cold path import checks, and deprecation and compatibility warnings. Use --json for stable machine readable output suitable for CI annotations. OpenClaw core should provide contracts and fixtures that the inspector can consume, but must not publish the inspector binary from the main openclaw package.
Maintainer acceptance lane
Use Crabbox backed Blacksmith Testbox for the installable package acceptance lane when validating the external inspector against OpenClaw plugin packages. Execute 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/plugin-inspector@0.1.0 -- ./extensions/telegram --json"
pnpm crabbox:run -- --provider blacksmith-testbox --timing-json --shell -- "npm exec --yes @openclaw/plugin-inspector@0.1.0 -- ./extensions/discord --json"
pnpm crabbox:run -- --provider blacksmith-testbox --timing-json --shell -- "npm exec --yes @openclaw/plugin-inspector@0.1.0 -- <clawhub-plugin-dir> --json"
Keep this lane opt in for maintainers, as it installs an external npm package and may inspect plugin packages cloned outside the repository. The local repo guards cover the SDK export map, compatibility registry metadata, deprecated SDK import burn down, and bundled extension import boundaries; the Testbox inspector proof covers the package as external plugin authors consume it.
Release notes
Release notes should list upcoming plugin deprecations with target dates and links to migration documentation, before a compatibility path transitions to removal-pending or removed.