// ST-BME: MVU (MagVarUpdate) compatibility helpers for private task prompts.
// These rules are intentionally narrow so we strip MVU artifacts without
// disturbing normal prompt or world info content.
export const MVU_ENTRY_COMMENT_REGEX = /\[(mvu_update|mvu_plot|initvar)\]/i;
const MVU_UPDATE_BLOCK_REGEX =
/\n?<(update(?:variable)?|variableupdate)>(?:(?!<\1>).)*?<\/\1>/gis;
const MVU_STATUS_PLACEHOLDER_REGEX = /\n?/gi;
const MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX =
/\n?[\s\S]*?<\/status_current_variables?>/gi;
const MVU_STATUS_CURRENT_VARIABLE_DETECT_REGEX =
/[\s\S]*?<\/status_current_variables?>/i;
const MVU_VARIABLE_OUTPUT_ENTRY_REGEX = /变量输出格式:\s*[\s\S]*?/i;
const MVU_VARIABLE_RULES_ENTRY_REGEX =
/变量更新规则:\s*[\s\S]*?(?:type:\s*|check:\s*|当前时间:|近期事务:)/i;
const MVU_FORMAT_EMPHASIS_ENTRY_REGEX =
/(?:变量输出格式强调|格式强调[::]?-?变量更新规则|格式强调[::]?-?剧情演绎|The following must be inserted to the end of (?:each )?reply,? and cannot be omitted)[\s\S]*?format:\s*\|-?/i;
function uniq(values = []) {
return [...new Set((Array.isArray(values) ? values : []).filter(Boolean))];
}
function normalizeText(value = "") {
return String(value || "").replace(/\r\n/g, "\n");
}
function collapseWhitespace(value = "") {
return String(value || "")
.replace(/[ \t]{2,}/g, " ")
.replace(/\n{3,}/g, "\n\n")
.trim();
}
function countRegexMatches(text = "", regex) {
if (!text || !(regex instanceof RegExp)) {
return 0;
}
const source = new RegExp(regex.source, regex.flags);
let count = 0;
while (source.exec(text)) {
count += 1;
}
return count;
}
function stripMvuPromptArtifactsDetailed(content = "") {
const input = normalizeText(content);
if (!input) {
return {
text: "",
changed: false,
artifactRemovedCount: 0,
};
}
const artifactRemovedCount =
countRegexMatches(input, MVU_UPDATE_BLOCK_REGEX) +
countRegexMatches(input, MVU_STATUS_PLACEHOLDER_REGEX) +
countRegexMatches(input, MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX);
const stripped = input
.replace(MVU_UPDATE_BLOCK_REGEX, "")
.replace(MVU_STATUS_PLACEHOLDER_REGEX, "")
.replace(MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX, "");
const normalized = collapseWhitespace(stripped);
return {
text: normalized,
changed: normalized !== collapseWhitespace(input),
artifactRemovedCount,
};
}
function stripBlockedPromptContentsDetailed(content = "", blockedContents = []) {
const input = normalizeText(content);
const normalizedBlocked = uniq(
(Array.isArray(blockedContents) ? blockedContents : [])
.map((item) => collapseWhitespace(item))
.filter(Boolean)
.sort((left, right) => right.length - left.length),
);
if (!input || normalizedBlocked.length === 0) {
return {
text: collapseWhitespace(input),
changed: false,
blockedHitCount: 0,
};
}
let output = input;
let blockedHitCount = 0;
for (const blocked of normalizedBlocked) {
let index = output.indexOf(blocked);
while (index >= 0) {
blockedHitCount += 1;
output = `${output.slice(0, index)}${output.slice(index + blocked.length)}`;
index = output.indexOf(blocked);
}
}
const normalized = collapseWhitespace(output);
return {
text: normalized,
changed: normalized !== collapseWhitespace(input),
blockedHitCount,
};
}
export function isMvuTaggedWorldInfoComment(comment = "") {
return MVU_ENTRY_COMMENT_REGEX.test(String(comment || ""));
}
export function isMvuTaggedWorldInfoNameOrComment(name = "", comment = "") {
return (
MVU_ENTRY_COMMENT_REGEX.test(String(name || "")) ||
MVU_ENTRY_COMMENT_REGEX.test(String(comment || ""))
);
}
export function isLikelyMvuWorldInfoContent(content = "") {
const normalized = collapseWhitespace(content);
if (!normalized) {
return false;
}
return (
MVU_STATUS_CURRENT_VARIABLE_DETECT_REGEX.test(normalized) ||
MVU_VARIABLE_OUTPUT_ENTRY_REGEX.test(normalized) ||
MVU_VARIABLE_RULES_ENTRY_REGEX.test(normalized) ||
MVU_FORMAT_EMPHASIS_ENTRY_REGEX.test(normalized)
);
}
export function stripMvuPromptArtifacts(content = "") {
return stripMvuPromptArtifactsDetailed(content).text;
}
export function stripBlockedPromptContents(content = "", blockedContents = []) {
return stripBlockedPromptContentsDetailed(content, blockedContents).text;
}
export function sanitizeMvuContent(
content = "",
{ mode = "aggressive", blockedContents = [] } = {},
) {
const originalText = normalizeText(content);
const originalCollapsed = collapseWhitespace(originalText);
const sanitizedMode = String(mode || "aggressive").trim().toLowerCase();
const artifactResult = stripMvuPromptArtifactsDetailed(originalCollapsed);
const blockedResult = stripBlockedPromptContentsDetailed(
artifactResult.text,
blockedContents,
);
const reasons = [];
if (artifactResult.artifactRemovedCount > 0) {
reasons.push("artifact_stripped");
}
if (blockedResult.blockedHitCount > 0) {
reasons.push("blocked_content_removed");
}
let text = blockedResult.text;
let dropped = false;
if (sanitizedMode === "aggressive") {
if (
isLikelyMvuWorldInfoContent(originalCollapsed) ||
isLikelyMvuWorldInfoContent(text)
) {
text = "";
dropped = true;
reasons.push("likely_mvu_content");
}
}
return {
text: collapseWhitespace(text),
changed: collapseWhitespace(text) !== originalCollapsed,
dropped,
reasons: uniq(reasons),
blockedHitCount: blockedResult.blockedHitCount,
artifactRemovedCount: artifactResult.artifactRemovedCount,
};
}