What changed
Several wide-open import surfaces used to let plugins reach almost anything from a single entry point:openclaw/plugin-sdkandopenclaw/plugin-sdk/compat- re-exported dozens of helpers while the focused SDK was being built. Both roots are now removed; import a documented subpath instead.openclaw/plugin-sdk/infra-runtime- a broad barrel mixing system events, heartbeat state, delivery queues, fetch/proxy helpers, file helpers, approval types, and unrelated utilities.openclaw/plugin-sdk/config-runtime- a broad config barrel retained for compatibility, including deprecated directloadConfigandwriteConfigFileexports. Those methods were removed from the injected plugin runtime, not from this retained facade.openclaw/extension-api- a removed bridge that gave plugins direct access to host-side helpers like the embedded agent runner.api.registerEmbeddedExtensionFactory(...)- a removed embedded-runner-only hook that observed embedded-runner events such astool_result. Use agent tool-result middleware instead (see Migrate embedded tool-result extensions to middleware).
infra-runtime and config-runtime remain only for their
separately recorded later windows; new plugins should use focused subpaths.
OpenClaw does not remove or reinterpret documented plugin behavior in the same
change that introduces a replacement. Breaking contract changes go through a
compatibility adapter, diagnostics, docs, and a deprecation window first. That
applies to SDK imports, manifest fields, setup APIs, hooks, and runtime
registration behavior.
ChatCommandDefinition.category retains the "docks" value accepted by the
2026.8.1 SDK. Command lists display these legacy definitions under Tools;
the category does not enable channel docking or restore retired docking commands.
New definitions should use "tools".
Why
- Slow startup - importing one helper loaded dozens of unrelated modules.
- Circular dependencies - broad re-exports made import cycles easy to create.
- Unclear API surface - no way to tell stable exports from internal ones.
api.ts or runtime-api.ts:
- Anthropic keeps Claude-specific stream helpers in its own
api.ts/contract-api.tsseam. - OpenAI keeps provider builders, default-model helpers, and realtime provider
builders in its own
api.ts. - OpenRouter keeps provider builder and onboarding/config helpers in its own
api.ts.
Compatibility policy
External-plugin compatibility work follows this order:- Add the new contract.
- Keep the old behavior wired through a compatibility adapter.
- Emit a diagnostic or warning naming the old path and replacement.
- Cover both paths in tests.
- Document the deprecation and migration path.
- Remove only after the announced migration window, usually in a major release.
Retained helper contracts
Retained compatibility entrypoints keep their shipped caller names:inbound-envelope uses resolveStorePath, provider-catalog-runtime exports
resolvePluginProviders, and agent-runtime’s
resolveThinkingDefaultWithRuntimeCatalog accepts loadModelCatalog.
text-chunking retains positional CodeRegion inputs with start and end
offsets for isInsideCode. Regions returned by findCodeRegions additionally
include parser-owned block metadata; callers supplying their own ranges do not
need to provide it.
Harness attempt result migration
In OpenClaw 2026.8.1,EmbeddedRunAttemptResult from
openclaw/plugin-sdk/agent-harness-runtime requires the canonical terminal
field. Source written against the 2026.7 direct alias must migrate when it
constructs results with legacy fields such as aborted, timedOut, and
promptError; retaining the alias name does not make those old constructors
source-compatible.
Use AgentHarnessAttemptResult from the same subpath while migrating a
legacy result producer. That union accepts both the legacy fields and the
canonical result, and the host lifecycle normalizes legacy results before
core consumes them. New producers should construct terminal; consumers of
the union must narrow the result before reading it. The current
EmbeddedRunAttemptResult contract keeps terminal required.
Model-provider result compatibility
openclaw/plugin-sdk/models-provider-runtime preserves the ModelsProviderData
construction shape and buildModelsProviderData return signature published in
v2026.7.1-2, including typed adapters that return that shape. These contracts
remain supported until an explicitly approved SDK-breaking boundary.
Call buildPreparedModelsProviderData when forwarding model selections. Its
result includes the required modelCatalog with
the selected physical-route metadata. Both builders use one metadata producer;
callers must carry prepared rows forward rather than reconstructing them from IDs.
Memory read missing results
Memory managers now returnstatus: "ok" for successful excerpts and
status: "not_found" when an allowed file is missing. This keeps empty files
and empty ranges distinct from missing files without relying on pagination
metadata.
At registration, every statusless result from an older external memory manager
preserves its legacy successful-read semantics and becomes status: "ok",
including empty results without range metadata. Only an explicit
status: "not_found" reports absence. New producers must emit that status for
missing files; registered-input normalization remains available through the
next Plugin SDK major.
Plugin state migration declarations
Plugins should declaredoctorContract.stateMigrations: true in
openclaw.plugin.json and export stateMigrations from their doctor-contract
artifact. Plan-based migrations can use
definePluginDoctorMigrationFromPlans(...) from
openclaw/plugin-sdk/runtime-doctor-migrations to preserve existing move, copy, preview,
and plugin-state import behavior.
For single-file imports, defineLegacyJsonStateMigration(...) skips missing
sources (ENOENT) and values the plugin parser rejects with null. Other read
errors and invalid JSON reach Doctor’s detection or migration warnings; the
source remains untouched so the operator can fix it and retry.
Use phase: "after-session-repair" when a migration needs canonical session
ownership evidence. Ordinary Doctor detects these migrations; --fix applies
them after session repair under SQLite maintenance ownership. The context
provides bounded readPluginStateEntriesInKeyRange and
readSessionIdentityEvidenceBatch reads, plus
deletePluginStateEntriesIfUnchanged only during a fenced repair. Preserve
unknown or ambiguous ownership. Delete only the observed raw rows; callbacks
retained after maintenance ends cannot authorize later writes.
The setup-entry legacyStateMigrations option and feature flag,
setupFeatures.legacyStateMigrations,
BundledChannelLegacyStateMigrationDetector, and
ChannelPlugin.lifecycle.detectLegacyStateMigrations remain supported through
one doctor-pipeline adapter for external plugins, but are deprecated. Removal
plan: remove that adapter after OpenClaw 2027.1 only when a published-plugin
reader sweep finds no remaining users.
AuthStorage SQLite migration
AuthStorage.forAgent(agentDir) is the canonical constructor for host session
storage. It persists provider-default credentials through the agent’s
openclaw-agent.sqlite auth-profile rows and never creates auth.json.
Harness plugins receive the prepared storage instance as params.authStorage.
AuthStorage.create(authPath) remains as a named deprecated adapter for
existing plugins. The path is used only to derive the owning agent directory;
the adapter reads and writes SQLite, not the named JSON file. Migrate to
forAgent(...) now. The path-taking form emits
AUTH_STORAGE_CREATE_DEPRECATED and is eligible for removal after
2026-10-01, provided the published-plugin reader sweep is clean.
FileAuthStorageBackend is an internal SQLite-backed adapter, not an exported
Plugin SDK backend. It is not available as a named import from
openclaw/plugin-sdk/agent-sessions. Harness plugins should use the
host-prepared params.authStorage; host code that constructs storage should
use AuthStorage.forAgent(agentDir). The internal adapter emits
FILE_AUTH_STORAGE_BACKEND_DEPRECATED and never reads or writes the legacy
file. Its internal deprecation window does not preserve the former SDK import.
If a manifest field is still accepted, keep using it until docs and
diagnostics say otherwise. New code should prefer the documented replacement;
existing plugins should not break during ordinary minor releases.
The dated compatibility registry also tracks shipped annotations that do not
belong to one legacy subpath. These records use 2026-10-01 as the earliest
review date; removal still requires the reader condition in the final column.
Published channel setup compatibility
Slack, Discord, Signal, and Microsoft Teams packages published through2026.7.1 import channel-specific config schemas from
openclaw/plugin-sdk/bundled-channel-config-schema. The published Slack and
Discord packages also import createLegacyCompatChannelDmPolicy and
promptLegacyChannelAllowFromForAccount from
openclaw/plugin-sdk/setup-runtime.
Those exports remain available as deprecated runtime compatibility adapters.
New and republished plugins should own their config schemas and setup policy
locally, using generic primitives from channel-config-schema and
setup-runtime. The compatibility exports can be removed only after the
minimum supported published package versions no longer import them.
Channel setup input field compatibility
ChannelSetupInput now keeps only the cross-channel setup envelope typed
permanently. Channel-specific fields remain typed in a deprecated compatibility
tier so existing external plugins still compile while plugin authors move those
fields into plugin-local setup input types.
OpenClaw does not ship major releases. A registry sweep on 2026-07-22 inspected
426 published out-of-tree channel plugins and removed 21 fields with no readers.
The 22 retained fields each have a known published reader. Each further field is
deleted as soon as no published plugin reads it; the retained set shrinks as
plugin authors migrate to plugin-local setup input types.
The same sweep removed 23 legacy undeclared-adapter promotion keys with no
published dependents. Six common keys and the setup-only rooms key remain.
That set also shrinks as published plugins declare singleAccountKeysToMove.
The shared type has no index signature. Plugin-owned keys can still be present
on runtime input objects; declare them in a plugin-local intersection or narrow
them through the owning plugin’s setup schema.
singleAccountKeysToMove, including an empty array when the
plugin needs no extra promotion keys, so the shared fallback can be retired one
key at a time.
Verifying readers
- Page through
https://clawhub.ai/api/v1/packages?family=code-plugin&limit=100with eachnextCursor, and keep packages whosecategoriesincludechannels. - Add npm candidates from
npm search --json --searchlimit=1000 "openclaw channel plugin". Add source-only candidates from GitHub code searches foropenclaw/plugin-sdk/channel-setup,openclaw/plugin-sdk/setup, andopenclaw/plugin-sdk/core. - Resolve each candidate’s latest published version. Run
npm pack <package>@<version> --json --pack-destination <temp-dir>, unpack it, and inspect shippeddistJavaScript and declarations for direct or destructured field reads. Download the ClawHub artifact when a package has no npm release. - Record package, version, field or promotion key, and matching file. A field or key is deletable only when no published plugin artifact reads it. Keep the reader names in the code comments beside the retained field and key lists synchronized with the sweep.
removeAfter: 2026-10-01, but setup input runtime objects and behavior are
unchanged. The date starts a review; each field remains until its published
artifact reader count is zero.
Audit the current migration queue with pnpm plugins:boundary-report:
pnpm plugins:boundary-report:ci runs with the compatibility fail flag.
For dated deprecated records, removeAfter is the final compatibility day:
2026-09-01 becomes eligible at 2026-09-02T00:00:00Z, not at the start of
September 1. removal-pending records are separate: they become due for review
at 00:00 UTC on their removeAfter date and are reported with blockers, but do
not trigger this fail flag. Neither state authorizes automatic removal.
Deprecated records normally have an explicit removeAfter date. A contract
tied to a version boundary instead declares a removalGate;
next-plugin-sdk-major is an approved major-version gate, not a pending owner
decision, and is never date-eligible. A record with neither field appears as
no-date and remains ineligible until its owner publishes a gate. The report
displays either the date or named gate, counts local code/doc references, lists
removal-pending records with their blockers and surface-token reader
references, and summarizes the private memory-host SDK bridge. Those reader
references are triage signals, not published-artifact proof.
Media legacy projection
Themedia-legacy-projection compatibility record covers the old parallel
media fields, payload builders, hook metadata aliases, and media template
names. Its approved removeAfter date is 2026-10-01 (two release trains
after the facts-first replacements shipped). Removal additionally requires a
clean published-plugin artifact sweep at that time; migrate before the date.
For channel ingress, replace singular/plural MediaPath, MediaUrl,
MediaType, MediaPaths, MediaUrls, MediaTypes,
MediaTranscribedIndexes, MediaWorkspaceDir, and MediaStaged with ordered
facts:
event.media in inbound_claim and message_received hooks. If remote
media is not locally staged, use event.originalMedia for identity/diagnostics
and wait for event.media; event.mediaStagingPending distinguishes that
state. Do not read the deprecated singular/plural properties from
event.metadata.
For CLI media models, replace {{MediaPath}}, {{MediaUrl}}, {{MediaType}},
and {{MediaDir}} with {{AttachmentPath}}, {{AttachmentUrl}},
{{AttachmentContentType}}, and {{AttachmentDir}}. Use
{{AttachmentIndex}} when attachment position matters.
For local media read policy, import getAgentScopedMediaLocalRoots(...) or
getAgentScopedMediaLocalRootsForSources(...) from
openclaw/plugin-sdk/media-local-roots. The
openclaw/plugin-sdk/agent-media-payload facade and its
buildAgentMediaPayload(...) projection are deprecated.
How to migrate
Migrate runtime config load/write helpers
api.runtime.config.loadConfig() and
api.runtime.config.writeConfigFile(...) directly. Prefer config already
passed into the active call path. Long-lived handlers that need the
current process snapshot can use api.runtime.config.current(). Long-lived
agent tools should read ctx.getRuntimeConfig() inside execute so a tool
created before a config write still sees the refreshed config.Config writes go through the transactional helper with an explicit
after-write policy:afterWrite: { mode: "restart", reason: "..." } when the change needs
a clean gateway restart, and afterWrite: { mode: "none", reason: "..." }
only when the caller owns the follow-up and deliberately suppresses the
reload planner. Mutation results include a typed followUp summary for
tests and logging; the gateway remains responsible for applying or
scheduling the restart.loadConfig and writeConfigFile have been removed from the plugin
runtime. Bundled plugins and repo runtime code are guarded by
pnpm check:deprecated-api-usage and
pnpm check:no-runtime-action-load-config: new production plugin usage
fails outright, direct config writes fail, gateway server methods must use
the request runtime snapshot, runtime channel send/action/client helpers
must receive config from their boundary, and long-lived runtime modules
allow zero ambient loadConfig() calls.New plugin code should avoid the broad openclaw/plugin-sdk/config-runtime
barrel. Use the narrow subpath for the job:api.pluginConfig is registration-scoped, not a live getter. Replacing
resolveLivePluginConfigObject(...) requires preserving freshness through
the current config supplied by the runtime boundary. The injected markdown
resolver preserves channel/account precedence and channel defaults;
markdown-table-runtime is a private, JavaScript-only host export.Check named types separately. config-contracts does not export TtsMode,
TtsPersonaConfig, TtsPersonaFallbackPolicy, or SessionResetMode;
session-store-runtime does not export SessionResetMode either. Existing
callers needing those names must keep retained type imports or explicitly
adapt their types. Talk config, cron-store operations, context-visibility
config resolution, and dangerous-name checks also lack a complete modern
typed-public mapping. Missing public contracts require an SDK-owner decision,
not an import of the private focused implementation.Bundled plugins and their tests are scanner-guarded against the broad
barrel so imports and mocks stay local to the behavior they need. The
barrel still exists for external compatibility, but new code should not
depend on it.Migrate embedded tool-result extensions to middleware
api.registerEmbeddedExtensionFactory(...) tool-result handlers with
runtime-neutral middleware:contracts.agentToolResultMiddleware. Undeclared installed middleware
registrations are rejected.Migrate approval-native handlers to capability facts
approvalCapability.nativeRuntime plus the shared runtime-context
registry:- Replace
approvalCapability.handler.loadRuntime(...)withapprovalCapability.nativeRuntime. - Move approval-specific auth/delivery off legacy
plugin.auth/plugin.approvalswiring and ontoapprovalCapability. ChannelPlugin.approvalshas been removed from the public channel-plugin contract; move delivery/native/render fields ontoapprovalCapability.plugin.authremains for channel login/logout flows only; core no longer reads approval auth hooks there.- Register channel-owned runtime objects (clients, tokens, Bolt apps)
through
openclaw/plugin-sdk/channel-runtime-context. - Do not send plugin-owned reroute notices from native approval handlers; core owns routed-elsewhere notices from actual delivery results.
- When passing
channelRuntimeintocreateChannelManager(...), provide a realcreatePluginRuntime().channelsurface - partial stubs are rejected.
Audit Windows wrapper fallback behavior
openclaw/plugin-sdk/windows-spawn, unresolved Windows
.cmd/.bat wrappers now fail closed unless you explicitly pass
allowShellFallback: true:allowShellFallback and handle the thrown error instead.Find deprecated imports
Replace with focused imports
createChannelReplyPipeline(...)
call sites. The modern export is createChannelMessageReplyPipeline;
see Retained channel facade mappings
for the remaining functions and named types.For host-side helpers, use the injected plugin runtime instead of
importing directly:Replace broad infra-runtime imports
openclaw/plugin-sdk/infra-runtime still exists for external
compatibility, but new code should use the supported surface it actually
needs:heartbeat-runtime, delivery-queue-runtime,
fetch-runtime, runtime-fetch, and file-lock are JavaScript-only host
exports, not typed third-party APIs. Heartbeat event/summary/visibility
helpers, pending-delivery drain, transport readiness, concurrency, and file
locking do not have equivalent modern typed-public mappings here. Retain
existing compatibility imports for those operations pending an SDK-owner
decision.fetchWithSsrFGuard is not a drop-in replacement for dispatcher-aware fetch:
it takes an options object and returns { response, finalUrl, release, ... },
not a bare Response; callers must release its resources. The named types
PinnedDispatcherPolicy, GuardedFetchOptions, and GuardedFetchResult
are not exported by ssrf-runtime. Similarly, dedupe-runtime does not
export the legacy DedupeCache or DedupeCacheOptions names. Migrate type
usage explicitly rather than assuming a function move also moves its types.The error mapping does not cover hasErrnoCode, isErrno,
stringifyNonErrorCause, ErrorKind, or detectErrorKind; the last helper
preserves legacy substring classification. The numeric and random mappings
likewise do not cover every timer, expiry, hex, fraction, or integer helper.
Keep unsupported retained imports until their public contract is resolved.System event snapshot inspection and consume helpers remain available only
through the deprecated openclaw/plugin-sdk/infra-runtime compatibility
surface; there is no modern public replacement. Current snapshots carry an
opaque id for one queued occurrence. Preserve it through copies and
serialization when returning a snapshot to consume. Legacy ID-less callers
retain structural matching, which can be ambiguous after queue churn. Do
not treat the ID as persistent or valid across restarts.File-lock nesting is owner-scoped. Pass the same reentrantOwner only for
nested acquisitions in one logical operation; omit it for ordinary locking.
Never use a process-wide constant, because unrelated work would incorrectly
share the critical section.Bundled plugins are scanner-guarded against infra-runtime, so repo code
cannot regress to the broad barrel.Migrate channel route helpers
openclaw/plugin-sdk/channel-route. The older
route-key names remain as compatibility aliases:{ channel, to, accountId, threadId }
consistently across native approvals, reply suppression, inbound dedupe,
cron delivery, and session routing.Channel plugins use messaging.targetResolver.resolveTarget(...) for target-id normalization
and directory-miss fallback,
messaging.inferTargetChatType(...) when core needs an early peer kind,
and messaging.resolveOutboundSessionRoute(...) for provider-native
session and thread identity.Build and test
Import path reference
Use the topical SDK guides linked from SDK overview and prefer the narrowest documented typed-public subpath. Inpackage.json,
these subpaths have both types and default export targets.
The compiler inventory in scripts/lib/plugin-sdk-entrypoints.json also contains
private-local entries. Their classification is maintained in
scripts/lib/plugin-sdk-private-local-only-subpaths.json. Production-private
entries may have JavaScript-only default exports for bundled or separately
published official plugins, but their declarations are excluded from the package.
A runtime export or a source file is not a typed third-party SDK contract.
The mappings on this page are a migration subset, not the full SDK surface.
Check both the public subpath and its actual named exports before replacing an
import.
Reserved bundled-plugin helper seams have been retired from the public SDK
export map except for explicitly documented compatibility facades such as the
deprecated plugin-sdk/discord shim retained for external plugins that still
import the published @openclaw/discord package directly. Owner-specific
helpers live inside the owning plugin package; shared host behavior moves
through generic SDK contracts such as plugin-sdk/gateway-runtime,
plugin-sdk/security-runtime, and the injected plugin API.
Use the narrowest import that matches the job. If you cannot find an export,
check the source at src/plugin-sdk/ or ask maintainers which generic
contract should own it.
Retained channel facade mappings
The retained channel facades are not interchangeable withchannel-outbound.
Migrate each function and type separately.
For openclaw/plugin-sdk/channel-reply-pipeline, use these exports from
openclaw/plugin-sdk/channel-outbound:
channel-outbound does not export
ChannelReplyPipeline, CreateTypingCallbacksParams, ReplyPrefixContext,
ReplyPrefixContextBundle, ReplyPrefixOptions, or TypingCallbacks.
SourceReplyDeliveryMode is available from the typed-public
openclaw/plugin-sdk/reply-runtime subpath. Callers that still need the other
named imports must retain their compatibility type imports until an SDK owner
approves a public replacement; do not import the internal channel-reply-core
source file.
From openclaw/plugin-sdk/channel-lifecycle, these functions move unchanged to
channel-outbound: createAccountStatusSink, createChannelRunQueue,
keepHttpServerTaskAlive, runPassiveAccountLifecycle, waitUntilAbort,
createDraftStreamLoop, createFinalizableDraftLifecycle,
createFinalizableDraftStreamControlsForState, and takeMessageIdAfterStop.
Other lifecycle helpers need more than a path change:
ChannelRunQueue, ChannelRunQueueParams,
ChannelRunQueueTaskContext, DraftPreviewFinalizerDraft,
DraftPreviewFinalizerResult, DraftStreamLoop, FinalizableDraftStreamState,
ArmableStallWatchdog, and StallWatchdogTimeoutMeta are not exported by
channel-outbound. Nor does it export deliverFinalizableLivePreview,
LivePreviewFinalizerDraft, or LivePreviewFinalizerResult, despite the legacy
finalizer annotations recommending them. Keep needed compatibility type imports;
inferred factory results are not necessarily identical to caller-implemented
legacy interfaces.
For openclaw/plugin-sdk/channel-message, move outbound exports unchanged to
channel-outbound, but migrate its three dispatch aliases to
openclaw/plugin-sdk/channel-inbound:
Removed compatibility surfaces
The July 2026 sweep removed the root SDK and compat barrels, the extension API bridge, the expired SDK subpath aliases, unused SDK subpaths, and typed-public access to bundled-only SDK modules. Private-local build mappings remain for repository owners, and production-private JavaScript exports support official plugin runtimes. Neither provides typed third-party SDK access.Process-global API-provider publication
registerApiProvider(...) and unregisterApiProviders(...) were removed from
openclaw/plugin-sdk/llm. They published API transports into process-global
state, which lifecycle-owned model runtimes then had to copy into each prepared
registry.
Provider plugins should register text-inference providers through
api.registerProvider(...). Host-owned code and tests that construct an
ApiRegistry should register directly on that registry so provider ownership
and teardown stay scoped to the prepared runtime.
Deactivate hook alias
Theapi.on("deactivate", handler) compatibility alias was removed. Register
the same shutdown cleanup with gateway_stop:
Private testing barrel
openclaw/plugin-sdk/testing was repo-local and excluded from shipped package
artifacts, so it was removed before its 2026-07-28 removeAfter date. Repository
tests use focused subpaths such as plugin-sdk/plugin-test-runtime,
plugin-sdk/channel-test-helpers, plugin-sdk/channel-target-testing,
plugin-sdk/test-env, and plugin-sdk/test-fixtures.
Migration reference
These mappings cover both removed July 2026 surfaces and later-window active deprecations. A mapping is migration guidance, not evidence that the old surface remains available; consult the compatibility registry and removal timeline for current status.command-auth help builders -> command-status
command-auth help builders -> command-status
openclaw/plugin-sdk/command-auth): buildCommandsMessage,
buildCommandsMessagePaginated, buildHelpMessage.New (openclaw/plugin-sdk/command-status): same signatures, imported
from the narrower subpath. The command-auth compatibility re-exports
have been removed.Mention gating helpers -> resolveInboundMentionDecision
Mention gating helpers -> resolveInboundMentionDecision
resolveMentionGating(params) and
resolveMentionGatingWithBypass(params) from
openclaw/plugin-sdk/channel-inbound or
openclaw/plugin-sdk/channel-mention-gating.New: resolveInboundMentionDecision({ facts, policy }) - one decision
object instead of two split call shapes.Adopted across Discord, iMessage, Matrix, MS Teams, QQBot, Signal,
Telegram, WhatsApp, and Zalo. Slack’s own app_mention event model does
not use this helper.Channel runtime shim and channel actions helpers
Channel runtime shim and channel actions helpers
openclaw/plugin-sdk/channel-runtime has been removed. Use
openclaw/plugin-sdk/channel-runtime-context for registering runtime
objects.The native message schema helpers in openclaw/plugin-sdk/channel-actions
were removed alongside raw “actions” channel exports. Expose capabilities
through the semantic presentation surface instead - channel plugins
declare what they render (cards, buttons, selects) rather than which raw
action names they accept.Web search provider tool() helper -> createTool() on the plugin
Web search provider tool() helper -> createTool() on the plugin
tool() factory from openclaw/plugin-sdk/provider-web-search.New: implement createTool(...) directly on the provider plugin.
OpenClaw no longer needs the SDK helper to register the tool wrapper.Plaintext channel envelopes -> BodyForAgent
Plaintext channel envelopes -> BodyForAgent
api.runtime.channel.reply.formatInboundEnvelope(...) (and the
channelEnvelope field on inbound message objects) to build a flat
plaintext prompt envelope from inbound channel messages.New: BodyForAgent plus structured user-context blocks. Channel
plugins attach routing metadata (thread, topic, reply-to, reactions) as
typed fields instead of concatenating them into a prompt string. The
formatAgentEnvelope(...) helper is still supported for synthesized
assistant-facing envelopes, but inbound plaintext envelopes are on the way
out.Affected areas: inbound_claim, message_received, and any custom
channel plugin that post-processed the old envelope text.subagent_spawning hook -> core thread binding
subagent_spawning hook -> core thread binding
api.on("subagent_spawning", handler) returning
threadBindingReady or deliveryOrigin.New: let core prepare thread: true subagent bindings through the
channel session-binding adapter. Use api.on("subagent_spawned", handler)
only for post-launch observation.subagent_spawning hook and its event/result types were removed in
August 2026 after thread binding moved to the core session-binding path.Provider discovery types -> provider catalog types
Provider discovery types -> provider catalog types
ProviderCapabilities static bag have been
removed. Provider plugins
should use explicit provider hooks such as buildReplayPolicy,
normalizeToolSchemas, and wrapStreamFn rather than a static object.Thinking policy hooks -> resolveThinkingProfile
Thinking policy hooks -> resolveThinkingProfile
ProviderThinkingPolicy):
isBinaryThinking(ctx), supportsXHighThinking(ctx), and
resolveDefaultThinkingLevel(ctx).New: a single resolveThinkingProfile(ctx) that returns a
ProviderThinkingProfile with the canonical id, optional label, and a
ranked level list. OpenClaw downgrades stale stored values by profile rank
automatically.The context includes provider, modelId, optional merged reasoning,
and optional merged model compat facts. Provider plugins can use those
catalog facts to expose a model-specific profile only when the configured
request contract supports it.Implement one hook instead of three. The legacy hooks have been removed.External auth providers -> contracts.externalAuthProviders
External auth providers -> contracts.externalAuthProviders
contracts.externalAuthProviders in the plugin manifest
and implement resolveExternalAuthProfiles(...).Provider env-var lookup -> setup.providers[].envVars
Provider env-var lookup -> setup.providers[].envVars
providerAuthEnvVars: { anthropic: ["ANTHROPIC_API_KEY"] }.New: mirror the same env-var lookup into setup.providers[].envVars
on the manifest. This consolidates setup/status env metadata in one place
and avoids booting the plugin runtime just to answer env-var lookups.providerAuthEnvVars is no longer accepted.Memory plugin registration -> registerMemoryCapability
Memory plugin registration -> registerMemoryCapability
api.registerMemoryPromptSection(...),
api.registerMemoryFlushPlan(...), api.registerMemoryRuntime(...).New: one call on the memory-state API -
registerMemoryCapability(pluginId, { promptBuilder, flushPlanResolver, runtime }).Same slots, single registration call. Additive prompt and corpus helpers
(registerMemoryPromptSupplement, registerMemoryCorpusSupplement) are
not affected.Memory embedding provider API
Memory embedding provider API
api.registerMemoryEmbeddingProvider(...) plus
contracts.memoryEmbeddingProviders.New: api.registerEmbeddingProvider(...) plus
contracts.embeddingProviders.The generic embedding provider contract is reusable outside memory and is
the supported path for every provider. The memory-specific registration API
and manifest contract were removed after the 2026-08-21 migration
deadline.Raw channel send results -> OutboundDeliveryResult
Raw channel send results -> OutboundDeliveryResult
{ ok, messageId, error } through
ChannelSendRawResult and normalize it with
createRawChannelSendResultAdapter(...).New: return OutboundDeliveryResult fields and attach the channel with
createAttachedChannelResultAdapter(...). Failed sends should throw instead
of returning an error string. Put the platform destination in
target: { kind: "chat" | "channel" | "room" | "conversation", id };
the old parallel chatId, channelId, roomId, and conversationId
result fields are no longer accepted. The raw result type remains available
until the next plugin-SDK major release.Subagent session messages types renamed
Subagent session messages types renamed
src/plugins/runtime/types.ts:readSession is deprecated in favor of
getSessionMessages. Same signature; the old method calls through to the
new one.Removed session and transcript file APIs
Removed session and transcript file APIs
sessions.json stores, JSONL transcript paths, or lists
of session files. Runtime plugins should use session identity and SDK runtime
helpers instead of resolving or mutating active files.v2026.7.1-beta.5 imported the four
deprecated helpers above. openclaw/plugin-sdk/session-store-runtime keeps
that exact bridge through 2026-10-12; new plugins must use the replacements.
resolveStorePath(...) remains a supported SDK helper and is not part of
this deprecation.openclaw plugins inspect --all --runtime reports non-bundled plugins whose
load errors or diagnostics still reference these removed file APIs. The
@openclaw/plugin-inspector advisory sweep must use version 0.3.17 or
newer so external package scans also flag whole-store session helpers,
session file-path helpers, legacy transcript file targets, and low-level
transcript helpers before release.Agent harness attempt params -> V2 host-capability contract
Agent harness attempt params -> V2 host-capability contract
AgentHarnessV2 and use
AgentHarnessAttemptParamsV2, EmbeddedRunAttemptParamsV2, or
AgentHarnessSideQuestionParamsV2. The V2 parameter types require
hostCapabilities, matching what core supplies at the selected-harness
boundary. A plugin that adopts these V2 contracts must declare
openclaw.compat.pluginApi: ">=2026.8.1" (or a newer floor) in its package
manifest so an older host rejects the plugin before loading it.Existing plugins may continue implementing AgentHarness and constructing
the legacy AgentHarnessAttemptParams, EmbeddedRunAttemptParams, or
AgentHarnessSideQuestionParams types without that field through
2026-10-12. Those contracts keep the capability optional only for source
compatibility; they do not create a capability-free runtime path. Migrate
by changing the imported type name and binding tool or native-action surfaces through
params.hostCapabilities.runtime.tasks.flow -> runtime.tasks.managedFlows
runtime.tasks.flow -> runtime.tasks.managedFlows
runtime.tasks.flow (singular) returned a live task-flow
accessor.New: runtime.tasks.managedFlows keeps the managed TaskFlow mutation
runtime for plugins that create, update, cancel, or run child tasks from a
flow. Use runtime.tasks.flows when the plugin only needs DTO-based
reads.Embedded extension factories -> agent tool-result middleware
Embedded extension factories -> agent tool-result middleware
api.registerEmbeddedExtensionFactory(...) path is replaced by
api.registerAgentToolResultMiddleware(...) with an explicit runtime list
in contracts.agentToolResultMiddleware.OpenClawSchemaType alias -> OpenClawConfig
OpenClawSchemaType alias -> OpenClawConfig
OpenClawSchemaType root-SDK alias was removed. Use the canonical
OpenClawConfig name.extensions/) are tracked inside their own api.ts and runtime-api.ts
barrels. They do not affect third-party plugin contracts and are not listed
here. If you consume a bundled plugin’s local barrel directly, read the
deprecation comments in that barrel before upgrading.Talk and realtime voice migration
Realtime voice, telephony, meeting, and browser Talk code shares one Talk session controller exported byopenclaw/plugin-sdk/realtime-voice. The
controller owns the common Talk event envelope, active turn state, capture
state, output-audio state, recent event history, and stale-turn rejection.
Provider plugins own vendor-specific realtime sessions. Browser-meeting plugins
use openclaw/plugin-sdk/meeting-runtime for session, browser, audio, node-host,
agent-consult, and voice-call mechanics, then implement MeetingPlatformAdapter
for URL rules, DOM scripts, manual-action mapping, captions, creation, and dial-in
plans. Platform REST APIs, OAuth, artifacts, selectors, and wire names remain in
the plugin. Browser permission plans receive the requested meeting URL so each
platform can grant only its exact supported origins. Session runtimes must also
normalize platform-specific live health after confirmed browser departure;
historical transcript fields may remain, but caption and audio readiness must
not stay active after leave.
All bundled surfaces run on the shared controller: browser relay,
managed-room handoff, voice-call realtime, voice-call streaming STT, Google
Meet realtime, and native push-to-talk. Gateway advertises one live Talk event
channel in hello-ok.features.events: talk.event.
New code should not call createTalkEventSequencer(...) directly unless
implementing a low-level adapter or test fixture. Use the shared controller so
turn-scoped events cannot be emitted without a turn id, stale turnEnd /
turnCancel calls cannot clear a newer active turn, and output-audio
lifecycle events stay consistent across telephony, meetings, browser relay,
managed-room handoff, and native Talk clients.
The public API shape:
talk.client.create,
because the browser owns provider negotiation and media transport while the
Gateway owns credentials, instructions, and tool policy. talk.session.* is
the common Gateway-managed surface for gateway-relay realtime, gateway-relay
transcription, and managed-room native STT/TTS sessions.
Legacy configs that place realtime selectors beside talk.provider /
talk.providers should be repaired with openclaw doctor --fix; runtime Talk
does not reinterpret speech/TTS provider config as realtime provider config.
The supported talk.session.create combinations are intentionally small:
talk.realtime.* /
talk.transcription.* / talk.handoff.* families (all removed):
Removal timeline
agent-config-primitives,
channel-logging, channel-secret-runtime, channel-streaming,
group-access, matrix, text-runtime, and zod were retired early by
explicit SDK-owner approval in August 2026. Use the focused replacements in
the Plugin SDK subpath catalog, and import zod
directly from the zod package. inbound-reply-dispatch remains available
until the next Plugin SDK major.
deprecated.
For 2026.9.1, the release maintainer approved renewing their removeAfter date
from 2026-09-01 to 2026-10-01 on September 2, 2026. The registry keeps them
removal-pending with the same replacement mappings. Removal awaits verification
that supported external plugins have migrated. infra-runtime additionally retains
system-event snapshot inspection and consumption until a modern public replacement
exists. This changes compatibility tracking only, not the exported SDK or runtime
behavior.
Bundled-plugin migration does not prove that every external caller can use a
path-only replacement. Migrate the functions with verified typed-public mappings;
keep retained imports where a named type or required behavior still lacks a
public replacement and ask the SDK owner to resolve that gap. Run
pnpm plugins:boundary-report to see the dates, gates, and blockers for the
surfaces your plugin uses.
Related
- Getting Started - build your first plugin
- SDK Overview - full subpath import reference
- Channel Plugins - building channel plugins
- Provider Plugins - building provider plugins
- Plugin Internals - architecture deep dive
- Plugin Manifest - manifest schema reference