UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

1,193 lines (1,173 loc) 2.37 MB
import { a as resolveOAuthDir, i as resolveGatewayPort, n as resolveConfigPath, s as resolveStateDir, t as STATE_DIR } from "./paths-B1kfl4h5.js"; import { A as normalizeAccountId$3, 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$1, x as loadWorkspaceBootstrapFiles, y as ensureAgentWorkspace, z as resolveThreadParentSessionKey } from "./agent-scope-Csu2B6AM.js"; import { A as normalizeE164, B as danger, C as CONFIG_DIR, D as formatTerminalLink, E as ensureDir$3, F as shortenHomePath, G as success, H as logVerbose, I as sleep, J as isRich, K as warn, L as sliceUtf16Safe, M as resolveJidToE164, N as resolveUserPath, O as isSelfChatMode, P as shortenHomeInString, Q as normalizeLogLevel, R as toWhatsappJid, S as setActivePluginRegistry, U as setVerbose, V as info, W as shouldLogVerbose, X as getChildLogger, Y as theme, _ as normalizeAnyChannelId, a as logDebug, b as getActivePluginRegistry, c as logWarn, d as clearActiveProgressLine, f as registerActiveProgressLine, h as CHAT_CHANNEL_ORDER, i as spawnWithFallback, k as jidToE164, l as createSubsystemLogger, n as runExec, o as logError, p as unregisterActiveProgressLine, q as colorize, r as formatSpawnError, s as logInfo, t as runCommandWithTimeout, u as defaultRuntime, v as normalizeChannelId, w as clampInt, x as requireActivePluginRegistry, z as truncateUtf16Safe } from "./exec-BMnoMcZW.js"; import { B as resolveOpenClawAgentDir, C as getShellPathFromLoginShell, F as resolveApiKeyForProfile, H as DEFAULT_CONTEXT_TOKENS, I as listProfilesForProvider, L as markAuthProfileGood, M as isProfileInCooldown, N as markAuthProfileFailure, O as isTruthyEnvValue, P as markAuthProfileUsed, R as ensureAuthProfileStore, S as resolveModelAuthMode, T as resolveShellEnvFallbackTimeoutMs, U as DEFAULT_MODEL, V as resolveAuthProfileDisplayLabel, W as DEFAULT_PROVIDER, _ as getApiKeyForModel, a as modelKey, b as resolveApiKeyForProvider, c as resolveConfiguredModelRef, d as resolveThinkingDefault, f as normalizeGoogleModelId, i as isCliProvider, j as resolveAuthProfileOrder, k as parseBooleanValue$1, l as resolveDefaultModelForAgent, n as buildConfiguredAllowlistKeys, o as normalizeProviderId, r as buildModelAliasIndex, t as buildAllowedModelSet, u as resolveModelRefFromString, v as getCustomProviderApiKey, x as resolveEnvApiKey, y as requireApiKey, z as resolveAuthStorePathForDisplay } from "./model-selection-mzTqrNoj.js"; import { a as saveJsonFile, i as loadJsonFile } from "./github-copilot-token-B3SA95yo.js"; import { n as resolveCliName, t as formatCliCommand } from "./command-format-CFzL448l.js"; import { A as MediaFetchError, B as resolveTextChunkLimit, C as resolveFetch, D as markdownToIRWithMeta, E as markdownToIR, F as chunkMarkdownText, G as SILENT_REPLY_TOKEN, H as isSafeFenceBreak, I as chunkMarkdownTextWithMode, K as isSilentReplyText, L as chunkText, M as fetchWithSsrFGuard, N as resolveMarkdownTableMode, O as loadWebMedia, P as chunkByNewline, R as chunkTextWithMode, S as parseInlineDirectives$1, T as chunkMarkdownIR, U as parseFenceSpans, V as findFenceSpanAt, W as HEARTBEAT_TOKEN, _ as buildTargetResolverSignature, a as applyReplyThreading, b as parseReplyDirectives, c as shouldSuppressMessagingToolReplies, d as sendMessageSignal, f as sendReadReceiptSignal, g as streamSignalEvents, h as signalRpcRequest, i as applyReplyTagsToPayload, j as fetchRemoteMedia, k as loadWebMediaRaw, l as createReplyToModeFilterForChannel, m as signalCheck, o as filterMessagingToolDuplicates, p as sendTypingSignal, r as normalizeReplyPayloadsForDelivery, s as isRenderablePayload, t as deliverOutboundPayloads, u as resolveReplyToMode, v as normalizeChannelTargetInput, w as wrapFetchWithAbortSignal, x as splitMediaFromOutput, y as normalizeTargetForProvider, z as resolveChunkMode } from "./deliver-CZPhTA7x.js"; import { $ as updateSessionStoreEntry, $t as normalizeToolName, A as isCloudCodeAssistFormatError, An as listChannelPlugins, B as parseImageSizeError, Bn as resolveSlackAppToken, Bt as buildWorkspaceSkillCommandSpecs, C as BILLING_ERROR_USER_MESSAGE, Cn as kindFromMime, Ct as resolveGroupSessionKey, D as getApiErrorPayloadFingerprint, Dn as listEnabledSignalAccounts, Dt as resolveProfile, E as formatRawAssistantErrorForUi, En as mediaKindFromMime, Et as resolveBrowserConfig, F as isLikelyContextOverflowError, Fn as listTelegramAccountIds, Ft as SsrFBlockedError, G as appendAssistantMessageToSessionTranscript, Gn as normalizeDiscordToken, Gt as applySkillEnvOverrides, H as ensureSandboxWorkspaceForSession, Hn as normalizeChatType, Ht as loadWorkspaceSkillEntries, I as isRateLimitAssistantError, In as resolveTelegramAccount, J as readSessionUpdatedAt, Jt as applyOwnerOnlyToolPolicy, Kt as applySkillEnvOverridesFromSnapshot, L as isRawApiErrorPayload, Ln as resolveTelegramToken, M as isContextOverflowError, Mn as isWhatsAppGroupJid, Mt as resizeToJpeg, N as isFailoverAssistantError, Nn as normalizeWhatsAppTarget, Nt as getMediaDir, O as isAuthAssistantError, On as resolveSignalAccount, P as isFailoverErrorMessage, Pn as listEnabledTelegramAccounts, Pt as saveMediaBuffer, Q as updateSessionStore, Qt as expandToolGroups, R as isTimeoutErrorMessage, Rn as listBindings, S as isGoogleModelApi, Sn as isGifMedia, St as resolveConversationLabel, T as formatAssistantErrorText, Tt as registerBrowserRoutes, U as resolveSandboxContext, Un as listEnabledDiscordAccounts, Ut as resolveSkillsPromptForRun, V as sanitizeUserFacingText, Vn as resolveSlackBotToken, Vt as buildWorkspaceSkillSnapshot, W as resolveSandboxRuntimeStatus, Wn as resolveDiscordAccount, Wt as resolvePluginSkillDirs, X as saveSessionStore, Xt as collectExplicitAllowlist, Y as recordSessionMetaFromInbound, Yt as buildPluginToolGroups, Z as updateLastRoute, Zt as expandPolicyWithPluginGroups, _ as sanitizeSessionMessagesImages, _n as detectMime, _t as listChannelDocks, a as formatXHighModelHint, an as sanitizeGoogleTurnOrdering, at as normalizeDeliveryContext, b as downgradeOpenAIReasoningBlocks, bn as imageMimeFromFormat, bt as resolveChannelGroupToolsPolicy, c as normalizeReasoningLevel, cn as isInternalMessageChannel, ct as evaluateSessionFreshness, d as normalizeVerboseLevel, dn as normalizeMessageChannel, dt as resolveSessionResetType, en as resolveToolProfilePolicy, et as isCacheEnabled, f as resolveResponseUsageMode, fn as resolveGatewayMessageChannel, ft as resolveThreadFlag, g as normalizeTextForComparison, gn as GATEWAY_CLIENT_NAMES, gt as getChannelDock, h as isMessagingToolDuplicateNormalized, hn as GATEWAY_CLIENT_MODES, ht as deriveSessionMetaPatch, i as formatThinkingLevels, in as resolveBootstrapMaxChars, it as mergeDeliveryContext, j as isCompactionFailureError, jn as normalizeChannelId$1, k as isBillingAssistantError, kn as getChannelPlugin, kt as getImageMetadata, l as normalizeThinkLevel, ln as isMarkdownCapableMessageChannel, lt as resolveChannelResetConfig, mn as GATEWAY_CLIENT_IDS, mt as resolveMainSessionKey, n as validateGeminiTurns, nn as buildBootstrapContextFiles, nt as deliveryContextFromSession, o as listThinkingLevels, on as INTERNAL_MESSAGE_CHANNEL, ot as normalizeSessionDeliveryFields, p as supportsXHighThinking, pn as resolveMessageChannel, pt as DEFAULT_RESET_TRIGGERS, q as loadSessionStore, qt as resolveSandboxConfigForAgent, r as pickFallbackThinkingLevel, rn as ensureSessionHeader, rt as deliveryContextKey, s as normalizeElevatedLevel, sn as isDeliverableMessageChannel, st as resolveSessionKey$1, t as validateAnthropicTurns, tn as stripPluginOnlyAllowlist, tt as resolveCacheTtlMs$1, u as normalizeUsageDisplay, un as listDeliverableMessageChannels, ut as resolveSessionResetPolicy, v as sanitizeImageBlocks, vn as extensionForMime, vt as resolveChannelGroupPolicy, w as classifyFailoverReason, wn as MAX_IMAGE_BYTES, wt as createBrowserRouteContext, x as isAntigravityClaude, xn as isAudioFileName, xt as resolveIMessageAccount, y as sanitizeToolResultImages, yn as getFileExtension, yt as resolveChannelGroupRequireMention, z as parseImageDimensionError, zn as resolveSlackAccount } from "./pi-embedded-helpers-BxqZh6U7.js"; import { A as getWebAuthAgeMs, C as getConfigValueAtPath, D as resolveAgentMaxConcurrent, E as unsetConfigValueAtPath, M as logoutWeb, O as VERSION, P as readWebSelfId, R as webAuthExists, S as unsetConfigOverride, T as setConfigValueAtPath, _ as resolveEnableState, a as validateConfigObjectWithPlugins, b as resetConfigOverrides, c as resolveTelegramCustomCommands, d as validateJsonSchemaValue, f as loadPluginManifestRegistry, g as normalizePluginsConfig, i as writeConfigFile, j as logWebSelfId, k as resolveWhatsAppAccount, l as isSafeExecutableValue, n as readConfigFileSnapshot, o as TELEGRAM_COMMAND_NAME_PATTERN, p as discoverOpenClawPlugins, r as resolveConfigSnapshotHash, s as normalizeTelegramCommandName, t as loadConfig, u as parseDurationMs, v as resolveMemorySlotDecision, w as parseConfigPath, x as setConfigOverride, y as getConfigOverrides } from "./config-CG73z4h6.js"; import { n as discoverModels, t as discoverAuthStorage } from "./pi-model-discovery-EhM2JAQo.js"; import { C as DEFAULT_AI_SNAPSHOT_MAX_CHARS, _ as rawDataToString, b as formatUncaughtError, h as ensureChromeExtensionRelayServer, v as extractErrorCode, y as formatErrorMessage$1 } from "./chrome-B3IuUad-.js"; import { a as resolveStorePath, i as resolveSessionTranscriptsDirForAgent, n as resolveSessionFilePath, r as resolveSessionTranscriptPath } from "./paths-B4kigINg.js"; import { t as emitSessionTranscriptUpdate } from "./transcript-events-JLH5W4He.js"; import { _ as stripThinkingTagsFromText, a as decodeDataUrl, c as extractAssistantText$1, d as extractThinkingFromTaggedText, f as formatReasoningMessage, g as stripMinimaxToolCallXml, h as stripDowngradedToolCallText, i as coerceImageModelConfig, l as extractAssistantThinking, m as promoteThinkingTagsToBlocks, o as resolveProviderVisionModelFromConfig, p as inferToolMetaFromArgs, r as coerceImageAssistantText, s as minimaxUnderstandImage, t as describeImageWithModel, u as extractThinkingFromTaggedStream, v as resolveToolDisplay, y as ensureOpenClawModelsJson } from "./image-CgBndiQy.js"; import { i as resolveMemorySearchConfig, n as resolveRetryConfig, r as retryAsync } from "./manager-C4ILl-d3.js"; import { c as listMemoryFiles, l as normalizeExtraMemoryPaths } from "./sqlite-Dnmf3LS7.js"; import { t as redactSensitiveText } from "./redact-BIMJ3ntQ.js"; import { a as formatError$1, i as createWaSocket, n as startWebLoginWithQr, o as getStatusCode$1, r as waitForWebLogin, s as waitForWaConnection } from "./login-qr-CcOWO_dR.js"; import { createRequire } from "node:module"; 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, 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 lockfile from "proper-lockfile"; import crypto, { X509Certificate, randomUUID } from "node:crypto"; import { complete, completeSimple, streamSimple } from "@mariozechner/pi-ai"; import { CURRENT_SESSION_VERSION, SessionManager, SettingsManager, codingTools, createAgentSession, createEditTool, createReadTool, createWriteTool, estimateTokens, readTool } from "@mariozechner/pi-coding-agent"; import { EdgeTTS } from "node-edge-tts"; import AjvPkg from "ajv"; import { createServer } from "node:http"; import { ProxyAgent, fetch as fetch$1 } from "undici"; import { WebSocket } from "ws"; import { Buffer as Buffer$1 } from "node:buffer"; import * as net$1 from "node:net"; import net from "node:net"; import { createJiti } from "jiti"; import { Type } from "@sinclair/typebox"; import chokidar from "chokidar"; import { WebClient } from "@slack/web-api"; import readline, { createInterface } from "node:readline"; 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 { API_CONSTANTS, Bot, GrammyError, HttpError, InputFile, webhookCallback } from "grammy"; import process$1 from "node:process"; import { setTimeout as setTimeout$1 } from "node:timers/promises"; import { DisconnectReason, downloadMediaMessage, extractMessageContent, getContentType, isJidGroup, normalizeMessageContent } from "@whiskeysockets/baileys"; import { spinner } from "@clack/prompts"; import { createOscProgressController, supportsOscProgress } from "osc-progress"; import { GatewayIntents, GatewayPlugin } from "@buape/carbon/gateway"; import { messagingApi } from "@line/bot-sdk"; import SlackBolt from "@slack/bolt"; import { run, sequentialize } from "@grammyjs/runner"; import { apiThrottler } from "@grammyjs/transformer-throttler"; import { EventEmitter } from "node:events"; //#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]"; } /** Helper to extract a channel config value by dynamic key. */ function getChannelConfig(cfg, channel) { const value = cfg.channels?.[channel]; return typeof value === "object" && value !== null ? value : void 0; } function resolveResponsePrefix(cfg, agentId, opts) { if (opts?.channel && opts?.accountId) { const accountPrefix = (getChannelConfig(cfg, opts.channel)?.accounts)?.[opts.accountId]?.responsePrefix; if (accountPrefix !== void 0) { if (accountPrefix === "auto") return resolveIdentityNamePrefix(cfg, agentId); return accountPrefix; } } if (opts?.channel) { const channelPrefix = getChannelConfig(cfg, opts.channel)?.responsePrefix; if (channelPrefix !== void 0) { if (channelPrefix === "auto") return resolveIdentityNamePrefix(cfg, agentId); return channelPrefix; } } 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, { channel: opts?.channel, accountId: opts?.accountId }) }; } 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/auto-reply/heartbeat.ts const HEARTBEAT_PROMPT = "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK."; const DEFAULT_HEARTBEAT_ACK_MAX_CHARS = 300; function resolveHeartbeatPrompt(raw) { return (typeof raw === "string" ? raw.trim() : "") || HEARTBEAT_PROMPT; } function stripTokenAtEdges(raw) { let text = raw.trim(); if (!text) return { text: "", didStrip: false }; const token = HEARTBEAT_TOKEN; if (!text.includes(token)) return { text, didStrip: false }; let didStrip = false; let changed = true; while (changed) { changed = false; const next = text.trim(); if (next.startsWith(token)) { text = next.slice(token.length).trimStart(); didStrip = true; changed = true; continue; } if (next.endsWith(token)) { text = next.slice(0, Math.max(0, next.length - token.length)).trimEnd(); didStrip = true; changed = true; } } return { text: text.replace(/\s+/g, " ").trim(), didStrip }; } function stripHeartbeatToken(raw, opts = {}) { if (!raw) return { shouldSkip: true, text: "", didStrip: false }; const trimmed = raw.trim(); if (!trimmed) return { shouldSkip: true, text: "", didStrip: false }; const mode = opts.mode ?? "message"; const maxAckCharsRaw = opts.maxAckChars; const parsedAckChars = typeof maxAckCharsRaw === "string" ? Number(maxAckCharsRaw) : maxAckCharsRaw; const maxAckChars = Math.max(0, typeof parsedAckChars === "number" && Number.isFinite(parsedAckChars) ? parsedAckChars : DEFAULT_HEARTBEAT_ACK_MAX_CHARS); const stripMarkup = (text) => text.replace(/<[^>]*>/g, " ").replace(/&nbsp;/gi, " ").replace(/^[*`~_]+/, "").replace(/[*`~_]+$/, ""); const trimmedNormalized = stripMarkup(trimmed); if (!(trimmed.includes(HEARTBEAT_TOKEN) || trimmedNormalized.includes(HEARTBEAT_TOKEN))) return { shouldSkip: false, text: trimmed, didStrip: false }; const strippedOriginal = stripTokenAtEdges(trimmed); const strippedNormalized = stripTokenAtEdges(trimmedNormalized); const picked = strippedOriginal.didStrip && strippedOriginal.text ? strippedOriginal : strippedNormalized; if (!picked.didStrip) return { shouldSkip: false, text: trimmed, didStrip: false }; if (!picked.text) return { shouldSkip: true, text: "", didStrip: true }; const rest = picked.text.trim(); if (mode === "heartbeat") { if (rest.length <= maxAckChars) return { shouldSkip: true, text: "", didStrip: true }; } return { shouldSkip: false, text: rest, didStrip: true }; } //#endregion //#region src/channels/targets.ts function normalizeTargetId(kind, id) { return `${kind}:${id}`.toLowerCase(); } function buildMessagingTarget(kind, id, raw) { return { kind, id, raw, normalized: normalizeTargetId(kind, id) }; } function ensureTargetId(params) { if (!params.pattern.test(params.candidate)) throw new Error(params.errorMessage); return params.candidate; } function requireTargetKind(params) { const kindLabel = params.kind; if (!params.target) throw new Error(`${params.platform} ${kindLabel} id is required.`); if (params.target.kind !== params.kind) throw new Error(`${params.platform} ${kindLabel} id is required (use ${kindLabel}:<id>).`); return params.target.id; } //#endregion //#region src/slack/targets.ts function parseSlackTarget(raw, options = {}) { const trimmed = raw.trim(); if (!trimmed) return; const mentionMatch = trimmed.match(/^<@([A-Z0-9]+)>$/i); if (mentionMatch) return buildMessagingTarget("user", mentionMatch[1], trimmed); if (trimmed.startsWith("user:")) { const id = trimmed.slice(5).trim(); return id ? buildMessagingTarget("user", id, trimmed) : void 0; } if (trimmed.startsWith("channel:")) { const id = trimmed.slice(8).trim(); return id ? buildMessagingTarget("channel", id, trimmed) : void 0; } if (trimmed.startsWith("slack:")) { const id = trimmed.slice(6).trim(); return id ? buildMessagingTarget("user", id, trimmed) : void 0; } if (trimmed.startsWith("@")) return buildMessagingTarget("user", ensureTargetId({ candidate: trimmed.slice(1).trim(), pattern: /^[A-Z0-9]+$/i, errorMessage: "Slack DMs require a user id (use user:<id> or <@id>)" }), trimmed); if (trimmed.startsWith("#")) return buildMessagingTarget("channel", ensureTargetId({ candidate: trimmed.slice(1).trim(), pattern: /^[A-Z0-9]+$/i, errorMessage: "Slack channels require a channel id (use channel:<id>)" }), trimmed); if (options.defaultKind) return buildMessagingTarget(options.defaultKind, trimmed, trimmed); return buildMessagingTarget("channel", trimmed, trimmed); } function resolveSlackChannelId(raw) { return requireTargetKind({ platform: "Slack", target: parseSlackTarget(raw, { defaultKind: "channel" }), kind: "channel" }); } //#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 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; } //#endregion //#region src/channels/allowlist-match.ts function formatAllowlistMatchMeta(match) { return `matchKey=${match?.matchKey ?? "none"} matchSource=${match?.matchSource ?? "none"}`; } //#endregion //#region src/config/channel-capabilities.ts const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string"); function normalizeCapabilities(capabilities) { if (!isStringArray(capabilities)) return; const normalized = capabilities.map((entry) => entry.trim()).filter(Boolean); return normalized.length > 0 ? normalized : void 0; } function resolveAccountCapabilities(params) { const cfg = params.cfg; if (!cfg) return; const normalizedAccountId = normalizeAccountId$3(params.accountId); const accounts = cfg.accounts; if (accounts && typeof accounts === "object") { const direct = accounts[normalizedAccountId]; if (direct) return normalizeCapabilities(direct.capabilities) ?? normalizeCapabilities(cfg.capabilities); const matchKey = Object.keys(accounts).find((key) => key.toLowerCase() === normalizedAccountId.toLowerCase()); const match = matchKey ? accounts[matchKey] : void 0; if (match) return normalizeCapabilities(match.capabilities) ?? normalizeCapabilities(cfg.capabilities); } return normalizeCapabilities(cfg.capabilities); } function resolveChannelCapabilities(params) { const cfg = params.cfg; const channel = normalizeChannelId$1(params.channel); if (!cfg || !channel) return; return resolveAccountCapabilities({ cfg: cfg.channels?.[channel] ?? cfg[channel], accountId: params.accountId }); } //#endregion //#region src/infra/machine-name.ts const execFileAsync$1 = promisify(execFile); let cachedPromise = null; async function tryScutil(key) { try { const { stdout } = await execFileAsync$1("/usr/sbin/scutil", ["--get", key], { timeout: 1e3, windowsHide: true }); const value = String(stdout ?? "").trim(); return value.length > 0 ? value : null; } catch { return null; } } function fallbackHostName() { return os.hostname().replace(/\.local$/i, "").trim() || "openclaw"; } async function getMachineDisplayName() { if (cachedPromise) return cachedPromise; cachedPromise = (async () => { if (process.env.VITEST || false) return fallbackHostName(); if (process.platform === "darwin") { const computerName = await tryScutil("ComputerName"); if (computerName) return computerName; const localHostName = await tryScutil("LocalHostName"); if (localHostName) return localHostName; } return fallbackHostName(); })(); return cachedPromise; } //#endregion //#region src/infra/diagnostic-events.ts let seq = 0; const listeners$3 = /* @__PURE__ */ new Set(); function isDiagnosticsEnabled(config) { return config?.diagnostics?.enabled === true; } function emitDiagnosticEvent(event) { const enriched = { ...event, seq: seq += 1, ts: Date.now() }; for (const listener of listeners$3) try { listener(enriched); } catch {} } //#endregion //#region src/logging/diagnostic.ts const diag = createSubsystemLogger("diagnostic"); const sessionStates = /* @__PURE__ */ new Map(); const webhookStats = { received: 0, processed: 0, errors: 0, lastReceived: 0 }; let lastActivityAt = 0; function markActivity() { lastActivityAt = Date.now(); } function resolveSessionKey({ sessionKey, sessionId }) { return sessionKey ?? sessionId ?? "unknown"; } function getSessionState(ref) { const key = resolveSessionKey(ref); const existing = sessionStates.get(key); if (existing) { if (ref.sessionId) existing.sessionId = ref.sessionId; if (ref.sessionKey) existing.sessionKey = ref.sessionKey; return existing; } const created = { sessionId: ref.sessionId, sessionKey: ref.sessionKey, lastActivity: Date.now(), state: "idle", queueDepth: 0 }; sessionStates.set(key, created); return created; } function logWebhookReceived(params) { webhookStats.received += 1; webhookStats.lastReceived = Date.now(); diag.debug(`webhook received: channel=${params.channel} type=${params.updateType ?? "unknown"} chatId=${params.chatId ?? "unknown"} total=${webhookStats.received}`); emitDiagnosticEvent({ type: "webhook.received", channel: params.channel, updateType: params.updateType, chatId: params.chatId }); markActivity(); } function logWebhookProcessed(params) { webhookStats.processed += 1; diag.debug(`webhook processed: channel=${params.channel} type=${params.updateType ?? "unknown"} chatId=${params.chatId ?? "unknown"} duration=${params.durationMs ?? 0}ms processed=${webhookStats.processed}`); emitDiagnosticEvent({ type: "webhook.processed", channel: params.channel, updateType: params.updateType, chatId: params.chatId, durationMs: params.durationMs }); markActivity(); } function logWebhookError(params) { webhookStats.errors += 1; diag.error(`webhook error: channel=${params.channel} type=${params.updateType ?? "unknown"} chatId=${params.chatId ?? "unknown"} error="${params.error}" errors=${webhookStats.errors}`); emitDiagnosticEvent({ type: "webhook.error", channel: params.channel, updateType: params.updateType, chatId: params.chatId, error: params.error }); markActivity(); } function logMessageQueued(params) { const state = getSessionState(params); state.queueDepth += 1; state.lastActivity = Date.now(); diag.debug(`message queued: sessionId=${state.sessionId ?? "unknown"} sessionKey=${state.sessionKey ?? "unknown"} source=${params.source} queueDepth=${state.queueDepth} sessionState=${state.state}`); emitDiagnosticEvent({ type: "message.queued", sessionId: state.sessionId, sessionKey: state.sessionKey, channel: params.channel, source: params.source, queueDepth: state.queueDepth }); markActivity(); } function logMessageProcessed(params) { const payload = `message processed: channel=${params.channel} chatId=${params.chatId ?? "unknown"} messageId=${params.messageId ?? "unknown"} sessionId=${params.sessionId ?? "unknown"} sessionKey=${params.sessionKey ?? "unknown"} outcome=${params.outcome} duration=${params.durationMs ?? 0}ms${params.reason ? ` reason=${params.reason}` : ""}${params.error ? ` error="${params.error}"` : ""}`; if (params.outcome === "error") diag.error(payload); else if (params.outcome === "skipped") diag.debug(payload); else diag.debug(payload); emitDiagnosticEvent({ type: "message.processed", channel: params.channel, chatId: params.chatId, messageId: params.messageId, sessionId: params.sessionId, sessionKey: params.sessionKey, durationMs: params.durationMs, outcome: params.outcome, reason: params.reason, error: params.error }); markActivity(); } function logSessionStateChange(params) { const state = getSessionState(params); const isProbeSession = state.sessionId?.startsWith("probe-") ?? false; const prevState = state.state; state.state = params.state; state.lastActivity = Date.now(); if (params.state === "idle") state.queueDepth = Math.max(0, state.queueDepth - 1); if (!isProbeSession) diag.debug(`session state: sessionId=${state.sessionId ?? "unknown"} sessionKey=${state.sessionKey ?? "unknown"} prev=${prevState} new=${params.state} reason="${params.reason ?? ""}" queueDepth=${state.queueDepth}`); emitDiagnosticEvent({ type: "session.state", sessionId: state.sessionId, sessionKey: state.sessionKey, prevState, state: params.state, reason: params.reason, queueDepth: state.queueDepth }); markActivity(); } function logSessionStuck(params) { const state = getSessionState(params); diag.warn(`stuck session: sessionId=${state.sessionId ?? "unknown"} sessionKey=${state.sessionKey ?? "unknown"} state=${params.state} age=${Math.round(params.ageMs / 1e3)}s queueDepth=${state.queueDepth}`); emitDiagnosticEvent({ type: "session.stuck", sessionId: state.sessionId, sessionKey: state.sessionKey, state: params.state, ageMs: params.ageMs, queueDepth: state.queueDepth }); markActivity(); } function logLaneEnqueue(lane, queueSize) { diag.debug(`lane enqueue: lane=${lane} queueSize=${queueSize}`); emitDiagnosticEvent({ type: "queue.lane.enqueue", lane, queueSize }); markActivity(); } function logLaneDequeue(lane, waitMs, queueSize) { diag.debug(`lane dequeue: lane=${lane} waitMs=${waitMs} queueSize=${queueSize}`); emitDiagnosticEvent({ type: "queue.lane.dequeue", lane, queueSize, waitMs }); markActivity(); } let heartbeatInterval = null; function startDiagnosticHeartbeat() { if (heartbeatInterval) return; heartbeatInterval = setInterval(() => { const now = Date.now(); const activeCount = Array.from(sessionStates.values()).filter((s) => s.state === "processing").length; const waitingCount = Array.from(sessionStates.values()).filter((s) => s.state === "waiting").length; const totalQueued = Array.from(sessionStates.values()).reduce((sum, s) => sum + s.queueDepth, 0); if (!(lastActivityAt > 0 || webhookStats.received > 0 || activeCount > 0 || waitingCount > 0 || totalQueued > 0)) return; if (now - lastActivityAt > 12e4 && activeCount === 0 && waitingCount === 0) return; diag.debug(`heartbeat: webhooks=${webhookStats.received}/${webhookStats.processed}/${webhookStats.errors} active=${activeCount} waiting=${waitingCount} queued=${totalQueued}`); emitDiagnosticEvent({ type: "diagnostic.heartbeat", webhooks: { received: webhookStats.received, processed: webhookStats.processed, errors: webhookStats.errors }, active: activeCount, waiting: waitingCount, queued: totalQueued }); for (const [, state] of sessionStates) { const ageMs = now - state.lastActivity; if (state.state === "processing" && ageMs > 12e4) logSessionStuck({ sessionId: state.sessionId, sessionKey: state.sessionKey, state: state.state, ageMs }); } }, 3e4); heartbeatInterval.unref?.(); } function stopDiagnosticHeartbeat() { if (heartbeatInterval) { clearInterval(heartbeatInterval); heartbeatInterval = null; } } //#endregion //#region src/process/lanes.ts let CommandLane = /* @__PURE__ */ function(CommandLane) { CommandLane["Main"] = "main"; CommandLane["Cron"] = "cron"; CommandLane["Subagent"] = "subagent"; CommandLane["Nested"] = "nested"; return CommandLane; }({}); //#endregion //#region src/process/command-queue.ts const lanes = /* @__PURE__ */ new Map(); function getLaneState(lane) { const existing = lanes.get(lane); if (existing) return existing; const created = { lane, queue: [], active: 0, maxConcurrent: 1, draining: false }; lanes.set(lane, created); return created; } function drainLane(lane) { const state = getLaneState(lane); if (state.draining) return; state.draining = true; const pump = () => { while (state.active < state.maxConcurrent && state.queue.length > 0) { const entry = state.queue.shift(); const waitedMs = Date.now() - entry.enqueuedAt; if (waitedMs >= entry.warnAfterMs) { entry.onWait?.(waitedMs, state.queue.length); diag.warn(`lane wait exceeded: lane=${lane} waitedMs=${waitedMs} queueAhead=${state.queue.length}`); } logLaneDequeue(lane, waitedMs, state.queue.length); state.active += 1; (async () => { const startTime = Date.now(); try { const result = await entry.task(); state.active -= 1; diag.debug(`lane task done: lane=${lane} durationMs=${Date.now() - startTime} active=${state.active} queued=${state.queue.length}`); pump(); entry.resolve(result); } catch (err) { state.active -= 1; if (!(lane.startsWith("auth-probe:") || lane.startsWith("session:probe-"))) diag.error(`lane task error: lane=${lane} durationMs=${Date.now() - startTime} error="${String(err)}"`); pump(); entry.reject(err); } })(); } state.draining = false; }; pump(); } function enqueueCommandInLane(lane, task, opts) { const cleaned = lane.trim() || CommandLane.Main; const warnAfterMs = opts?.warnAfterMs ?? 2e3; const state = getLaneState(cleaned); return new Promise((resolve, reject) => { state.queue.push({ task: () => task(), resolve: (value) => resolve(value), reject, enqueuedAt: Date.now(), warnAfterMs, onWait: opts?.onWait }); logLaneEnqueue(cleaned, state.queue.length + state.active); drainLane(cleaned); }); } function getQueueSize(lane = CommandLane.Main) { const resolved = lane.trim() || CommandLane.Main; const state = lanes.get(resolved); if (!state) return 0; return state.queue.length + state.active; } function clearCommandLane(lane = CommandLane.Main) { const cleaned = lane.trim() || CommandLane.Main; const state = lanes.get(cleaned); if (!state) return 0; const removed = state.queue.length; state.queue.length = 0; return removed; } //#endregion //#region src/signal/reaction-level.ts /** * Resolve the effective reaction level and its implications for Signal. * * Levels: * - "off": No reactions at all * - "ack": Only automatic ack reactions (👀 when processing), no agent reactions * - "minimal": Agent can react, but sparingly (default) * - "extensive": Agent can react liberally */ function resolveSignalReactionLevel(params) { const level = resolveSignalAccount({ cfg: params.cfg, accountId: params.accountId }).config.reactionLevel ?? "minimal"; switch (level) { case "off": return { level, ackEnabled: false, agentReactionsEnabled: false }; case "ack": return { level, ackEnabled: true, agentReactionsEnabled: false }; case "minimal": return { level, ackEnabled: false, agentReactionsEnabled: true, agentReactionGuidance: "minimal" }; case "extensive": return { level, ackEnabled: false, agentReactionsEnabled: true, agentReactionGuidance: "extensive" }; default: return { level: "minimal", ackEnabled: false, agentReactionsEnabled: true, agentReactionGuidance: "minimal" }; } } //#endregion //#region src/telegram/targets.ts function stripTelegramInternalPrefixes(to) { let trimmed = to.trim(); let strippedTelegramPrefix = false; while (true) { const next = (() => { if (/^(telegram|tg):/i.test(trimmed)) { strippedTelegramPrefix = true; return trimmed.replace(/^(telegram|tg):/i, "").trim(); } if (strippedTelegramPrefix && /^group:/i.test(trimmed)) return trimmed.replace(/^group:/i, "").trim(); return trimmed; })(); if (next === trimmed) return trimmed; trimmed = next; } } /** * Parse a Telegram delivery target into chatId and optional topic/thread ID. * * Supported formats: * - `chatId` (plain chat ID, t.me link, @username, or internal prefixes like `telegram:...`) * - `chatId:topicId` (numeric topic/thread ID) * - `chatId:topic:topicId` (explicit topic marker; preferred) */ function parseTelegramTarget(to) { const normalized = stripTelegramInternalPrefixes(to); const topicMatch = /^(.+?):topic:(\d+)$/.exec(normalized); if (topicMatch) return { chatId: topicMatch[1], messageThreadId: Number.parseInt(topicMatch[2], 10) }; const colonMatch = /^(.+):(\d+)$/.exec(normalized); if (colonMatch) return { chatId: colonMatch[1], messageThreadId: Number.parseInt(colonMatch[2], 10) }; return { chatId: normalized }; } //#endregion //#region src/telegram/inline-buttons.ts const DEFAULT_INLINE_BUTTONS_SCOPE = "allowlist"; function normalizeInlineButtonsScope(value) { if (typeof value !== "string") return; const trimmed = value.trim().toLowerCase(); if (trimmed === "off" || trimmed === "dm" || trimmed === "group" || trimmed === "all" || trimmed === "allowlist") return trimmed; } function resolveInlineButtonsScopeFromCapabilities(capabilities) { if (!capabilities) return DEFAULT_INLINE_BUTTONS_SCOPE; if (Array.isArray(capabilities)) return capabilities.some((entry) => String(entry).trim().toLowerCase() === "inlinebuttons") ? "all" : "off"; if (typeof capabilities === "object") { const inlineButtons = capabilities.inlineButtons; return normalizeInlineButtonsScope(inlineButtons) ?? DEFAULT_INLINE_BUTTONS_SCOPE; } return DEFAULT_INLINE_BUTTONS_SCOPE; } function resolveTelegramInlineButtonsScope(params) { return resolveInlineButtonsScopeFromCapabilities(resolveTelegramAccount({ cfg: params.cfg, accountId: params.accountId }).config.capabilities); } function isTelegramInlineButtonsEnabled(params) { if (params.accountId) return resolveTelegramInlineButtonsScope(params) !== "off"; const accountIds = listTelegramAccountIds(params.cfg); if (accountIds.length === 0) return resolveTelegramInlineButtonsScope(params) !== "off"; return accountIds.some((accountId) => resolveTelegramInlineButtonsScope({ cfg: params.cfg, accountId }) !== "off"); } function resolveTelegramTargetChatType(target) { if (!target.trim()) return "unknown"; const chatId = parseTelegramTarget(target).chatId.trim(); if (!chatId) return "unknown"; if (/^-?\d+$/.test(chatId)) return chatId.startsWith("-") ? "group" : "direct"; return "unknown"; } //#endregion //#region src/telegram/reaction-level.ts /** * Resolve the effective reaction level and its implications. */ function resolveTelegramReactionLevel(params) { const level = resolveTelegramAccount({ cfg: params.cfg, accountId: params.accountId }).config.reactionLevel ?? "minimal"; switch (level) { case "off": return { level, ackEnabled: false, agentReactionsEnabled: false }; case "ack": return { level, ackEnabled: true, agentReactionsEnabled: false }; case "minimal": return { level, ackEnabled: false, agentReactionsEnabled: true, agentReactionGuidance: "minimal" }; case "extensive": return { level, ackEnabled: false, agentReactionsEnabled: true, agentReactionGuidance: "extensive" }; default: return { level: "ack", ackEnabled: true, agentReactionsEnabled: false }; } } //#endregion //#region src/agents/model-compat.ts function isOpenAiCompletionsModel(model) { return model.api === "openai-completions"; } function normalizeModelCompat(model) { const baseUrl = model.baseUrl ?? ""; if (!(model.provider === "zai" || baseUrl.includes("api.z.ai")) || !isOpenAiCompletionsModel(model)) return model; const openaiModel = model; const compat = openaiModel.compat ?? void 0; if (compat?.supportsDeveloperRole === false) return model; openaiModel.compat = compat ? { ...compat, supportsDeveloperRole: false } : { supportsDeveloperRole: false }; return openaiModel; } //#endregion //#region src/agents/pi-embedded-runner/model.ts const OPENAI_CODEX_GPT_53_MODEL_ID = "gpt-5.3-codex"; const OPENAI_CODEX_TEMPLATE_MODEL_IDS = ["gpt-5.2-codex"]; function resolveOpenAICodexGpt53FallbackModel(provider, modelId, modelRegistry) { const normalizedProvider = normalizeProviderId(provider); const trimmedModelId = modelId.trim(); if (normalizedProvider !== "openai-codex") return; if (trimmedModelId.toLowerCase() !== OPENAI_CODEX_GPT_53_MODEL_ID) return; for (const templateId of OPENAI_CODEX_TEMPLATE_MODEL_IDS) { const template = modelRegistry.find(normalizedProvider, templateId); if (!template) continue; return normalizeModelCompat({ ...template, id: trimmedModelId, name: trimmedModelId }); } return normalizeModelCompat({ id: trimmedModelId, name: trimmedModelId, api: "openai-codex-responses", provider: normalizedProvider, baseUrl: "https://chatgpt.com/backend-api", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: DEFAULT_CONTEXT_TOKENS, maxTokens: DEFAULT_CONTEXT_TOKENS }); } function buildInlineProviderModels(providers) { return Object.entries(providers).flatMap(([providerId, entry]) => { const trimmed = providerId.trim(); if (!trimmed) return []; return (entry?.models ?? []).map((model) => ({ ...model, provider: trimmed, baseUrl: entry?.baseUrl, api: model.api ?? entry?.api })); }); } function buildModelAliasLines$1(cfg) { const models = cfg?.agents?.defaults?.models ?? {}; const entries = []; for (const [keyRaw, entryRaw] of Object.entries(models)) { const model = String(keyRaw ?? "").trim(); if (!model) continue; const alias = String(entryRaw?.alias ?? "").trim(); if (!alias) continue; entries.push({ alias, model }); } return entries.toSorted((a, b) => a.alias.localeCompare(b.alias)).map((entry) => `- ${entry.alias}: ${entry.model}`); } function resolveModel$4(provider, modelId, agentDir, cfg) { const resolvedAgentDir = agentDir ?? resolveOpenClawAgentDir(); const authStorage = discoverAuthStorage(resolvedAgentDir); const modelRegistry = discoverModels(authStorage, resolvedAgentDir); const model = modelRegistry.find(provider, modelId); if (!model) { const providers = cfg?.models?.providers ?? {}; const inlineModels = buildInlineProviderModels(providers); const normalizedProvider = normalizeProviderId(provider); const inlineMatch = inlineModels.find((entry) => normalizeProviderId(entry.provider) === normalizedProvider && entry.id === modelId); if (inlineMatch) return { model: normalizeModelCompat(inlineMatch), authStorage, modelRegistry }; const codexForwardCompat = resolveOpenAICodexGpt53FallbackModel(provider, modelId, modelRegistry); if (codexForwardCompat) return { model: codexForwardCompat, authStorage, modelRegistry }; const providerCfg = providers[provider]; if (providerCfg || modelId.startsWith("mock-")) return { model: normalizeModelCompat({ id: modelId, name: modelId, api: providerCfg?.api ?? "openai-responses", provider, baseUrl: providerCfg?.baseUrl, reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: providerCfg?.models?.[0]?.contextWindow ?? DEFAULT_CONTEXT_TOKENS, maxTokens: providerCfg?.models?.[0]?.maxTokens ?? DEFAULT_CONTEXT_TOKENS }), authStorage, modelRegistry }; return { error: `Unknown model: ${provider}/${modelId}`, authStorage, modelRegistry }; } return { model: normalizeModelCompat(model), authStorage, modelRegistry }; } //#endregion //#region src/media/audio.ts const VOICE_AUDIO_EXTENSIONS = new Set([ ".oga", ".ogg", ".opus" ]); function isVoiceCompatibleAudio(opts) { const mime = opts.contentType?.toLowerCase(); if (mime && (mime.includes("ogg") || mime.includes("opus"))) return true; const fileName = opts.fileName?.trim(); if (!fileName) return false; const ext = getFileExtension(fileName); if (!ext) return false; return VOICE_AUDIO_EXTENSIONS.has(ext); } //#endregion //#region src/tts/tts.ts const DEFAULT_TIMEOUT_MS$1 = 3e4; const DEFAULT_TTS_MAX_LENGTH = 1500; const DEFAULT_TTS_SUMMARIZE = true; const DEFAULT_MAX_TEXT_LENGTH = 4096; const TEMP_FILE_CLEANUP_DELAY_MS = 300 * 1e3; const DEFAULT_ELEVENLABS_BASE_URL = "https://api.elevenlabs.io"; const DEFAULT_ELEVENLABS_VOICE_ID = "pMsXgVXv3BLzUgSXRplE"; const DEFAULT_ELEVENLABS_MODEL_ID = "eleven_multilingual_v2"; const DEFAULT_OPENAI_MODEL = "gpt-4o-mini-tts"; const DEFAULT_OPENAI_VOICE = "alloy"; const DEFAULT_EDGE_VOICE = "en-US-MichelleNeural"; const DEFAULT_EDGE_LANG = "en-US"; const DEFAULT_EDGE_OUTPUT_FORMAT = "audio-24khz-48kbitrate-mono-mp3"; const DEFAULT_ELEVENLABS_VOICE_SETTINGS = { stability: .5, similarityBoost: .75, style: 0, useSpeakerBoost: true, speed: 1 }; const TELEGRAM_OUTPUT = { openai: "opus", elevenlabs: "opus_48000_64", extension: ".opus", voiceCompatible: true }; const DEFAULT_OUTPUT = { openai: "mp3", elevenlabs: "mp3_44100_128", extension: ".mp3", voiceCompatible: false }; const TELEPHONY_OUTPUT = { openai: { format: "pcm", sampleRate: 24e3 }, elevenlabs: { format: "pcm_22050", sampleRate: 22050 } }; const TTS_AUTO_MODES = new Set([ "off", "always", "inbound", "tagged" ]); let lastTtsAttempt; function normalizeTtsAutoMode(value) { if (typeof value !== "string") return; const normalized = value.trim().toLowerCase(); if (TTS_AUTO_MODES.has(normalized)) return normalized; } function resolveModelOverridePolicy(overrides) { if (!(overrides?.enabled ?? true)) return { enabled: false, allowText: false, allowProvider: false, allowVoice: false, allowModelId: false, allowVoiceSettings: false, allowNormalization: false, allowSeed: false }; const allow = (value) => value ?? true; return { enabled: true, allowText: allow(overrides?.allowText), allowProvider: allow(overrides?.allowProvider), allowVoice: allow(overrides?.allowVoice), allowModelId: allow(overrides?.allowModelId), allowVoiceSettings: allow(overrides?.allowVoiceSettings), allowNormalization: allow(overrides?.allowNormalization), allowSeed: allow(overrides?.allowSeed) }; } function resolveTtsConfig(cfg) { const raw = cfg.messages?.tts ?? {}; const providerSource = raw.provider ? "config" : "default"; const edgeOutputFormat = raw.edge?.outputFormat?.trim(); return { auto: normalizeTtsAutoMode(raw.auto) ?? (raw.enabled ? "always" : "off"), mode: raw.mode ?? "final", provider: raw.provider ?? "edge", providerSource, summaryModel: raw.summaryModel?.trim() || void 0, modelOverrides: resolveModelOverridePolicy(raw.modelOverrides), elevenlabs: { apiKey: raw.elevenlabs?.apiKey, baseUrl: raw.elevenlabs?.baseUrl?.trim() || DEFAU