UNPKG

@gguf/claw

Version:

Multi-channel AI gateway with extensible messaging integrations

813 lines (806 loc) 29.4 kB
import { l as escapeRegExp, m as normalizeE164 } from "./utils-CP9YLh6M.js"; import { c as normalizeAccountId } from "./session-key-CZ6OwgSB.js"; import { f as requireActivePluginRegistry, n as CHAT_CHANNEL_ORDER, o as getChatChannelMeta } from "./registry-B-j4DRfe.js"; import { c as resolveSlackAccount, l as resolveSlackReplyToMode, m as resolveDiscordAccount, n as normalizeWhatsAppTarget, o as resolveTelegramAccount } from "./normalize-Ci8UyR3Z.js"; import { n as resolveWhatsAppAccount } from "./accounts-Bj1dJ-fd.js"; import { t as resolveIMessageAccount } from "./accounts-DOl1Wkxo.js"; import { n as resolveSignalAccount } from "./accounts-t6R5_PzC.js"; //#region src/shared/string-normalization.ts function normalizeStringEntries(list) { return (list ?? []).map((entry) => String(entry).trim()).filter(Boolean); } function normalizeStringEntriesLower(list) { return normalizeStringEntries(list).map((entry) => entry.toLowerCase()); } function normalizeHyphenSlug(raw) { const trimmed = raw?.trim().toLowerCase() ?? ""; if (!trimmed) return ""; return trimmed.replace(/\s+/g, "-").replace(/[^a-z0-9#@._+-]+/g, "-").replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, ""); } function normalizeAtHashSlug(raw) { const trimmed = raw?.trim().toLowerCase() ?? ""; if (!trimmed) return ""; return trimmed.replace(/^[@#]+/, "").replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]+/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, ""); } //#endregion //#region src/config/group-policy.ts function resolveChannelGroupConfig(groups, groupId, caseInsensitive = false) { if (!groups) return; const direct = groups[groupId]; if (direct) return direct; if (!caseInsensitive) return; const target = groupId.toLowerCase(); const matchedKey = Object.keys(groups).find((key) => key !== "*" && key.toLowerCase() === target); if (!matchedKey) return; return groups[matchedKey]; } function normalizeSenderKey(value) { const trimmed = value.trim(); if (!trimmed) return ""; return (trimmed.startsWith("@") ? trimmed.slice(1) : trimmed).toLowerCase(); } function resolveToolsBySender(params) { const toolsBySender = params.toolsBySender; if (!toolsBySender) return; const entries = Object.entries(toolsBySender); if (entries.length === 0) return; const normalized = /* @__PURE__ */ new Map(); let wildcard; for (const [rawKey, policy] of entries) { if (!policy) continue; const key = normalizeSenderKey(rawKey); if (!key) continue; if (key === "*") { wildcard = policy; continue; } if (!normalized.has(key)) normalized.set(key, policy); } const candidates = []; const pushCandidate = (value) => { const trimmed = value?.trim(); if (!trimmed) return; candidates.push(trimmed); }; pushCandidate(params.senderId); pushCandidate(params.senderE164); pushCandidate(params.senderUsername); pushCandidate(params.senderName); for (const candidate of candidates) { const key = normalizeSenderKey(candidate); if (!key) continue; const match = normalized.get(key); if (match) return match; } return wildcard; } function resolveChannelGroups(cfg, channel, accountId) { const normalizedAccountId = normalizeAccountId(accountId); const channelConfig = cfg.channels?.[channel]; if (!channelConfig) return; return channelConfig.accounts?.[normalizedAccountId]?.groups ?? channelConfig.accounts?.[Object.keys(channelConfig.accounts ?? {}).find((key) => key.toLowerCase() === normalizedAccountId.toLowerCase()) ?? ""]?.groups ?? channelConfig.groups; } function resolveChannelGroupPolicy(params) { const { cfg, channel } = params; const groups = resolveChannelGroups(cfg, channel, params.accountId); const allowlistEnabled = Boolean(groups && Object.keys(groups).length > 0); const normalizedId = params.groupId?.trim(); const groupConfig = normalizedId ? resolveChannelGroupConfig(groups, normalizedId, params.groupIdCaseInsensitive) : void 0; const defaultConfig = groups?.["*"]; return { allowlistEnabled, allowed: !allowlistEnabled || allowlistEnabled && Boolean(groups && Object.hasOwn(groups, "*")) || Boolean(groupConfig), groupConfig, defaultConfig }; } function resolveChannelGroupRequireMention(params) { const { requireMentionOverride, overrideOrder = "after-config" } = params; const { groupConfig, defaultConfig } = resolveChannelGroupPolicy(params); const configMention = typeof groupConfig?.requireMention === "boolean" ? groupConfig.requireMention : typeof defaultConfig?.requireMention === "boolean" ? defaultConfig.requireMention : void 0; if (overrideOrder === "before-config" && typeof requireMentionOverride === "boolean") return requireMentionOverride; if (typeof configMention === "boolean") return configMention; if (overrideOrder !== "before-config" && typeof requireMentionOverride === "boolean") return requireMentionOverride; return true; } function resolveChannelGroupToolsPolicy(params) { const { groupConfig, defaultConfig } = resolveChannelGroupPolicy(params); const groupSenderPolicy = resolveToolsBySender({ toolsBySender: groupConfig?.toolsBySender, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); if (groupSenderPolicy) return groupSenderPolicy; if (groupConfig?.tools) return groupConfig.tools; const defaultSenderPolicy = resolveToolsBySender({ toolsBySender: defaultConfig?.toolsBySender, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); if (defaultSenderPolicy) return defaultSenderPolicy; if (defaultConfig?.tools) return defaultConfig.tools; } //#endregion //#region src/slack/threading-tool-context.ts function buildSlackThreadingToolContext(params) { const configuredReplyToMode = resolveSlackReplyToMode(resolveSlackAccount({ cfg: params.cfg, accountId: params.accountId }), params.context.ChatType); const effectiveReplyToMode = params.context.ThreadLabel ? "all" : configuredReplyToMode; const threadId = params.context.MessageThreadId ?? params.context.ReplyToId; return { currentChannelId: params.context.To?.startsWith("channel:") ? params.context.To.slice(8) : void 0, currentThreadTs: threadId != null ? String(threadId) : void 0, replyToMode: effectiveReplyToMode, hasRepliedRef: params.hasRepliedRef }; } //#endregion //#region src/channels/plugins/group-mentions.ts function normalizeDiscordSlug(value) { return normalizeAtHashSlug(value); } function parseTelegramGroupId(value) { const raw = value?.trim() ?? ""; if (!raw) return { chatId: void 0, topicId: void 0 }; const parts = raw.split(":").filter(Boolean); if (parts.length >= 3 && parts[1] === "topic" && /^-?\d+$/.test(parts[0]) && /^\d+$/.test(parts[2])) return { chatId: parts[0], topicId: parts[2] }; if (parts.length >= 2 && /^-?\d+$/.test(parts[0]) && /^\d+$/.test(parts[1])) return { chatId: parts[0], topicId: parts[1] }; return { chatId: raw, topicId: void 0 }; } function resolveTelegramRequireMention(params) { const { cfg, chatId, topicId } = params; if (!chatId) return; const groupConfig = cfg.channels?.telegram?.groups?.[chatId]; const groupDefault = cfg.channels?.telegram?.groups?.["*"]; const topicConfig = topicId && groupConfig?.topics ? groupConfig.topics[topicId] : void 0; const defaultTopicConfig = topicId && groupDefault?.topics ? groupDefault.topics[topicId] : void 0; if (typeof topicConfig?.requireMention === "boolean") return topicConfig.requireMention; if (typeof defaultTopicConfig?.requireMention === "boolean") return defaultTopicConfig.requireMention; if (typeof groupConfig?.requireMention === "boolean") return groupConfig.requireMention; if (typeof groupDefault?.requireMention === "boolean") return groupDefault.requireMention; } function resolveDiscordGuildEntry(guilds, groupSpace) { if (!guilds || Object.keys(guilds).length === 0) return null; const space = groupSpace?.trim() ?? ""; if (space && guilds[space]) return guilds[space]; const normalized = normalizeDiscordSlug(space); if (normalized && guilds[normalized]) return guilds[normalized]; if (normalized) { const match = Object.values(guilds).find((entry) => normalizeDiscordSlug(entry?.slug ?? void 0) === normalized); if (match) return match; } return guilds["*"] ?? null; } function resolveDiscordChannelEntry(channelEntries, params) { if (!channelEntries || Object.keys(channelEntries).length === 0) return; const groupChannel = params.groupChannel; const channelSlug = normalizeDiscordSlug(groupChannel); return (params.groupId ? channelEntries[params.groupId] : void 0) ?? (channelSlug ? channelEntries[channelSlug] ?? channelEntries[`#${channelSlug}`] : void 0) ?? (groupChannel ? channelEntries[normalizeDiscordSlug(groupChannel)] : void 0); } function resolveSlackChannelPolicyEntry(params) { const channels = resolveSlackAccount({ cfg: params.cfg, accountId: params.accountId }).channels ?? {}; if (Object.keys(channels).length === 0) return; const channelId = params.groupId?.trim(); const channelName = params.groupChannel?.replace(/^#/, ""); const normalizedName = normalizeHyphenSlug(channelName); const candidates = [ channelId ?? "", channelName ? `#${channelName}` : "", channelName ?? "", normalizedName ].filter(Boolean); for (const candidate of candidates) if (candidate && channels[candidate]) return channels[candidate]; return channels["*"]; } function resolveTelegramGroupRequireMention(params) { const { chatId, topicId } = parseTelegramGroupId(params.groupId); const requireMention = resolveTelegramRequireMention({ cfg: params.cfg, chatId, topicId }); if (typeof requireMention === "boolean") return requireMention; return resolveChannelGroupRequireMention({ cfg: params.cfg, channel: "telegram", groupId: chatId ?? params.groupId, accountId: params.accountId }); } function resolveWhatsAppGroupRequireMention(params) { return resolveChannelGroupRequireMention({ cfg: params.cfg, channel: "whatsapp", groupId: params.groupId, accountId: params.accountId }); } function resolveIMessageGroupRequireMention(params) { return resolveChannelGroupRequireMention({ cfg: params.cfg, channel: "imessage", groupId: params.groupId, accountId: params.accountId }); } function resolveDiscordGroupRequireMention(params) { const guildEntry = resolveDiscordGuildEntry(params.cfg.channels?.discord?.guilds, params.groupSpace); const channelEntries = guildEntry?.channels; if (channelEntries && Object.keys(channelEntries).length > 0) { const entry = resolveDiscordChannelEntry(channelEntries, params); if (entry && typeof entry.requireMention === "boolean") return entry.requireMention; } if (typeof guildEntry?.requireMention === "boolean") return guildEntry.requireMention; return true; } function resolveGoogleChatGroupRequireMention(params) { return resolveChannelGroupRequireMention({ cfg: params.cfg, channel: "googlechat", groupId: params.groupId, accountId: params.accountId }); } function resolveGoogleChatGroupToolPolicy(params) { return resolveChannelGroupToolsPolicy({ cfg: params.cfg, channel: "googlechat", groupId: params.groupId, accountId: params.accountId, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); } function resolveSlackGroupRequireMention(params) { const resolved = resolveSlackChannelPolicyEntry(params); if (typeof resolved?.requireMention === "boolean") return resolved.requireMention; return true; } function resolveTelegramGroupToolPolicy(params) { const { chatId } = parseTelegramGroupId(params.groupId); return resolveChannelGroupToolsPolicy({ cfg: params.cfg, channel: "telegram", groupId: chatId ?? params.groupId, accountId: params.accountId, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); } function resolveWhatsAppGroupToolPolicy(params) { return resolveChannelGroupToolsPolicy({ cfg: params.cfg, channel: "whatsapp", groupId: params.groupId, accountId: params.accountId, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); } function resolveIMessageGroupToolPolicy(params) { return resolveChannelGroupToolsPolicy({ cfg: params.cfg, channel: "imessage", groupId: params.groupId, accountId: params.accountId, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); } function resolveDiscordGroupToolPolicy(params) { const guildEntry = resolveDiscordGuildEntry(params.cfg.channels?.discord?.guilds, params.groupSpace); const channelEntries = guildEntry?.channels; if (channelEntries && Object.keys(channelEntries).length > 0) { const entry = resolveDiscordChannelEntry(channelEntries, params); const senderPolicy = resolveToolsBySender({ toolsBySender: entry?.toolsBySender, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); if (senderPolicy) return senderPolicy; if (entry?.tools) return entry.tools; } const guildSenderPolicy = resolveToolsBySender({ toolsBySender: guildEntry?.toolsBySender, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); if (guildSenderPolicy) return guildSenderPolicy; if (guildEntry?.tools) return guildEntry.tools; } function resolveSlackGroupToolPolicy(params) { const resolved = resolveSlackChannelPolicyEntry(params); if (!resolved) return; const senderPolicy = resolveToolsBySender({ toolsBySender: resolved?.toolsBySender, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); if (senderPolicy) return senderPolicy; if (resolved?.tools) return resolved.tools; } //#endregion //#region src/channels/plugins/normalize/signal.ts function normalizeSignalMessagingTarget(raw) { const trimmed = raw.trim(); if (!trimmed) return; let normalized = trimmed; if (normalized.toLowerCase().startsWith("signal:")) normalized = normalized.slice(7).trim(); if (!normalized) return; const lower = normalized.toLowerCase(); if (lower.startsWith("group:")) { const id = normalized.slice(6).trim(); return id ? `group:${id}` : void 0; } if (lower.startsWith("username:")) { const id = normalized.slice(9).trim(); return id ? `username:${id}`.toLowerCase() : void 0; } if (lower.startsWith("u:")) { const id = normalized.slice(2).trim(); return id ? `username:${id}`.toLowerCase() : void 0; } if (lower.startsWith("uuid:")) { const id = normalized.slice(5).trim(); return id ? id.toLowerCase() : void 0; } return normalized.toLowerCase(); } //#endregion //#region src/channels/dock.ts const formatLower = (allowFrom) => allowFrom.map((entry) => String(entry).trim()).filter(Boolean).map((entry) => entry.toLowerCase()); const formatDiscordAllowFrom = (allowFrom) => allowFrom.map((entry) => String(entry).trim().replace(/^<@!?/, "").replace(/>$/, "").replace(/^discord:/i, "").replace(/^user:/i, "").replace(/^pk:/i, "").trim().toLowerCase()).filter(Boolean); function resolveDirectOrGroupChannelId(context) { return (context.ChatType?.toLowerCase() === "direct" ? context.From ?? context.To : context.To)?.trim() || void 0; } function buildSignalThreadToolContext(params) { const currentChannelIdRaw = resolveDirectOrGroupChannelId(params.context); return { currentChannelId: currentChannelIdRaw ? normalizeSignalMessagingTarget(currentChannelIdRaw) ?? currentChannelIdRaw.trim() : void 0, currentThreadTs: params.context.ReplyToId, hasRepliedRef: params.hasRepliedRef }; } function buildIMessageThreadToolContext(params) { return { currentChannelId: resolveDirectOrGroupChannelId(params.context), currentThreadTs: params.context.ReplyToId, hasRepliedRef: params.hasRepliedRef }; } function resolveCaseInsensitiveAccount(accounts, accountId) { if (!accounts) return; const normalized = normalizeAccountId(accountId); return accounts[normalized] ?? accounts[Object.keys(accounts).find((key) => key.toLowerCase() === normalized.toLowerCase()) ?? ""]; } const DOCKS = { telegram: { id: "telegram", capabilities: { chatTypes: [ "direct", "group", "channel", "thread" ], nativeCommands: true, blockStreaming: true }, outbound: { textChunkLimit: 4e3 }, config: { resolveAllowFrom: ({ cfg, accountId }) => (resolveTelegramAccount({ cfg, accountId }).config.allowFrom ?? []).map((entry) => String(entry)), formatAllowFrom: ({ allowFrom }) => allowFrom.map((entry) => String(entry).trim()).filter(Boolean).map((entry) => entry.replace(/^(telegram|tg):/i, "")).map((entry) => entry.toLowerCase()), resolveDefaultTo: ({ cfg, accountId }) => { const val = resolveTelegramAccount({ cfg, accountId }).config.defaultTo; return val != null ? String(val) : void 0; } }, groups: { resolveRequireMention: resolveTelegramGroupRequireMention, resolveToolPolicy: resolveTelegramGroupToolPolicy }, threading: { resolveReplyToMode: ({ cfg }) => cfg.channels?.telegram?.replyToMode ?? "off", buildToolContext: ({ context, hasRepliedRef }) => { const threadId = context.MessageThreadId ?? context.ReplyToId; return { currentChannelId: context.To?.trim() || void 0, currentThreadTs: threadId != null ? String(threadId) : void 0, hasRepliedRef }; } } }, whatsapp: { id: "whatsapp", capabilities: { chatTypes: ["direct", "group"], polls: true, reactions: true, media: true }, commands: { enforceOwnerForCommands: true, skipWhenConfigEmpty: true }, outbound: { textChunkLimit: 4e3 }, config: { resolveAllowFrom: ({ cfg, accountId }) => resolveWhatsAppAccount({ cfg, accountId }).allowFrom ?? [], formatAllowFrom: ({ allowFrom }) => allowFrom.map((entry) => String(entry).trim()).filter((entry) => Boolean(entry)).map((entry) => entry === "*" ? entry : normalizeWhatsAppTarget(entry)).filter((entry) => Boolean(entry)), resolveDefaultTo: ({ cfg, accountId }) => { const root = cfg.channels?.whatsapp; const normalized = normalizeAccountId(accountId); return ((root?.accounts?.[normalized])?.defaultTo ?? root?.defaultTo)?.trim() || void 0; } }, groups: { resolveRequireMention: resolveWhatsAppGroupRequireMention, resolveToolPolicy: resolveWhatsAppGroupToolPolicy, resolveGroupIntroHint: () => "WhatsApp IDs: SenderId is the participant JID (group participant id)." }, mentions: { stripPatterns: ({ ctx }) => { const selfE164 = (ctx.To ?? "").replace(/^whatsapp:/, ""); if (!selfE164) return []; const escaped = escapeRegExp(selfE164); return [escaped, `@${escaped}`]; } }, threading: { buildToolContext: ({ context, hasRepliedRef }) => { return { currentChannelId: context.From?.trim() || context.To?.trim() || void 0, currentThreadTs: context.ReplyToId, hasRepliedRef }; } } }, discord: { id: "discord", capabilities: { chatTypes: [ "direct", "channel", "thread" ], polls: true, reactions: true, media: true, nativeCommands: true, threads: true }, outbound: { textChunkLimit: 2e3 }, streaming: { blockStreamingCoalesceDefaults: { minChars: 1500, idleMs: 1e3 } }, elevated: { allowFromFallback: ({ cfg }) => cfg.channels?.discord?.allowFrom ?? cfg.channels?.discord?.dm?.allowFrom }, config: { resolveAllowFrom: ({ cfg, accountId }) => { const account = resolveDiscordAccount({ cfg, accountId }); return (account.config.allowFrom ?? account.config.dm?.allowFrom ?? []).map((entry) => String(entry)); }, formatAllowFrom: ({ allowFrom }) => formatDiscordAllowFrom(allowFrom), resolveDefaultTo: ({ cfg, accountId }) => resolveDiscordAccount({ cfg, accountId }).config.defaultTo?.trim() || void 0 }, groups: { resolveRequireMention: resolveDiscordGroupRequireMention, resolveToolPolicy: resolveDiscordGroupToolPolicy }, mentions: { stripPatterns: () => ["<@!?\\d+>"] }, threading: { resolveReplyToMode: ({ cfg }) => cfg.channels?.discord?.replyToMode ?? "off", buildToolContext: ({ context, hasRepliedRef }) => ({ currentChannelId: context.To?.trim() || void 0, currentThreadTs: context.ReplyToId, hasRepliedRef }) } }, irc: { id: "irc", capabilities: { chatTypes: ["direct", "group"], media: true, blockStreaming: true }, outbound: { textChunkLimit: 350 }, streaming: { blockStreamingCoalesceDefaults: { minChars: 300, idleMs: 1e3 } }, config: { resolveAllowFrom: ({ cfg, accountId }) => { const channel = cfg.channels?.irc; return (resolveCaseInsensitiveAccount(channel?.accounts, accountId)?.allowFrom ?? channel?.allowFrom ?? []).map((entry) => String(entry)); }, formatAllowFrom: ({ allowFrom }) => allowFrom.map((entry) => String(entry).trim()).filter(Boolean).map((entry) => entry.replace(/^irc:/i, "").replace(/^user:/i, "").toLowerCase()), resolveDefaultTo: ({ cfg, accountId }) => { const channel = cfg.channels?.irc; const normalized = normalizeAccountId(accountId); return ((channel?.accounts?.[normalized] ?? channel?.accounts?.[Object.keys(channel?.accounts ?? {}).find((key) => key.toLowerCase() === normalized.toLowerCase()) ?? ""])?.defaultTo ?? channel?.defaultTo)?.trim() || void 0; } }, groups: { resolveRequireMention: ({ cfg, accountId, groupId }) => { if (!groupId) return true; return resolveChannelGroupRequireMention({ cfg, channel: "irc", groupId, accountId, groupIdCaseInsensitive: true }); }, resolveToolPolicy: ({ cfg, accountId, groupId, senderId, senderName, senderUsername }) => { if (!groupId) return; return resolveChannelGroupToolsPolicy({ cfg, channel: "irc", groupId, accountId, groupIdCaseInsensitive: true, senderId, senderName, senderUsername }); } } }, googlechat: { id: "googlechat", capabilities: { chatTypes: [ "direct", "group", "thread" ], reactions: true, media: true, threads: true, blockStreaming: true }, outbound: { textChunkLimit: 4e3 }, config: { resolveAllowFrom: ({ cfg, accountId }) => { const channel = cfg.channels?.googlechat; return (resolveCaseInsensitiveAccount(channel?.accounts, accountId)?.dm?.allowFrom ?? channel?.dm?.allowFrom ?? []).map((entry) => String(entry)); }, formatAllowFrom: ({ allowFrom }) => allowFrom.map((entry) => String(entry).trim()).filter(Boolean).map((entry) => entry.replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:/i, "").replace(/^users\//i, "").toLowerCase()), resolveDefaultTo: ({ cfg, accountId }) => { const channel = cfg.channels?.googlechat; const normalized = normalizeAccountId(accountId); return ((channel?.accounts?.[normalized] ?? channel?.accounts?.[Object.keys(channel?.accounts ?? {}).find((key) => key.toLowerCase() === normalized.toLowerCase()) ?? ""])?.defaultTo ?? channel?.defaultTo)?.trim() || void 0; } }, groups: { resolveRequireMention: resolveGoogleChatGroupRequireMention, resolveToolPolicy: resolveGoogleChatGroupToolPolicy }, threading: { resolveReplyToMode: ({ cfg }) => cfg.channels?.googlechat?.replyToMode ?? "off", buildToolContext: ({ context, hasRepliedRef }) => { const threadId = context.MessageThreadId ?? context.ReplyToId; return { currentChannelId: context.To?.trim() || void 0, currentThreadTs: threadId != null ? String(threadId) : void 0, hasRepliedRef }; } } }, slack: { id: "slack", capabilities: { chatTypes: [ "direct", "channel", "thread" ], reactions: true, media: true, nativeCommands: true, threads: true }, outbound: { textChunkLimit: 4e3 }, streaming: { blockStreamingCoalesceDefaults: { minChars: 1500, idleMs: 1e3 } }, config: { resolveAllowFrom: ({ cfg, accountId }) => { const account = resolveSlackAccount({ cfg, accountId }); return (account.config.allowFrom ?? account.dm?.allowFrom ?? []).map((entry) => String(entry)); }, formatAllowFrom: ({ allowFrom }) => formatLower(allowFrom), resolveDefaultTo: ({ cfg, accountId }) => resolveSlackAccount({ cfg, accountId }).config.defaultTo?.trim() || void 0 }, groups: { resolveRequireMention: resolveSlackGroupRequireMention, resolveToolPolicy: resolveSlackGroupToolPolicy }, mentions: { stripPatterns: () => ["<@[^>]+>"] }, threading: { resolveReplyToMode: ({ cfg, accountId, chatType }) => resolveSlackReplyToMode(resolveSlackAccount({ cfg, accountId }), chatType), allowExplicitReplyTagsWhenOff: true, buildToolContext: (params) => buildSlackThreadingToolContext(params) } }, signal: { id: "signal", capabilities: { chatTypes: ["direct", "group"], reactions: true, media: true }, outbound: { textChunkLimit: 4e3 }, streaming: { blockStreamingCoalesceDefaults: { minChars: 1500, idleMs: 1e3 } }, config: { resolveAllowFrom: ({ cfg, accountId }) => (resolveSignalAccount({ cfg, accountId }).config.allowFrom ?? []).map((entry) => String(entry)), formatAllowFrom: ({ allowFrom }) => allowFrom.map((entry) => String(entry).trim()).filter(Boolean).map((entry) => entry === "*" ? "*" : normalizeE164(entry.replace(/^signal:/i, ""))).filter(Boolean), resolveDefaultTo: ({ cfg, accountId }) => resolveSignalAccount({ cfg, accountId }).config.defaultTo?.trim() || void 0 }, threading: { buildToolContext: ({ context, hasRepliedRef }) => buildSignalThreadToolContext({ context, hasRepliedRef }) } }, imessage: { id: "imessage", capabilities: { chatTypes: ["direct", "group"], reactions: true, media: true }, outbound: { textChunkLimit: 4e3 }, config: { resolveAllowFrom: ({ cfg, accountId }) => (resolveIMessageAccount({ cfg, accountId }).config.allowFrom ?? []).map((entry) => String(entry)), formatAllowFrom: ({ allowFrom }) => allowFrom.map((entry) => String(entry).trim()).filter(Boolean), resolveDefaultTo: ({ cfg, accountId }) => resolveIMessageAccount({ cfg, accountId }).config.defaultTo?.trim() || void 0 }, groups: { resolveRequireMention: resolveIMessageGroupRequireMention, resolveToolPolicy: resolveIMessageGroupToolPolicy }, threading: { buildToolContext: ({ context, hasRepliedRef }) => buildIMessageThreadToolContext({ context, hasRepliedRef }) } } }; function buildDockFromPlugin(plugin) { return { id: plugin.id, capabilities: plugin.capabilities, commands: plugin.commands, outbound: plugin.outbound?.textChunkLimit ? { textChunkLimit: plugin.outbound.textChunkLimit } : void 0, streaming: plugin.streaming ? { blockStreamingCoalesceDefaults: plugin.streaming.blockStreamingCoalesceDefaults } : void 0, elevated: plugin.elevated, config: plugin.config ? { resolveAllowFrom: plugin.config.resolveAllowFrom, formatAllowFrom: plugin.config.formatAllowFrom, resolveDefaultTo: plugin.config.resolveDefaultTo } : void 0, groups: plugin.groups, mentions: plugin.mentions, threading: plugin.threading, agentPrompt: plugin.agentPrompt }; } function listPluginDockEntries() { const registry = requireActivePluginRegistry(); const entries = []; const seen = /* @__PURE__ */ new Set(); for (const entry of registry.channels) { const plugin = entry.plugin; const id = String(plugin.id).trim(); if (!id || seen.has(id)) continue; seen.add(id); if (CHAT_CHANNEL_ORDER.includes(plugin.id)) continue; const dock = entry.dock ?? buildDockFromPlugin(plugin); entries.push({ id: plugin.id, dock, order: plugin.meta.order }); } return entries; } function listChannelDocks() { const baseEntries = CHAT_CHANNEL_ORDER.map((id) => ({ id, dock: DOCKS[id], order: getChatChannelMeta(id).order })); const pluginEntries = listPluginDockEntries(); const combined = [...baseEntries, ...pluginEntries]; combined.sort((a, b) => { const indexA = CHAT_CHANNEL_ORDER.indexOf(a.id); const indexB = CHAT_CHANNEL_ORDER.indexOf(b.id); const orderA = a.order ?? (indexA === -1 ? 999 : indexA); const orderB = b.order ?? (indexB === -1 ? 999 : indexB); if (orderA !== orderB) return orderA - orderB; return String(a.id).localeCompare(String(b.id)); }); return combined.map((entry) => entry.dock); } function getChannelDock(id) { const core = DOCKS[id]; if (core) return core; const pluginEntry = requireActivePluginRegistry().channels.find((entry) => entry.plugin.id === id); if (!pluginEntry) return; return pluginEntry.dock ?? buildDockFromPlugin(pluginEntry.plugin); } //#endregion export { resolveChannelGroupRequireMention as a, normalizeHyphenSlug as c, resolveChannelGroupPolicy as i, normalizeStringEntries as l, listChannelDocks as n, resolveChannelGroupToolsPolicy as o, normalizeSignalMessagingTarget as r, normalizeAtHashSlug as s, getChannelDock as t, normalizeStringEntriesLower as u };