Plugin SDK reference
Plugin SDK overview
The plugin SDK is the typed contract between plugins and core. This page is the reference for what to import and what you can register.
Import convention
Always import from a specific subpath:
Each subpath is a small, self-contained module. This keeps startup fast and
prevents circular dependency issues. For channel-specific entry/build helpers,
prefer openclaw/plugin-sdk/channel-core; keep openclaw/plugin-sdk/core for
the broader umbrella surface and shared helpers such as
buildChannelConfigSchema.
For channel config, publish the channel-owned JSON Schema through
openclaw.plugin.json#channelConfigs. The plugin-sdk/channel-config-schema
subpath is for shared schema primitives and the generic builder. OpenClaw's
bundled plugins use plugin-sdk/bundled-channel-config-schema for retained
bundled-channel schemas. Deprecated compatibility exports remain on
plugin-sdk/channel-config-schema-legacy; neither bundled schema subpath is a
pattern for new plugins.
Subpath reference
The plugin SDK is exposed as a set of narrow subpaths grouped by area (plugin entry, channel, provider, auth, runtime, capability, memory, and reserved bundled-plugin helpers). For the full catalog — grouped and linked — see Plugin SDK subpaths.
The compiler entrypoint inventory lives in
scripts/lib/plugin-sdk-entrypoints.json; package exports are generated from
the public subset after subtracting repo-local test/internal subpaths listed in
scripts/lib/plugin-sdk-private-local-only-subpaths.json. Run
pnpm plugin-sdk:surface to audit the public export count. Deprecated public
subpaths that are old enough and unused by bundled extension production code are
tracked in scripts/lib/plugin-sdk-deprecated-public-subpaths.json; broad
deprecated re-export barrels are tracked in
scripts/lib/plugin-sdk-deprecated-barrel-subpaths.json.
Registration API
The register(api) callback receives an OpenClawPluginApi object with these
methods:
Capability registration
| Method | What it registers |
|---|---|
api.registerProvider(...) |
Text inference (LLM) |
api.registerWorkerProvider(...) |
Cloud-worker lifecycle leases |
api.registerModelCatalogProvider(...) |
Model catalog rows for text and media generation |
api.registerAgentHarness(...) |
Experimental native agent executor (Codex, Copilot) |
api.registerCliBackend(...) |
Local CLI inference backend |
api.registerChannel(...) |
Messaging channel |
api.registerEmbeddingProvider(...) |
Reusable vector embedding provider |
api.registerSpeechProvider(...) |
Text-to-speech / STT synthesis |
api.registerRealtimeTranscriptionProvider(...) |
Streaming realtime transcription |
api.registerRealtimeVoiceProvider(...) |
Duplex realtime voice sessions |
api.registerMediaUnderstandingProvider(...) |
Image/audio/video analysis |
api.registerTranscriptSourceProvider(...) |
Live or imported meeting transcript source |
api.registerImageGenerationProvider(...) |
Image generation |
api.registerMusicGenerationProvider(...) |
Music generation |
api.registerVideoGenerationProvider(...) |
Video generation |
api.registerWebFetchProvider(...) |
Web fetch / scrape provider |
api.registerWebSearchProvider(...) |
Web search |
api.registerCompactionProvider(...) |
Pluggable transcript-compaction backend |
Worker providers must also declare their id in contracts.workerProviders.
Core persists durable intent before provision(profile, operationId). Providers validate settings before external allocation and throw WorkerProviderError for permanent profile rejection. provision must adopt the same lease when the operation id repeats.
Core persists the validated profile settings with the lease and supplies that snapshot to destroy({ leaseId, profile }), which must be idempotent, and inspect({ leaseId, profile }), which returns active, destroyed, or unknown. This lets providers route lifecycle calls after a gateway restart or named-profile removal. SSH endpoints use a SecretRef for keyRef, never inline key material, and include a hostKey from trusted provisioning output as exactly algorithm base64, without a hostname or comment. Core pins hostKey and never trusts a key from the first connection. A provider that mints a dynamic keyRef can implement resolveSshIdentity({ leaseId, profile, keyRef }); when present, that resolver is authoritative, while providers without it use the configured generic secret resolver.
Providers with renewable leases can also implement renew(leaseId).
inspect must throw on transient or indeterminate failures; return unknown only for authoritative absence. Core marks an active local record orphaned, or treats the absence as teardown completion after a persisted destroy request.
Embedding providers registered with api.registerEmbeddingProvider(...) must
also be listed in contracts.embeddingProviders in the plugin manifest. This
is the generic embedding surface for reusable vector generation. Memory search
can consume this generic provider surface. The older
api.registerMemoryEmbeddingProvider(...) and
contracts.memoryEmbeddingProviders seam is deprecated compatibility while
existing memory-specific providers migrate.
Memory-specific providers that still expose a runtime batchEmbed(...) stay on
the existing per-file batching contract unless their runtime explicitly sets
sourceWideBatchEmbed: true. That opt-in lets the memory host submit chunks from
multiple dirty memory files and enabled sources in one batchEmbed(...) call up
to the host batch limits. Batch adapters that upload JSONL request files must
split provider jobs before their upload-size cap as well as their request-count
cap. The provider must return one embedding per input chunk in the same order as
batch.chunks; omit the flag when the provider expects file-local batches or
cannot preserve input ordering across a larger source-wide job.
Tools and commands
Use defineToolPlugin for simple tool-only plugins
with fixed tool names. Use api.registerTool(...) directly for mixed plugins
or fully dynamic tool registration.
| Method | What it registers |
|---|---|
api.registerTool(tool, opts?) |
Agent tool (required or { optional: true }) |
api.registerCommand(def) |
Custom command (bypasses the LLM) |
api.registerNodeHostCommand(command) |
Command handled by openclaw node run; optional agentTool metadata can expose it as an agent-visible tool while the node is connected |
Plugin commands can set agentPromptGuidance when the agent needs a short,
command-owned routing hint. Keep that text about the command itself; do not add
provider- or plugin-specific policy to core prompt builders.
Guidance entries may be legacy strings, which apply to every prompt surface, or structured entries:
agentPromptGuidance: [ "Global command hint.", { text: "Only show this in the main OpenClaw prompt.", surfaces: ["openclaw_main"] },];Structured surfaces may include openclaw_main, codex_app_server,
cli_backend, acp_backend, or subagent. pi_main remains a deprecated alias
for openclaw_main. Omit surfaces for intentional all-surface guidance. Do
not pass an empty surfaces array; it is rejected so accidental scope loss does
not become global prompt text.
Native Codex app-server developer instructions are stricter than other prompt
surfaces: only guidance explicitly scoped to codex_app_server is promoted into
that higher-priority lane. Legacy string guidance and unscoped structured
guidance remain available to non-Codex prompt surfaces for compatibility.
Node-host commands run on the connected node host, not inside the Gateway
process. If agentTool is present, the node publishes a descriptor after a
successful Gateway connect; the Gateway exposes it to agent runs only while that
node is connected and only if the descriptor's command is in the node's
approved command surface. Set agentTool.defaultPlatforms to opt a
non-dangerous command into the default node command allowlist; otherwise require
explicit gateway.nodes.allowCommands or a node-invoke policy. agentTool.name
must be provider-safe: start with a letter, use only letters, digits,
underscores, or hyphens, and stay within 64 characters. MCP-backed node tools
can set agentTool.mcp metadata so catalog and tool-search surfaces can show
the remote MCP server/tool identity, but execution still goes through the
advertised node command.
Infrastructure
| Method | What it registers |
|---|---|
api.registerHook(events, handler, opts?) |
Event hook |
api.registerHttpRoute(params) |
Gateway HTTP endpoint |
api.registerGatewayMethod(name, handler) |
Gateway RPC method |
api.registerGatewayDiscoveryService(service) |
Local Gateway discovery advertiser |
api.registerCli(registrar, opts?) |
CLI subcommand |
api.registerNodeCliFeature(registrar, opts?) |
Node feature CLI under openclaw nodes |
api.registerService(service) |
Background service |
api.registerInteractiveHandler(registration) |
Interactive handler |
api.registerAgentToolResultMiddleware(...) |
Runtime tool-result middleware |
api.registerMemoryPromptSupplement(builder) |
Additive memory-adjacent prompt section |
api.registerMemoryCorpusSupplement(adapter) |
Additive memory search/read corpus |
api.registerHostedMediaResolver(resolver) |
Resolver for browser-style hosted media URLs |
api.registerTextTransforms(transforms) |
Plugin-owned prompt/message compatibility text rewrites |
api.registerConfigMigration(migrate) |
Lightweight config migration run before plugin runtime loads |
api.registerMigrationProvider(provider) |
Importer for openclaw migrate |
api.registerAutoEnableProbe(probe) |
Config probe that can auto-enable this plugin |
api.registerReload(registration) |
Restart/hot/noop config-prefix policy for reload handling |
api.registerNodeHostCommand(command) |
Command handler exposed to paired nodes |
api.registerNodeInvokePolicy(policy) |
Allowlist/approval policy for node-invoked commands |
api.registerSecurityAuditCollector(collector) |
Findings collector for openclaw security audit |
Memory prompt supplement builders receive optional agentId,
agentSessionKey, and sandboxed context. Memory corpus supplement search
and get calls receive optional agentId and sandboxed context. Plugins with
agent-owned storage should resolve that storage for each call instead of
capturing one global path during registration. If an agent id is required but
missing in a multi-agent operation, fail closed rather than choosing an
arbitrary agent.
Telegram interactive handlers can return { submitText } to route text through
Telegram's normal inbound agent path after the handler succeeds. OpenClaw keeps
the callback button when inbound policy skips the text or processing fails, so
the user can retry after the blocking condition changes. This result field is
Telegram-specific; other channels keep their own interactive result contracts.
Host hooks for workflow plugins
Host hooks are the SDK seams for plugins that need to participate in the host lifecycle rather than only adding a provider, channel, or tool. They are generic contracts; Plan Mode can use them, but so can approval workflows, workspace policy gates, background monitors, setup wizards, and UI companion plugins.
| Method | Contract it owns |
|---|---|
api.session.state.registerSessionExtension(...) |
Plugin-owned, JSON-compatible session state projected through Gateway sessions |
api.session.workflow.enqueueNextTurnInjection(...) |
Durable exactly-once context injected into the next agent turn for one session |
api.registerTrustedToolPolicy(...) |
Manifest-gated trusted pre-plugin tool policy that can block or rewrite tool params |
api.registerToolMetadata(...) |
Tool catalog display metadata without changing the tool implementation |
api.registerCommand(...) |
Scoped plugin commands; command results can set continueAgent: true or suppressReply: true; Discord native commands support descriptionLocalizations |
api.session.controls.registerControlUiDescriptor(...) |
Control UI contribution descriptors for session, tool, run, settings, or tab surfaces |
api.lifecycle.registerRuntimeLifecycle(...) |
Cleanup callbacks for plugin-owned runtime resources on reset/delete/reload paths |
api.agent.events.registerAgentEventSubscription(...) |
Sanitized event subscriptions for workflow state and monitors |
api.runContext.setRunContext(...) / getRunContext(...) / clearRunContext(...) |
Per-run plugin scratch state cleared on terminal run lifecycle |
api.session.workflow.registerSessionSchedulerJob(...) |
Cleanup metadata for plugin-owned scheduler jobs; does not schedule work or create task records |
api.session.workflow.sendSessionAttachment(...) |
Bundled-only host-mediated file attachment delivery to the active direct-outbound session route |
api.session.workflow.scheduleSessionTurn(...) / unscheduleSessionTurnsByTag(...) |
Bundled-only Cron-backed scheduled session turns plus tag-based cleanup |
api.session.controls.registerSessionAction(...) |
Typed session actions clients can dispatch through the Gateway |
A surface: "tab" descriptor adds a sidebar tab to the Control UI. Active
plugins' tab descriptors are advertised to dashboard clients in the gateway
hello (controlUiTabs), so the tab appears only while the plugin is enabled.
Bundled plugins may ship a first-class dashboard view for their tab; other
plugins can set path to a plugin HTTP route (see
api.registerHttpRoute(...)) that the dashboard renders in a sandboxed frame.
icon is a dashboard icon name hint, group picks the sidebar section
(control or agent), order sorts among plugin tabs, and requiredScopes
hides the tab from connections lacking those operator scopes:
api.session.controls.registerControlUiDescriptor({ surface: "tab", id: "logbook", label: "Logbook", description: "Your day as a timeline, built from screen snapshots.", icon: "sun", group: "control", requiredScopes: ["operator.write"],});Use the grouped namespaces for new plugin code:
api.session.state.registerSessionExtension(...)api.session.workflow.enqueueNextTurnInjection(...)api.session.workflow.registerSessionSchedulerJob(...)api.session.workflow.sendSessionAttachment(...)api.session.workflow.scheduleSessionTurn(...)api.session.workflow.unscheduleSessionTurnsByTag(...)api.session.controls.registerSessionAction(...)api.session.controls.registerControlUiDescriptor(...)api.agent.events.registerAgentEventSubscription(...)api.agent.events.emitAgentEvent(...)api.runContext.setRunContext(...)/getRunContext(...)/clearRunContext(...)api.lifecycle.registerRuntimeLifecycle(...)
The equivalent flat methods remain available as deprecated compatibility
aliases for existing plugins. Do not add new plugin code that calls
api.registerSessionExtension, api.enqueueNextTurnInjection,
api.registerControlUiDescriptor, api.registerRuntimeLifecycle,
api.registerAgentEventSubscription, api.emitAgentEvent,
api.setRunContext, api.getRunContext, api.clearRunContext,
api.registerSessionSchedulerJob, api.registerSessionAction,
api.sendSessionAttachment, api.scheduleSessionTurn, or
api.unscheduleSessionTurnsByTag directly.
scheduleSessionTurn(...) is a session-scoped convenience over the Gateway
Cron scheduler. Cron owns timing and creates the background task record when the
turn runs; the Plugin SDK only constrains the target session, plugin-owned
naming, and cleanup. Use api.runtime.tasks.managedFlows inside the scheduled
turn when the work itself needs durable multi-step Task Flow state.
The contracts intentionally split authority:
- External plugins can own session extensions, UI descriptors, commands, tool metadata, next-turn injections, and normal hooks.
- Trusted tool policies run before ordinary
before_tool_callhooks and are host-trusted. Bundled policies run first; installed-plugin policies require explicit enablement plus their local ids incontracts.trustedToolPolicies, and run next in plugin-load order. Policy ids are scoped to the registering plugin. - Reserved command ownership is bundled-only. External plugins should use their own command names or aliases.
allowPromptInjection=falsedisables prompt-mutating hooks includingagent_turn_prepare,before_prompt_build,heartbeat_prompt_contribution, prompt fields from legacybefore_agent_start, andenqueueNextTurnInjection.
Examples of non-Plan consumers:
| Plugin archetype | Hooks used |
|---|---|
| Approval workflow | Session extension, command continuation, next-turn injection, UI descriptor |
| Budget/workspace policy gate | Trusted tool policy, tool metadata, session projection |
| Background lifecycle monitor | Runtime lifecycle cleanup, agent event subscription, session scheduler ownership/cleanup, heartbeat prompt contribution, UI descriptor |
| Setup or onboarding wizard | Session extension, scoped commands, Control UI descriptor |
When to use tool-result middleware
Bundled plugins and explicitly enabled installed plugins with matching
manifest contracts can use api.registerAgentToolResultMiddleware(...) when
they need to rewrite a tool result after execution and before the runtime
feeds that result back into the model. This is the trusted runtime-neutral
seam for async output reducers such as tokenjuice.
Plugins must declare contracts.agentToolResultMiddleware for each targeted
runtime, for example ["openclaw", "codex"]. Installed plugins without that
contract, or without explicit enablement, cannot register this middleware; keep
normal OpenClaw plugin hooks for work that does not need pre-model tool-result
timing. The old
embedded-runner-only extension factory registration path has been removed.
Gateway discovery registration
api.registerGatewayDiscoveryService(...) lets a plugin advertise the active
Gateway on a local discovery transport such as mDNS/Bonjour. OpenClaw calls the
service during Gateway startup when local discovery is enabled, passes the
current Gateway ports and non-secret TXT hint data, and calls the returned
stop handler during Gateway shutdown.
api.registerGatewayDiscoveryService({ id: "my-discovery", async advertise(ctx) { const handle = await startMyAdvertiser({ gatewayPort: ctx.gatewayPort, tls: ctx.gatewayTlsEnabled, displayName: ctx.machineDisplayName, }); return { stop: () => handle.stop() }; },});Gateway discovery plugins must not treat advertised TXT values as secrets or authentication. Discovery is a routing hint; Gateway auth and TLS pinning still own trust.
CLI registration metadata
api.registerCli(registrar, opts?) accepts two kinds of command metadata:
commands: explicit command names owned by the registrardescriptors: parse-time command descriptors used for CLI help, routing, and lazy plugin CLI registrationparentPath: optional parent command path for nested command groups, such as["nodes"]
For paired-node features, prefer
api.registerNodeCliFeature(registrar, opts?). It is a small wrapper around
api.registerCli(..., { parentPath: ["nodes"] }) and makes commands such as
openclaw nodes canvas explicit plugin-owned node features.
If you want a plugin command to stay lazy-loaded in the normal root CLI path,
provide descriptors that cover every top-level command root exposed by that
registrar.
api.registerCli( async ({ program }) => { const { registerMatrixCli } = await import("./src/cli.js"); registerMatrixCli({ program }); }, { descriptors: [ { name: "matrix", description: "Manage Matrix accounts, verification, devices, and profile state", hasSubcommands: true, }, ], },);Nested commands receive the resolved parent command as program:
api.registerCli( async ({ program }) => { const { registerNodesCanvasCommands } = await import("./src/cli.js"); registerNodesCanvasCommands(program); }, { parentPath: ["nodes"], descriptors: [ { name: "canvas", description: "Capture or render canvas content from a paired node", hasSubcommands: true, }, ], },);Use commands by itself only when you do not need lazy root CLI registration.
That eager compatibility path remains supported, but it does not install
descriptor-backed placeholders for parse-time lazy loading.
CLI backend registration
api.registerCliBackend(...) lets a plugin own the default config for a local
AI CLI backend such as claude-cli or my-cli.
- The backend
idbecomes the provider prefix in model refs likemy-cli/gpt-5. - The backend
configuses the same shape asagents.defaults.cliBackends.<id>. - User config still wins. OpenClaw merges
agents.defaults.cliBackends.<id>over the plugin default before running the CLI. - Use
normalizeConfigwhen a backend needs compatibility rewrites after merge (for example normalizing old flag shapes). - Use
resolveExecutionArgsfor request-scoped argv rewrites that belong to the CLI dialect, such as mapping OpenClaw thinking levels to a native effort flag. The hook receivesctx.executionMode; use"side-question"to add backend-native isolation flags for ephemeral/btwcalls. If those flags reliably disable native tools for an otherwise always-on CLI, declaresideQuestionToolMode: "disabled"too. - Backends that can disable all native tools for a specific run may declare
nativeToolMode: "selectable". Restricted calls pass an emptyctx.toolAvailability.nativetuple plus an exact host-isolated MCP allowlist;resolveExecutionArgsmust enforce both on the final fresh or resume argv. OpenClaw fails closed if the backend cannot do so.
For an end-to-end authoring guide, see CLI backend plugins.
Exclusive slots
| Method | What it registers |
|---|---|
api.registerContextEngine(id, factory) |
Context engine (one active at a time). Lifecycle callbacks receive runtimeSettings when the host can provide model/provider/mode diagnostics; older strict engines are retried without that key. |
api.registerMemoryCapability(capability) |
Unified memory capability |
api.registerMemoryPromptSection(builder) |
Memory prompt section builder |
api.registerMemoryFlushPlan(resolver) |
Memory flush plan resolver |
api.registerMemoryRuntime(runtime) |
Memory runtime adapter |
Deprecated memory embedding adapters
| Method | What it registers |
|---|---|
api.registerMemoryEmbeddingProvider(adapter) |
Memory embedding adapter for the active plugin |
registerMemoryCapabilityis the preferred exclusive memory-plugin API.registerMemoryCapabilitymay also exposepublicArtifacts.listArtifacts(...)so companion plugins can consume exported memory artifacts throughopenclaw/plugin-sdk/memory-host-coreinstead of reaching into a specific memory plugin's private layout.registerMemoryPromptSection,registerMemoryFlushPlan, andregisterMemoryRuntimeare legacy-compatible exclusive memory-plugin APIs.MemoryFlushPlan.modelcan pin the flush turn to an exactprovider/modelreference, such asollama/qwen3:8b, without inheriting the active fallback chain.registerMemoryEmbeddingProvideris deprecated. New embedding providers should useapi.registerEmbeddingProvider(...)andcontracts.embeddingProviders.- Existing memory-specific providers continue to work during the migration window, but plugin inspection reports this as compatibility debt for non-bundled plugins.
Events and lifecycle
| Method | What it does |
|---|---|
api.on(hookName, handler, opts?) |
Typed lifecycle hook |
api.onConversationBindingResolved(handler) |
Conversation binding callback |
See Plugin hooks for examples, common hook names, and guard semantics.
Hook decision semantics
before_install is a plugin-runtime lifecycle hook, not the operator install
policy surface. Use security.installPolicy when an allow/block decision must
cover CLI and Gateway-backed install or update paths.
before_tool_call: returning{ block: true }is terminal. Once any handler sets it, lower-priority handlers are skipped.before_tool_call: returning{ block: false }is treated as no decision (same as omittingblock), not as an override.before_install: returning{ block: true }is terminal. Once any handler sets it, lower-priority handlers are skipped.before_install: returning{ block: false }is treated as no decision (same as omittingblock), not as an override.reply_dispatch: returning{ handled: true, ... }is terminal. Once any handler claims dispatch, lower-priority handlers and the default model dispatch path are skipped.message_sending: returning{ cancel: true }is terminal. Once any handler sets it, lower-priority handlers are skipped.message_sending: returning{ cancel: false }is treated as no decision (same as omittingcancel), not as an override.message_received: use the typedthreadIdfield when you need inbound thread/topic routing. Keepmetadatafor channel-specific extras.message_sending: use typedreplyToId/threadIdrouting fields before falling back to channel-specificmetadata.gateway_start: usectx.config,ctx.workspaceDir, andctx.getCron?.()for gateway-owned startup state instead of relying on internalgateway:startuphooks. Cron may still be loading at this point.cron_reconciled: rebuild a full external cron projection after startup or scheduler reload. It includesreasonand the effectiveenabledstate, includingenabled: false, whilectx.getCron?.()returns the exact reconciled scheduler. Passctx.abortSignalinto durable projection work; it aborts when that scheduler snapshot is superseded or the Gateway closes.cron_changed: observe gateway-owned cron lifecycle changes.scheduledandremovedevents are post-commit reconciliation hints, not an ordered delta log. A scheduled event'sevent.nextRunAtMsis absent when the job has no next wake; a removed event still carries the deleted job snapshot.
External wake schedulers should debounce or coalesce cron_changed events,
then reread the full durable view from the scheduler last captured by
cron_reconciled. Do not adopt the scheduler from a cron_changed context: a
detached hint from an older scheduler can overlap a later reload.
Use cron_reconciled as the full-snapshot trigger for durable state loaded at
Gateway startup or scheduler replacement. It is not replayed for a plugin-only
hot reload. Observation handlers run in parallel, and fire-and-forget
dispatches can overlap, so consumers must not depend on event completion order.
Keep OpenClaw as the source of truth for due checks and execution.
For a single-flight adapter with durable replacement, retry/backoff, and clean shutdown, see Safe external cron projection.
API object fields
| Field | Type | Description |
|---|---|---|
api.id |
string |
Plugin id |
api.name |
string |
Display name |
api.version |
string? |
Plugin version (optional) |
api.description |
string? |
Plugin description (optional) |
api.source |
string |
Plugin source path |
api.rootDir |
string? |
Plugin root directory (optional) |
api.config |
OpenClawConfig |
Current config snapshot (active in-memory runtime snapshot when available) |
api.pluginConfig |
Record<string, unknown> |
Plugin-specific config from plugins.entries.<id>.config |
api.runtime |
PluginRuntime |
Runtime helpers |
api.logger |
PluginLogger |
Scoped logger (debug, info, warn, error) |
api.registrationMode |
PluginRegistrationMode |
Current load mode; "setup-runtime" is the lightweight pre-full-entry startup/setup window |
api.resolvePath(input) |
(string) => string |
Resolve path relative to plugin root |
Internal module convention
Within your plugin, use local barrel files for internal imports:
my-plugin/ api.ts # Public exports for external consumers runtime-api.ts # Internal-only runtime exports index.ts # Plugin entry point setup-entry.ts # Lightweight setup-only entry (optional)Facade-loaded bundled plugin public surfaces (api.ts, runtime-api.ts,
index.ts, setup-entry.ts, and similar public entry files) prefer the
active runtime config snapshot when OpenClaw is already running. If no runtime
snapshot exists yet, they fall back to the resolved config file on disk.
Packaged bundled plugin facades should be loaded through OpenClaw's plugin
facade loaders; direct imports from dist/extensions/... bypass the manifest
and runtime sidecar checks that packaged installs use for plugin-owned code.
Provider plugins can expose a narrow plugin-local contract barrel when a helper is intentionally provider-specific and does not belong in a generic SDK subpath yet. Bundled examples:
- Anthropic: public
api.ts/contract-api.tsseam for Claude beta-header andservice_tierstream helpers. @openclaw/openai-provider:api.tsexports provider builders, default-model helpers, and realtime provider builders.@openclaw/openrouter-provider:api.tsexports the provider builder plus onboarding/config helpers.
Related
definePluginEntry and defineChannelPluginEntry options.
Full api.runtime namespace reference.
Packaging, manifests, and config schemas.
Test utilities and lint rules.
Migrating from deprecated surfaces.
Deep architecture and capability model.