Channel Abstraction Layer
The channel abstraction layer is the core design of OpenClaw's channel system. By separating the ChannelPlugin interface from ChannelDock metadata, it achieves an architecture where heavy runtime logic is loaded on demand while lightweight metadata remains globally available.
ChannelPlugin Interface
Source: src/channels/plugins/types.ts
ChannelPlugin is the core interface that every channel implementation must provide. It is composed of multiple adapters:
// src/channels/plugins/types.ts (simplified)
interface ChannelPlugin {
// Capabilities declaration
capabilities: ChannelCapabilities;
// Adapters
outbound: ChannelOutboundAdapter; // Send messages out
messaging: ChannelMessagingAdapter; // Receive messages
commands?: ChannelCommandAdapter; // Handle commands
groups?: ChannelGroupAdapter; // Group-specific behavior
threading?: ChannelThreadingAdapter; // Reply/thread handling
mentions?: ChannelMentionAdapter; // @mention parsing
agentPrompt?: ChannelAgentPromptAdapter; // Platform-specific prompts
}Capabilities Declaration
// src/channels/plugins/types.ts (simplified)
interface ChannelCapabilities {
chatTypes: ("direct" | "group" | "channel" | "thread")[];
nativeCommands: boolean; // Supports / commands
blockStreaming: boolean; // Can stream responses
polls: boolean; // Supports polls
reactions: boolean; // Supports reactions
media: MediaCapabilities; // Image/audio/video support
}Each channel declares its supported capabilities. The Agent and routing layers use this information to adjust behavior accordingly.
Adapter Details
OutboundAdapter -- Message Sending
interface ChannelOutboundAdapter {
sendText(opts: SendTextOpts): Promise<SendResult>;
sendMedia(opts: SendMediaOpts): Promise<SendResult>;
sendPayload?(opts: SendPayloadOpts): Promise<SendResult>;
sendReaction?(opts: SendReactionOpts): Promise<void>;
}ThreadingAdapter -- Threads and Replies
interface ChannelThreadingAdapter {
// "first": reply to first message only
// "all": reply to every message
resolveReplyToMode(): "first" | "all";
// Build context for tool execution
buildToolContext(): {
currentChannelId: string;
currentThreadTs?: string;
};
}Telegram defaults to "first" mode (reply only to the first message), while Slack may use thread mode.
MentionAdapter -- @Mention Handling
interface ChannelMentionAdapter {
parseMention(text: string): MentionResult | null;
stripMention(text: string): string;
}ChannelDock Metadata
Source: src/channels/dock.ts
ChannelDock is a lightweight metadata object that is globally available in core code without depending on the plugin runtime:
// src/channels/dock.ts (simplified)
interface ChannelDock {
id: string; // Channel identifier
capabilities: ChannelCapabilities;
outbound?: ChannelOutboundAdapter;
commands?: ChannelCommandAdapter;
groups?: ChannelGroupAdapter;
mentions?: ChannelMentionAdapter;
threading?: ChannelThreadingAdapter;
agentPrompt?: ChannelAgentPromptAdapter;
// Lightweight metadata (no runtime loading needed)
textChunkLimit: number; // Max chars per message
streamingDefaults: boolean; // Streaming enabled by default
allowFrom: AllowFromConfig; // Access control
}Plugin vs Dock Separation
Why this separation?
- Dock is a configuration reader and formatting tool -- reading
textChunkLimitorallowFromdoes not require starting a Telegram bot - Plugin handles runtime operations -- connecting bots, listening for messages, and sending replies requires actual network I/O
- This separation allows core routing logic to access channel metadata quickly without waiting for plugins to load
Mention Gating
Source: src/channels/mention-gating.ts
Mention gating determines whether an @mention is required before the AI responds in a group:
// src/channels/mention-gating.ts (simplified)
function resolveMentionGating(
channel: string,
chatType: "direct" | "group",
config: ChannelConfig
): boolean {
// Direct messages: never require mention
if (chatType === "direct") return false;
// Group messages: check per-group settings
// Falls back to channel-level default
return config.requireMention ?? true;
}Gating Flow
Channel Configuration
Source: src/channels/channel-config.ts
Each channel has its own configuration module:
// src/channels/channel-config.ts (simplified)
interface ChannelConfig {
enabled: boolean;
allowFrom?: AllowFromConfig; // Who can send messages
textChunkLimit?: number; // Override default chunk limit
requireMention?: boolean; // Override mention gating
// Channel-specific settings...
}Summary
- ChannelPlugin interface defines 7 adapters: Outbound, Messaging, Command, Group, Threading, Mention, AgentPrompt
- ChannelDock provides lightweight metadata without runtime loading
- Dual-layer separation allows core routing to quickly access metadata while plugins load on demand
- Mention gating controls whether @mention is required to trigger AI in groups
- Each channel declares its own Capabilities, which the Agent uses to adjust behavior
Next: Telegram Implementation