@gguf/claw
Version:
WhatsApp gateway CLI (Baileys web) with Pi RPC agent
1,157 lines (1,131 loc) • 2.48 MB
JavaScript
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