mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-23 16:01:17 +00:00
4.5 KiB
4.5 KiB
title, sidebarTitle, summary, read_when
| title | sidebarTitle | summary | read_when | |||
|---|---|---|---|---|---|---|
| Channel Plugin SDK | Channel Plugins | Contracts and helpers for native messaging channel plugins, including actions, routing, pairing, and setup |
|
Channel Plugin SDK
Channel plugins use defineChannelPluginEntry(...) from
openclaw/plugin-sdk/core and implement the ChannelPlugin contract.
Minimal channel entry
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { exampleChannelPlugin } from "./src/channel.js";
import { setExampleRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
id: "example-channel",
name: "Example Channel",
description: "Example native channel plugin",
plugin: exampleChannelPlugin,
setRuntime: setExampleRuntime,
});
ChannelPlugin shape
Important sections of the contract:
meta: docs, labels, and picker metadatacapabilities: replies, polls, reactions, threads, media, and chat typesconfigandconfigSchema: account resolution and config parsingsetupandsetupWizard: onboarding/setup flowsecurity: DM policy and allowlist behaviormessaging: target parsing and outbound session routingactions: sharedmessagetool discovery and executionpairing,threading,status,lifecycle,groups,directory
For pure types, import from openclaw/plugin-sdk/channel-contract.
Shared message tool
Channel plugins own their channel-specific part of the shared message tool
through ChannelMessageActionAdapter.
import { Type } from "@sinclair/typebox";
import { createMessageToolButtonsSchema } from "openclaw/plugin-sdk/channel-actions";
export const exampleActions = {
describeMessageTool() {
return {
actions: ["send", "edit"],
capabilities: ["buttons"],
schema: {
visibility: "current-channel",
properties: {
buttons: createMessageToolButtonsSchema(),
threadId: Type.String(),
},
},
};
},
async handleAction(ctx) {
if (ctx.action === "send") {
return {
content: [{ type: "text", text: `send to ${String(ctx.params.to)}` }],
};
}
return {
content: [{ type: "text", text: `unsupported action: ${ctx.action}` }],
};
},
};
Key types:
ChannelMessageActionAdapterChannelMessageActionContextChannelMessageActionDiscoveryContextChannelMessageToolDiscovery
Outbound routing helpers
When a channel plugin needs custom outbound routing, implement
messaging.resolveOutboundSessionRoute(...).
Use buildChannelOutboundSessionRoute(...) from plugin-sdk/core to return the
standard route payload:
import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/core";
const messaging = {
resolveOutboundSessionRoute({ cfg, agentId, accountId, target }) {
return buildChannelOutboundSessionRoute({
cfg,
agentId,
channel: "example-channel",
accountId,
peer: { kind: "direct", id: target },
chatType: "direct",
from: accountId ?? "default",
to: target,
});
},
};
Pairing helpers
Use plugin-sdk/channel-pairing for DM approval flows:
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
const pairing = createChannelPairingController({
core: runtime,
channel: "example-channel",
accountId: "default",
});
const result = pairing.issueChallenge({
agentId: "assistant",
requesterId: "user-123",
});
That surface also gives you scoped access to pairing storage helpers such as allowlist reads and request upserts.
Channel setup helpers
Use:
plugin-sdk/channel-setupfor optional or installable channelsplugin-sdk/setupfor setup adapters, DM policy, and allowlist promptsplugin-sdk/webhook-ingressfor plugin-owned webhook routes
Channel plugin guidance
- Keep transport-specific execution inside the channel package.
- Use
channel-contracttypes in tests and local helpers. - Keep
describeMessageTool(...)andhandleAction(...)aligned. - Keep session routing in
messaging, not in ad-hoc command handlers. - Prefer focused subpaths over broad runtime coupling.