import type { ReplyPayload } from "../../auto-reply/types.js"; import type { ConfiguredBindingRule } from "../../config/bindings.js"; import type { OpenClawConfig } from "../../config/config.js"; import type { GroupToolPolicyConfig } from "../../config/types.tools.js"; import type { ExecApprovalRequest, ExecApprovalResolved } from "../../infra/exec-approvals.js"; import type { OutboundDeliveryResult, OutboundSendDeps } from "../../infra/outbound/deliver.js"; import type { OutboundIdentity } from "../../infra/outbound/identity.js"; import type { PluginApprovalRequest, PluginApprovalResolved } from "../../infra/plugin-approvals.js"; import type { OutboundMediaAccess } from "../../media/load-options.js"; import type { PluginRuntime } from "../../plugins/runtime/types.js"; import type { RuntimeEnv } from "../../runtime.js"; import type { ConfigWriteTarget } from "./config-writes.js"; import type { ChannelAccountSnapshot, ChannelAccountState, ChannelDirectoryEntry, ChannelGroupContext, ChannelHeartbeatDeps, ChannelLogSink, ChannelOutboundTargetMode, ChannelPollContext, ChannelPollResult, ChannelSecurityContext, ChannelSecurityDmPolicy, ChannelSetupInput, ChannelStatusIssue } from "./types.core.js"; export type ChannelActionAvailabilityState = { kind: "enabled"; } | { kind: "disabled"; } | { kind: "unsupported"; }; export type ChannelApprovalInitiatingSurfaceState = ChannelActionAvailabilityState; export type ChannelApprovalForwardTarget = { channel: string; to: string; accountId?: string | null; threadId?: string | number | null; source?: "session" | "target"; }; export type ChannelCapabilitiesDisplayTone = "default" | "muted" | "success" | "warn" | "error"; export type ChannelCapabilitiesDisplayLine = { text: string; tone?: ChannelCapabilitiesDisplayTone; }; export type ChannelCapabilitiesDiagnostics = { lines?: ChannelCapabilitiesDisplayLine[]; details?: Record; }; type BivariantCallback unknown> = { bivarianceHack: T; }["bivarianceHack"]; 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; afterAccountConfigWritten?: (params: { previousCfg: OpenClawConfig; cfg: OpenClawConfig; accountId: string; input: ChannelSetupInput; runtime: RuntimeEnv; }) => Promise | void; 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; audioAsVoice?: boolean; mediaAccess?: OutboundMediaAccess; mediaLocalRoots?: readonly string[]; mediaReadFile?: (filePath: string) => Promise; gifPlayback?: boolean; /** Send image as document to avoid Telegram compression. */ forceDocument?: boolean; replyToId?: string | null; threadId?: string | number | null; accountId?: string | null; identity?: OutboundIdentity; deps?: OutboundSendDeps; silent?: boolean; gatewayClientScopes?: readonly string[]; }; export type ChannelOutboundPayloadContext = ChannelOutboundContext & { payload: ReplyPayload; }; export type ChannelOutboundPayloadHint = { kind: "approval-pending"; approvalKind: "exec" | "plugin"; } | { kind: "approval-resolved"; approvalKind: "exec" | "plugin"; }; export type ChannelOutboundTargetRef = { channel: string; to: string; accountId?: string | null; threadId?: string | number | null; }; export type ChannelOutboundFormattedContext = ChannelOutboundContext & { abortSignal?: AbortSignal; }; export type ChannelOutboundAdapter = { deliveryMode: "direct" | "gateway" | "hybrid"; chunker?: ((text: string, limit: number) => string[]) | null; chunkerMode?: "text" | "markdown"; textChunkLimit?: number; pollMaxOptions?: number; normalizePayload?: (params: { payload: ReplyPayload; }) => ReplyPayload | null; shouldSkipPlainTextSanitization?: (params: { payload: ReplyPayload; }) => boolean; resolveEffectiveTextChunkLimit?: (params: { cfg: OpenClawConfig; accountId?: string | null; fallbackLimit?: number; }) => number | undefined; shouldSuppressLocalPayloadPrompt?: (params: { cfg: OpenClawConfig; accountId?: string | null; payload: ReplyPayload; hint?: ChannelOutboundPayloadHint; }) => boolean; beforeDeliverPayload?: (params: { cfg: OpenClawConfig; target: ChannelOutboundTargetRef; payload: ReplyPayload; hint?: ChannelOutboundPayloadHint; }) => Promise | void; 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; sendFormattedText?: (ctx: ChannelOutboundFormattedContext) => Promise; sendFormattedMedia?: (ctx: ChannelOutboundFormattedContext & { mediaUrl: string; }) => 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; formatCapabilitiesProbe?: BivariantCallback<(params: { probe: Probe; }) => ChannelCapabilitiesDisplayLine[]>; auditAccount?: (params: { account: ResolvedAccount; timeoutMs: number; cfg: OpenClawConfig; probe?: Probe; }) => Promise; buildCapabilitiesDiagnostics?: BivariantCallback<(params: { account: ResolvedAccount; timeoutMs: number; cfg: OpenClawConfig; probe?: Probe; audit?: Audit; target?: string; }) => 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.stableclaw.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; accountId?: 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; authorizeActorAction?: (params: { cfg: OpenClawConfig; accountId?: string | null; senderId?: string | null; action: "approve"; approvalKind: "exec" | "plugin"; }) => { authorized: boolean; reason?: string; }; getActionAvailabilityState?: (params: { cfg: OpenClawConfig; accountId?: string | null; action: "approve"; }) => ChannelActionAvailabilityState; }; 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 ChannelLifecycleAdapter = { onAccountConfigChanged?: (params: { prevCfg: OpenClawConfig; nextCfg: OpenClawConfig; accountId: string; runtime: RuntimeEnv; }) => Promise | void; onAccountRemoved?: (params: { prevCfg: OpenClawConfig; accountId: string; runtime: RuntimeEnv; }) => Promise | void; }; export type ChannelApprovalDeliveryAdapter = { hasConfiguredDmRoute?: (params: { cfg: OpenClawConfig; }) => boolean; shouldSuppressForwardingFallback?: (params: { cfg: OpenClawConfig; approvalKind: ChannelApprovalKind; target: ChannelApprovalForwardTarget; request: ExecApprovalRequest; }) => boolean; }; export type ChannelApprovalKind = "exec" | "plugin"; export type ChannelApprovalNativeSurface = "origin" | "approver-dm"; export type ChannelApprovalNativeTarget = { to: string; threadId?: string | number | null; }; export type ChannelApprovalNativeDeliveryPreference = ChannelApprovalNativeSurface | "both"; export type ChannelApprovalNativeRequest = ExecApprovalRequest | PluginApprovalRequest; export type ChannelApprovalNativeDeliveryCapabilities = { enabled: boolean; preferredSurface: ChannelApprovalNativeDeliveryPreference; supportsOriginSurface: boolean; supportsApproverDmSurface: boolean; notifyOriginWhenDmOnly?: boolean; }; export type ChannelApprovalNativeAdapter = { describeDeliveryCapabilities: (params: { cfg: OpenClawConfig; accountId?: string | null; approvalKind: ChannelApprovalKind; request: ChannelApprovalNativeRequest; }) => ChannelApprovalNativeDeliveryCapabilities; resolveOriginTarget?: (params: { cfg: OpenClawConfig; accountId?: string | null; approvalKind: ChannelApprovalKind; request: ChannelApprovalNativeRequest; }) => ChannelApprovalNativeTarget | null | Promise; resolveApproverDmTargets?: (params: { cfg: OpenClawConfig; accountId?: string | null; approvalKind: ChannelApprovalKind; request: ChannelApprovalNativeRequest; }) => ChannelApprovalNativeTarget[] | Promise; }; export type ChannelApprovalRenderAdapter = { exec?: { buildPendingPayload?: (params: { cfg: OpenClawConfig; request: ExecApprovalRequest; target: ChannelApprovalForwardTarget; nowMs: number; }) => ReplyPayload | null; buildResolvedPayload?: (params: { cfg: OpenClawConfig; resolved: ExecApprovalResolved; target: ChannelApprovalForwardTarget; }) => ReplyPayload | null; }; plugin?: { buildPendingPayload?: (params: { cfg: OpenClawConfig; request: PluginApprovalRequest; target: ChannelApprovalForwardTarget; nowMs: number; }) => ReplyPayload | null; buildResolvedPayload?: (params: { cfg: OpenClawConfig; resolved: PluginApprovalResolved; target: ChannelApprovalForwardTarget; }) => ReplyPayload | null; }; }; export type ChannelApprovalCapability = ChannelApprovalAdapter & { authorizeActorAction?: ChannelAuthAdapter["authorizeActorAction"]; getActionAvailabilityState?: ChannelAuthAdapter["getActionAvailabilityState"]; }; export type ChannelApprovalAdapter = { delivery?: ChannelApprovalDeliveryAdapter; render?: ChannelApprovalRenderAdapter; native?: ChannelApprovalNativeAdapter; }; export type ChannelAllowlistAdapter = { applyConfigEdit?: (params: { cfg: OpenClawConfig; parsedConfig: Record; accountId?: string | null; scope: "dm" | "group"; action: "add" | "remove"; entry: string; }) => { kind: "ok"; changed: boolean; pathLabel: string; writeTarget: ConfigWriteTarget; } | { kind: "invalid-entry"; } | Promise<{ kind: "ok"; changed: boolean; pathLabel: string; writeTarget: ConfigWriteTarget; } | { kind: "invalid-entry"; }> | null; readConfig?: (params: { cfg: OpenClawConfig; accountId?: string | null; }) => { dmAllowFrom?: Array; groupAllowFrom?: Array; dmPolicy?: string; groupPolicy?: string; groupOverrides?: Array<{ label: string; entries: Array; }>; } | Promise<{ dmAllowFrom?: Array; groupAllowFrom?: Array; dmPolicy?: string; groupPolicy?: string; groupOverrides?: Array<{ label: string; entries: Array; }>; }>; resolveNames?: (params: { cfg: OpenClawConfig; accountId?: string | null; scope: "dm" | "group"; entries: string[]; }) => Array<{ input: string; resolved: boolean; name?: string | null; }> | Promise>; supportsScope?: (params: { scope: "dm" | "group" | "all"; }) => boolean; }; export type ChannelConfiguredBindingConversationRef = { conversationId: string; parentConversationId?: string; }; export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingConversationRef & { matchPriority?: number; }; export type ChannelCommandConversationContext = { accountId: string; threadId?: string; threadParentId?: string; senderId?: string; sessionKey?: string; parentSessionKey?: string; originatingTo?: string; commandTo?: string; fallbackTo?: string; }; export type ChannelConfiguredBindingProvider = { compileConfiguredBinding: (params: { binding: ConfiguredBindingRule; conversationId: string; }) => ChannelConfiguredBindingConversationRef | null; matchInboundConversation: (params: { binding: ConfiguredBindingRule; compiledBinding: ChannelConfiguredBindingConversationRef; conversationId: string; parentConversationId?: string; }) => ChannelConfiguredBindingMatch | null; resolveCommandConversation?: (params: ChannelCommandConversationContext) => ChannelConfiguredBindingConversationRef | null; }; export type ChannelConversationBindingSupport = { supportsCurrentConversationBinding?: boolean; setIdleTimeoutBySessionKey?: (params: { targetSessionKey: string; accountId?: string | null; idleTimeoutMs: number; }) => Array<{ boundAt: number; lastActivityAt: number; idleTimeoutMs?: number; maxAgeMs?: number; }>; setMaxAgeBySessionKey?: (params: { targetSessionKey: string; accountId?: string | null; maxAgeMs: number; }) => Array<{ boundAt: number; lastActivityAt: number; idleTimeoutMs?: number; maxAgeMs?: number; }>; createManager?: (params: { cfg: OpenClawConfig; accountId?: string | null; }) => { stop: () => void | Promise; } | Promise<{ stop: () => void | Promise; }>; }; export type ChannelSecurityAdapter = { resolveDmPolicy?: (ctx: ChannelSecurityContext) => ChannelSecurityDmPolicy | null; collectWarnings?: (ctx: ChannelSecurityContext) => Promise | string[]; }; export {};