UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

1,157 lines (1,131 loc) 2.48 MB
import { $ as setActivePluginRegistry, A as truncateUtf16Safe, B as theme, C as resolveJidToE164, D as sleep, E as shortenHomePath, F as shouldLogVerbose, H as registerLogTransport, I as success, J as normalizeAnyChannelId, K as CHAT_CHANNEL_ORDER, L as warn, M as info, N as logVerbose, O as sliceUtf16Safe, P as setVerbose, Q as requireActivePluginRegistry, R as colorize, T as shortenHomeInString, V as getChildLogger, W as normalizeLogLevel, X as normalizeChatChannelId, Y as normalizeChannelId, Z as getActivePluginRegistry, _ as ensureDir$3, a as logDebug, b as jidToE164, c as logWarn, d as clearActiveProgressLine, f as registerActiveProgressLine, h as clampInt, i as spawnWithFallback, j as danger, k as toWhatsappJid, l as createSubsystemLogger, m as CONFIG_DIR, n as runExec, o as logError, p as unregisterActiveProgressLine, q as getChatChannelMeta, r as formatSpawnError, s as logInfo, t as runCommandWithTimeout, u as defaultRuntime, v as formatTerminalLink, w as resolveUserPath, x as normalizeE164, y as isSelfChatMode, z as isRich } from "./exec-CjyVHUuE.js"; import { $ as requireOpenAllowFrom, A as logoutWeb, B as IMessageConfigSchema, C as VERSION, D as resolveWhatsAppAuthDir, E as resolveWhatsAppAccount, G as ToolPolicySchema, H as SignalConfigSchema, I as webAuthExists, J as DmPolicySchema, K as BlockStreamingCoalesceSchema, L as WhatsAppConfigSchema, M as readWebSelfId, O as getWebAuthAgeMs, Q as normalizeAllowFrom, R as DiscordConfigSchema, S as resolveAgentMaxConcurrent, T as resolveDefaultWhatsAppAccountId, U as SlackConfigSchema, V as MSTeamsConfigSchema, W as TelegramConfigSchema, X as MarkdownConfigSchema, Y as GroupPolicySchema, Z as MarkdownTableModeSchema, _ as unsetConfigOverride, a as validateConfigObjectWithPlugins, b as setConfigValueAtPath, c as discoverOpenClawPlugins, d as normalizePluginsConfig, et as isSafeExecutableValue, f as resolveEnableState, g as setConfigOverride, h as resetConfigOverrides, i as writeConfigFile, it as resolveTelegramCustomCommands, k as logWebSelfId, m as getConfigOverrides, n as readConfigFileSnapshot, nt as TELEGRAM_COMMAND_NAME_PATTERN, o as validateJsonSchemaValue, p as resolveMemorySlotDecision, q as DmConfigSchema, r as resolveConfigSnapshotHash, rt as normalizeTelegramCommandName, s as loadPluginManifestRegistry, t as loadConfig, tt as parseDurationMs, v as getConfigValueAtPath, w as listWhatsAppAccountIds, x as unsetConfigValueAtPath, y as parseConfigPath, z as GoogleChatConfigSchema } from "./config-D5CsedNn.js"; import { A as normalizeAccountId, D as buildAgentMainSessionKey, E as DEFAULT_MAIN_KEY, F as sanitizeAgentId, I as isAcpSessionKey, L as isSubagentSessionKey, M as normalizeMainKey, N as resolveAgentIdFromSessionKey, O as buildAgentPeerSessionKey, P as resolveThreadSessionKeys, R as parseAgentSessionKey, S as resolveOpenClawPackageRoot, T as DEFAULT_AGENT_ID, b as filterBootstrapFilesForSession, c as resolveDefaultAgentId, f as DEFAULT_AGENT_WORKSPACE_DIR, i as resolveAgentModelFallbacksOverride, j as normalizeAgentId, k as buildGroupHistoryKey, l as resolveSessionAgentId, n as resolveAgentConfig, o as resolveAgentSkillsFilter, p as DEFAULT_BOOTSTRAP_FILENAME, r as resolveAgentDir, s as resolveAgentWorkspaceDir, t as listAgentIds, u as resolveSessionAgentIds, w as DEFAULT_ACCOUNT_ID, x as loadWorkspaceBootstrapFiles, y as ensureAgentWorkspace, z as resolveThreadParentSessionKey } from "./agent-scope-qNPdmst1.js"; import { a as resolveOAuthDir, i as resolveGatewayPort, n as resolveConfigPath, s as resolveStateDir, t as STATE_DIR } from "./paths-BFxmmTT5.js"; import { A as chunkMarkdownTextWithMode, B as MediaFetchError, C as markdownToIR, D as wrapFetchWithAbortSignal, E as resolveFetch, F as findFenceSpanAt, G as SILENT_REPLY_TOKEN, H as fetchWithSsrFGuard, I as isSafeFenceBreak, K as isSilentReplyText, L as parseFenceSpans, M as chunkTextWithMode, N as resolveChunkMode, O as chunkByNewline, P as resolveTextChunkLimit, R as loadWebMedia, S as chunkMarkdownIR, T as resolveMarkdownTableMode, U as resolveChannelMediaMaxBytes, V as fetchRemoteMedia, W as HEARTBEAT_TOKEN, _ as sendReadReceiptSignal, a as filterMessagingToolDuplicates, b as signalRpcRequest, c as createReplyToModeFilterForChannel, d as normalizeChannelTargetInput, f as normalizeTargetForProvider, g as sendMessageSignal, h as parseInlineDirectives$1, i as applyReplyThreading, j as chunkText, k as chunkMarkdownText, l as resolveReplyToMode, m as splitMediaFromOutput, n as normalizeReplyPayloadsForDelivery, o as isRenderablePayload, p as parseReplyDirectives, r as applyReplyTagsToPayload, s as shouldSuppressMessagingToolReplies, t as deliverOutboundPayloads, u as buildTargetResolverSignature, v as sendTypingSignal, w as markdownToIRWithMeta, x as streamSignalEvents, y as signalCheck, z as loadWebMediaRaw } from "./deliver-Dl2ednkv.js"; import { $ as sanitizeGoogleTurnOrdering, $n as normalizeWhatsAppTarget, $t as resolveSessionResetPolicy, A as resolveSandboxRuntimeStatus, An as normalizeSlackMessagingTarget, Ar as resolveToolsBySender, At as isGifMedia, B as applySkillEnvOverrides, Bn as resolveDiscordGroupRequireMention, Bt as updateLastRoute, C as isRawApiErrorPayload, Cn as listSlackDirectoryGroupsFromConfig, Cr as listEnabledDiscordAccounts, D as sanitizeUserFacingText, Dn as listWhatsAppDirectoryGroupsFromConfig, Dr as resolveChannelGroupPolicy, Dt as getFileExtension, E as parseImageSizeError, En as listTelegramDirectoryPeersFromConfig, Er as normalizeDiscordToken, Et as extensionForMime, F as buildWorkspaceSkillCommandSpecs, Fn as requireTargetKind, Ft as appendAssistantMessageToSessionTranscript, G as expandPolicyWithPluginGroups, Gn as resolveIMessageGroupToolPolicy, Gt as deliveryContextFromSession, H as resolveSandboxConfigForAgent, Hn as resolveGoogleChatGroupRequireMention, Ht as updateSessionStoreEntry, I as buildWorkspaceSkillSnapshot, In as getChannelDock, J as resolveToolProfilePolicy, Jn as resolveTelegramGroupRequireMention, Jt as normalizeDeliveryContext, K as expandToolGroups, Kn as resolveSlackGroupRequireMention, Kt as deliveryContextKey, L as loadWorkspaceSkillEntries, Ln as listChannelDocks, Lt as loadSessionStore, M as registerBrowserRoutes, Mn as resolveSlackChannelId, Mt as MAX_IMAGE_BYTES, N as resolveBrowserConfig, Nn as buildMessagingTarget, O as ensureSandboxWorkspaceForSession, On as listWhatsAppDirectoryPeersFromConfig, Or as resolveChannelGroupRequireMention, Ot as imageMimeFromFormat, P as resolveProfile, Pn as ensureTargetId, Pt as mediaKindFromMime, Q as resolveBootstrapMaxChars, Qn as isWhatsAppGroupJid, Qt as resolveChannelResetConfig, R as resolveSkillsPromptForRun, Rn as resolveBlueBubblesGroupRequireMention, Rt as readSessionUpdatedAt, S as isRateLimitAssistantError, Sn as listDiscordDirectoryPeersFromConfig, Sr as listDiscordAccountIds, T as parseImageDimensionError, Tn as listTelegramDirectoryGroupsFromConfig, Tr as resolveDiscordAccount, Tt as detectMime, U as buildPluginToolGroups, Un as resolveGoogleChatGroupToolPolicy, Ut as isCacheEnabled, V as applySkillEnvOverridesFromSnapshot, Vn as resolveDiscordGroupToolPolicy, Vt as updateSessionStore, W as collectExplicitAllowlist, Wn as resolveIMessageGroupRequireMention, Wt as resolveCacheTtlMs$1, X as buildBootstrapContextFiles, Xn as resolveWhatsAppGroupRequireMention, Xt as resolveSessionKey$1, Y as stripPluginOnlyAllowlist, Yn as resolveTelegramGroupToolPolicy, Yt as normalizeSessionDeliveryFields, Z as ensureSessionHeader, Zn as resolveWhatsAppGroupToolPolicy, Zt as evaluateSessionFreshness, _ as isCompactionFailureError, _n as GATEWAY_CLIENT_NAMES, _r as resolveDefaultSignalAccountId, a as isMessagingToolDuplicateNormalized, an as resolveConversationLabel, ar as listBindings, at as normalizeThinkLevel, b as isFailoverErrorMessage, bn as normalizeChannelId$1, br as resolveDefaultIMessageAccountId, c as downgradeOpenAIReasoningBlocks, cn as isDeliverableMessageChannel, cr as listSlackAccountIds, ct as resolveResponseUsageMode, d as classifyFailoverReason, dn as listDeliverableMessageChannels, dr as resolveSlackReplyToMode, dt as getMediaDir, en as resolveSessionResetType, er as listEnabledTelegramAccounts, et as formatThinkingLevels, f as formatAssistantErrorText, fn as normalizeMessageChannel, fr as resolveSlackAppToken, ft as saveMediaBuffer, g as isCloudCodeAssistFormatError, gn as GATEWAY_CLIENT_MODES, gr as listSignalAccountIds, h as isAuthAssistantError, hn as GATEWAY_CLIENT_IDS, hr as listEnabledSignalAccounts, in as deriveSessionMetaPatch, ir as resolveTelegramToken, it as normalizeReasoningLevel, j as createBrowserRouteContext, jn as parseSlackTarget, jt as kindFromMime, k as resolveSandboxContext, kn as looksLikeSlackTargetId, kr as resolveChannelGroupToolsPolicy, kt as isAudioFileName, l as isAntigravityClaude, ln as isInternalMessageChannel, lr as resolveDefaultSlackAccountId, lt as supportsXHighThinking, m as getApiErrorPayloadFingerprint, mn as resolveMessageChannel, mr as normalizeChatType, n as validateGeminiTurns, nn as DEFAULT_RESET_TRIGGERS, nr as resolveDefaultTelegramAccountId, nt as listThinkingLevels, o as normalizeTextForComparison, on as resolveGroupSessionKey, or as buildSlackThreadingToolContext, ot as normalizeUsageDisplay, p as formatRawAssistantErrorForUi, pn as resolveGatewayMessageChannel, pr as resolveSlackBotToken, pt as SsrFBlockedError, q as normalizeToolName, qn as resolveSlackGroupToolPolicy, qt as mergeDeliveryContext, r as pickFallbackThinkingLevel, rn as resolveMainSessionKey, rr as resolveTelegramAccount, rt as normalizeElevatedLevel, s as sanitizeSessionMessagesImages, sn as INTERNAL_MESSAGE_CHANNEL, sr as listEnabledSlackAccounts, st as normalizeVerboseLevel, t as validateAnthropicTurns, tn as resolveThreadFlag, tr as listTelegramAccountIds, tt as formatXHighModelHint, u as isGoogleModelApi, un as isMarkdownCapableMessageChannel, ur as resolveSlackAccount, ut as extractOriginalFilename, v as isContextOverflowError, vn as getChannelPlugin, vr as resolveSignalAccount, vt as sanitizeImageBlocks, w as isTimeoutErrorMessage, wn as listSlackDirectoryPeersFromConfig, wr as resolveDefaultDiscordAccountId, wt as resizeToJpeg, x as isLikelyContextOverflowError, xn as listDiscordDirectoryGroupsFromConfig, xr as resolveIMessageAccount, xt as getImageMetadata, y as isFailoverAssistantError, yn as listChannelPlugins, yr as listIMessageAccountIds, yt as sanitizeToolResultImages, z as resolvePluginSkillDirs, zn as resolveBlueBubblesGroupToolPolicy, zt as recordSessionMetaFromInbound } from "./pi-embedded-helpers-fm5_Eokw.js"; import { A as listProfilesForProvider, B as getShellPathFromLoginShell, D as resolveApiKeyForProfile, E as markAuthProfileUsed, F as resolveOpenClawAgentDir, G as isTruthyEnvValue, H as resolveShellEnvFallbackTimeoutMs, I as resolveAuthProfileDisplayLabel, K as parseBooleanValue$1, L as DEFAULT_CONTEXT_TOKENS, M as ensureAuthProfileStore, P as resolveAuthStorePathForDisplay, R as DEFAULT_MODEL, S as resolveAuthProfileOrder, T as markAuthProfileFailure, _ as getCustomProviderApiKey, a as normalizeProviderId, b as resolveEnvApiKey, c as resolveDefaultModelForAgent, d as normalizeGoogleModelId, g as getApiKeyForModel, i as modelKey, j as markAuthProfileGood, l as resolveModelRefFromString, n as buildModelAliasIndex, o as parseModelRef, r as isCliProvider, s as resolveConfiguredModelRef, t as buildAllowedModelSet, u as resolveThinkingDefault, v as requireApiKey, w as isProfileInCooldown, x as resolveModelAuthMode, y as resolveApiKeyForProvider, z as DEFAULT_PROVIDER } from "./model-selection-Bo7pocNu.js"; import { n as resolveCliName, t as formatCliCommand } from "./command-format-CFzL448l.js"; import { a as saveJsonFile, i as loadJsonFile } from "./github-copilot-token-BoPqI-5_.js"; import { a as resolveStorePath, i as resolveSessionTranscriptsDirForAgent, n as resolveSessionFilePath, r as resolveSessionTranscriptPath } from "./paths-Bkjq_KcG.js"; import { t as emitSessionTranscriptUpdate } from "./transcript-events-DOI14YeV.js"; import { D as formatErrorMessage$1, E as extractErrorCode, O as formatUncaughtError, T as rawDataToString, h as ensureChromeExtensionRelayServer, y as DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "./chrome-DMuEXuKT.js"; import { i as retryAsync, n as resolveMemorySearchConfig, r as resolveRetryConfig } from "./manager-WCGzgWdf.js"; import { _ as ensureOpenClawModelsJson, a as resolveProviderVisionModelFromConfig, c as extractThinkingFromTaggedStream, d as inferToolMetaFromArgs, f as promoteThinkingTagsToBlocks, g as resolveToolDisplay, h as stripThinkingTagsFromText, i as decodeDataUrl, l as extractThinkingFromTaggedText, m as stripMinimaxToolCallXml, n as coerceImageAssistantText, o as extractAssistantText$1, p as stripDowngradedToolCallText, r as coerceImageModelConfig, s as extractAssistantThinking, t as describeImageWithModel, u as formatReasoningMessage, v as minimaxUnderstandImage } from "./image-B_t0xXCb.js"; import { i as discoverModels, r as discoverAuthStorage } from "./pi-model-discovery-CLgDNnGq.js"; import { t as redactSensitiveText } from "./redact-wH73ib3-.js"; import { c as listMemoryFiles, l as normalizeExtraMemoryPaths } from "./sqlite-CqVsQPIl.js"; import { a as getStatusCode$1, i as formatError$1, n as waitForWebLogin, o as waitForWaConnection, r as createWaSocket, t as startWebLoginWithQr } from "./login-qr-BVBRxQRt.js"; import { createRequire } from "node:module"; import { z } from "zod"; import * as os$1 from "node:os"; import os, { homedir, tmpdir } from "node:os"; import * as path$1 from "node:path"; import path from "node:path"; import fs, { constants, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs"; import * as fs$2 from "node:fs/promises"; import fs$1 from "node:fs/promises"; import { execFile, execSync, spawn, spawnSync } from "node:child_process"; import { inspect, promisify } from "node:util"; import { fileURLToPath } from "node:url"; import crypto, { X509Certificate, randomUUID } from "node:crypto"; import lockfile from "proper-lockfile"; import { complete, completeSimple, streamSimple } from "@mariozechner/pi-ai"; import AjvPkg from "ajv"; import { CURRENT_SESSION_VERSION, SessionManager, SettingsManager, codingTools, createAgentSession, createEditTool, createReadTool, createWriteTool, estimateTokens, readTool } from "@mariozechner/pi-coding-agent"; import { Type } from "@sinclair/typebox"; import { createServer } from "node:http"; import { request } from "node:https"; import { pipeline } from "node:stream/promises"; import { ProxyAgent, fetch as fetch$1 } from "undici"; import { ApplicationCommandOptionType, ButtonStyle, ChannelType, PermissionFlagsBits, Routes } from "discord-api-types/v10"; import { Button, ChannelType as ChannelType$1, Client, Command, MessageCreateListener, MessageReactionAddListener, MessageReactionRemoveListener, MessageType, PresenceUpdateListener, RateLimitError, RequestClient, Row } from "@buape/carbon"; import { PollLayoutType } from "discord-api-types/payloads/v10"; import { spinner } from "@clack/prompts"; import { WebSocket as WebSocket$1 } from "ws"; import { Buffer as Buffer$1 } from "node:buffer"; import { WebClient } from "@slack/web-api"; import * as net$1 from "node:net"; import net from "node:net"; import { EdgeTTS } from "node-edge-tts"; import { createJiti } from "jiti"; import chokidar from "chokidar"; import { API_CONSTANTS, Bot, GrammyError, HttpError, InputFile, webhookCallback } from "grammy"; import process$1 from "node:process"; import { createOscProgressController, supportsOscProgress } from "osc-progress"; import { GatewayIntents, GatewayPlugin } from "@buape/carbon/gateway"; import { setTimeout as setTimeout$1 } from "node:timers/promises"; import readline, { createInterface } from "node:readline"; import { messagingApi } from "@line/bot-sdk"; import SlackBolt from "@slack/bolt"; import { run, sequentialize } from "@grammyjs/runner"; import { apiThrottler } from "@grammyjs/transformer-throttler"; import { DisconnectReason, downloadMediaMessage, extractMessageContent, getContentType, isJidGroup, normalizeMessageContent } from "@whiskeysockets/baileys"; import { EventEmitter } from "node:events"; import * as Lark from "@larksuiteoapi/node-sdk"; //#region src/channels/plugins/message-action-names.ts const CHANNEL_MESSAGE_ACTION_NAMES = [ "send", "broadcast", "poll", "react", "reactions", "read", "edit", "unsend", "reply", "sendWithEffect", "renameGroup", "setGroupIcon", "addParticipant", "removeParticipant", "leaveGroup", "sendAttachment", "delete", "pin", "unpin", "list-pins", "permissions", "thread-create", "thread-list", "thread-reply", "search", "sticker", "sticker-search", "member-info", "role-info", "emoji-list", "emoji-upload", "sticker-upload", "role-add", "role-remove", "channel-info", "channel-list", "channel-create", "channel-edit", "channel-delete", "channel-move", "category-create", "category-edit", "category-delete", "voice-status", "event-list", "event-create", "timeout", "kick", "ban", "set-presence" ]; //#endregion //#region src/channels/plugins/bluebubbles-actions.ts const BLUEBUBBLES_ACTIONS = { react: { gate: "reactions" }, edit: { gate: "edit", unsupportedOnMacOS26: true }, unsend: { gate: "unsend" }, reply: { gate: "reply" }, sendWithEffect: { gate: "sendWithEffect" }, renameGroup: { gate: "renameGroup", groupOnly: true }, setGroupIcon: { gate: "setGroupIcon", groupOnly: true }, addParticipant: { gate: "addParticipant", groupOnly: true }, removeParticipant: { gate: "removeParticipant", groupOnly: true }, leaveGroup: { gate: "leaveGroup", groupOnly: true }, sendAttachment: { gate: "sendAttachment" } }; const BLUEBUBBLES_ACTION_SPECS = BLUEBUBBLES_ACTIONS; const BLUEBUBBLES_ACTION_NAMES = Object.keys(BLUEBUBBLES_ACTIONS); const BLUEBUBBLES_GROUP_ACTIONS = new Set(BLUEBUBBLES_ACTION_NAMES.filter((action) => BLUEBUBBLES_ACTION_SPECS[action]?.groupOnly)); //#endregion //#region src/plugins/http-path.ts function normalizePluginHttpPath(path, fallback) { const trimmed = path?.trim(); if (!trimmed) { const fallbackTrimmed = fallback?.trim(); if (!fallbackTrimmed) return null; return fallbackTrimmed.startsWith("/") ? fallbackTrimmed : `/${fallbackTrimmed}`; } return trimmed.startsWith("/") ? trimmed : `/${trimmed}`; } //#endregion //#region src/plugins/http-registry.ts function registerPluginHttpRoute(params) { const registry = params.registry ?? requireActivePluginRegistry(); const routes = registry.httpRoutes ?? []; registry.httpRoutes = routes; const normalizedPath = normalizePluginHttpPath(params.path, params.fallbackPath); const suffix = params.accountId ? ` for account "${params.accountId}"` : ""; if (!normalizedPath) { params.log?.(`plugin: webhook path missing${suffix}`); return () => {}; } if (routes.some((entry) => entry.path === normalizedPath)) { const pluginHint = params.pluginId ? ` (${params.pluginId})` : ""; params.log?.(`plugin: webhook path ${normalizedPath} already registered${suffix}${pluginHint}`); return () => {}; } const entry = { path: normalizedPath, handler: params.handler, pluginId: params.pluginId, source: params.source }; routes.push(entry); return () => { const index = routes.indexOf(entry); if (index >= 0) routes.splice(index, 1); }; } //#endregion //#region src/plugins/config-schema.ts function error(message) { return { success: false, error: { issues: [{ path: [], message }] } }; } function emptyPluginConfigSchema() { return { safeParse(value) { if (value === void 0) return { success: true, data: void 0 }; if (!value || typeof value !== "object" || Array.isArray(value)) return error("expected config object"); if (Object.keys(value).length > 0) return error("config must be empty"); return { success: true, data: value }; }, jsonSchema: { type: "object", additionalProperties: false, properties: {} } }; } //#endregion //#region src/agents/identity.ts const DEFAULT_ACK_REACTION = "👀"; function resolveAgentIdentity(cfg, agentId) { return resolveAgentConfig(cfg, agentId)?.identity; } function resolveAckReaction(cfg, agentId) { const configured = cfg.messages?.ackReaction; if (configured !== void 0) return configured.trim(); return resolveAgentIdentity(cfg, agentId)?.emoji?.trim() || DEFAULT_ACK_REACTION; } function resolveIdentityNamePrefix(cfg, agentId) { const name = resolveAgentIdentity(cfg, agentId)?.name?.trim(); if (!name) return; return `[${name}]`; } /** Returns just the identity name (without brackets) for template context. */ function resolveIdentityName(cfg, agentId) { return resolveAgentIdentity(cfg, agentId)?.name?.trim() || void 0; } function resolveMessagePrefix(cfg, agentId, opts) { const configured = opts?.configured ?? cfg.messages?.messagePrefix; if (configured !== void 0) return configured; if (opts?.hasAllowFrom === true) return ""; return resolveIdentityNamePrefix(cfg, agentId) ?? opts?.fallback ?? "[openclaw]"; } function resolveResponsePrefix(cfg, agentId) { const configured = cfg.messages?.responsePrefix; if (configured !== void 0) { if (configured === "auto") return resolveIdentityNamePrefix(cfg, agentId); return configured; } } function resolveEffectiveMessagesConfig(cfg, agentId, opts) { return { messagePrefix: resolveMessagePrefix(cfg, agentId, { hasAllowFrom: opts?.hasAllowFrom, fallback: opts?.fallbackMessagePrefix }), responsePrefix: resolveResponsePrefix(cfg, agentId) }; } function resolveHumanDelayConfig(cfg, agentId) { const defaults = cfg.agents?.defaults?.humanDelay; const overrides = resolveAgentConfig(cfg, agentId)?.humanDelay; if (!defaults && !overrides) return; return { mode: overrides?.mode ?? defaults?.mode, minMs: overrides?.minMs ?? defaults?.minMs, maxMs: overrides?.maxMs ?? defaults?.maxMs }; } //#endregion //#region src/channels/channel-config.ts function applyChannelMatchMeta(result, match) { if (match.matchKey && match.matchSource) { result.matchKey = match.matchKey; result.matchSource = match.matchSource; } return result; } function resolveChannelMatchConfig(match, resolveEntry) { if (!match.entry) return null; return applyChannelMatchMeta(resolveEntry(match.entry), match); } function normalizeChannelSlug(value) { return value.trim().toLowerCase().replace(/^#/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); } function buildChannelKeyCandidates(...keys) { const seen = /* @__PURE__ */ new Set(); const candidates = []; for (const key of keys) { if (typeof key !== "string") continue; const trimmed = key.trim(); if (!trimmed || seen.has(trimmed)) continue; seen.add(trimmed); candidates.push(trimmed); } return candidates; } function resolveChannelEntryMatch(params) { const entries = params.entries ?? {}; const match = {}; for (const key of params.keys) { if (!Object.prototype.hasOwnProperty.call(entries, key)) continue; match.entry = entries[key]; match.key = key; break; } if (params.wildcardKey && Object.prototype.hasOwnProperty.call(entries, params.wildcardKey)) { match.wildcardEntry = entries[params.wildcardKey]; match.wildcardKey = params.wildcardKey; } return match; } function resolveChannelEntryMatchWithFallback(params) { const direct = resolveChannelEntryMatch({ entries: params.entries, keys: params.keys, wildcardKey: params.wildcardKey }); if (direct.entry && direct.key) return { ...direct, matchKey: direct.key, matchSource: "direct" }; const normalizeKey = params.normalizeKey; if (normalizeKey) { const normalizedKeys = params.keys.map((key) => normalizeKey(key)).filter(Boolean); if (normalizedKeys.length > 0) for (const [entryKey, entry] of Object.entries(params.entries ?? {})) { const normalizedEntry = normalizeKey(entryKey); if (normalizedEntry && normalizedKeys.includes(normalizedEntry)) return { ...direct, entry, key: entryKey, matchKey: entryKey, matchSource: "direct" }; } } const parentKeys = params.parentKeys ?? []; if (parentKeys.length > 0) { const parent = resolveChannelEntryMatch({ entries: params.entries, keys: parentKeys }); if (parent.entry && parent.key) return { ...direct, entry: parent.entry, key: parent.key, parentEntry: parent.entry, parentKey: parent.key, matchKey: parent.key, matchSource: "parent" }; if (normalizeKey) { const normalizedParentKeys = parentKeys.map((key) => normalizeKey(key)).filter(Boolean); if (normalizedParentKeys.length > 0) for (const [entryKey, entry] of Object.entries(params.entries ?? {})) { const normalizedEntry = normalizeKey(entryKey); if (normalizedEntry && normalizedParentKeys.includes(normalizedEntry)) return { ...direct, entry, key: entryKey, parentEntry: entry, parentKey: entryKey, matchKey: entryKey, matchSource: "parent" }; } } } if (direct.wildcardEntry && direct.wildcardKey) return { ...direct, entry: direct.wildcardEntry, key: direct.wildcardKey, matchKey: direct.wildcardKey, matchSource: "wildcard" }; return direct; } function resolveNestedAllowlistDecision(params) { if (!params.outerConfigured) return true; if (!params.outerMatched) return false; if (!params.innerConfigured) return true; return params.innerMatched; } //#endregion //#region src/channels/allowlist-match.ts function formatAllowlistMatchMeta(match) { return `matchKey=${match?.matchKey ?? "none"} matchSource=${match?.matchSource ?? "none"}`; } //#endregion //#region src/auto-reply/reply/mentions.ts function escapeRegExp$4(text) { return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } function deriveMentionPatterns(identity) { const patterns = []; const name = identity?.name?.trim(); if (name) { const parts = name.split(/\s+/).filter(Boolean).map(escapeRegExp$4); const re = parts.length ? parts.join(String.raw`\s+`) : escapeRegExp$4(name); patterns.push(String.raw`\b@?${re}\b`); } const emoji = identity?.emoji?.trim(); if (emoji) patterns.push(escapeRegExp$4(emoji)); return patterns; } const BACKSPACE_CHAR = "\b"; const CURRENT_MESSAGE_MARKER = "[Current message - respond to this]"; function normalizeMentionPattern(pattern) { if (!pattern.includes(BACKSPACE_CHAR)) return pattern; return pattern.split(BACKSPACE_CHAR).join("\\b"); } function normalizeMentionPatterns(patterns) { return patterns.map(normalizeMentionPattern); } function resolveMentionPatterns(cfg, agentId) { if (!cfg) return []; const agentConfig = agentId ? resolveAgentConfig(cfg, agentId) : void 0; const agentGroupChat = agentConfig?.groupChat; if (agentGroupChat && Object.hasOwn(agentGroupChat, "mentionPatterns")) return agentGroupChat.mentionPatterns ?? []; const globalGroupChat = cfg.messages?.groupChat; if (globalGroupChat && Object.hasOwn(globalGroupChat, "mentionPatterns")) return globalGroupChat.mentionPatterns ?? []; const derived = deriveMentionPatterns(agentConfig?.identity); return derived.length > 0 ? derived : []; } function buildMentionRegexes(cfg, agentId) { return normalizeMentionPatterns(resolveMentionPatterns(cfg, agentId)).map((pattern) => { try { return new RegExp(pattern, "i"); } catch { return null; } }).filter((value) => Boolean(value)); } function normalizeMentionText(text) { return (text ?? "").replace(/[\u200b-\u200f\u202a-\u202e\u2060-\u206f]/g, "").toLowerCase(); } function matchesMentionPatterns(text, mentionRegexes) { if (mentionRegexes.length === 0) return false; const cleaned = normalizeMentionText(text ?? ""); if (!cleaned) return false; return mentionRegexes.some((re) => re.test(cleaned)); } function matchesMentionWithExplicit(params) { const cleaned = normalizeMentionText(params.text ?? ""); const explicit = params.explicit?.isExplicitlyMentioned === true; const explicitAvailable = params.explicit?.canResolveExplicit === true; if (params.explicit?.hasAnyMention === true && explicitAvailable) return explicit || params.mentionRegexes.some((re) => re.test(cleaned)); if (!cleaned) return explicit; return explicit || params.mentionRegexes.some((re) => re.test(cleaned)); } function stripStructuralPrefixes(text) { return (text.includes(CURRENT_MESSAGE_MARKER) ? text.slice(text.indexOf(CURRENT_MESSAGE_MARKER) + 35).trimStart() : text).replace(/\[[^\]]+\]\s*/g, "").replace(/^[ \t]*[A-Za-z0-9+()\-_. ]+:\s*/gm, "").replace(/\\n/g, " ").replace(/\s+/g, " ").trim(); } function stripMentions(text, ctx, cfg, agentId) { let result = text; const providerId = ctx.Provider ? normalizeChannelId$1(ctx.Provider) : null; const providerMentions = providerId ? getChannelDock(providerId)?.mentions : void 0; const patterns = normalizeMentionPatterns([...resolveMentionPatterns(cfg, agentId), ...providerMentions?.stripPatterns?.({ ctx, cfg, agentId }) ?? []]); for (const p of patterns) try { const re = new RegExp(p, "gi"); result = result.replace(re, " "); } catch {} if (providerMentions?.stripMentions) result = providerMentions.stripMentions({ text: result, ctx, cfg, agentId }); result = result.replace(/@[0-9+]{5,}/g, " "); return result.replace(/\s+/g, " ").trim(); } //#endregion //#region src/auto-reply/reply/history.ts const HISTORY_CONTEXT_MARKER = "[Chat messages since your last reply - for context]"; const DEFAULT_GROUP_HISTORY_LIMIT = 50; /** Maximum number of group history keys to retain (LRU eviction when exceeded). */ const MAX_HISTORY_KEYS = 1e3; /** * Evict oldest keys from a history map when it exceeds MAX_HISTORY_KEYS. * Uses Map's insertion order for LRU-like behavior. */ function evictOldHistoryKeys(historyMap, maxKeys = MAX_HISTORY_KEYS) { if (historyMap.size <= maxKeys) return; const keysToDelete = historyMap.size - maxKeys; const iterator = historyMap.keys(); for (let i = 0; i < keysToDelete; i++) { const key = iterator.next().value; if (key !== void 0) historyMap.delete(key); } } function buildHistoryContext(params) { const { historyText, currentMessage } = params; const lineBreak = params.lineBreak ?? "\n"; if (!historyText.trim()) return currentMessage; return [ HISTORY_CONTEXT_MARKER, historyText, "", CURRENT_MESSAGE_MARKER, currentMessage ].join(lineBreak); } function appendHistoryEntry(params) { const { historyMap, historyKey, entry } = params; if (params.limit <= 0) return []; const history = historyMap.get(historyKey) ?? []; history.push(entry); while (history.length > params.limit) history.shift(); if (historyMap.has(historyKey)) historyMap.delete(historyKey); historyMap.set(historyKey, history); evictOldHistoryKeys(historyMap); return history; } function recordPendingHistoryEntry(params) { return appendHistoryEntry(params); } function recordPendingHistoryEntryIfEnabled(params) { if (!params.entry) return []; if (params.limit <= 0) return []; return recordPendingHistoryEntry({ historyMap: params.historyMap, historyKey: params.historyKey, entry: params.entry, limit: params.limit }); } function buildPendingHistoryContextFromMap(params) { if (params.limit <= 0) return params.currentMessage; return buildHistoryContextFromEntries({ entries: params.historyMap.get(params.historyKey) ?? [], currentMessage: params.currentMessage, formatEntry: params.formatEntry, lineBreak: params.lineBreak, excludeLast: false }); } function clearHistoryEntries(params) { params.historyMap.set(params.historyKey, []); } function clearHistoryEntriesIfEnabled(params) { if (params.limit <= 0) return; clearHistoryEntries({ historyMap: params.historyMap, historyKey: params.historyKey }); } function buildHistoryContextFromEntries(params) { const lineBreak = params.lineBreak ?? "\n"; const entries = params.excludeLast === false ? params.entries : params.entries.slice(0, -1); if (entries.length === 0) return params.currentMessage; return buildHistoryContext({ historyText: entries.map(params.formatEntry).join(lineBreak), currentMessage: params.currentMessage, lineBreak }); } //#endregion //#region src/channels/allowlists/resolve-utils.ts function mergeAllowlist(params) { const seen = /* @__PURE__ */ new Set(); const merged = []; const push = (value) => { const normalized = value.trim(); if (!normalized) return; const key = normalized.toLowerCase(); if (seen.has(key)) return; seen.add(key); merged.push(normalized); }; for (const entry of params.existing ?? []) push(String(entry)); for (const entry of params.additions) push(entry); return merged; } function summarizeMapping(label, mapping, unresolved, runtime) { const lines = []; if (mapping.length > 0) { const sample = mapping.slice(0, 6); const suffix = mapping.length > sample.length ? ` (+${mapping.length - sample.length})` : ""; lines.push(`${label} resolved: ${sample.join(", ")}${suffix}`); } if (unresolved.length > 0) { const sample = unresolved.slice(0, 6); const suffix = unresolved.length > sample.length ? ` (+${unresolved.length - sample.length})` : ""; lines.push(`${label} unresolved: ${sample.join(", ")}${suffix}`); } if (lines.length > 0) runtime.log?.(lines.join("\n")); } //#endregion //#region src/channels/mention-gating.ts function resolveMentionGating(params) { const implicit = params.implicitMention === true; const bypass = params.shouldBypassMention === true; const effectiveWasMentioned = params.wasMentioned || implicit || bypass; return { effectiveWasMentioned, shouldSkip: params.requireMention && params.canDetectMention && !effectiveWasMentioned }; } function resolveMentionGatingWithBypass(params) { const shouldBypassMention = params.isGroup && params.requireMention && !params.wasMentioned && !(params.hasAnyMention ?? false) && params.allowTextCommands && params.commandAuthorized && params.hasControlCommand; return { ...resolveMentionGating({ requireMention: params.requireMention, canDetectMention: params.canDetectMention, wasMentioned: params.wasMentioned, implicitMention: params.implicitMention, shouldBypassMention }), shouldBypassMention }; } //#endregion //#region src/channels/ack-reactions.ts function shouldAckReaction(params) { const scope = params.scope ?? "group-mentions"; if (scope === "off" || scope === "none") return false; if (scope === "all") return true; if (scope === "direct") return params.isDirect; if (scope === "group-all") return params.isGroup; if (scope === "group-mentions") { if (!params.isMentionableGroup) return false; if (!params.requireMention) return false; if (!params.canDetectMention) return false; return params.effectiveWasMentioned || params.shouldBypassMention === true; } return false; } function shouldAckReactionForWhatsApp(params) { if (!params.emoji) return false; if (params.isDirect) return params.directEnabled; if (!params.isGroup) return false; if (params.groupMode === "never") return false; if (params.groupMode === "always") return true; return shouldAckReaction({ scope: "group-mentions", isDirect: false, isGroup: true, isMentionableGroup: true, requireMention: true, canDetectMention: true, effectiveWasMentioned: params.wasMentioned, shouldBypassMention: params.groupActivated }); } function removeAckReactionAfterReply(params) { if (!params.removeAfterReply) return; if (!params.ackReactionPromise) return; if (!params.ackReactionValue) return; params.ackReactionPromise.then((didAck) => { if (!didAck) return; params.remove().catch((err) => params.onError?.(err)); }); } //#endregion //#region src/channels/typing.ts function createTypingCallbacks(params) { const stop = params.stop; const onReplyStart = async () => { try { await params.start(); } catch (err) { params.onStartError(err); } }; return { onReplyStart, onIdle: stop ? () => { stop().catch((err) => (params.onStopError ?? params.onStartError)(err)); } : void 0 }; } //#endregion //#region src/auto-reply/reply/response-prefix-template.ts const TEMPLATE_VAR_PATTERN = /\{([a-zA-Z][a-zA-Z0-9.]*)\}/g; /** * Interpolate template variables in a response prefix string. * * @param template - The template string with `{variable}` placeholders * @param context - Context object with values for interpolation * @returns The interpolated string, or undefined if template is undefined * * @example * resolveResponsePrefixTemplate("[{model} | think:{thinkingLevel}]", { * model: "gpt-5.2", * thinkingLevel: "high" * }) * // Returns: "[gpt-5.2 | think:high]" */ function resolveResponsePrefixTemplate(template, context) { if (!template) return; return template.replace(TEMPLATE_VAR_PATTERN, (match, varName) => { switch (varName.toLowerCase()) { case "model": return context.model ?? match; case "modelfull": return context.modelFull ?? match; case "provider": return context.provider ?? match; case "thinkinglevel": case "think": return context.thinkingLevel ?? match; case "identity.name": case "identityname": return context.identityName ?? match; default: return match; } }); } /** * Extract short model name from a full model string. * * Strips: * - Provider prefix (e.g., "openai/" from "openai/gpt-5.2") * - Date suffixes (e.g., "-20251101" from "claude-opus-4-5-20251101") * - Common version suffixes (e.g., "-latest") * * @example * extractShortModelName("openai-codex/gpt-5.2") // "gpt-5.2" * extractShortModelName("claude-opus-4-5-20251101") // "claude-opus-4-5" * extractShortModelName("gpt-5.2-latest") // "gpt-5.2" */ function extractShortModelName(fullModel) { const slash = fullModel.lastIndexOf("/"); return (slash >= 0 ? fullModel.slice(slash + 1) : fullModel).replace(/-\d{8}$/, "").replace(/-latest$/, ""); } //#endregion //#region src/channels/reply-prefix.ts function createReplyPrefixContext(params) { const { cfg, agentId } = params; const prefixContext = { identityName: resolveIdentityName(cfg, agentId) }; const onModelSelected = (ctx) => { prefixContext.provider = ctx.provider; prefixContext.model = extractShortModelName(ctx.model); prefixContext.modelFull = `${ctx.provider}/${ctx.model}`; prefixContext.thinkingLevel = ctx.thinkLevel ?? "off"; }; return { prefixContext, responsePrefix: resolveEffectiveMessagesConfig(cfg, agentId).responsePrefix, responsePrefixContextProvider: () => prefixContext, onModelSelected }; } //#endregion //#region src/channels/logging.ts function logInboundDrop(params) { const target = params.target ? ` target=${params.target}` : ""; params.log(`${params.channel}: drop ${params.reason}${target}`); } function logTypingFailure(params) { const target = params.target ? ` target=${params.target}` : ""; const action = params.action ? ` action=${params.action}` : ""; params.log(`${params.channel} typing${action} failed${target}: ${String(params.error)}`); } function logAckFailure(params) { const target = params.target ? ` target=${params.target}` : ""; params.log(`${params.channel} ack cleanup failed${target}: ${String(params.error)}`); } //#endregion //#region src/channels/location.ts function resolveLocation(location) { const source = location.source ?? (location.isLive ? "live" : location.name || location.address ? "place" : "pin"); const isLive = Boolean(location.isLive ?? source === "live"); return { ...location, source, isLive }; } function formatAccuracy(accuracy) { if (!Number.isFinite(accuracy)) return ""; return ` ±${Math.round(accuracy ?? 0)}m`; } function formatCoords(latitude, longitude) { return `${latitude.toFixed(6)}, ${longitude.toFixed(6)}`; } function formatLocationText(location) { const resolved = resolveLocation(location); const coords = formatCoords(resolved.latitude, resolved.longitude); const accuracy = formatAccuracy(resolved.accuracy); const caption = resolved.caption?.trim(); let header = ""; if (resolved.source === "live" || resolved.isLive) header = `🛰 Live location: ${coords}${accuracy}`; else if (resolved.name || resolved.address) header = `📍 ${[resolved.name, resolved.address].filter(Boolean).join(" — ")} (${coords}${accuracy})`; else header = `📍 ${coords}${accuracy}`; return caption ? `${header}\n${caption}` : header; } function toLocationContext(location) { const resolved = resolveLocation(location); return { LocationLat: resolved.latitude, LocationLon: resolved.longitude, LocationAccuracy: resolved.accuracy, LocationName: resolved.name, LocationAddress: resolved.address, LocationSource: resolved.source, LocationIsLive: resolved.isLive }; } //#endregion //#region src/channels/command-gating.ts function resolveCommandAuthorizedFromAuthorizers(params) { const { useAccessGroups, authorizers } = params; const mode = params.modeWhenAccessGroupsOff ?? "allow"; if (!useAccessGroups) { if (mode === "allow") return true; if (mode === "deny") return false; if (!authorizers.some((entry) => entry.configured)) return true; return authorizers.some((entry) => entry.configured && entry.allowed); } return authorizers.some((entry) => entry.configured && entry.allowed); } function resolveControlCommandGate(params) { const commandAuthorized = resolveCommandAuthorizedFromAuthorizers({ useAccessGroups: params.useAccessGroups, authorizers: params.authorizers, modeWhenAccessGroupsOff: params.modeWhenAccessGroupsOff }); return { commandAuthorized, shouldBlock: params.allowTextCommands && params.hasControlCommand && !commandAuthorized }; } //#endregion //#region src/channels/session.ts async function recordInboundSession(params) { const { storePath, sessionKey, ctx, groupResolution, createIfMissing } = params; recordSessionMetaFromInbound({ storePath, sessionKey, ctx, groupResolution, createIfMissing }).catch(params.onRecordError); const update = params.updateLastRoute; if (!update) return; await updateLastRoute({ storePath, sessionKey: update.sessionKey, deliveryContext: { channel: update.channel, to: update.to, accountId: update.accountId, threadId: update.threadId }, ctx, groupResolution }); } //#endregion //#region src/infra/outbound/message-action-spec.ts const MESSAGE_ACTION_TARGET_MODE = { send: "to", broadcast: "none", poll: "to", react: "to", reactions: "to", read: "to", edit: "to", unsend: "to", reply: "to", sendWithEffect: "to", renameGroup: "to", setGroupIcon: "to", addParticipant: "to", removeParticipant: "to", leaveGroup: "to", sendAttachment: "to", delete: "to", pin: "to", unpin: "to", "list-pins": "to", permissions: "to", "thread-create": "to", "thread-list": "none", "thread-reply": "to", search: "none", sticker: "to", "sticker-search": "none", "member-info": "none", "role-info": "none", "emoji-list": "none", "emoji-upload": "none", "sticker-upload": "none", "role-add": "none", "role-remove": "none", "channel-info": "channelId", "channel-list": "none", "channel-create": "none", "channel-edit": "channelId", "channel-delete": "channelId", "channel-move": "channelId", "category-create": "none", "category-edit": "none", "category-delete": "none", "voice-status": "none", "event-list": "none", "event-create": "none", timeout: "none", kick: "none", ban: "none", "set-presence": "none" }; const ACTION_TARGET_ALIASES = { unsend: ["messageId"], edit: ["messageId"], react: [ "chatGuid", "chatIdentifier", "chatId" ], renameGroup: [ "chatGuid", "chatIdentifier", "chatId" ], setGroupIcon: [ "chatGuid", "chatIdentifier", "chatId" ], addParticipant: [ "chatGuid", "chatIdentifier", "chatId" ], removeParticipant: [ "chatGuid", "chatIdentifier", "chatId" ], leaveGroup: [ "chatGuid", "chatIdentifier", "chatId" ] }; function actionRequiresTarget(action) { return MESSAGE_ACTION_TARGET_MODE[action] !== "none"; } function actionHasTarget(action, params) { if (typeof params.to === "string" ? params.to.trim() : "") return true; if (typeof params.channelId === "string" ? params.channelId.trim() : "") return true; const aliases = ACTION_TARGET_ALIASES[action]; if (!aliases) return false; return aliases.some((alias) => { const value = params[alias]; if (typeof value === "string") return value.trim().length > 0; if (typeof value === "number") return Number.isFinite(value); return false; }); } //#endregion //#region src/infra/outbound/channel-target.ts const CHANNEL_TARGET_DESCRIPTION = "Recipient/channel: E.164 for WhatsApp/Signal, Telegram chat id/@username, Discord/Slack channel/user, or iMessage handle/chat_id"; const CHANNEL_TARGETS_DESCRIPTION = "Recipient/channel targets (same format as --target); accepts ids or names when the directory is available."; function applyTargetToParams(params) { const target = typeof params.args.target === "string" ? params.args.target.trim() : ""; const hasLegacyTo = typeof params.args.to === "string"; const hasLegacyChannelId = typeof params.args.channelId === "string"; const mode = MESSAGE_ACTION_TARGET_MODE[params.action] ?? "none"; if (mode !== "none") { if (hasLegacyTo || hasLegacyChannelId) throw new Error("Use `target` instead of `to`/`channelId`."); } else if (hasLegacyTo) throw new Error("Use `target` for actions that accept a destination."); if (!target) return; if (mode === "channelId") { params.args.channelId = target; return; } if (mode === "to") { params.args.to = target; return; } throw new Error(`Action ${params.action} does not accept a target.`); } //#endregion //#region src/agents/schema/typebox.ts function stringEnum(values, options = {}) { return Type.Unsafe({ type: "string", enum: [...values], ...options }); } function optionalStringEnum(values, options = {}) { return Type.Optional(stringEnum(values, options)); } function channelTargetSchema(options) { return Type.String({ description: options?.description ?? CHANNEL_TARGET_DESCRIPTION }); } function channelTargetsSchema(options) { return Type.Array(channelTargetSchema({ description: options?.description ?? CHANNEL_TARGETS_DESCRIPTION })); } //#endregion //#region src/channels/plugins/config-schema.ts function buildChannelConfigSchema(schema) { return { schema: schema.toJSONSchema({ target: "draft-07", unrepresentable: "any" }) }; } //#endregion //#region src/channels/plugins/config-helpers.ts function setAccountEnabledInConfigSection(params) { const accountKey = params.accountId || DEFAULT_ACCOUNT_ID; const base = params.cfg.channels?.[params.sectionKey]; const hasAccounts = Boolean(base?.accounts); if (params.allowTopLevel && accountKey === DEFAULT_ACCOUNT_ID && !hasAccounts) return { ...params.cfg, channels: { ...params.cfg.channels, [params.sectionKey]: { ...base, enabled: params.enabled } } }; const baseAccounts = base?.accounts ?? {}; const existing = baseAccounts[accountKey] ?? {}; return { ...params.cfg, channels: { ...params.cfg.channels, [params.sectionKey]: { ...base, accounts: { ...baseAccounts, [accountKey]: { ...existing, enabled: params.enabled } } } } }; } function deleteAccountFromConfigSection(params) { const accountKey = params.accountId || DEFAULT_ACCOUNT_ID; const base = params.cfg.channels?.[params.sectionKey]; if (!base) return params.cfg; const baseAccounts = base.accounts && typeof base.accounts === "object" ? { ...base.accounts } : void 0; if (accountKey !== DEFAULT_ACCOUNT_ID) { const accounts = baseAccounts ? { ...baseAccounts } : {}; delete accounts[accountKey]; return { ...params.cfg, channels: { ...params.cfg.channels, [params.sectionKey]: { ...base, accounts: Object.keys(accounts).length ? accounts : void 0 } } }; } if (baseAccounts && Object.keys(baseAccounts).length > 0) { delete baseAccounts[accountKey]; const baseRecord = { ...base }; for (const field of params.clearBaseFields ?? []) if (field in baseRecord) baseRecord[field] = void 0; return { ...params.cfg, channels: { ...params.cfg.channels, [params.sectionKey]: { ...baseRecord, accounts: Object.keys(baseAccounts).length ? baseAccounts : void 0 } } }; } const nextChannels = { ...params.cfg.channels }; delete nextChannels[params.sectionKey]; const nextCfg = { ...params.cfg }; if (Object.keys(nextChannels).length > 0) nextCfg.channels = nextChannels; else delete nextCfg.channels; return nextCfg; } //#endregion //#region src/channels/plugins/setup-helpers.ts function channelHasAccounts(cfg, channelKey) { const base = cfg.channels?.[channelKey]; return Boolean(base?.accounts && Object.keys(base.accounts).length > 0); } function shouldStoreNameInAccounts(params) { if (params.alwaysUseAccounts) return true; if (params.accountId !== DEFAULT_ACCOUNT_ID) return true; return channelHasAccounts(params.cfg, params.channelKey); } function applyAccountNameToChannelSection(params) { const trimmed = params.name?.trim(); if (!trimmed) return params.cfg; const accountId = normalizeAccountId(params.accountId); const baseConfig = params.cfg.channels?.[params.channelKey]; const base = typeof baseConfig === "object" && baseConfig ? baseConfig : void 0; if (!shouldStoreNameInAccounts({ cfg: params.cfg, channelKey: params.channelKey, accountId, alwaysUseAccounts: params.alwaysUseAccounts }) && accountId === DEFAULT_ACCOUNT_ID) { const safeBase = base ?? {}; return { ...params.cfg, channels: { ...params.cfg.channels, [params.channelKey]: { ...safeBase, name: trimmed } } }; } const baseAccounts = base?.accounts ?? {}; const existingAccount = baseAccounts[accountId] ?? {}; const baseWithoutName = accountId === DEFAULT_ACCOUNT_ID ? (({ name: _ignored, ...rest }) => rest)(base ?? {}) : base ?? {}; return { ...params.cfg, channels: { ...params.cfg.channels, [params.channelKey]: { ...baseWithoutName, accounts: { ...baseA