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