Skip to main content

Plugin SDK Migration

Velaclaw has moved from a broad backwards-compatibility layer to a modern plugin architecture with focused, documented imports. If your plugin was built before the new architecture, this guide helps you migrate.

What is changing

The old plugin system provided two wide-open surfaces that let plugins import anything they needed from a single entry point:
  • velaclaw/plugin-sdk/compat — a single import that re-exported dozens of helpers. It was introduced to keep older hook-based plugins working while the new plugin architecture was being built.
  • velaclaw/extension-api — a bridge that gave plugins direct access to host-side helpers like the embedded agent runner.
Both surfaces are now deprecated. They still work at runtime, but new plugins must not use them, and existing plugins should migrate before the next major release removes them.
The backwards-compatibility layer will be removed in a future major release. Plugins that still import from these surfaces will break when that happens.

Why this changed

The old approach caused problems:
  • Slow startup — importing one helper loaded dozens of unrelated modules
  • Circular dependencies — broad re-exports made it easy to create import cycles
  • Unclear API surface — no way to tell which exports were stable vs internal
The modern plugin SDK fixes this: each import path (velaclaw/plugin-sdk/\<subpath\>) is a small, self-contained module with a clear purpose and documented contract. Legacy provider convenience seams for bundled channels are also gone. Imports such as velaclaw/plugin-sdk/slack, velaclaw/plugin-sdk/discord, velaclaw/plugin-sdk/signal, velaclaw/plugin-sdk/whatsapp, channel-branded helper seams, and velaclaw/plugin-sdk/telegram-core were private mono-repo shortcuts, 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. Current bundled provider examples:
  • 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

How to migrate

1

Migrate approval-native handlers to capability facts

Approval-capable channel plugins now expose native approval behavior through approvalCapability.nativeRuntime plus the shared runtime-context registry.Key changes:
  • Replace approvalCapability.handler.loadRuntime(...) with approvalCapability.nativeRuntime
  • Move approval-specific auth/delivery off legacy plugin.auth / plugin.approvals wiring and onto approvalCapability
  • ChannelPlugin.approvals has been removed from the public channel-plugin contract; move delivery/native/render fields onto approvalCapability
  • plugin.auth remains for channel login/logout flows only; approval auth hooks there are no longer read by core
  • Register channel-owned runtime objects such as clients, tokens, or Bolt apps through velaclaw/plugin-sdk/channel-runtime-context
  • Do not send plugin-owned reroute notices from native approval handlers; core now owns routed-elsewhere notices from actual delivery results
  • When passing channelRuntime into createChannelManager(...), provide a real createPluginRuntime().channel surface. Partial stubs are rejected.
See /plugins/sdk-channel-plugins for the current approval capability layout.
2

Audit Windows wrapper fallback behavior

If your plugin uses velaclaw/plugin-sdk/windows-spawn, unresolved Windows .cmd/.bat wrappers now fail closed unless you explicitly pass allowShellFallback: true.
If your caller does not intentionally rely on shell fallback, do not set allowShellFallback and handle the thrown error instead.
3

Find deprecated imports

Search your plugin for imports from either deprecated surface:
4

Replace with focused imports

Each export from the old surface maps to a specific modern import path:
For host-side helpers, use the injected plugin runtime instead of importing directly:
The same pattern applies to other legacy bridge helpers:
5

Build and test

Import path reference

This table is intentionally the common migration subset, not the full SDK surface. The full list of 200+ entrypoints lives in scripts/lib/plugin-sdk-entrypoints.json. That list still includes some bundled-plugin helper seams such as plugin-sdk/feishu, plugin-sdk/feishu-setup, plugin-sdk/zalo, plugin-sdk/zalo-setup, and plugin-sdk/matrix*. Those remain exported for bundled-plugin maintenance and compatibility, but they are intentionally omitted from the common migration table and are not the recommended target for new plugin code. The same rule applies to other bundled-helper families such as:
  • browser support helpers: plugin-sdk/browser-cdp, plugin-sdk/browser-config-runtime, plugin-sdk/browser-config-support, plugin-sdk/browser-control-auth, plugin-sdk/browser-node-runtime, plugin-sdk/browser-profiles, plugin-sdk/browser-security-runtime, plugin-sdk/browser-setup-tools, plugin-sdk/browser-support
  • Matrix: plugin-sdk/matrix*
  • LINE: plugin-sdk/line*
  • IRC: plugin-sdk/irc*
  • bundled helper/plugin surfaces like plugin-sdk/googlechat, plugin-sdk/zalouser, plugin-sdk/bluebubbles*, plugin-sdk/mattermost*, plugin-sdk/msteams, plugin-sdk/nextcloud-talk, plugin-sdk/nostr, plugin-sdk/tlon, plugin-sdk/twitch, plugin-sdk/github-copilot-login, plugin-sdk/github-copilot-token, plugin-sdk/diagnostics-otel, plugin-sdk/diffs, plugin-sdk/llm-task, plugin-sdk/thread-ownership, and plugin-sdk/voice-call
plugin-sdk/github-copilot-token currently exposes the narrow token-helper surface DEFAULT_COPILOT_API_BASE_URL, deriveCopilotApiBaseUrlFromToken, and resolveCopilotApiToken. Use the narrowest import that matches the job. If you cannot find an export, check the source at src/plugin-sdk/ or ask in Discord.

Removal timeline

All core plugins have already been migrated. External plugins should migrate before the next major release.

Suppressing the warnings temporarily

Set these environment variables while you work on migrating:
This is a temporary escape hatch, not a permanent solution.