openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
200 lines (199 loc) • 8.91 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { h as compileConfigRegexes } from "./redact-DduLliKq.js";
import "./utils-CCC-BEJH.js";
import { t as escapeRegExp } from "./regexp-BZyMFTlj.js";
import { t as createSubsystemLogger } from "./subsystem-BzXSmsuh.js";
import "./agent-scope-MrLta7Pq.js";
import { r as resolveAgentConfig } from "./agent-scope-config-CgCYpZfK.js";
import { a as normalizeAnyChannelId } from "./registry-9YoS2BJP.js";
import { t as getLoadedChannelPluginById } from "./registry-loaded-C3DZQnLP.js";
//#region src/channels/mention-pattern-policy.ts
/**
* Mention-pattern policy resolver.
*
* Applies provider and conversation allow/deny rules to mention pattern matching.
*/
function normalizeIdList(values) {
const normalized = /* @__PURE__ */ new Set();
for (const value of values ?? []) {
const next = normalizeOptionalString(value);
if (next) normalized.add(next);
}
return normalized;
}
function isMentionPatternsPolicyConfig(value) {
return value != null && typeof value === "object" && !Array.isArray(value);
}
function isRecord(value) {
return value != null && typeof value === "object" && !Array.isArray(value);
}
function resolveProviderMentionPatternsPolicy(cfg, provider) {
if (!cfg || !provider) return;
const channelConfig = cfg.channels?.[provider];
const policy = isRecord(channelConfig) ? channelConfig.mentionPatterns : void 0;
return isMentionPatternsPolicyConfig(policy) ? policy : void 0;
}
/**
* Resolves provider-scoped mention-pattern policy for a single conversation.
*/
function resolveMentionPatternPolicy(params) {
const conversationId = normalizeOptionalString(params.conversationId ?? void 0) ?? void 0;
const providerPolicy = params.providerPolicy ?? resolveProviderMentionPatternsPolicy(params.cfg, params.provider);
const effectiveMode = providerPolicy?.mode === "allow" || providerPolicy?.mode === "deny" ? providerPolicy.mode : "allow";
const allowMatched = conversationId != null && normalizeIdList(providerPolicy?.allowIn).has(conversationId);
const denyMatched = conversationId != null && normalizeIdList(providerPolicy?.denyIn).has(conversationId);
return {
effectiveMode,
allowMatched,
denyMatched,
enabled: effectiveMode === "allow" ? !denyMatched : allowMatched && !denyMatched
};
}
//#endregion
//#region src/auto-reply/reply/mentions.ts
/** Mention matching, stripping, and explicit mention handling for group triggers. */
function deriveMentionPatterns(identity) {
const patterns = [];
const name = normalizeOptionalString(identity?.name);
if (name) {
const parts = name.split(/\s+/).filter(Boolean).map(escapeRegExp);
const re = parts.length ? parts.join(String.raw`\s+`) : escapeRegExp(name);
patterns.push(String.raw`\b@?${re}\b`);
}
const emoji = normalizeOptionalString(identity?.emoji);
if (emoji) patterns.push(escapeRegExp(emoji));
return patterns;
}
const BACKSPACE_CHAR = "\b";
const mentionMatchRegexCompileCache = /* @__PURE__ */ new Map();
const mentionStripRegexCompileCache = /* @__PURE__ */ new Map();
const MAX_MENTION_REGEX_COMPILE_CACHE_KEYS = 512;
const mentionPatternWarningCache = /* @__PURE__ */ new Set();
const MAX_MENTION_PATTERN_WARNING_KEYS = 512;
const log = createSubsystemLogger("mentions");
const CURRENT_MESSAGE_MARKER = "[Current message - respond to this]";
function normalizeMentionPattern(pattern) {
if (!pattern.includes(BACKSPACE_CHAR)) return pattern;
return pattern.split(BACKSPACE_CHAR).join("\\b");
}
function normalizeMentionPatterns(patterns) {
return patterns.map(normalizeMentionPattern);
}
function warnRejectedMentionPattern(pattern, flags, reason) {
const key = `${flags}::${reason}::${pattern}`;
if (mentionPatternWarningCache.has(key)) return;
mentionPatternWarningCache.add(key);
if (mentionPatternWarningCache.size > MAX_MENTION_PATTERN_WARNING_KEYS) {
mentionPatternWarningCache.clear();
mentionPatternWarningCache.add(key);
}
log.warn("Ignoring unsupported group mention pattern", {
pattern,
flags,
reason
});
}
function cacheMentionRegexes(cache, cacheKey, regexes) {
cache.set(cacheKey, regexes);
if (cache.size > MAX_MENTION_REGEX_COMPILE_CACHE_KEYS) {
cache.clear();
cache.set(cacheKey, regexes);
}
return [...regexes];
}
function compileMentionPatternsCached(params) {
if (params.patterns.length === 0) return [];
const cacheKey = `${params.flags}\u001e${params.patterns.join("")}`;
const cached = params.cache.get(cacheKey);
if (cached) return [...cached];
const compiled = compileConfigRegexes(params.patterns, params.flags);
if (params.warnRejected) for (const rejected of compiled.rejected) warnRejectedMentionPattern(rejected.pattern, rejected.flags, rejected.reason);
return cacheMentionRegexes(params.cache, cacheKey, compiled.regexes);
}
function resolveMentionPatterns(cfg, agentId) {
if (!cfg) return [];
const agentConfig = agentId ? resolveAgentConfig(cfg, agentId) : void 0;
const agentGroupChat = agentConfig?.groupChat;
if (agentGroupChat && Object.hasOwn(agentGroupChat, "mentionPatterns")) return agentGroupChat.mentionPatterns ?? [];
const globalGroupChat = cfg.messages?.groupChat;
if (globalGroupChat && Object.hasOwn(globalGroupChat, "mentionPatterns")) return globalGroupChat.mentionPatterns ?? [];
const derived = deriveMentionPatterns(agentConfig?.identity);
return derived.length > 0 ? derived : [];
}
/** Builds mention regexes from config, agent identity, and channel policy. */
function buildMentionRegexes(cfg, agentId, options) {
if (!resolveMentionPatternPolicy({
...options,
cfg,
agentId
}).enabled) return [];
return compileMentionPatternsCached({
patterns: normalizeMentionPatterns(resolveMentionPatterns(cfg, agentId)),
flags: "i",
cache: mentionMatchRegexCompileCache,
warnRejected: true
});
}
/** Normalizes text before mention matching. */
function normalizeMentionText(text) {
return normalizeLowercaseStringOrEmpty((text ?? "").replace(/[\u200b-\u200f\u202a-\u202e\u2060-\u206f]/g, ""));
}
/** Returns true when text matches one of the configured mention patterns. */
function matchesMentionPatterns(text, mentionRegexes) {
if (mentionRegexes.length === 0) return false;
const cleaned = normalizeMentionText(text ?? "");
return mentionRegexes.some((re) => re.test(cleaned));
}
/** Combines regex mention matching with provider-native explicit mention metadata. */
function matchesMentionWithExplicit(params) {
const cleaned = normalizeMentionText(params.text ?? "");
const explicit = params.explicit?.isExplicitlyMentioned === true;
const transcriptCleaned = params.transcript ? normalizeMentionText(params.transcript) : "";
const textToCheck = cleaned || transcriptCleaned;
return explicit || params.mentionRegexes.some((re) => re.test(textToCheck));
}
/** Removes structural prompt prefixes before mention stripping. */
function stripStructuralPrefixes(text) {
if (!text) return "";
const afterMarker = text.includes("[Current message - respond to this]") ? text.slice(text.indexOf(CURRENT_MESSAGE_MARKER) + 35).trimStart() : text;
const afterEnvelope = afterMarker.replace(/\[[^\]]+\]\s*/g, "");
const senderPrefixPattern = afterEnvelope === afterMarker ? /^[ \t]*(?!\/)[^\n:]{1,120}:\s+/gm : /^[ \t]*[^\n:]{1,120}:\s+/gm;
return afterEnvelope.replace(senderPrefixPattern, "").replace(/\\n/g, " ").replace(/\s+/g, " ").trim();
}
/** Removes bot mentions from command text before command normalization. */
function stripMentions(text, ctx, cfg, agentId) {
let result = text;
const providerId = (ctx.Provider ? normalizeAnyChannelId(ctx.Provider) : null) ?? normalizeOptionalLowercaseString(ctx.Provider) ?? null;
const providerMentions = providerId ? getLoadedChannelPluginById(providerId)?.mentions : void 0;
const configRegexes = compileMentionPatternsCached({
patterns: normalizeMentionPatterns(resolveMentionPatterns(cfg, agentId)),
flags: "gi",
cache: mentionStripRegexCompileCache,
warnRejected: true
});
const providerRegexes = providerMentions?.stripRegexes?.({
ctx,
cfg,
agentId
}) ?? compileMentionPatternsCached({
patterns: normalizeMentionPatterns(providerMentions?.stripPatterns?.({
ctx,
cfg,
agentId
}) ?? []),
flags: "gi",
cache: mentionStripRegexCompileCache,
warnRejected: false
});
for (const re of [...configRegexes, ...providerRegexes]) result = result.replace(re, " ");
if (providerMentions?.stripMentions) result = providerMentions.stripMentions({
text: result,
ctx,
cfg,
agentId
});
result = result.replace(/@[0-9+]{5,}/g, " ");
return result.replace(/\s+/g, " ").trim();
}
//#endregion
export { normalizeMentionText as a, resolveMentionPatternPolicy as c, matchesMentionWithExplicit as i, buildMentionRegexes as n, stripMentions as o, matchesMentionPatterns as r, stripStructuralPrefixes as s, CURRENT_MESSAGE_MARKER as t };