Apus 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:

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 before upgrading.

Apus 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 apus/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. Their inactive incognito composition accepts a prepared session reader whose current-source assertion reaches the binding transaction and commit grants. The preparation retains the original actor revision and ACP metadata publication fence until cleanup releases it. The local prepareAcpSessionEntryRead helper captures an existing private actor binding and supplies that retained reader; it returns undefined without a binding. Discord and Telegram startup cleanup use it by default. Ordinary production acquisition stays native until atomic incognito activation; guarded durable cleanup remains separate work. IncognitoSessionEndedError and IncognitoSessionSyncAccessError, exposed through the local apus/plugin-sdk/acp-runtime facade, must propagate through cleanup and status probes. Use rethrowIncognitoSessionError(error) before treating other failures as an absent session or an uncertain probe; it also recognizes nested refusals. 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.

Session reset freshness

Channel runtime consumers should await runtime.channel.session.resolveEntryResetFreshnessAsync(...) when deciding whether a session needs reset. File-backed entries and transcript lifecycle timestamps are read from the same worker snapshot, with the original database owner retained through validation and cleanup. The synchronous resolveEntryResetFreshness(...) method shipped in v2026.9.8 remains available for existing JavaScript consumers and is deprecated for runtime use; removal requires the next Plugin SDK major and explicit breaking-release approval. Both methods retain the existing reset policy and process-held incognito behavior.

Why

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:

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.

Related

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