import type { ReplyPayload } from "../../auto-reply/types.js"; import type { OpenClawConfig } from "../../config/config.js"; import type { GroupToolPolicyConfig } from "../../config/types.tools.js"; import type { OutboundDeliveryResult, OutboundSendDeps } from "../../infra/outbound/deliver.js"; import type { OutboundIdentity } from "../../infra/outbound/identity.js"; import type { PluginRuntime } from "../../plugins/runtime/types.js"; import type { RuntimeEnv } from "../../runtime.js"; import type { ChannelAccountSnapshot, ChannelAccountState, ChannelDirectoryEntry, ChannelGroupContext, ChannelHeartbeatDeps, ChannelLogSink, ChannelOutboundTargetMode, ChannelPollContext, ChannelPollResult, ChannelSecurityContext, ChannelSecurityDmPolicy, ChannelSetupInput, ChannelStatusIssue, } from "./types.core.js"; export type ChannelSetupAdapter = { resolveAccountId?: (params: { cfg: OpenClawConfig; accountId?: string; input?: ChannelSetupInput; }) => string; resolveBindingAccountId?: (params: { cfg: OpenClawConfig; agentId: string; accountId?: string; }) => string | undefined; applyAccountName?: (params: { cfg: OpenClawConfig; accountId: string; name?: string; }) => OpenClawConfig; applyAccountConfig: (params: { cfg: OpenClawConfig; accountId: string; input: ChannelSetupInput; }) => OpenClawConfig; validateInput?: (params: { cfg: OpenClawConfig; accountId: string; input: ChannelSetupInput; }) => string | null; }; export type ChannelConfigAdapter = { listAccountIds: (cfg: OpenClawConfig) => string[]; resolveAccount: (cfg: OpenClawConfig, accountId?: string | null) => ResolvedAccount; inspectAccount?: (cfg: OpenClawConfig, accountId?: string | null) => unknown; defaultAccountId?: (cfg: OpenClawConfig) => string; setAccountEnabled?: (params: { cfg: OpenClawConfig; accountId: string; enabled: boolean; }) => OpenClawConfig; deleteAccount?: (params: { cfg: OpenClawConfig; accountId: string }) => OpenClawConfig; isEnabled?: (account: ResolvedAccount, cfg: OpenClawConfig) => boolean; disabledReason?: (account: ResolvedAccount, cfg: OpenClawConfig) => string; isConfigured?: (account: ResolvedAccount, cfg: OpenClawConfig) => boolean | Promise; unconfiguredReason?: (account: ResolvedAccount, cfg: OpenClawConfig) => string; describeAccount?: (account: ResolvedAccount, cfg: OpenClawConfig) => ChannelAccountSnapshot; resolveAllowFrom?: (params: { cfg: OpenClawConfig; accountId?: string | null; }) => Array | undefined; formatAllowFrom?: (params: { cfg: OpenClawConfig; accountId?: string | null; allowFrom: Array; }) => string[]; resolveDefaultTo?: (params: { cfg: OpenClawConfig; accountId?: string | null; }) => string | undefined; }; export type ChannelGroupAdapter = { resolveRequireMention?: (params: ChannelGroupContext) => boolean | undefined; resolveGroupIntroHint?: (params: ChannelGroupContext) => string | undefined; resolveToolPolicy?: (params: ChannelGroupContext) => GroupToolPolicyConfig | undefined; }; export type ChannelOutboundContext = { cfg: OpenClawConfig; to: string; text: string; mediaUrl?: string; mediaLocalRoots?: readonly string[]; gifPlayback?: boolean; replyToId?: string | null; threadId?: string | number | null; accountId?: string | null; identity?: OutboundIdentity; deps?: OutboundSendDeps; silent?: boolean; }; export type ChannelOutboundPayloadContext = ChannelOutboundContext & { payload: ReplyPayload; }; export type ChannelOutboundAdapter = { deliveryMode: "direct" | "gateway" | "hybrid"; chunker?: ((text: string, limit: number) => string[]) | null; chunkerMode?: "text" | "markdown"; textChunkLimit?: number; pollMaxOptions?: number; resolveTarget?: (params: { cfg?: OpenClawConfig; to?: string; allowFrom?: string[]; accountId?: string | null; mode?: ChannelOutboundTargetMode; }) => { ok: true; to: string } | { ok: false; error: Error }; sendPayload?: (ctx: ChannelOutboundPayloadContext) => Promise; sendText?: (ctx: ChannelOutboundContext) => Promise; sendMedia?: (ctx: ChannelOutboundContext) => Promise; sendPoll?: (ctx: ChannelPollContext) => Promise; }; export type ChannelStatusAdapter = { defaultRuntime?: ChannelAccountSnapshot; buildChannelSummary?: (params: { account: ResolvedAccount; cfg: OpenClawConfig; defaultAccountId: string; snapshot: ChannelAccountSnapshot; }) => Record | Promise>; probeAccount?: (params: { account: ResolvedAccount; timeoutMs: number; cfg: OpenClawConfig; }) => Promise; auditAccount?: (params: { account: ResolvedAccount; timeoutMs: number; cfg: OpenClawConfig; probe?: Probe; }) => Promise; buildAccountSnapshot?: (params: { account: ResolvedAccount; cfg: OpenClawConfig; runtime?: ChannelAccountSnapshot; probe?: Probe; audit?: Audit; }) => ChannelAccountSnapshot | Promise; logSelfId?: (params: { account: ResolvedAccount; cfg: OpenClawConfig; runtime: RuntimeEnv; includeChannelPrefix?: boolean; }) => void; resolveAccountState?: (params: { account: ResolvedAccount; cfg: OpenClawConfig; configured: boolean; enabled: boolean; }) => ChannelAccountState; collectStatusIssues?: (accounts: ChannelAccountSnapshot[]) => ChannelStatusIssue[]; }; export type ChannelGatewayContext = { cfg: OpenClawConfig; accountId: string; account: ResolvedAccount; runtime: RuntimeEnv; abortSignal: AbortSignal; log?: ChannelLogSink; getStatus: () => ChannelAccountSnapshot; setStatus: (next: ChannelAccountSnapshot) => void; /** * Optional channel runtime helpers for external channel plugins. * * This field provides access to advanced Plugin SDK features that are * available to external plugins but not to built-in channels (which can * directly import internal modules). * * ## Available Features * * - **reply**: AI response dispatching, formatting, and delivery * - **routing**: Agent route resolution and matching * - **text**: Text chunking, markdown processing, and control command detection * - **session**: Session management and metadata tracking * - **media**: Remote media fetching and buffer saving * - **commands**: Command authorization and control command handling * - **groups**: Group policy resolution and mention requirements * - **pairing**: Channel pairing and allow-from management * * ## Use Cases * * External channel plugins (e.g., email, SMS, custom integrations) that need: * - AI-powered response generation and delivery * - Advanced text processing and formatting * - Session tracking and management * - Agent routing and policy resolution * * ## Example * * ```typescript * const emailGatewayAdapter: ChannelGatewayAdapter = { * startAccount: async (ctx) => { * // Check availability (for backward compatibility) * if (!ctx.channelRuntime) { * ctx.log?.warn?.("channelRuntime not available - skipping AI features"); * return; * } * * // Use AI dispatch * await ctx.channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({ * ctx: { ... }, * cfg: ctx.cfg, * dispatcherOptions: { * deliver: async (payload) => { * // Send reply via email * }, * }, * }); * }, * }; * ``` * * ## Backward Compatibility * * - This field is **optional** - channels that don't need it can ignore it * - Built-in channels (slack, discord, etc.) typically don't use this field * because they can directly import internal modules * - External plugins should check for undefined before using * * @since Plugin SDK 2026.2.19 * @see {@link https://docs.openclaw.ai/plugins/developing-plugins | Plugin SDK documentation} */ channelRuntime?: PluginRuntime["channel"]; }; export type ChannelLogoutResult = { cleared: boolean; loggedOut?: boolean; [key: string]: unknown; }; export type ChannelLoginWithQrStartResult = { qrDataUrl?: string; message: string; }; export type ChannelLoginWithQrWaitResult = { connected: boolean; message: string; }; export type ChannelLogoutContext = { cfg: OpenClawConfig; accountId: string; account: ResolvedAccount; runtime: RuntimeEnv; log?: ChannelLogSink; }; export type ChannelPairingAdapter = { idLabel: string; normalizeAllowEntry?: (entry: string) => string; notifyApproval?: (params: { cfg: OpenClawConfig; id: string; runtime?: RuntimeEnv; }) => Promise; }; export type ChannelGatewayAdapter = { startAccount?: (ctx: ChannelGatewayContext) => Promise; stopAccount?: (ctx: ChannelGatewayContext) => Promise; loginWithQrStart?: (params: { accountId?: string; force?: boolean; timeoutMs?: number; verbose?: boolean; }) => Promise; loginWithQrWait?: (params: { accountId?: string; timeoutMs?: number; }) => Promise; logoutAccount?: (ctx: ChannelLogoutContext) => Promise; }; export type ChannelAuthAdapter = { login?: (params: { cfg: OpenClawConfig; accountId?: string | null; runtime: RuntimeEnv; verbose?: boolean; channelInput?: string | null; }) => Promise; }; export type ChannelHeartbeatAdapter = { checkReady?: (params: { cfg: OpenClawConfig; accountId?: string | null; deps?: ChannelHeartbeatDeps; }) => Promise<{ ok: boolean; reason: string }>; resolveRecipients?: (params: { cfg: OpenClawConfig; opts?: { to?: string; all?: boolean } }) => { recipients: string[]; source: string; }; }; type ChannelDirectorySelfParams = { cfg: OpenClawConfig; accountId?: string | null; runtime: RuntimeEnv; }; type ChannelDirectoryListParams = { cfg: OpenClawConfig; accountId?: string | null; query?: string | null; limit?: number | null; runtime: RuntimeEnv; }; type ChannelDirectoryListGroupMembersParams = { cfg: OpenClawConfig; accountId?: string | null; groupId: string; limit?: number | null; runtime: RuntimeEnv; }; export type ChannelDirectoryAdapter = { self?: (params: ChannelDirectorySelfParams) => Promise; listPeers?: (params: ChannelDirectoryListParams) => Promise; listPeersLive?: (params: ChannelDirectoryListParams) => Promise; listGroups?: (params: ChannelDirectoryListParams) => Promise; listGroupsLive?: (params: ChannelDirectoryListParams) => Promise; listGroupMembers?: ( params: ChannelDirectoryListGroupMembersParams, ) => Promise; }; export type ChannelResolveKind = "user" | "group"; export type ChannelResolveResult = { input: string; resolved: boolean; id?: string; name?: string; note?: string; }; export type ChannelResolverAdapter = { resolveTargets: (params: { cfg: OpenClawConfig; accountId?: string | null; inputs: string[]; kind: ChannelResolveKind; runtime: RuntimeEnv; }) => Promise; }; export type ChannelElevatedAdapter = { allowFromFallback?: (params: { cfg: OpenClawConfig; accountId?: string | null; }) => Array | undefined; }; export type ChannelCommandAdapter = { enforceOwnerForCommands?: boolean; skipWhenConfigEmpty?: boolean; }; export type ChannelSecurityAdapter = { resolveDmPolicy?: ( ctx: ChannelSecurityContext, ) => ChannelSecurityDmPolicy | null; collectWarnings?: (ctx: ChannelSecurityContext) => Promise | string[]; };