import type { ReplyPayload } from "../../auto-reply/reply-payload.js"; import type { LegacyConfigRule } from "../../config/legacy.shared.js"; import type { AgentBinding } from "../../config/types.agents.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { GroupToolPolicyConfig } from "../../config/types.tools.js"; import type { ChannelApprovalNativeRuntimeAdapter } from "../../infra/approval-handler-runtime-types.js"; import type { ChannelApprovalKind } from "../../infra/approval-types.js"; import type { ExecApprovalRequest, ExecApprovalResolved } from "../../infra/exec-approvals.js"; import type { PluginApprovalRequest, PluginApprovalResolved } from "../../infra/plugin-approvals.js"; import type { RuntimeEnv } from "../../runtime.js"; import type { ResolverContext, SecretDefaults } from "../../secrets/runtime-shared.js"; import type { SecretTargetRegistryEntry } from "../../secrets/target-registry-types.js"; import type { ChannelApprovalNativeAdapter } from "./approval-native.types.js"; import type { ChannelRuntimeSurface } from "./channel-runtime-surface.types.js"; import type { ConfigWriteTarget } from "./config-writes.js"; export type { ChannelOutboundAdapter, ChannelOutboundChunkContext, ChannelOutboundContext, ChannelOutboundFormattedContext, ChannelOutboundPayloadContext, ChannelOutboundPayloadHint, ChannelOutboundTargetRef, } from "./outbound.types.js"; import type { ChannelAccountSnapshot, ChannelAccountState, ChannelDirectoryEntry, ChannelGroupContext, ChannelHeartbeatDeps, ChannelLegacyStateMigrationPlan, ChannelLogSink, ChannelSecurityContext, ChannelSecurityDmPolicy, ChannelSetupInput, ChannelStatusIssue } from "./types.core.js"; export type { ChannelPairingAdapter } from "./pairing.types.js"; type ConfiguredBindingRule = AgentBinding; export type { ChannelApprovalKind } from "../../infra/approval-types.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 ChannelAdapterCallback unknown> = T; 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; singleAccountKeysToMove?: readonly string[]; namedAccountPromotionKeys?: readonly string[]; resolveSingleAccountPromotionTarget?: (params: { channel: Record; }) => string | undefined; }; 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?: ChannelAdapterCallback<(account: ResolvedAccount, cfg: OpenClawConfig) => boolean>; disabledReason?: ChannelAdapterCallback<(account: ResolvedAccount, cfg: OpenClawConfig) => string>; isConfigured?: ChannelAdapterCallback<(account: ResolvedAccount, cfg: OpenClawConfig) => boolean | Promise>; unconfiguredReason?: ChannelAdapterCallback<(account: ResolvedAccount, cfg: OpenClawConfig) => string>; describeAccount?: ChannelAdapterCallback<(account: ResolvedAccount, cfg: OpenClawConfig) => ChannelAccountSnapshot>; resolveAllowFrom?: (params: { cfg: OpenClawConfig; accountId?: string | null; }) => Array | undefined; formatAllowFrom?: (params: { cfg: OpenClawConfig; accountId?: string | null; allowFrom: Array; }) => string[]; hasConfiguredState?: (params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; }) => boolean; hasPersistedAuthState?: (params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; }) => boolean; resolveDefaultTo?: (params: { cfg: OpenClawConfig; accountId?: string | null; }) => string | undefined; }; export type ChannelSecretsAdapter = { secretTargetRegistryEntries?: readonly SecretTargetRegistryEntry[]; unsupportedSecretRefSurfacePatterns?: readonly string[]; collectUnsupportedSecretRefConfigCandidates?: (raw: unknown) => Array<{ path: string; value: unknown; }>; collectRuntimeConfigAssignments?: (params: { config: OpenClawConfig; defaults: SecretDefaults | undefined; context: ResolverContext; }) => void; }; export type ChannelGroupAdapter = { resolveRequireMention?: (params: ChannelGroupContext) => boolean | undefined; resolveGroupIntroHint?: (params: ChannelGroupContext) => string | undefined; resolveToolPolicy?: (params: ChannelGroupContext) => GroupToolPolicyConfig | undefined; }; export type ChannelStatusAdapter = { defaultRuntime?: ChannelAccountSnapshot; buildChannelSummary?: ChannelAdapterCallback<(params: { account: ResolvedAccount; cfg: OpenClawConfig; defaultAccountId: string; snapshot: ChannelAccountSnapshot; }) => Record | Promise>>; probeAccount?: ChannelAdapterCallback<(params: { account: ResolvedAccount; timeoutMs: number; cfg: OpenClawConfig; }) => Promise>; formatCapabilitiesProbe?: ChannelAdapterCallback<(params: { probe: Probe; }) => ChannelCapabilitiesDisplayLine[]>; auditAccount?: ChannelAdapterCallback<(params: { account: ResolvedAccount; timeoutMs: number; cfg: OpenClawConfig; probe?: Probe; }) => Promise>; buildCapabilitiesDiagnostics?: ChannelAdapterCallback<(params: { account: ResolvedAccount; timeoutMs: number; cfg: OpenClawConfig; probe?: Probe; audit?: Audit; target?: string; }) => Promise>; buildAccountSnapshot?: ChannelAdapterCallback<(params: { account: ResolvedAccount; cfg: OpenClawConfig; runtime?: ChannelAccountSnapshot; probe?: Probe; audit?: Audit; }) => ChannelAccountSnapshot | Promise>; logSelfId?: ChannelAdapterCallback<(params: { account: ResolvedAccount; cfg: OpenClawConfig; runtime: RuntimeEnv; includeChannelPrefix?: boolean; }) => void>; resolveAccountState?: ChannelAdapterCallback<(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 * - Bundled channels typically don't use this field * because they can directly import internal modules * - External plugins should check for undefined before using * - `runtimeContexts` is the stable startup-safe subset. Bundled channels * may receive only that subset during provider boot. * - External channel plugins that need reply/routing/session helpers receive * a full `createPluginRuntime().channel` surface from the Gateway. * * @since Plugin SDK 2026.2.19 * @see {@link https://docs.openclaw.ai/plugins/building-plugins | Plugin SDK documentation} */ channelRuntime?: ChannelRuntimeSurface; }; export type ChannelLogoutResult = { cleared: boolean; loggedOut?: boolean; [key: string]: unknown; }; export type ChannelLoginWithQrStartResult = { qrDataUrl?: string; message: string; connected?: boolean; }; export type ChannelLoginWithQrWaitResult = { connected: boolean; message: string; qrDataUrl?: string; }; export type ChannelLogoutContext = { cfg: OpenClawConfig; accountId: string; account: ResolvedAccount; runtime: RuntimeEnv; log?: ChannelLogSink; }; export type ChannelGatewayAdapter = { startAccount?: (ctx: ChannelGatewayContext) => Promise; stopAccount?: (ctx: ChannelGatewayContext) => Promise; /** Keep gateway auth bypass resolution mirrored through a lightweight top-level `gateway-auth-api.ts` artifact. */ resolveGatewayAuthBypassPaths?: (params: { cfg: OpenClawConfig; }) => string[]; loginWithQrStart?: (params: { accountId?: string; force?: boolean; timeoutMs?: number; verbose?: boolean; }) => Promise; loginWithQrWait?: (params: { accountId?: string; timeoutMs?: number; currentQrDataUrl?: string; }) => 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; }>; sendTyping?: (params: { cfg: OpenClawConfig; to: string; accountId?: string | null; threadId?: string | number | null; deps?: ChannelHeartbeatDeps; }) => Promise | void; clearTyping?: (params: { cfg: OpenClawConfig; to: string; accountId?: string | null; threadId?: string | number | null; deps?: ChannelHeartbeatDeps; }) => Promise | void; }; 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; nativeCommandsAutoEnabled?: boolean; nativeSkillsAutoEnabled?: boolean; preferSenderE164ForCommands?: boolean; resolveNativeCommandName?: (params: { commandKey: string; defaultName: string; }) => string | undefined; buildCommandsListChannelData?: (params: { currentPage: number; totalPages: number; agentId?: string; }) => ReplyPayload["channelData"] | null; buildModelsMenuChannelData?: (params: { providers: Array<{ id: string; count: number; }>; }) => ReplyPayload["channelData"] | null; buildModelsProviderChannelData?: (params: { providers: Array<{ id: string; count: number; }>; }) => ReplyPayload["channelData"] | null; buildModelsAddProviderChannelData?: (params: { providers: Array<{ id: string; }>; }) => ReplyPayload["channelData"] | null; buildModelsListChannelData?: (params: { provider: string; models: readonly string[]; currentModel?: string; currentPage: number; totalPages: number; pageSize?: number; modelNames?: ReadonlyMap; }) => ReplyPayload["channelData"] | null; buildModelBrowseChannelData?: () => ReplyPayload["channelData"] | null; }; export type ChannelDoctorConfigMutation = { config: OpenClawConfig; changes: string[]; warnings?: string[]; }; export type ChannelDoctorLegacyConfigRule = LegacyConfigRule; export type ChannelDoctorSequenceResult = { changeNotes: string[]; warningNotes: string[]; }; export type ChannelDoctorEmptyAllowlistAccountContext = { account: Record; channelName: string; dmPolicy?: string; effectiveAllowFrom?: Array; parent?: Record; prefix: string; }; export type ChannelDoctorAdapter = { dmAllowFromMode?: "topOnly" | "topOrNested" | "nestedOnly"; groupModel?: "sender" | "route" | "hybrid"; groupAllowFromFallbackToAllowFrom?: boolean; warnOnEmptyGroupSenderAllowlist?: boolean; legacyConfigRules?: LegacyConfigRule[]; normalizeCompatibilityConfig?: (params: { cfg: OpenClawConfig; }) => ChannelDoctorConfigMutation; collectPreviewWarnings?: (params: { cfg: OpenClawConfig; doctorFixCommand: string; env?: NodeJS.ProcessEnv; }) => string[] | Promise; collectMutableAllowlistWarnings?: (params: { cfg: OpenClawConfig; }) => string[] | Promise; repairConfig?: (params: { cfg: OpenClawConfig; doctorFixCommand: string; }) => ChannelDoctorConfigMutation | Promise; runConfigSequence?: (params: { cfg: OpenClawConfig; env: NodeJS.ProcessEnv; shouldRepair: boolean; }) => ChannelDoctorSequenceResult | Promise; cleanStaleConfig?: (params: { cfg: OpenClawConfig; }) => ChannelDoctorConfigMutation | Promise; collectEmptyAllowlistExtraWarnings?: (params: ChannelDoctorEmptyAllowlistAccountContext) => string[]; shouldSkipDefaultEmptyGroupAllowlistWarning?: (params: ChannelDoctorEmptyAllowlistAccountContext) => 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; runStartupMaintenance?: (params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; log: { info?: (message: string) => void; warn?: (message: string) => void; }; trigger?: string; logPrefix?: string; }) => Promise | void; detectLegacyStateMigrations?: (params: { cfg: OpenClawConfig; env: NodeJS.ProcessEnv; stateDir: string; oauthDir: string; }) => ChannelLegacyStateMigrationPlan[] | Promise; }; export type ChannelApprovalDeliveryAdapter = { hasConfiguredDmRoute?: (params: { cfg: OpenClawConfig; }) => boolean; shouldSuppressForwardingFallback?: (params: { cfg: OpenClawConfig; approvalKind: ChannelApprovalKind; target: ChannelApprovalForwardTarget; request: ExecApprovalRequest; }) => boolean; }; export type ChannelApproveCommandBehavior = { kind: "allow"; } | { kind: "ignore"; } | { kind: "reply"; text: string; }; export type { ChannelApprovalNativeAdapter, ChannelApprovalNativeDeliveryCapabilities, ChannelApprovalNativeDeliveryPreference, ChannelApprovalNativeRequest, ChannelApprovalNativeSurface, ChannelApprovalNativeTarget, } from "./approval-native.types.js"; 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 ChannelApprovalAdapter = { delivery?: ChannelApprovalDeliveryAdapter; nativeRuntime?: ChannelApprovalNativeRuntimeAdapter; render?: ChannelApprovalRenderAdapter; native?: ChannelApprovalNativeAdapter; describeExecApprovalSetup?: (params: { channel: string; channelLabel: string; accountId?: string; }) => string | null | undefined; }; export type ChannelApprovalCapability = ChannelApprovalAdapter & { 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"; approvalKind?: ChannelApprovalKind; }) => ChannelActionAvailabilityState; /** Exec-native client availability for the initiating surface; distinct from same-chat auth. */ getExecInitiatingSurfaceState?: (params: { cfg: OpenClawConfig; accountId?: string | null; action: "approve"; }) => ChannelActionAvailabilityState; resolveApproveCommandBehavior?: (params: { cfg: OpenClawConfig; accountId?: string | null; senderId?: string | null; approvalKind: ChannelApprovalKind; }) => ChannelApproveCommandBehavior | undefined; }; 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; from?: string; chatType?: string; originatingTo?: string; commandTo?: string; fallbackTo?: string; }; export type ChannelConfiguredBindingProvider = { selfParentConversationByDefault?: boolean; 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; /** * Preferred placement when a command is started from a top-level conversation * without an existing native thread id. * * - `current`: bind/spawn in the current conversation * - `child`: create a child thread/conversation first */ defaultTopLevelPlacement?: "current" | "child"; resolveConversationRef?: (params: { accountId?: string | null; conversationId: string; parentConversationId?: string; threadId?: string | number | null; }) => { conversationId: string; parentConversationId?: string; } | null; buildBoundReplyPayload?: (params: { operation: "acp-spawn"; placement: "current" | "child"; conversation: { channel: string; accountId?: string | null; conversationId: string; parentConversationId?: string; }; }) => Pick | null | Promise | null>; buildModelOverrideParentCandidates?: (params: { parentConversationId?: string | null; }) => string[] | null | undefined; shouldStripThreadFromAnnounceOrigin?: (params: { requester: { channel?: string; to?: string; threadId?: string | number; }; entry: { channel?: string; to?: string; threadId?: string | number; }; }) => 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 = { applyConfigFixes?: (params: { cfg: OpenClawConfig; env: NodeJS.ProcessEnv; }) => ChannelDoctorConfigMutation | Promise; resolveDmPolicy?: ChannelAdapterCallback<(ctx: ChannelSecurityContext) => ChannelSecurityDmPolicy | null>; collectWarnings?: ChannelAdapterCallback<(ctx: ChannelSecurityContext) => Promise | string[]>; collectAuditFindings?: ChannelAdapterCallback<(ctx: ChannelSecurityContext & { sourceConfig: OpenClawConfig; orderedAccountIds: string[]; hasExplicitAccountPath: boolean; }) => Promise> | Array<{ checkId: string; severity: "info" | "warn" | "critical"; title: string; detail: string; remediation?: string; }>>; };