UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

133 lines (132 loc) 5.52 kB
import "./src-vebZIeLe.js"; import { t as expectDefined } from "./expect-CyE8FADM.js"; import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { n as isInsideCode, t as findCodeRegions } from "./code-regions-BXMpZZLd.js"; //#region src/utils/directive-tags.ts const AUDIO_TAG_RE = /\[\[\s*audio_as_voice\s*\]\]/gi; const REPLY_TAG_RE = /\[\[\s*(?:reply_to_current|reply_to\s*:\s*([^\]\n]+))\s*\]\]/gi; const INLINE_DIRECTIVE_TAG_WITH_PADDING_RE = /(?:\s*(?:\[\[\s*audio_as_voice\s*\]\]|\[\[\s*(?:reply_to_current|reply_to\s*:\s*[^\]\n]+)\s*\]\])\s*|^[\t ]*\[\[\s*(?:reply_to_current(?:[\t ]*\](?!\])|(?=[\t ]+\S)|[\t ]*$)|reply_to\s*:\s*(?:[^\]\r\n]*\](?!\])|[\t ]*$))[\t ]*)/giu; const MAX_REPLY_DIRECTIVE_ID_LENGTH = 256; const UNSAFE_REPLY_DIRECTIVE_CHARS_RE = /[\p{Cc}[\]]/gu; const NO_INLINE_DIRECTIVES = { audioAsVoice: false, replyToCurrent: false, hasAudioTag: false, hasReplyTag: false }; function replacementPreservesWordBoundary(source, offset, length) { const before = source[offset - 1]; const after = source[offset + length]; return before && after && !/\s/u.test(before) && !/\s/u.test(after) ? " " : ""; } const BLOCK_SENTINEL_SEED = ""; function createBlockSentinel(text) { let sentinel = BLOCK_SENTINEL_SEED; while (text.includes(sentinel)) sentinel += BLOCK_SENTINEL_SEED; return sentinel; } function replaceOutsideCodeRegions(text, regex, replacement) { const codeRegions = text.includes("[[") ? findCodeRegions(text) : []; return text.replace(regex, (...args) => { const match = String(args[0]); const offset = args.at(-2); return typeof offset === "number" && isInsideCode(offset + match.indexOf("[["), codeRegions) ? match : replacement(match, args.slice(1, -2), Number(offset), text); }); } function normalizeDirectiveWhitespace(text) { const blockSentinel = createBlockSentinel(text); const blockPlaceholderRe = new RegExp(`${blockSentinel}(\\d+)${blockSentinel}`, "g"); const blocks = []; const codeRegions = text.includes("`") || text.includes("~~~") ? findCodeRegions(text) : []; let masked = ""; let cursor = 0; for (const span of codeRegions) { blocks.push(text.slice(span.start, span.end)); masked += `${text.slice(cursor, span.start)}${blockSentinel}${blocks.length - 1}${blockSentinel}`; cursor = span.end; } masked = `${masked}${text.slice(cursor)}`.replace(/(?:(?:^|\n)(?: |\t)[^\n]*)+/gm, (block) => { blocks.push(block); return `${blockSentinel}${blocks.length - 1}${blockSentinel}`; }); return masked.replace(/\r\n/g, "\n").replace(/([^\s])[ \t]{2,}([^\s])/g, "$1 $2").replace(/^\n+/, "").replace(/^[ \t](?=\S)/, "").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trimEnd().replace(blockPlaceholderRe, (_, i) => expectDefined(blocks[Number(i)], "blocks entry at number(i)")); } function stripInlineDirectiveTagsForDisplay(text) { if (!text) return { text, changed: false }; const stripped = replaceOutsideCodeRegions(replaceOutsideCodeRegions(text, AUDIO_TAG_RE, () => ""), REPLY_TAG_RE, () => ""); return { text: stripped, changed: stripped !== text }; } function sanitizeReplyDirectiveId(rawReplyToId) { const trimmed = rawReplyToId?.trim(); if (!trimmed) return; const sanitized = trimmed.replace(UNSAFE_REPLY_DIRECTIVE_CHARS_RE, "").trim(); if (!sanitized) return; return sanitized.length <= MAX_REPLY_DIRECTIVE_ID_LENGTH ? sanitized : Array.from(sanitized).slice(0, MAX_REPLY_DIRECTIVE_ID_LENGTH).join(""); } function stripInlineDirectiveTagsForDelivery(text) { if (!text) return { text, changed: false }; const stripped = replaceOutsideCodeRegions(text, INLINE_DIRECTIVE_TAG_WITH_PADDING_RE, (match) => match.includes("]]") ? " " : ""); const changed = stripped !== text; return { text: changed ? stripped.trim() : text, changed }; } function parseInlineDirectives(text, options = {}) { const { currentMessageId, stripAudioTag = true, stripReplyTags = true } = options; if (!text) return { text: "", ...NO_INLINE_DIRECTIVES }; if (!text.includes("[[")) return { text: normalizeDirectiveWhitespace(text), ...NO_INLINE_DIRECTIVES }; let cleaned = text; let audioAsVoice = false; let hasAudioTag = false; let hasReplyTag = false; let sawCurrent = false; let lastExplicitId; cleaned = replaceOutsideCodeRegions(cleaned, AUDIO_TAG_RE, (match, _captures, offset, source) => { audioAsVoice = true; hasAudioTag = true; return stripAudioTag ? replacementPreservesWordBoundary(source, offset, match.length) : match; }); cleaned = replaceOutsideCodeRegions(cleaned, REPLY_TAG_RE, (match, captures, offset, source) => { const idRaw = typeof captures[0] === "string" ? captures[0] : void 0; hasReplyTag = true; if (idRaw === void 0) sawCurrent = true; else { const id = sanitizeReplyDirectiveId(idRaw); if (id) lastExplicitId = id; } return stripReplyTags ? replacementPreservesWordBoundary(source, offset, match.length) : match; }); if (!hasAudioTag && !hasReplyTag) return { text, ...NO_INLINE_DIRECTIVES }; cleaned = normalizeDirectiveWhitespace(cleaned); const replyToId = lastExplicitId ?? (sawCurrent ? normalizeOptionalString(currentMessageId) : void 0); return { text: cleaned, audioAsVoice, replyToId, replyToExplicitId: lastExplicitId, replyToCurrent: sawCurrent, hasAudioTag, hasReplyTag }; } //#endregion export { stripInlineDirectiveTagsForDisplay as a, stripInlineDirectiveTagsForDelivery as i, replaceOutsideCodeRegions as n, sanitizeReplyDirectiveId as r, parseInlineDirectives as t };