Skip to content

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:

typescript
// 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

typescript
// 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

typescript
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

typescript
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

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

typescript
// 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 textChunkLimit or allowFrom does 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:

typescript
// 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:

typescript
// 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

OpenClaw Source Code Tutorial