UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

1,214 lines (1,201 loc) 2.26 MB
import { A as getChildLogger, C as setVerbose, D as colorize, E as warn, F as CONFIG_PATH, L as STATE_DIR, O as isRich, P as normalizeLogLevel, Q as CHAT_CHANNEL_ORDER, T as success, X as resolveStateDir, a as parseBooleanValue$1, at as normalizeChannelId, ct as requireActivePluginRegistry, it as normalizeAnyChannelId, k as theme, lt as setActivePluginRegistry, n as isTruthyEnvValue, o as createSubsystemLogger, p as defaultRuntime, v as danger, w as shouldLogVerbose, x as logVerbose, y as info } from "./entry.js"; import { $ as getApiKeyForModel, B as normalizeGoogleModelId, C as buildAllowedModelSet, D as isCliProvider, I as resolveModelRefFromString, L as resolveThinkingDefault, N as resolveConfiguredModelRef, O as modelKey, P as resolveDefaultModelForAgent, S as resolveOpenClawAgentDir, T as buildModelAliasIndex, _ as ensureAuthProfileStore, _t as DEFAULT_PROVIDER, a as markAuthProfileUsed, at as resolveModelAuthMode, et as getCustomProviderApiKey, gt as DEFAULT_MODEL, ht as DEFAULT_CONTEXT_TOKENS, i as markAuthProfileFailure, it as resolveEnvApiKey, k as normalizeProviderId, lt as resolveShellEnvFallbackTimeoutMs, m as markAuthProfileGood, n as resolveAuthProfileOrder, nt as resolveApiKeyForProvider, p as listProfilesForProvider, r as isProfileInCooldown, s as resolveApiKeyForProfile, st as getShellPathFromLoginShell, tt as requireApiKey, vt as resolveAuthProfileDisplayLabel, w as buildConfiguredAllowlistKeys, x as resolveAuthStorePathForDisplay } from "./auth-profiles-CYBuGiBb.js"; import { t as formatCliCommand } from "./command-format-ayFsmwwz.js"; import { _ as parseAgentSessionKey, a as buildAgentPeerSessionKey, c as normalizeAgentId, d as resolveThreadSessionKeys, f as sanitizeAgentId, g as isSubagentSessionKey, h as isAcpSessionKey, i as buildAgentMainSessionKey, l as normalizeMainKey, n as DEFAULT_AGENT_ID, o as buildGroupHistoryKey, r as DEFAULT_MAIN_KEY, s as normalizeAccountId$3, t as DEFAULT_ACCOUNT_ID$1, u as resolveAgentIdFromSessionKey, v as resolveThreadParentSessionKey } from "./session-key-CZkcvAtx.js"; import { _ as sleep, b as truncateUtf16Safe, c as isSelfChatMode, g as shortenHomePath, h as shortenHomeInString, l as jidToE164, m as resolveUserPath, n as clampInt, p as resolveJidToE164, t as CONFIG_DIR, u as normalizeE164, v as sliceUtf16Safe, y as toWhatsappJid } from "./utils-DX85MiPR.js"; import { a as logDebug, c as logWarn, i as spawnWithFallback, n as runExec, o as logError, r as formatSpawnError, s as logInfo, t as runCommandWithTimeout } from "./exec-B8JKbXKW.js"; import { t as resolveOpenClawPackageRoot } from "./openclaw-root-9ILYSmJ9.js"; import { C as loadWorkspaceBootstrapFiles, S as filterBootstrapFilesForSession, c as resolveDefaultAgentId, f as DEFAULT_AGENT_WORKSPACE_DIR, i as resolveAgentModelFallbacksOverride, 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, x as ensureAgentWorkspace } from "./agent-scope-C9VjJXEK.js"; import { a as saveJsonFile, i as loadJsonFile } from "./github-copilot-token-SLWintYd.js"; import { n as discoverModels, t as discoverAuthStorage } from "./pi-model-discovery-DzEIEgHL.js"; import { C as setConfigValueAtPath, S as parseConfigPath, _ as getConfigOverrides, b as unsetConfigOverride, c as writeConfigFile, d as TELEGRAM_COMMAND_NAME_PATTERN, f as normalizeTelegramCommandName, g as validateJsonSchemaValue, h as parseDurationMs, i as loadConfig, j as VERSION, k as resolveAgentMaxConcurrent, l as validateConfigObjectWithPlugins, m as isSafeExecutableValue, o as readConfigFileSnapshot, p as resolveTelegramCustomCommands, s as resolveConfigSnapshotHash, v as resetConfigOverrides, w as unsetConfigValueAtPath, x as getConfigValueAtPath, y as setConfigOverride } from "./config-CKLedg5Y.js"; import { c as resolveEnableState, l as resolveMemorySlotDecision, n as discoverOpenClawPlugins, s as normalizePluginsConfig, t as loadPluginManifestRegistry } from "./manifest-registry-C69Z-I4v.js"; import { a as resolveBrowserConfig } from "./server-context-yKyxyxOJ.js"; import { E as DEFAULT_AI_SNAPSHOT_MAX_CHARS, n as formatErrorMessage, r as formatUncaughtError, t as extractErrorCode } from "./errors-CZ9opC6L.js"; import { n as createBrowserControlContext, r as startBrowserControlServiceFromConfig } from "./control-service-D2E9NKqQ.js"; import { t as pickPrimaryTailnetIPv4 } from "./tailnet-Byp3obcc.js"; import { Et as SESSION_LABEL_MAX_LENGTH, t as GatewayClient } from "./client-CxbkcEZ7.js"; import { i as randomIdempotencyKey, n as callGateway } from "./call-90HgQQ8o.js"; import { a as isInternalMessageChannel, c as listDeliverableMessageChannels, d as resolveMessageChannel, h as GATEWAY_CLIENT_NAMES, l as normalizeMessageChannel, m as GATEWAY_CLIENT_MODES, n as isDeliverableMessageChannel, o as isMarkdownCapableMessageChannel, p as GATEWAY_CLIENT_IDS, t as INTERNAL_MESSAGE_CHANNEL, u as resolveGatewayMessageChannel } from "./message-channel-BlgPSDAh.js"; import { t as formatDocsLink } from "./links-D0uzJbi6.js"; import { _ as normalizeChatType, a as normalizeWhatsAppTarget, b as normalizeDiscordToken, c as resolveTelegramAccount, d as listBindings, g as resolveSlackBotToken, h as resolveSlackAppToken, i as isWhatsAppGroupJid, l as resolveTelegramToken, n as listChannelPlugins, o as listEnabledTelegramAccounts, p as resolveSlackAccount, r as normalizeChannelId$1, s as listTelegramAccountIds, t as getChannelPlugin, v as listEnabledDiscordAccounts, y as resolveDiscordAccount } from "./plugins-BUPpq5aS.js"; import { a as logoutWeb, d as webAuthExists, i as logWebSelfId, n as resolveWhatsAppAccount, r as getWebAuthAgeMs, s as readWebSelfId } from "./accounts-Dto4p9zB.js"; import { n as withProgress, r as withProgressTotals } from "./progress-Da1ehW-x.js"; import { r as stylePromptTitle } from "./prompt-style-Dc0C5HC9.js"; import { i as resolveMemorySearchConfig, n as resolveRetryConfig, r as retryAsync } from "./manager-BXiIQku7.js"; import { a as resolveSessionTranscriptsDirForAgent, n as resolveSessionFilePath, o as resolveStorePath, r as resolveSessionTranscriptPath } from "./paths-CTg8F3AE.js"; import { t as emitSessionTranscriptUpdate } from "./transcript-events-CZ8CG4ht.js"; import { c as listMemoryFiles, l as normalizeExtraMemoryPaths } from "./sqlite-DqUEZnjO.js"; import { C as mediaKindFromMime, _ as imageMimeFromFormat, b as kindFromMime, g as getFileExtension, h as extensionForMime, i as SsrFBlockedError, m as detectMime, n as getMediaDir, p as resizeToJpeg, r as saveMediaBuffer, u as getImageMetadata, v as isAudioFileName, x as MAX_IMAGE_BYTES, y as isGifMedia } from "./routes-BSfXf8a5.js"; import { A as buildBootstrapContextFiles, B as normalizeThinkLevel, C as isLikelyContextOverflowError, D as parseImageDimensionError, E as isTimeoutErrorMessage, F as formatXHighModelHint, G as sanitizeImageBlocks, H as normalizeVerboseLevel, K as sanitizeToolResultImages, M as resolveBootstrapMaxChars, N as sanitizeGoogleTurnOrdering, O as parseImageSizeError, P as formatThinkingLevels, R as normalizeElevatedLevel, S as isFailoverErrorMessage, T as isRawApiErrorPayload, U as resolveResponseUsageMode, V as normalizeUsageDisplay, W as supportsXHighThinking, _ as isBillingAssistantError, a as isMessagingToolDuplicateNormalized, b as isContextOverflowError, c as downgradeOpenAIReasoningBlocks, d as BILLING_ERROR_USER_MESSAGE, f as classifyFailoverReason, g as isAuthAssistantError, h as getApiErrorPayloadFingerprint, j as ensureSessionHeader, k as sanitizeUserFacingText, l as isAntigravityClaude, m as formatRawAssistantErrorForUi, n as validateGeminiTurns, o as normalizeTextForComparison, p as formatAssistantErrorText, r as pickFallbackThinkingLevel, s as sanitizeSessionMessagesImages, t as validateAnthropicTurns, u as isGoogleModelApi, v as isCloudCodeAssistFormatError, w as isRateLimitAssistantError, x as isFailoverAssistantError, y as isCompactionFailureError, z as normalizeReasoningLevel } from "./pi-embedded-helpers-DF8SAHU-.js"; import { A as isSilentReplyText, B as chunkByNewline, C as normalizeChannelTargetInput, D as parseInlineDirectives$1, E as splitMediaFromOutput, F as loadWebMediaRaw, G as resolveChunkMode, H as chunkMarkdownTextWithMode, I as MediaFetchError, J as isSafeFenceBreak, K as resolveTextChunkLimit, L as fetchRemoteMedia, M as markdownToIR, N as markdownToIRWithMeta, O as HEARTBEAT_TOKEN, P as loadWebMedia, R as fetchWithSsrFGuard, S as buildTargetResolverSignature, T as parseReplyDirectives, U as chunkText, V as chunkMarkdownText, W as chunkTextWithMode, Y as parseFenceSpans, _ as signalCheck, b as resolveFetch, c as applyReplyThreading, d as shouldSuppressMessagingToolReplies, f as createReplyToModeFilterForChannel, g as sendTypingSignal, h as sendReadReceiptSignal, j as chunkMarkdownIR, k as SILENT_REPLY_TOKEN, l as filterMessagingToolDuplicates, m as sendMessageSignal, o as normalizeReplyPayloadsForDelivery, p as resolveReplyToMode, q as findFenceSpanAt, s as applyReplyTagsToPayload, t as deliverOutboundPayloads, u as isRenderablePayload, v as signalRpcRequest, w as normalizeTargetForProvider, x as wrapFetchWithAbortSignal, y as streamSignalEvents, z as resolveMarkdownTableMode } from "./deliver-Cau4HL7W.js"; import { $ as stripPluginOnlyAllowlist, A as resolveSessionResetType, B as resolveConversationLabel, C as normalizeDeliveryContext, D as evaluateSessionFreshness, E as resolveSessionKey$1, H as resolveGroupSessionKey, I as resolveMainSessionKey, J as collectExplicitAllowlist, K as applyOwnerOnlyToolPolicy, M as DEFAULT_RESET_TRIGGERS, N as canonicalizeMainSessionAlias, O as resolveChannelResetConfig, Q as resolveToolProfilePolicy, R as deriveSessionMetaPatch, S as mergeDeliveryContext, U as resolveSandboxConfigForAgent, V as buildGroupDisplayName, Y as expandPolicyWithPluginGroups, Z as normalizeToolName, _ as updateSessionStoreEntry, a as ensureSandboxWorkspaceForSession, at as resolveChannelGroupPolicy, b as deliveryContextFromSession, c as resolveSandboxRuntimeStatus, ct as listEnabledSignalAccounts, d as loadSessionStore, f as readSessionUpdatedAt, g as updateSessionStore, h as updateLastRoute, it as listChannelDocks, j as resolveThreadFlag, k as resolveSessionResetPolicy, l as appendAssistantMessageToSessionTranscript, lt as resolveSignalAccount, o as resolveSandboxContext, ot as resolveChannelGroupRequireMention, p as recordSessionMetaFromInbound, q as buildPluginToolGroups, rt as getChannelDock, ut as resolveIMessageAccount, v as isCacheEnabled, w as normalizeSessionDeliveryFields, x as deliveryContextKey, y as resolveCacheTtlMs$1 } from "./sandbox-DuqLKN5J.js"; import { C as shouldHandleTextCommands, S as serializeCommandArgs, _ as listNativeCommandSpecsForConfig, b as resolveCommandArgChoices, f as buildCommandTextFromArgs, g as listNativeCommandSpecs, h as listChatCommandsForConfig, m as listChatCommands, o as extractTextFromMessage, p as findCommandByNativeName, t as buildChannelSummary, v as normalizeCommandBody, x as resolveCommandArgMenu, y as parseCommandArgs } from "./channel-summary-D9nzC5WB.js"; import { i as getMachineDisplayName, r as createBrowserRouteDispatcher, t as isWSL } from "./wsl-ATjkMwMA.js"; import { a as resolveSkillsPromptForRun, i as loadWorkspaceSkillEntries, l as applySkillEnvOverrides, n as buildWorkspaceSkillCommandSpecs, r as buildWorkspaceSkillSnapshot, s as resolvePluginSkillDirs, u as applySkillEnvOverridesFromSnapshot } from "./skills-CmU0Q92f.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 ensureOpenClawModelsJson } from "./image-nRwqkmtf.js"; import { a as evaluateShellAllowlist, d as requiresExecApproval, f as resolveExecApprovals, h as resolveSafeBins, o as maxAsk, p as resolveExecApprovalsFromFile, s as minSecurity, t as addAllowlistEntry, u as recordAllowlistUse } from "./exec-approvals-BCEFzcbC.js"; import { a as canvasSnapshotTempPath, c as parseCameraClipPayload, d as buildNodeShellCommand, i as parseEnvPairs, l as parseCameraSnapPayload, n as screenRecordTempPath, o as parseCanvasSnapshotPayload, r as writeScreenRecordToFile, s as cameraTempPath, t as parseScreenRecordPayload, u as writeBase64ToFile } from "./nodes-screen-DGlNPbk4.js"; import { n as redactSensitiveText } from "./redact-B8YiFlwn.js"; import { n as resolveToolDisplay } from "./tool-display-DmgKs6-V.js"; import { t as parseAbsoluteTimeMs } from "./parse-gTOGQPH6.js"; import { d as resolveGatewaySystemdServiceName, l as resolveGatewayLaunchAgentLabel } from "./constants-D1op9uGI.js"; import { n as resolveMessageChannelSelection, t as listConfiguredMessageChannels } from "./channel-selection-PZuuCvrp.js"; import { t as parseTimeoutMs } from "./parse-timeout-CbVKLZ4B.js"; import { c as derivePromptTokens, l as hasNonzeroUsage, n as loadCostUsageSummary, o as extractToolCallNames, r as loadSessionCostSummary, s as hasToolCall, u as normalizeUsage } from "./session-cost-usage-BTXosU1k.js"; import { i as resolveModelCostConfig, n as formatTokenCount$2, r as formatUsd$1, t as estimateUsageCost } from "./usage-format-E3bMcUMV.js"; import { a as isToolAllowedByPolicies, c as resolveSubagentToolPolicy, i as filterToolsByPolicy, n as resolveNativeCommandsEnabled, o as resolveEffectiveToolPolicy, r as resolveNativeSkillsEnabled, s as resolveGroupToolPolicy, t as isNativeCommandsExplicitlyDisabled } from "./commands-DAC7XMAT.js"; import { a as removeChannelAllowFromStoreEntry, c as listPairingChannels, i as readChannelAllowFromStore, o as upsertChannelPairingRequest, t as addChannelAllowFromStoreEntry } from "./pairing-store-DTfv_FGA.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-Cmsf7BGt.js"; import { i as withManager, t as formatErrorMessage$1 } from "./cli-utils-ByANh4Sp.js"; import { t as resolvePairingIdLabel } from "./pairing-labels-BbydDT7w.js"; import { createRequire } from "node:module"; import { execSync, spawn, spawnSync } from "node:child_process"; import path from "node:path"; import process$1 from "node:process"; import os, { homedir, tmpdir } from "node:os"; import fs, { constants, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs"; import { inspect } from "node:util"; import fs$1 from "node:fs/promises"; import { fileURLToPath } from "node:url"; import { complete, completeSimple, streamSimple } from "@mariozechner/pi-ai"; import crypto, { randomUUID } from "node:crypto"; import { CURRENT_SESSION_VERSION, SessionManager, SettingsManager, codingTools, createAgentSession, createEditTool, createReadTool, createWriteTool, estimateTokens, readTool } from "@mariozechner/pi-coding-agent"; import { createServer } from "node:http"; import { Buffer as Buffer$1 } from "node:buffer"; import * as net$1 from "node:net"; import { createInterface } from "node:readline"; import { Type } from "@sinclair/typebox"; import { cancel, isCancel } from "@clack/prompts"; import { createJiti } from "jiti"; import chokidar from "chokidar"; import { WebClient } from "@slack/web-api"; import { ProxyAgent, fetch as fetch$1 } from "undici"; import { EdgeTTS } from "node-edge-tts"; import { EventEmitter } from "node:events"; 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 { setTimeout as setTimeout$1 } from "node:timers/promises"; import { DisconnectReason, downloadMediaMessage, extractMessageContent, getContentType, isJidGroup, normalizeMessageContent } from "@whiskeysockets/baileys"; 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"; //#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/plugins/commands.ts const pluginCommands = /* @__PURE__ */ new Map(); let registryLocked = false; const MAX_ARGS_LENGTH = 4096; /** * Reserved command names that plugins cannot override. * These are built-in commands from commands-registry.data.ts. */ const RESERVED_COMMANDS = new Set([ "help", "commands", "status", "whoami", "context", "stop", "restart", "reset", "new", "compact", "config", "debug", "allowlist", "activation", "skill", "subagents", "model", "models", "queue", "send", "bash", "exec", "think", "verbose", "reasoning", "elevated", "usage" ]); /** * Validate a command name. * Returns an error message if invalid, or null if valid. */ function validateCommandName(name) { const trimmed = name.trim().toLowerCase(); if (!trimmed) return "Command name cannot be empty"; if (!/^[a-z][a-z0-9_-]*$/.test(trimmed)) return "Command name must start with a letter and contain only letters, numbers, hyphens, and underscores"; if (RESERVED_COMMANDS.has(trimmed)) return `Command name "${trimmed}" is reserved by a built-in command`; return null; } /** * Register a plugin command. * Returns an error if the command name is invalid or reserved. */ function registerPluginCommand(pluginId, command) { if (registryLocked) return { ok: false, error: "Cannot register commands while processing is in progress" }; if (typeof command.handler !== "function") return { ok: false, error: "Command handler must be a function" }; const validationError = validateCommandName(command.name); if (validationError) return { ok: false, error: validationError }; const key = `/${command.name.toLowerCase()}`; if (pluginCommands.has(key)) { const existing = pluginCommands.get(key); return { ok: false, error: `Command "${command.name}" already registered by plugin "${existing.pluginId}"` }; } pluginCommands.set(key, { ...command, pluginId }); logVerbose(`Registered plugin command: ${key} (plugin: ${pluginId})`); return { ok: true }; } /** * Clear all registered plugin commands. * Called during plugin reload. */ function clearPluginCommands() { pluginCommands.clear(); } /** * Check if a command body matches a registered plugin command. * Returns the command definition and parsed args if matched. * * Note: If a command has `acceptsArgs: false` and the user provides arguments, * the command will not match. This allows the message to fall through to * built-in handlers or the agent. Document this behavior to plugin authors. */ function matchPluginCommand(commandBody) { const trimmed = commandBody.trim(); if (!trimmed.startsWith("/")) return null; const spaceIndex = trimmed.indexOf(" "); const commandName = spaceIndex === -1 ? trimmed : trimmed.slice(0, spaceIndex); const args = spaceIndex === -1 ? void 0 : trimmed.slice(spaceIndex + 1).trim(); const key = commandName.toLowerCase(); const command = pluginCommands.get(key); if (!command) return null; if (args && !command.acceptsArgs) return null; return { command, args: args || void 0 }; } /** * Sanitize command arguments to prevent injection attacks. * Removes control characters and enforces length limits. */ function sanitizeArgs(args) { if (!args) return; if (args.length > MAX_ARGS_LENGTH) return args.slice(0, MAX_ARGS_LENGTH); let sanitized = ""; for (const char of args) { const code = char.charCodeAt(0); if (!(code <= 31 && code !== 9 && code !== 10 || code === 127)) sanitized += char; } return sanitized; } /** * Execute a plugin command handler. * * Note: Plugin authors should still validate and sanitize ctx.args for their * specific use case. This function provides basic defense-in-depth sanitization. */ async function executePluginCommand(params) { const { command, args, senderId, channel, isAuthorizedSender, commandBody, config } = params; if (command.requireAuth !== false && !isAuthorizedSender) { logVerbose(`Plugin command /${command.name} blocked: unauthorized sender ${senderId || "<unknown>"}`); return { text: "⚠️ This command requires authorization." }; } const ctx = { senderId, channel, isAuthorizedSender, args: sanitizeArgs(args), commandBody, config }; registryLocked = true; try { const result = await command.handler(ctx); logVerbose(`Plugin command /${command.name} executed successfully for ${senderId || "unknown"}`); return result; } catch (err) { const error = err; logVerbose(`Plugin command /${command.name} error: ${error.message}`); return { text: "⚠️ Command failed. Please try again later." }; } finally { registryLocked = false; } } /** * List all registered plugin commands. * Used for /help and /commands output. */ function listPluginCommands() { return Array.from(pluginCommands.values()).map((cmd) => ({ name: cmd.name, description: cmd.description, pluginId: cmd.pluginId })); } /** * Get plugin command specs for native command registration (e.g., Telegram). */ function getPluginCommandSpecs() { return Array.from(pluginCommands.values()).map((cmd) => ({ name: cmd.name, description: cmd.description })); } //#endregion //#region src/plugins/hooks.ts /** * Get hooks for a specific hook name, sorted by priority (higher first). */ function getHooksForName(registry, hookName) { return registry.typedHooks.filter((h) => h.hookName === hookName).toSorted((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); } /** * Create a hook runner for a specific registry. */ function createHookRunner(registry, options = {}) { const logger = options.logger; const catchErrors = options.catchErrors ?? true; /** * Run a hook that doesn't return a value (fire-and-forget style). * All handlers are executed in parallel for performance. */ async function runVoidHook(hookName, event, ctx) { const hooks = getHooksForName(registry, hookName); if (hooks.length === 0) return; logger?.debug?.(`[hooks] running ${hookName} (${hooks.length} handlers)`); const promises = hooks.map(async (hook) => { try { await hook.handler(event, ctx); } catch (err) { const msg = `[hooks] ${hookName} handler from ${hook.pluginId} failed: ${String(err)}`; if (catchErrors) logger?.error(msg); else throw new Error(msg, { cause: err }); } }); await Promise.all(promises); } /** * Run a hook that can return a modifying result. * Handlers are executed sequentially in priority order, and results are merged. */ async function runModifyingHook(hookName, event, ctx, mergeResults) { const hooks = getHooksForName(registry, hookName); if (hooks.length === 0) return; logger?.debug?.(`[hooks] running ${hookName} (${hooks.length} handlers, sequential)`); let result; for (const hook of hooks) try { const handlerResult = await hook.handler(event, ctx); if (handlerResult !== void 0 && handlerResult !== null) if (mergeResults && result !== void 0) result = mergeResults(result, handlerResult); else result = handlerResult; } catch (err) { const msg = `[hooks] ${hookName} handler from ${hook.pluginId} failed: ${String(err)}`; if (catchErrors) logger?.error(msg); else throw new Error(msg, { cause: err }); } return result; } /** * Run before_agent_start hook. * Allows plugins to inject context into the system prompt. * Runs sequentially, merging systemPrompt and prependContext from all handlers. */ async function runBeforeAgentStart(event, ctx) { return runModifyingHook("before_agent_start", event, ctx, (acc, next) => ({ systemPrompt: next.systemPrompt ?? acc?.systemPrompt, prependContext: acc?.prependContext && next.prependContext ? `${acc.prependContext}\n\n${next.prependContext}` : next.prependContext ?? acc?.prependContext })); } /** * Run agent_end hook. * Allows plugins to analyze completed conversations. * Runs in parallel (fire-and-forget). */ async function runAgentEnd(event, ctx) { return runVoidHook("agent_end", event, ctx); } /** * Run before_compaction hook. */ async function runBeforeCompaction(event, ctx) { return runVoidHook("before_compaction", event, ctx); } /** * Run after_compaction hook. */ async function runAfterCompaction(event, ctx) { return runVoidHook("after_compaction", event, ctx); } /** * Run message_received hook. * Runs in parallel (fire-and-forget). */ async function runMessageReceived(event, ctx) { return runVoidHook("message_received", event, ctx); } /** * Run message_sending hook. * Allows plugins to modify or cancel outgoing messages. * Runs sequentially. */ async function runMessageSending(event, ctx) { return runModifyingHook("message_sending", event, ctx, (acc, next) => ({ content: next.content ?? acc?.content, cancel: next.cancel ?? acc?.cancel })); } /** * Run message_sent hook. * Runs in parallel (fire-and-forget). */ async function runMessageSent(event, ctx) { return runVoidHook("message_sent", event, ctx); } /** * Run before_tool_call hook. * Allows plugins to modify or block tool calls. * Runs sequentially. */ async function runBeforeToolCall(event, ctx) { return runModifyingHook("before_tool_call", event, ctx, (acc, next) => ({ params: next.params ?? acc?.params, block: next.block ?? acc?.block, blockReason: next.blockReason ?? acc?.blockReason })); } /** * Run after_tool_call hook. * Runs in parallel (fire-and-forget). */ async function runAfterToolCall(event, ctx) { return runVoidHook("after_tool_call", event, ctx); } /** * Run tool_result_persist hook. * * This hook is intentionally synchronous: it runs in hot paths where session * transcripts are appended synchronously. * * Handlers are executed sequentially in priority order (higher first). Each * handler may return `{ message }` to replace the message passed to the next * handler. */ function runToolResultPersist(event, ctx) { const hooks = getHooksForName(registry, "tool_result_persist"); if (hooks.length === 0) return; let current = event.message; for (const hook of hooks) try { const out = hook.handler({ ...event, message: current }, ctx); if (out && typeof out.then === "function") { const msg = `[hooks] tool_result_persist handler from ${hook.pluginId} returned a Promise; this hook is synchronous and the result was ignored.`; if (catchErrors) { logger?.warn?.(msg); continue; } throw new Error(msg); } const next = out?.message; if (next) current = next; } catch (err) { const msg = `[hooks] tool_result_persist handler from ${hook.pluginId} failed: ${String(err)}`; if (catchErrors) logger?.error(msg); else throw new Error(msg, { cause: err }); } return { message: current }; } /** * Run session_start hook. * Runs in parallel (fire-and-forget). */ async function runSessionStart(event, ctx) { return runVoidHook("session_start", event, ctx); } /** * Run session_end hook. * Runs in parallel (fire-and-forget). */ async function runSessionEnd(event, ctx) { return runVoidHook("session_end", event, ctx); } /** * Run gateway_start hook. * Runs in parallel (fire-and-forget). */ async function runGatewayStart(event, ctx) { return runVoidHook("gateway_start", event, ctx); } /** * Run gateway_stop hook. * Runs in parallel (fire-and-forget). */ async function runGatewayStop(event, ctx) { return runVoidHook("gateway_stop", event, ctx); } /** * Check if any hooks are registered for a given hook name. */ function hasHooks(hookName) { return registry.typedHooks.some((h) => h.hookName === hookName); } /** * Get count of registered hooks for a given hook name. */ function getHookCount(hookName) { return registry.typedHooks.filter((h) => h.hookName === hookName).length; } return { runBeforeAgentStart, runAgentEnd, runBeforeCompaction, runAfterCompaction, runMessageReceived, runMessageSending, runMessageSent, runBeforeToolCall, runAfterToolCall, runToolResultPersist, runSessionStart, runSessionEnd, runGatewayStart, runGatewayStop, hasHooks, getHookCount }; } //#endregion //#region src/plugins/hook-runner-global.ts const log$11 = createSubsystemLogger("plugins"); let globalHookRunner = null; let globalRegistry = null; /** * Initialize the global hook runner with a plugin registry. * Called once when plugins are loaded during gateway startup. */ function initializeGlobalHookRunner(registry) { globalRegistry = registry; globalHookRunner = createHookRunner(registry, { logger: { debug: (msg) => log$11.debug(msg), warn: (msg) => log$11.warn(msg), error: (msg) => log$11.error(msg) }, catchErrors: true }); const hookCount = registry.hooks.length; if (hookCount > 0) log$11.info(`hook runner initialized with ${hookCount} registered hooks`); } /** * Get the global hook runner. * Returns null if plugins haven't been loaded yet. */ function getGlobalHookRunner() { return globalHookRunner; } //#endregion //#region src/hooks/internal-hooks.ts /** Registry of hook handlers by event key */ const handlers$1 = /* @__PURE__ */ new Map(); /** * Register a hook handler for a specific event type or event:action combination * * @param eventKey - Event type (e.g., 'command') or specific action (e.g., 'command:new') * @param handler - Function to call when the event is triggered * * @example * ```ts * // Listen to all command events * registerInternalHook('command', async (event) => { * console.log('Command:', event.action); * }); * * // Listen only to /new commands * registerInternalHook('command:new', async (event) => { * await saveSessionToMemory(event); * }); * ``` */ function registerInternalHook(eventKey, handler) { if (!handlers$1.has(eventKey)) handlers$1.set(eventKey, []); handlers$1.get(eventKey).push(handler); } /** * Clear all registered hooks (useful for testing) */ function clearInternalHooks() { handlers$1.clear(); } /** * Trigger a hook event * * Calls all handlers registered for: * 1. The general event type (e.g., 'command') * 2. The specific event:action combination (e.g., 'command:new') * * Handlers are called in registration order. Errors are caught and logged * but don't prevent other handlers from running. * * @param event - The event to trigger */ async function triggerInternalHook(event) { const typeHandlers = handlers$1.get(event.type) ?? []; const specificHandlers = handlers$1.get(`${event.type}:${event.action}`) ?? []; const allHandlers = [...typeHandlers, ...specificHandlers]; if (allHandlers.length === 0) return; for (const handler of allHandlers) try { await handler(event); } catch (err) { console.error(`Hook error [${event.type}:${event.action}]:`, err instanceof Error ? err.message : String(err)); } } /** * Create a hook event with common fields filled in * * @param type - The event type * @param action - The action within that type * @param sessionKey - The session key * @param context - Additional context */ function createInternalHookEvent(type, action, sessionKey, context = {}) { return { type, action, sessionKey, context, timestamp: /* @__PURE__ */ new Date(), messages: [] }; } //#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/registry.ts function createPluginRegistry(registryParams) { const registry = { plugins: [], tools: [], hooks: [], typedHooks: [], channels: [], providers: [], gatewayHandlers: {}, httpHandlers: [], httpRoutes: [], cliRegistrars: [], services: [], commands: [], diagnostics: [] }; const coreGatewayMethods = new Set(Object.keys(registryParams.coreGatewayHandlers ?? {})); const pushDiagnostic = (diag) => { registry.diagnostics.push(diag); }; const registerTool = (record, tool, opts) => { const names = opts?.names ?? (opts?.name ? [opts.name] : []); const optional = opts?.optional === true; const factory = typeof tool === "function" ? tool : (_ctx) => tool; if (typeof tool !== "function") names.push(tool.name); const normalized = names.map((name) => name.trim()).filter(Boolean); if (normalized.length > 0) record.toolNames.push(...normalized); registry.tools.push({ pluginId: record.id, factory, names: normalized, optional, source: record.source }); }; const registerHook = (record, events, handler, opts, config) => { const normalizedEvents = (Array.isArray(events) ? events : [events]).map((event) => event.trim()).filter(Boolean); const entry = opts?.entry ?? null; const name = entry?.hook.name ?? opts?.name?.trim(); if (!name) { pushDiagnostic({ level: "warn", pluginId: record.id, source: record.source, message: "hook registration missing name" }); return; } const description = entry?.hook.description ?? opts?.description ?? ""; const hookEntry = entry ? { ...entry, hook: { ...entry.hook, name, description, source: "openclaw-plugin", pluginId: record.id }, metadata: { ...entry.metadata, events: normalizedEvents } } : { hook: { name, description, source: "openclaw-plugin", pluginId: record.id, filePath: record.source, baseDir: path.dirname(record.source), handlerPath: record.source }, frontmatter: {}, metadata: { events: normalizedEvents }, invocation: { enabled: true } }; record.hookNames.push(name); registry.hooks.push({ pluginId: record.id, entry: hookEntry, events: normalizedEvents, source: record.source }); if (!(config?.hooks?.internal?.enabled === true) || opts?.register === false) return; for (const event of normalizedEvents) registerInternalHook(event, handler); }; const registerGatewayMethod = (record, method, handler) => { const trimmed = method.trim(); if (!trimmed) return; if (coreGatewayMethods.has(trimmed) || registry.gatewayHandlers[trimmed]) { pushDiagnostic({ level: "error", pluginId: record.id, source: record.source, message: `gateway method already registered: ${trimmed}` }); return; } registry.gatewayHandlers[trimmed] = handler; record.gatewayMethods.push(trimmed); }; const registerHttpHandler = (record, handler) => { record.httpHandlers += 1; registry.httpHandlers.push({ pluginId: record.id, handler, source: record.source }); }; const registerHttpRoute = (record, params) => { const normalizedPath = normalizePluginHttpPath(params.path); if (!normalizedPath) { pushDiagnostic({ level: "warn", pluginId: record.id, source: record.source, message: "http route registration missing path" }); return; } if (registry.httpRoutes.some((entry) => entry.path === normalizedPath)) { pushDiagnostic({ level: "error", pluginId: record.id, source: record.source, message: `http route already registered: ${normalizedPath}` }); return; } record.httpHandlers += 1; registry.httpRoutes.push({ pluginId: record.id, path: normalizedPath, handler: params.handler, source: record.source }); }; const registerChannel = (record, registration) => { const normalized = typeof registration.plugin === "object" ? registration : { plugin: registration }; const plugin = normalized.plugin; const id = typeof plugin?.id === "string" ? plugin.id.trim() : String(plugin?.id ?? "").trim(); if (!id) { pushDiagnostic({ level: "error", pluginId: record.id, source: record.source, message: "channel registration missing id" }); return; } record.channelIds.push(id); registry.channels.push({ pluginId: record.id, plugin, dock: normalized.dock, source: record.source }); }; const registerProvider = (record, provider) => { const id = typeof provider?.id === "string" ? provider.id.trim() : ""; if (!id) { pushDiagnostic({ level: "error", pluginId: record.id, source: record.source, message: "provider registration missing id" }); return; } const existing = registry.providers.find((entry) => entry.provider.id === id); if (existing) { pushDiagnostic({ level: "error", pluginId: record.id, source: record.source, message: `provider already registered: ${id} (${existing.pluginId})` }); return; } record.providerIds.push(id); registry.providers.push({ pluginId: record.id, provider, source: record.source }); }; const registerCli = (record, registrar, opts) => { const commands = (opts?.commands ?? []).map((cmd) => cmd.trim()).filter(Boolean); record.cliCommands.push(...commands); registry.cliRegistrars.push({ pluginId: record.id, register: registrar, commands, source: record.source }); }; const registerService = (record, service) => { const id = service.id.trim(); if (!id) return; record.services.push(id); registry.services.push({ pluginId: record.id, service, source: record.source }); }; const registerCommand = (record, command) => { const name = command.name.trim(); if (!name) { pushDiagnostic({ level: "error", pluginId: record.id, source: record.source, message: "command registration missing name" }); return; } const result = registerPluginCommand(record.id, command); if (!result.ok) { pushDiagnostic({ level: "error", pluginId: record.id, source: record.source, message: `command registration failed: ${result.error}` }); return; } record.commands.push(name); registry.commands.push({ pluginId: record.id, command, source: record.source }); }; const registerTypedHook = (record, hookName, handler, opts) => { record.hookCount += 1; registry.typedHooks.push({ pluginId: record.id, hookName, handler, priority: opts?.priority, source: record.source }); }; const normalizeLogger = (logger) => ({ info: logger.info, warn: logger.warn, error: logger.error, debug: logger.debug }); const createApi = (record, params) => { return { id: record.id, name: record.name, version: record.version, description: record.description, source: record.source, config: params.config, pluginConfig: params.pluginConfig, runtime: registryParams.runtime, logger: normalizeLogger(registryParams.logger), registerTool: (tool, opts) => registerTool(record, tool, opts), registerHook: (events, handler, opts) => registerHook(record, events, handler, opts, params.config), registerHttpHandler: (handler) => registerHttpHandler(record, handler), registerHttpRoute: (params) => registerHttpRoute(record, params), registerChannel: (registration) => registerChannel(record, registration), registerProvider: (provider) => registerProvider(record, provider), registerGatewayMethod: (method, handler) => registerGatewayMethod(record, method, handler), registerCli: (registrar, opts) => registerCli(record, registrar, opts), registerService: (service) => registerService(record, service), registerCommand: (command) => registerCommand(record, command), resolvePath: (input) => resolveUserPath(input), on: (hookName, handler, opts) => registerTypedHook(record, hookName, handler, opts) }; }; return { registry, createApi, pushDiagnostic, registerTool, registerChannel, registerProvider, registerGatewayMethod, registerCli, registerService, registerCommand, registerHook, registerTypedHook }; } //#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]"; } /** 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/utils/shell-argv.ts function splitShellArgs(raw) { const tokens = []; let buf = ""; let inSingle = false; let inDouble = false; let escaped = false; const pushToken = () => { if (buf.length > 0) { tokens.push(buf); buf = ""; } }; for (let i = 0; i < raw.length; i += 1) { const ch = raw[i]; if (escaped) { buf += ch; escaped = false; continue; } if (!inSingle && !inDouble && ch === "\\") { escaped = true; continue; } if (inSingle) { if (ch === "'") inSingle = false; else buf += ch; continue; } if (inDouble) { if (ch === "\"") inDouble = false; else buf += ch; continue; } if (ch === "'") { inSingle = true; continue; } if (ch === "\"") { inDouble = true; continue; } if (/\s/.test(ch)) { pushToken(); continue; } buf += ch; } if (escaped || inSingle || inDouble) return null; pushToken(); return tokens; } //#endregion //#region src/memory/backend-config.ts const DEFAULT_BACKEND = "builtin"; const DEFAULT_CITATIONS = "auto"; const DEFAULT_QMD_INTERVAL = "5m"; const DEFAULT_QMD_DEBOUNCE_MS = 15e3; const DEFAULT_QMD_TIMEOUT_MS = 4e3; const DEFAULT_QMD_EMBED_INTERVAL = "60m"; const DEFAULT_QMD_LIMITS