import { t as escapeRegExp } from "./regexp-BZyMFTlj.js";
import { it as stripInboundMetadata } from "./openclaw-state-db-Cb4e1jdd.js";
import { f as stripInternalRuntimeContext } from "./internal-runtime-context-E3ku7Huk.js";
import { n as isInsideCode, t as findCodeRegions } from "./code-regions-BSt0fAqe.js";
import { s as stripPlainTextToolCallBlocks } from "./src-DlLP3mBE.js";
import "./history-DLKGD0Dj.js";
import { t as coerceChatContentText } from "./chat-content-BbLAEXko.js";
import { c as stripLegacyBracketToolCallBlocks, l as stripMinimaxToolCallXml, m as stripFinalTags, o as stripAssistantInternalTraceLines, u as stripToolCallXmlTags } from "./assistant-visible-text-Bz5X-nia.js";
import "./bash-tools.exec-output-Cei3R_ZV.js";
import { S as renderSanitizedUserFacingText } from "./user-copy-BHKSaQoO.js";
//#region src/agents/embedded-agent-helpers/sanitize-user-facing-text.ts
/** Strips internal scaffolding from text before user-facing delivery. */
const TOOL_CALLS_OMITTED_PLACEHOLDER_LINE_RE = /^[ \t]*\[tool calls omitted\][ \t]*$/i;
function stripFinalTagsFromText(text) {
	const normalized = coerceChatContentText(text);
	return normalized ? stripFinalTags(normalized) : normalized;
}
function stripInternalPlaceholderLines(text) {
	if (!text.toLowerCase().includes("[tool calls omitted]") && !text.includes("(no output)")) return text;
	let protectedRegions;
	let result = "";
	let start = 0;
	while (start < text.length) {
		const newlineIndex = text.indexOf("\n", start);
		const end = newlineIndex === -1 ? text.length : newlineIndex + 1;
		const chunk = text.slice(start, end);
		const line = chunk.endsWith("\n") ? chunk.slice(0, -1).replace(/\r$/, "") : chunk;
		if (!(TOOL_CALLS_OMITTED_PLACEHOLDER_LINE_RE.test(line) || line.trim() === "(no output)") || isInsideCode(start, protectedRegions ??= findCodeRegions(text))) result += chunk;
		start = end;
	}
	return result;
}
const MARKDOWN_LINE_PREFIX = "[ \\t]*(?:(?:>|[-+*](?=[ \\t])|#{1,6}(?=[ \\t])|\\d{1,9}[.)](?=[ \\t]))[ \\t]*)*";
function hasConversationContextMarker(text) {
	return text.includes("[Chat messages since your last reply - for context]") || text.includes("[Current message - respond to this]");
}
function prepareVerifiedConversationContext(source) {
	if (!source || !hasConversationContextMarker(source)) return;
	const sourceCodeRegions = findCodeRegions(source);
	if (!["[Chat messages since your last reply - for context]", "[Current message - respond to this]"].some((marker) => {
		let markerOffset = source.indexOf(marker);
		while (markerOffset !== -1) {
			const markerEnd = markerOffset + marker.length;
			const startsLine = markerOffset === 0 || source[markerOffset - 1] === "\n";
			const endsLine = markerEnd === source.length || source[markerEnd] === "\n" || source[markerEnd] === "\r";
			if (startsLine && endsLine && !isInsideCode(markerOffset, sourceCodeRegions)) return true;
			markerOffset = source.indexOf(marker, markerEnd);
		}
		return false;
	})) return;
	return { normalizedSource: source.replace(/\r\n?/gu, "\n") };
}
function stripVerifiedConversationContext(text, context, streaming = false) {
	if (!context) return text;
	const { normalizedSource } = context;
	let result = text;
	if (hasConversationContextMarker(text)) {
		if (!context.copiedPrompt) {
			const promptPattern = (context.sourceLines ??= normalizedSource.split("\n")).map(escapeRegExp).join(`(?:\\r\\n?|\\n)${MARKDOWN_LINE_PREFIX}`);
			context.copiedPrompt = new RegExp(`(?:^${MARKDOWN_LINE_PREFIX})?${promptPattern}`, "gmu");
		}
		result = text.replace(context.copiedPrompt, "");
	}
	if (!streaming) return result;
	const sourceStart = normalizedSource.charAt(0);
	const firstSourceLine = context.firstSourceLine ??= normalizedSource.split("\n", 1)[0] ?? normalizedSource;
	const completedSourceStart = result.indexOf(firstSourceLine);
	const searchStart = completedSourceStart === -1 ? Math.max(0, result.length - normalizedSource.length * 2) : completedSourceStart;
	const markdownWrapper = context.markdownWrapper ??= new RegExp(`^${MARKDOWN_LINE_PREFIX}$`, "u");
	const incompleteMarkdownWrapper = context.incompleteMarkdownWrapper ??= new RegExp(`^${MARKDOWN_LINE_PREFIX}(?:[-+*]|#{1,6}|\\d{1,9}[.)]?)?$`, "u");
	let candidateStart = result.indexOf(sourceStart, searchStart);
	let completedCandidates = 0;
	while (candidateStart !== -1) {
		if (!(result.length - candidateStart >= firstSourceLine.length ? result.startsWith(firstSourceLine, candidateStart) : firstSourceLine.startsWith(result.slice(candidateStart)))) {
			candidateStart = result.indexOf(sourceStart, candidateStart + 1);
			continue;
		}
		if (++completedCandidates > 16) return result.slice(0, searchStart);
		const suffix = result.slice(candidateStart).replace(/\r\n?/gu, "\n");
		const sourceLines = context.sourceLines ??= normalizedSource.split("\n");
		let lineIndex = 0;
		const unwrappedSuffix = suffix.replace(/\n([^\n]*)/gu, (_match, line) => {
			const sourceLine = sourceLines[++lineIndex];
			if (sourceLine === void 0) return `\n${line}`;
			if (!sourceLine) return incompleteMarkdownWrapper.test(line) ? "\n" : `\n${line}`;
			const sourceLineStart = sourceLine.charAt(0);
			let contentStart = line.indexOf(sourceLineStart);
			while (contentStart !== -1) {
				const content = line.slice(contentStart);
				if (sourceLine.startsWith(content) && markdownWrapper.test(line.slice(0, contentStart))) return `\n${content}`;
				contentStart = line.indexOf(sourceLineStart, contentStart + 1);
			}
			return incompleteMarkdownWrapper.test(line) ? "\n" : `\n${line}`;
		});
		if (suffix.length < normalizedSource.length && normalizedSource.startsWith(suffix) || unwrappedSuffix.length < normalizedSource.length && normalizedSource.startsWith(unwrappedSuffix)) return result.slice(0, candidateStart);
		candidateStart = result.indexOf(sourceStart, candidateStart + 1);
	}
	return result;
}
function createVerifiedConversationContextStreamFilter(getConversationContext) {
	let accumulatedText = "";
	let releasedText = "";
	let conversationContextSource;
	let preparedConversationContext;
	return (delta) => {
		accumulatedText += delta;
		const conversationContext = getConversationContext?.();
		const sourceChanged = conversationContext !== conversationContextSource;
		if (sourceChanged) {
			preparedConversationContext = prepareVerifiedConversationContext(conversationContext?.trim());
			conversationContextSource = conversationContext;
		}
		const safeText = stripVerifiedConversationContext(accumulatedText, preparedConversationContext, true);
		if (releasedText === null || (sourceChanged || preparedConversationContext) && !safeText.startsWith(releasedText)) {
			releasedText = null;
			return "";
		}
		const newlySafeText = safeText.slice(releasedText.length);
		releasedText = safeText;
		return newlySafeText;
	};
}
function collapseConsecutiveDuplicateBlocks(text) {
	const trimmed = text.trim();
	if (!trimmed) return text;
	const blocks = trimmed.split(/\n{2,}/);
	if (blocks.length < 2) return text;
	const result = [];
	let lastNormalized = null;
	for (const block of blocks) {
		const normalized = block.trim().replace(/\s+/g, " ");
		if (lastNormalized && normalized === lastNormalized) continue;
		result.push(block.trim());
		lastNormalized = normalized;
	}
	return result.length === blocks.length ? text : result.join("\n\n");
}
function sanitizeUserFacingText(text, opts) {
	const raw = coerceChatContentText(text);
	if (!raw) return raw;
	const conversationContext = opts?.conversationContext?.trim();
	const withoutPlaceholder = stripInternalPlaceholderLines(stripToolCallXmlTags(stripMinimaxToolCallXml(stripInboundMetadata(stripInternalRuntimeContext(stripFinalTagsFromText(conversationContext && (opts?.streaming || hasConversationContextMarker(raw)) ? stripVerifiedConversationContext(raw, prepareVerifiedConversationContext(conversationContext), opts?.streaming) : raw)))), { stripFunctionCallsXmlPayloads: true }));
	const withoutToolCallBlocks = stripPlainTextToolCallBlocks(stripLegacyBracketToolCallBlocks(opts?.errorContext ? stripAssistantInternalTraceLines(withoutPlaceholder) : withoutPlaceholder), { resolveProtectedRanges: findCodeRegions });
	if (!withoutToolCallBlocks.trim()) return "";
	return collapseConsecutiveDuplicateBlocks(withoutToolCallBlocks.replace(/^(?:[ \t]*\r?\n)+/, ""));
}
//#endregion
//#region src/agents/embedded-agent-helpers/user-facing-text.ts
/** Compose internal-text stripping with the canonical failover copy renderer. */
function renderUserFacingText(text, opts) {
	return renderSanitizedUserFacingText(sanitizeUserFacingText(text, opts), opts);
}
//#endregion
export { createVerifiedConversationContextStreamFilter as n, sanitizeUserFacingText as r, renderUserFacingText as t };
