openclaw/docs/plugins/sdk-migration.md
Peter Steinberger 82a4cfbe57
feat(plugin-sdk): awaited session persistence; deprecate sync transcript writes (#163264)
* feat(plugin-sdk): await session persistence and deprecate sync writes

* test(sessions): fix awaited persistence fixture types

* fix(sessions): preserve binding and delivery publication

* test(sessions): isolate compaction retarget authority

Model the retarget as an independent operation so the fixture reaches post-commit publication validation. Keep the committed receipt, accounting, and replacement-transcript assertions intact.

* fix(ci): remove duplicate database-worker test routing

Main already routes attempt-phase-lifecycle.test.ts through the database-worker owner. Remove the duplicate introduced by the SDK branch. Exact-head preflight and the native local manifest both reproduced the failure; the corrected manifest passes with 69 selected Node rows. Unique test coverage and the duplicate guard stay intact. Formatting and P2 review pass.

* fix(sessions): keep maintenance projections with the host owner

Return committed projection-rebuild facts from maintenance workers and schedule them through the existing host owner. Preserve custody, rollback, and synchronous compatibility. Update async fixtures and host-broker test routing, restore the native wrapper import inventory, and route memory visibility declarations through their existing producer.

Focused Linux proof passed342 tests across17 suites; old-code controls fail at pending projections. SDK declarations retain legacy signatures with four additive exports. Types, lint, T1, Madge, focused routing checks, and P2 review pass.

* test(cli): await blocked-run hook entry without polling

Await the existing hook-entry gate instead of racing awaited transcript persistence against vi.waitFor's one-second default. Preserve the early-settlement failure and the assertion that agent_end must finish before the CLI run settles.

The two focused cases pass in 155.98s including preparation; their test bodies take 3.656s and 1.804s. Fresh P2 review is clean.

* test(cli): use the deferred helper default type

* test(gateway): keep worktree fixture on the shared state root

Use the nested fixture only for workspace and device files. Activating its environment switches process state roots underneath the shared Gateway, whose projection retains its startup environment. Preserve the registry witness guard and every cwd, transcript, initial-run, and follow-up assertion.

CI observed AgentDatabaseRegistryChangedError during creation. The exact callback interleaving was not captured, and unmodified main passed the whole file in 172.229s; that is non-reproduction, not inherited-failure qualification. The corrected fixture passes all 10 cases in original order in 164.077s. Semantic lint, formatting, and fresh P2 review pass.

* refactor(sessions): separate hydration types and CLI hook fixtures

Keep transcript hydration results beside their request contracts and retain aggregate exports. Move the CLI hook fixture owner into test support without changing coverage. This removes both line-cap increases after main integration; 112 composition tests and all 52 reliability tests pass.
2026-10-02 07:03:11 -07:00

15 KiB

summary title sidebarTitle read_when
Migrate from the legacy backwards-compatibility layer to the modern plugin SDK Plugin SDK migration Migrate to SDK
You used api.registerEmbeddedExtensionFactory before OpenClaw 2026.4.25
You are updating a plugin to the modern plugin architecture
You maintain an external OpenClaw plugin

OpenClaw replaced a broad backwards-compatibility layer with a modern plugin architecture built from small, focused imports. If your plugin predates that change, this guide gets it onto the current contracts.

What changed

Several wide-open import surfaces used to let plugins reach almost anything from a single entry point:

  • openclaw/plugin-sdk and openclaw/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 removed broad config barrel, including its deprecated direct loadConfig and writeConfigFile exports.
  • openclaw/plugin-sdk/channel-lifecycle, channel-message, and channel-reply-pipeline - removed channel compatibility facades. Use the focused outbound and inbound contracts, checking each named export.
  • 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 as tool_result. Use agent tool-result middleware instead (see Migrate embedded tool-result extensions to middleware).

The root SDK, compat barrel, extension bridge, embedded extension factory, and five channel/config/infrastructure compatibility facades have been removed. The latter retirement received explicit SDK-owner approval on September 30, 2026; see the removal timeline.

Plugins importing the removed SDK or extension surfaces no longer load. Follow the [import path mappings](/plugins/sdk-migration/import-paths) before upgrading.

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".

Bundled ACP integrations should await readAcpSessionEntryAsync from the local openclaw/plugin-sdk/acp-runtime facade for session metadata reads. This facade remains a JavaScript compatibility export; its declarations are excluded from the typed public SDK. File-backed reads run on the SQLite workers and preserve the session lifecycle throughout the metadata join. File-backed results are detached snapshots, including when clone: false is supplied. The returned storeReadFailed flag still distinguishes an unreadable session store from missing metadata; startup cleanup must keep bindings when that flag is set. Unbound legacy ACP metadata can still accompany that flag; it does not certify the session row. Source retirement and worker failures reject the read and must not be treated as an absent session.

The synchronous readAcpSessionEntry and getAcpSessionManager().resolveSession() contracts shipped in v2026.9.4 remain available for existing consumers of that compatibility export. They are deprecated for runtime use. Ordinary ACP manager and Gateway callers use readAcpSessionEntryAsync and getAcpSessionManager().resolveSessionAsync(). Discord and Telegram startup binding cleanup retain their existing synchronous reader until conditional deletion can validate metadata at the mutation owner. That cleanup migration remains unfinished. Removing the synchronous contracts requires a separately announced breaking SDK release. Incognito reads retain their existing native in-memory owner until that owner's worker migration.

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.

The typed public SDK is organized into focused subpaths with documented contracts. Not every SDK build entrypoint is a public plugin API.

Legacy provider convenience seams for bundled channels are gone too - channel-branded helper shortcuts were private mono-repo conveniences, not stable plugin contracts. Use narrow generic SDK subpaths instead. Inside the bundled plugin workspace, keep provider-owned helpers in that plugin's own api.ts or runtime-api.ts:

  • Anthropic keeps Claude-specific stream helpers in its own api.ts / contract-api.ts seam.
  • 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.

Where each topic lives

Every section of the single-page version lives on one of the six pages below. The anchors from the single-page version still resolve here.

Migration steps

How to migrate a plugin — the ordered migration steps.

Import paths

Import path reference — which typed-public subpath replaces each legacy import.

Removed surfaces and replacements

Removed surfaces and replacements — what was removed, and the replacement for each legacy API.

Talk and voice

Talk and realtime voice migration — the unified Talk session API and its method map.

Compatibility records

Compatibility policy and records — what is retained, why, and on what condition it can be removed.

Timeline

Removal timeline — when deprecated surfaces become eligible for removal.

For the removed Tasks and TaskFlow surfaces, see Tasks and TaskFlow API removal.