UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

2,312 lines 87.5 kB
import "./src-vebZIeLe.js";
import { i as asOptionalObjectRecord } from "./record-coerce-DItp3I4t.js";
import { n as safeParseJsonRecord } from "./json-coercion-AulM0PZ6.js";
//#region packages/tool-call-repair/src/grammar.ts
/** Legacy marker some models emit after a serialized JSON tool request. */
const END_TOOL_REQUEST = "[END_TOOL_REQUEST]";
/** Harmony stream marker that may close a serialized tool-call payload. */
const HARMONY_CALL_MARKER = "<|call|>";
/** Tool names in bracket/plain-text repairs intentionally match provider-safe ids only. */
function isPlainTextToolNameChar(char) {
	return Boolean(char && /[A-Za-z0-9_-]/.test(char));
}
/** XML-ish function tags allow namespace punctuation used by some model families. */
function isXmlishNameChar(char) {
	return Boolean(char && /[A-Za-z0-9_.:-]/.test(char));
}
/** Skips spaces and tabs only, preserving line boundaries for grammar decisions. */
function skipHorizontalWhitespace(text, start) {
	let index = start;
	while (index < text.length && (text[index] === " " || text[index] === "	")) index += 1;
	return index;
}
/** Skips indentation whitespace without crossing the current line boundary. */
function skipLineIndentation(text, start) {
	let index = start;
	while (index < text.length && /[^\S\r\n]/u.test(text[index] ?? "")) index += 1;
	return index;
}
/** Skips all JavaScript whitespace when line structure is no longer meaningful. */
function skipWhitespace(text, start) {
	let index = start;
	while (index < text.length && /\s/.test(text[index] ?? "")) index += 1;
	return index;
}
/** Consumes either Unix or Windows line endings and returns the first offset after them. */
function consumeLineBreak(text, start) {
	if (text[start] === "\r") return text[start + 1] === "\n" ? start + 2 : start + 1;
	if (text[start] === "\n") return start + 1;
	return null;
}
function consumeStructuralLineBreakAfterHorizontalWhitespace(text, start, options) {
	const right = skipHorizontalWhitespace(text, start);
	const actual = consumeLineBreak(text, right);
	if (actual !== null) return actual;
	for (let offset = start; offset <= right; offset += 1) if (options?.lineBreakOffsets.has(offset)) {
		options.usedLineBreakOffsets?.add(offset);
		return offset;
	}
	return null;
}
const utf8Encoder = new TextEncoder();
/** Returns the encoded byte length when a source span stays within its serialized limit. */
function utf8ByteLengthWithinLimit(text, start, end, maxBytes) {
	if (end - start > maxBytes) return null;
	const byteLength = utf8Encoder.encode(text.slice(start, end)).byteLength;
	return byteLength <= maxBytes ? byteLength : null;
}
const FUNCTION_OPEN = "<function=";
const FUNCTION_CLOSE = "</function>";
const PARAMETER_OPEN = "<parameter=";
const PARAMETER_CLOSE = "</parameter>";
function startsWithAsciiMarkerIgnoreCase(text, cursor, marker) {
	return text.slice(cursor, cursor + marker.length).toLowerCase() === marker;
}
function isAsciiMarkerPrefixIgnoreCase(text, cursor, marker) {
	const rest = text.slice(cursor, cursor + marker.length).toLowerCase();
	return rest.length < marker.length && marker.startsWith(rest);
}
function indexOfAsciiMarkerIgnoreCase(text, marker, start) {
	for (let cursor = text.indexOf("<", start); cursor !== -1; cursor = text.indexOf("<", cursor + 1)) if (startsWithAsciiMarkerIgnoreCase(text, cursor, marker)) return cursor;
	return -1;
}
/** Uncapped structural scan shared by parsing, stripping, and stream buffering. */
function scanXmlishToolCall(text, start = 0, structuralLineBreaks) {
	let cursor = start;
	let syntax;
	let name;
	if (text[cursor] === "<") {
		if (!startsWithAsciiMarkerIgnoreCase(text, cursor, FUNCTION_OPEN) && !isAsciiMarkerPrefixIgnoreCase(text, cursor, FUNCTION_OPEN)) return {
			kind: "invalid",
			at: start
		};
		if (text.length - cursor < 10) return { kind: "prefix" };
		cursor += 10;
		const nameStart = cursor;
		while (isXmlishNameChar(text[cursor]) && cursor - nameStart < 121) cursor += 1;
		name = {
			start: nameStart,
			end: cursor
		};
		syntax = "function";
		if (cursor - nameStart > 120) return {
			kind: "invalid",
			at: cursor
		};
		if (cursor === text.length) return {
			kind: "prefix",
			candidate: {
				syntax,
				name,
				nameComplete: false,
				parameters: []
			}
		};
		if (cursor === nameStart || text[cursor] !== ">") return {
			kind: "invalid",
			at: cursor
		};
		cursor += 1;
	} else if (text[cursor] === "[") {
		cursor += 1;
		const firstNameStart = cursor;
		while (isPlainTextToolNameChar(text[cursor]) && cursor - firstNameStart < 121) cursor += 1;
		if (cursor - firstNameStart > 120) return {
			kind: "invalid",
			at: cursor
		};
		const firstName = text.slice(firstNameStart, cursor);
		if (cursor === text.length && "tool".startsWith(firstName)) return { kind: "prefix" };
		syntax = "named-bracket";
		name = {
			start: firstNameStart,
			end: cursor
		};
		if (text[cursor] === ":" && firstName === "tool") {
			syntax = "tool-bracket";
			cursor += 1;
			const nameStart = cursor;
			while (isPlainTextToolNameChar(text[cursor]) && cursor - nameStart < 121) cursor += 1;
			name = {
				start: nameStart,
				end: cursor
			};
			if (cursor - nameStart > 120) return {
				kind: "invalid",
				at: cursor
			};
		}
		if (cursor === text.length) return {
			kind: "prefix",
			candidate: {
				syntax,
				name,
				nameComplete: false,
				parameters: []
			}
		};
		if (name.start === name.end || text[cursor] !== "]") return {
			kind: "invalid",
			at: cursor
		};
		cursor += 1;
		if (syntax === "named-bracket") {
			if (cursor === text.length) return {
				kind: "prefix",
				candidate: {
					syntax,
					name,
					nameComplete: true,
					parameters: []
				}
			};
			const afterLineBreak = consumeStructuralLineBreakAfterHorizontalWhitespace(text, cursor, structuralLineBreaks);
			if (afterLineBreak === null) return {
				kind: "invalid",
				at: cursor
			};
			cursor = afterLineBreak;
		}
	} else return {
		kind: "invalid",
		at: start
	};
	const bodyStart = cursor;
	const parameters = [];
	const candidate = (payloadEnd, activeParameterOpenEnd) => ({
		syntax,
		name,
		nameComplete: true,
		parameters,
		payload: {
			start: bodyStart,
			end: payloadEnd
		},
		...activeParameterOpenEnd === void 0 ? {} : { activeParameterOpenEnd }
	});
	let lastParameterEnd;
	const prefix = (payloadEnd, activeParameterOpenEnd) => ({
		kind: "prefix",
		candidate: candidate(payloadEnd, activeParameterOpenEnd),
		completeEnd: syntax === "tool-bracket" ? lastParameterEnd : void 0
	});
	const complete = (payloadEnd, end = payloadEnd) => ({
		kind: "complete",
		...candidate(payloadEnd),
		end
	});
	while (true) {
		const markerStart = skipWhitespace(text, cursor);
		if (markerStart === text.length) return syntax === "tool-bracket" && lastParameterEnd !== void 0 ? complete(lastParameterEnd) : {
			kind: "prefix",
			candidate: candidate(text.length)
		};
		if (startsWithAsciiMarkerIgnoreCase(text, markerStart, FUNCTION_CLOSE)) return syntax !== "function" && parameters.length === 0 ? {
			kind: "invalid",
			at: markerStart,
			candidate: candidate(markerStart)
		} : complete(markerStart, markerStart + 11);
		if (isAsciiMarkerPrefixIgnoreCase(text, markerStart, FUNCTION_CLOSE)) return prefix(markerStart);
		if (startsWithAsciiMarkerIgnoreCase(text, markerStart, PARAMETER_OPEN)) {
			const nameStart = markerStart + 11;
			let nameEnd = nameStart;
			while (isXmlishNameChar(text[nameEnd]) && nameEnd - nameStart < 121) nameEnd += 1;
			if (nameEnd - nameStart > 120) return {
				kind: "invalid",
				at: markerStart,
				candidate: candidate(markerStart)
			};
			if (nameEnd === text.length) return prefix(markerStart);
			if (nameEnd === nameStart || text[nameEnd] !== ">") return {
				kind: "invalid",
				at: markerStart,
				candidate: candidate(markerStart)
			};
			const valueStart = nameEnd + 1;
			const closeStart = indexOfAsciiMarkerIgnoreCase(text, PARAMETER_CLOSE, valueStart);
			if (closeStart === -1) return prefix(text.length, valueStart);
			const end = closeStart + 12;
			parameters.push({
				name: {
					start: nameStart,
					end: nameEnd
				},
				value: {
					start: valueStart,
					end: closeStart
				}
			});
			cursor = end;
			lastParameterEnd = end;
			continue;
		}
		if (isAsciiMarkerPrefixIgnoreCase(text, markerStart, PARAMETER_OPEN)) return prefix(markerStart);
		if (syntax === "tool-bracket" && lastParameterEnd !== void 0) return complete(lastParameterEnd);
		return {
			kind: "invalid",
			at: markerStart,
			candidate: candidate(markerStart)
		};
	}
}
//#endregion
//#region packages/tool-call-repair/src/contracts.ts
function isOffsetInProtectedRanges(offset, ranges) {
	return ranges.some((range) => offset >= range.start && offset < range.end);
}
//#endregion
//#region packages/tool-call-repair/src/payload.ts
const DEFAULT_MAX_PLAIN_TEXT_TOOL_PAYLOAD_BYTES = 256e3;
const MAX_PLAIN_TEXT_TOOL_NAME_CHARS = 120;
const HARMONY_CHANNELS = [
	"commentary",
	"analysis",
	"final"
];
function isLiteralPrefixAt(text, start, literal) {
	const available = text.length - start;
	return start >= 0 && available < literal.length && literal.startsWith(text.slice(start));
}
function scanToolNameEnd(text, start) {
	let end = start;
	while (isPlainTextToolNameChar(text[end])) {
		if (end - start === MAX_PLAIN_TEXT_TOOL_NAME_CHARS) return null;
		end += 1;
	}
	return end;
}
function candidate(syntax, name, nameComplete, payload, json) {
	return {
		syntax,
		name,
		nameComplete,
		...payload ? { payload } : {},
		...json ? { json } : {}
	};
}
function scanBracketOpening(text, start, structuralLineBreaks) {
	let cursor = start + 1;
	let syntax = "named-bracket";
	if (text.startsWith("tool:", cursor)) {
		syntax = "tool-bracket";
		cursor += 5;
	} else if (isLiteralPrefixAt(text, cursor, "tool:")) return { kind: "prefix" };
	const nameStart = cursor;
	const nameEnd = scanToolNameEnd(text, nameStart);
	if (nameEnd === null) return {
		kind: "invalid",
		at: nameStart + MAX_PLAIN_TEXT_TOOL_NAME_CHARS
	};
	const name = {
		start: nameStart,
		end: nameEnd
	};
	cursor = nameEnd;
	if (cursor === text.length) return {
		kind: "prefix",
		...nameStart === nameEnd ? {} : { candidate: candidate(syntax, name, false) }
	};
	if (nameStart === nameEnd || text[cursor] !== "]") return {
		kind: "invalid",
		at: cursor
	};
	cursor += 1;
	const value = candidate(syntax, name, true);
	if (syntax === "named-bracket") {
		const horizontalEnd = skipHorizontalWhitespace(text, cursor);
		if (horizontalEnd === text.length) return {
			kind: "prefix",
			candidate: value
		};
		const afterLineBreak = consumeStructuralLineBreakAfterHorizontalWhitespace(text, cursor, structuralLineBreaks);
		if (afterLineBreak === null) return {
			kind: "invalid",
			at: horizontalEnd,
			candidate: value
		};
		cursor = afterLineBreak;
	}
	return {
		kind: "complete",
		cursor,
		value
	};
}
function scanHarmonyOpening(text, start) {
	let cursor = start;
	if (text.startsWith("<|channel|>", cursor)) cursor += 11;
	else if (isLiteralPrefixAt(text, cursor, "<|channel|>")) return { kind: "prefix" };
	else if (text[cursor] === "<") return {
		kind: "invalid",
		at: cursor
	};
	const channel = HARMONY_CHANNELS.find((value) => text.startsWith(value, cursor));
	if (!channel) return HARMONY_CHANNELS.some((value) => isLiteralPrefixAt(text, cursor, value)) ? { kind: "prefix" } : {
		kind: "invalid",
		at: cursor
	};
	cursor += channel.length;
	if (cursor === text.length) return { kind: "prefix" };
	if (text[cursor] !== " " && text[cursor] !== "	") return {
		kind: "invalid",
		at: cursor
	};
	cursor = skipHorizontalWhitespace(text, cursor);
	if (!text.startsWith("to=", cursor)) return isLiteralPrefixAt(text, cursor, "to=") ? { kind: "prefix" } : {
		kind: "invalid",
		at: cursor
	};
	cursor += 3;
	const nameStart = cursor;
	const nameEnd = scanToolNameEnd(text, nameStart);
	if (nameEnd === null) return {
		kind: "invalid",
		at: nameStart + MAX_PLAIN_TEXT_TOOL_NAME_CHARS
	};
	const name = {
		start: nameStart,
		end: nameEnd
	};
	cursor = nameEnd;
	if (cursor === text.length) return {
		kind: "prefix",
		...nameStart === nameEnd ? {} : { candidate: candidate("harmony", name, false) }
	};
	if (nameStart === nameEnd || text[cursor] !== " " && text[cursor] !== "	") return {
		kind: "invalid",
		at: cursor
	};
	cursor = skipHorizontalWhitespace(text, cursor);
	const value = candidate("harmony", name, true);
	if (!text.startsWith("code", cursor)) return isLiteralPrefixAt(text, cursor, "code") ? {
		kind: "prefix",
		candidate: value
	} : {
		kind: "invalid",
		at: cursor,
		candidate: value
	};
	cursor = skipWhitespace(text, cursor + 4);
	if (text.startsWith("<|message|>", cursor)) cursor = skipWhitespace(text, cursor + 11);
	else if (isLiteralPrefixAt(text, cursor, "<|message|>")) return {
		kind: "prefix",
		candidate: value
	};
	else if (text[cursor] === "<") return {
		kind: "invalid",
		at: cursor,
		candidate: value
	};
	return {
		kind: "complete",
		cursor,
		value
	};
}
function scanJsonObject(text, start) {
	let depth = 0;
	let escaped = false;
	let inString = false;
	for (let index = start; index < text.length; index += 1) {
		const char = text[index];
		if (inString) {
			if (escaped) escaped = false;
			else if (char === "\\") escaped = true;
			else if (char === "\"") inString = false;
			continue;
		}
		if (char === "\"") inString = true;
		else if (char === "{") depth += 1;
		else if (char === "}") {
			depth -= 1;
			if (depth === 0) return {
				kind: "complete",
				end: index + 1,
				state: {
					depth,
					escaped,
					inString
				}
			};
		}
	}
	return {
		kind: "prefix",
		end: text.length,
		state: {
			depth,
			escaped,
			inString
		}
	};
}
/** Uncapped structural scan shared by parsing, stripping, and stream buffering. */
function scanPlainTextJsonToolCall(text, start = 0, structuralLineBreaks) {
	const opening = text[start] === "[" ? scanBracketOpening(text, start, structuralLineBreaks) : scanHarmonyOpening(text, start);
	if (opening.kind !== "complete") return opening;
	const value = opening.value;
	const payloadStart = skipWhitespace(text, opening.cursor);
	if (payloadStart === text.length) return {
		kind: "prefix",
		candidate: value
	};
	if (text[payloadStart] !== "{") return {
		kind: "invalid",
		at: payloadStart,
		candidate: value
	};
	const json = scanJsonObject(text, payloadStart);
	const payload = {
		start: payloadStart,
		end: json.end
	};
	if (json.kind === "prefix") return {
		kind: "prefix",
		candidate: candidate(value.syntax, value.name, true, payload, json.state)
	};
	const closingCandidate = candidate(value.syntax, value.name, true, payload, json.state);
	if (value.syntax !== "named-bracket") {
		const markerStart = skipWhitespace(text, json.end);
		const name = text.slice(value.name.start, value.name.end);
		const closings = [
			HARMONY_CALL_MARKER,
			END_TOOL_REQUEST,
			`[/${name}]`
		];
		for (const closing of closings) {
			if (text.startsWith(closing, markerStart)) return {
				...value,
				kind: "complete",
				payload,
				end: markerStart + closing.length
			};
			if (markerStart < text.length && isLiteralPrefixAt(text, markerStart, closing)) return {
				kind: "prefix",
				candidate: closingCandidate
			};
		}
		return {
			...value,
			kind: "complete",
			payload,
			end: json.end
		};
	}
	const closingStart = skipWhitespace(text, json.end);
	if (closingStart === text.length) return {
		kind: "prefix",
		candidate: closingCandidate
	};
	const name = text.slice(value.name.start, value.name.end);
	const closings = [END_TOOL_REQUEST, `[/${name}]`];
	for (const closing of closings) {
		if (text.startsWith(closing, closingStart)) return {
			...value,
			payload,
			kind: "complete",
			end: closingStart + closing.length
		};
		if (isLiteralPrefixAt(text, closingStart, closing)) return {
			kind: "prefix",
			candidate: closingCandidate
		};
	}
	return {
		kind: "invalid",
		at: closingStart,
		candidate: closingCandidate
	};
}
/** Classifies one JSON/XML call candidate and provides monotonic scan progress. */
function scanPlainTextToolCall(text, start = 0, options) {
	const xmlish = scanXmlishToolCall(text, start, options?.structuralLineBreaks);
	const json = scanPlainTextJsonToolCall(text, start, options?.structuralLineBreaks);
	const maxPayloadBytes = options?.maxPayloadBytes ?? DEFAULT_MAX_PLAIN_TEXT_TOOL_PAYLOAD_BYTES;
	const allowed = (scan) => {
		const value = scan.kind === "complete" ? scan : scan.candidate;
		if (!value) return { accepted: scan.kind === "prefix" };
		const name = text.slice(value.name.start, value.name.end);
		return (value.nameComplete ? options?.matcher?.hasExactName(name) ?? true : options?.matcher?.hasNamePrefix(name) ?? true) ? {
			accepted: true,
			value,
			...value.payload ? { payload: value.payload } : {}
		} : { accepted: false };
	};
	const xml = allowed(xmlish);
	const jsonValue = allowed(json);
	const branches = {
		json,
		matches: {
			json: jsonValue.accepted,
			xmlish: xml.accepted
		},
		xmlish
	};
	const overCap = (payload) => Boolean(payload && utf8ByteLengthWithinLimit(text, payload.start, payload.end, maxPayloadBytes) === null);
	const xmlOverCap = overCap(xml.payload);
	const jsonOverCap = overCap(jsonValue.payload);
	if (xml.accepted && xmlish.kind === "complete") return {
		...branches,
		end: xmlish.end,
		kind: "complete",
		next: xmlish.end,
		overCap: xmlOverCap,
		payloadStart: xmlish.payload.start
	};
	if (jsonValue.accepted && json.kind === "complete") {
		if (jsonOverCap || parseJsonArguments(text, json.payload)) return {
			...branches,
			end: json.end,
			kind: "complete",
			next: json.end,
			overCap: jsonOverCap,
			payloadStart: json.payload.start
		};
		return {
			...branches,
			at: json.end,
			kind: "invalid",
			next: json.end,
			overCap: false,
			payloadStart: json.payload.start
		};
	}
	if (xml.accepted && xmlish.kind === "invalid" && xmlOverCap && xml.payload) return {
		...branches,
		at: xmlish.at,
		kind: "invalid",
		next: xmlish.at,
		overCap: true,
		payloadStart: xml.payload.start
	};
	if (jsonValue.accepted && json.kind === "invalid" && jsonOverCap && jsonValue.payload) return {
		...branches,
		at: json.at,
		kind: "invalid",
		next: json.at,
		overCap: true,
		payloadStart: jsonValue.payload.start
	};
	const xmlPrefix = xml.accepted && xmlish.kind === "prefix";
	const jsonPrefix = jsonValue.accepted && json.kind === "prefix";
	if (xmlPrefix || jsonPrefix) {
		const payload = xmlPrefix ? xml.payload : jsonValue.payload;
		return {
			...branches,
			...xmlish.kind === "prefix" && xmlish.completeEnd !== void 0 ? { completeEnd: xmlish.completeEnd } : {},
			kind: "prefix",
			next: text.length,
			overCap: overCap(payload),
			...payload ? { payloadStart: payload.start } : {}
		};
	}
	let next = start + 1;
	if (xml.accepted) next = Math.max(next, xmlish.kind === "invalid" ? xmlish.at : text.length);
	if (jsonValue.accepted) next = Math.max(next, json.kind === "complete" ? json.end : json.kind === "invalid" ? json.at : text.length);
	return {
		...branches,
		at: next,
		kind: "invalid",
		next,
		overCap: false
	};
}
function parsePlainTextToolCallBlockAt(text, start, options, structuralLineBreaks) {
	const scan = scanPlainTextJsonToolCall(text, start, structuralLineBreaks);
	if (scan.kind !== "complete") return null;
	const name = text.slice(scan.name.start, scan.name.end);
	if (options?.allowedToolNames && !options.allowedToolNames.has(name)) return null;
	const maxPayloadBytes = options?.maxPayloadBytes ?? DEFAULT_MAX_PLAIN_TEXT_TOOL_PAYLOAD_BYTES;
	if (utf8ByteLengthWithinLimit(text, scan.payload.start, scan.payload.end, maxPayloadBytes) === null) return null;
	const argumentsValue = parseJsonArguments(text, scan.payload);
	if (!argumentsValue) return null;
	return {
		arguments: argumentsValue,
		end: scan.end,
		name,
		raw: text.slice(start, scan.end),
		start
	};
}
function parseJsonArguments(text, payload) {
	return safeParseJsonRecord(text.slice(payload.start, payload.end)) ?? null;
}
function extractXmlishParameterValue(text, start, end, structuralLineBreaks) {
	let value = text.slice(start, end);
	if (consumeLineBreak(text, skipHorizontalWhitespace(text, start)) === null) {
		const boundary = consumeStructuralLineBreakAfterHorizontalWhitespace(text, start, structuralLineBreaks);
		if (boundary !== null) {
			const offset = boundary - start;
			value = `${value.slice(0, offset)}\n${value.slice(offset)}`;
		}
	}
	const payloadStart = consumeLineBreak(value, 0);
	if (payloadStart === null) return value;
	return value.slice(payloadStart).replace(/(?:\r\n|[\r\n])$/u, "");
}
function parseXmlishPlainTextToolCallBlockAt(text, start, options, structuralLineBreaks) {
	const scan = scanXmlishToolCall(text, start, structuralLineBreaks);
	if (scan.kind !== "complete") return null;
	const name = text.slice(scan.name.start, scan.name.end);
	if (options?.allowedToolNames && !options.allowedToolNames.has(name)) return null;
	const maxPayloadBytes = options?.maxPayloadBytes ?? DEFAULT_MAX_PLAIN_TEXT_TOOL_PAYLOAD_BYTES;
	if (utf8ByteLengthWithinLimit(text, scan.payload.start, scan.payload.end, maxPayloadBytes) === null) return null;
	return {
		arguments: Object.fromEntries(scan.parameters.map((parameter) => [text.slice(parameter.name.start, parameter.name.end), extractXmlishParameterValue(text, parameter.value.start, parameter.value.end, structuralLineBreaks)])),
		end: scan.end,
		name,
		raw: text.slice(start, scan.end),
		start
	};
}
function parsePlainTextToolCallBlockAtAnySyntax(text, start, options, structuralLineBreaks) {
	return parsePlainTextToolCallBlockAt(text, start, options, structuralLineBreaks) ?? parseXmlishPlainTextToolCallBlockAt(text, start, options, structuralLineBreaks);
}
function normalizeParseOptions(options) {
	return options ? {
		...options,
		allowedToolNames: options.allowedToolNames ? new Set(options.allowedToolNames) : void 0
	} : void 0;
}
function parseStandalonePlainTextToolCallBlocks(text, options, structuralLineBreaks) {
	const blocks = [];
	const normalizedOptions = normalizeParseOptions(options);
	let cursor = skipWhitespace(text, 0);
	while (cursor < text.length) {
		const block = parsePlainTextToolCallBlockAtAnySyntax(text, cursor, normalizedOptions, structuralLineBreaks);
		if (!block) return null;
		blocks.push(block);
		cursor = skipWhitespace(text, block.end);
	}
	return blocks.length > 0 ? blocks : null;
}
/** Removes full-line standalone plain-text tool-call blocks from user-visible text. */
function stripPlainTextToolCallBlocks(text, options = {}) {
	if (!text || !/\[(?:tool:)?[A-Za-z0-9_-]+\]/.test(text) && !/(?:^|[\r\n])[^\S\r\n]*(?:<\|channel\|>)?(?:commentary|analysis|final)[ \t]+to=/.test(text) && !/(?:^|[\r\n])[^\S\r\n]*<function=/i.test(text)) return text;
	const protectedRanges = options.resolveProtectedRanges?.(text) ?? [];
	let result = "";
	let cursor = 0;
	let index = 0;
	while (index < text.length) {
		if (!(index === 0 || text[index - 1] === "\n" || text[index - 1] === "\r")) {
			index += 1;
			continue;
		}
		const blockStart = skipLineIndentation(text, index);
		if (isOffsetInProtectedRanges(blockStart, protectedRanges)) {
			index += 1;
			continue;
		}
		const scan = scanPlainTextToolCall(text, blockStart);
		if (scan.kind === "prefix" && scan.completeEnd === void 0) return result + text.slice(cursor);
		if (scan.kind === "invalid") {
			index = Math.max(index + 1, scan.next);
			continue;
		}
		let blockEnd = scan.kind === "complete" ? scan.end : scan.completeEnd;
		if (blockEnd === void 0) return result + text.slice(cursor);
		result += text.slice(cursor, index);
		while (true) {
			const adjacentStart = skipLineIndentation(text, blockEnd);
			if (isOffsetInProtectedRanges(adjacentStart, protectedRanges)) break;
			const adjacent = scanPlainTextToolCall(text, adjacentStart);
			const adjacentEnd = adjacent.kind === "complete" ? adjacent.end : adjacent.kind === "prefix" ? adjacent.completeEnd : void 0;
			if (adjacentEnd === void 0 || adjacentEnd <= blockEnd) break;
			blockEnd = adjacentEnd;
		}
		const lineBreakStart = skipLineIndentation(text, blockEnd);
		cursor = lineBreakStart === text.length ? lineBreakStart : consumeLineBreak(text, lineBreakStart) ?? blockEnd;
		index = cursor;
	}
	result += text.slice(cursor);
	return result;
}
//#endregion
//#region packages/tool-call-repair/src/protection-fast-path.ts
function createProtectionScanState() {
	return {
		fenceChar: void 0,
		fenceLength: 0,
		paragraphAmbiguous: false,
		structuralUnknown: false,
		partialLine: ""
	};
}
function cloneProtectionScanState(state) {
	return { ...state };
}
const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})/;
/** Blank means spaces and tabs only; Unicode spaces do not end a Markdown block. */
const BLANK_LINE = /^[ \t]*$/;
/**
* A CR that does not begin a CRLF pair. `findPotentialCallStart` treats a bare CR as a
* line start and the CommonMark resolver ends a line there, but this tracker splits on LF
* only, so it must decline rather than miss a fence boundary. A chunk that merely ends
* mid-CRLF also declines, which costs a parse but never correctness.
*/
const BARE_CARRIAGE_RETURN = /\r(?!\n)/;
/** A fence-length delimiter run outside a fence that did not open one at column 0. */
const UNCLASSIFIED_FENCE_DELIMITER = /`{3,}|~{3,}/;
/**
* Line starts that make indentation or delimiters structural, which this tracker does not
* model. Indentation counts in columns, so any leading tab qualifies (CommonMark expands
* one to four columns) and so does any run of four spaces, whatever follows it — including
* a further tab. Blank lines never reach here; they are handled before this test.
*/
const UNMODELED_BLOCK_START = /^(?: {0,3}(?:>|[-*+](?:\s|$)|\d{1,9}[.)](?:\s|$))| {4,}| *\t)/;
/** Applies one whole line (newline excluded) to the carried state. */
function applyLine(state, line) {
	if (state.structuralUnknown) return;
	if (state.fenceChar) {
		const run = /^ {0,3}(`{3,}|~{3,})[ \t]*$/.exec(line)?.[1];
		if (run && run[0] === state.fenceChar && run.length >= state.fenceLength) {
			state.fenceChar = void 0;
			state.fenceLength = 0;
		}
		return;
	}
	if (BLANK_LINE.test(line)) {
		state.paragraphAmbiguous = false;
		return;
	}
	const open = FENCE_OPEN.exec(line);
	const marker = open?.[1];
	if (marker) {
		const infoString = line.slice(open[0].length);
		if (state.paragraphAmbiguous || open[0].length !== marker.length || marker[0] === "`" && infoString.includes("`")) {
			state.structuralUnknown = true;
			return;
		}
		state.fenceChar = marker[0] === "~" ? "~" : "`";
		state.fenceLength = marker.length;
		return;
	}
	if (UNCLASSIFIED_FENCE_DELIMITER.test(line)) {
		state.structuralUnknown = true;
		return;
	}
	if (line.includes("`") || UNMODELED_BLOCK_START.test(line)) state.paragraphAmbiguous = true;
}
/** Feeds appended visible text into the carried state. */
function advanceProtectionScanState(state, text) {
	if (!text) return;
	if (BARE_CARRIAGE_RETURN.test(text)) {
		state.structuralUnknown = true;
		return;
	}
	let newline = text.indexOf("\n");
	if (newline === -1) {
		state.partialLine += text;
		return;
	}
	let line = (state.partialLine + text.slice(0, newline)).replace(/\r$/, "");
	applyLine(state, line);
	let cursor = newline + 1;
	newline = text.indexOf("\n", cursor);
	while (newline !== -1) {
		line = text.slice(cursor, newline).replace(/\r$/, "");
		applyLine(state, line);
		cursor = newline + 1;
		newline = text.indexOf("\n", cursor);
	}
	state.partialLine = text.slice(cursor);
}
/**
* Decides protection for candidate offsets inside `incoming`, given the state carried
* from the text before it. Returns `undefined` when the answer is not provable from
* fence state alone, which means the caller must run the authoritative full parse.
*/
function resolveProtectionFastPath(carried, incoming) {
	if (carried.paragraphAmbiguous || carried.structuralUnknown) return;
	if (BARE_CARRIAGE_RETURN.test(incoming)) return;
	const state = cloneProtectionScanState(carried);
	const lineStarts = [];
	let index = 0;
	let lineStart = 0;
	for (;;) {
		if (index >= incoming.length && !state.partialLine) break;
		const newline = incoming.indexOf("\n", index);
		const complete = newline !== -1;
		const line = `${state.partialLine}${incoming.slice(index, complete ? newline : void 0)}`.replace(/\r$/, "");
		const lineStartAbsolute = lineStart - state.partialLine.length;
		if (state.fenceChar) lineStarts.push([lineStartAbsolute, true]);
		else if (!complete) return;
		else {
			const probe = cloneProtectionScanState(state);
			applyLine(probe, line);
			if (probe.paragraphAmbiguous || probe.structuralUnknown) return;
			const opened = probe.fenceChar ? FENCE_OPEN.exec(line) : null;
			if (opened?.[1]) {
				lineStarts.push([lineStartAbsolute, false]);
				lineStarts.push([lineStartAbsolute + (opened[0].length - opened[1].length), true]);
			} else lineStarts.push([lineStartAbsolute, false]);
		}
		if (!complete) break;
		applyLine(state, line);
		if (state.paragraphAmbiguous || state.structuralUnknown) return;
		state.partialLine = "";
		index = newline + 1;
		lineStart = index;
		if (index > incoming.length) break;
	}
	let cursor = -1;
	let isProtected = false;
	return (offset) => {
		while (cursor + 1 < lineStarts.length) {
			const next = lineStarts[cursor + 1];
			if (next === void 0 || next[0] > offset) break;
			cursor += 1;
			isProtected = next[1];
		}
		return isProtected;
	};
}
//#endregion
//#region packages/tool-call-repair/src/stream-normalizer.ts
const MAX_PAYLOAD_BYTES = 256e3;
const MAX_PENDING_EVENTS = 256;
const MAX_PROTECTION_CONTEXT_CHARS = 1e6;
const MAX_TOOL_NAME_CHARS = 120;
function eventContentIndex(event) {
	const index = event.contentIndex;
	return typeof index === "number" && Number.isInteger(index) && index >= 0 ? index : 0;
}
function isTextStreamEvent(event) {
	return event.type === "text_start" || event.type === "text_delta" || event.type === "text_end";
}
function extractStandaloneCandidate(message, requireAssistantRole = false) {
	const record = asOptionalObjectRecord(message);
	if (!record || requireAssistantRole && record.role !== "assistant") return;
	if (typeof record.content === "string") return record.content.trim() ? {
		text: record.content,
		parts: []
	} : void 0;
	if (!Array.isArray(record.content)) return;
	const candidate = {
		text: "",
		parts: []
	};
	for (const [contentIndex, block] of record.content.entries()) {
		const value = asOptionalObjectRecord(block);
		if (!value) return;
		if (value.type !== "text") continue;
		if (typeof value.text !== "string") return;
		const start = candidate.text.length;
		candidate.text += value.text;
		candidate.parts.push({
			contentIndex,
			start,
			end: candidate.text.length
		});
	}
	return candidate.text.trim() ? candidate : void 0;
}
function scannedCall(scan) {
	if (scan.kind === "complete") return {
		end: scan.end,
		incomplete: false,
		overCap: scan.overCap,
		payloadStart: scan.payloadStart
	};
	if (scan.overCap && scan.payloadStart !== void 0) return {
		end: scan.kind === "prefix" ? scan.next : scan.at,
		incomplete: scan.kind === "prefix",
		overCap: true,
		payloadStart: scan.payloadStart
	};
	return null;
}
function scanHasNamedCandidate(scan) {
	return [scan.json, scan.xmlish].some((branch) => {
		const name = branch.candidate?.name ?? branch.name;
		return name !== void 0 && name.end > name.start;
	});
}
function consumeRemovedLineEnd(text, end) {
	const lineBreakStart = skipLineIndentation(text, end);
	if (lineBreakStart === text.length) return lineBreakStart;
	return consumeLineBreak(text, lineBreakStart) ?? end;
}
function findUtf8OverCapOffset(text, start) {
	let bytes = 0;
	for (let index = start; index < text.length;) {
		const code = text.codePointAt(index) ?? 0;
		index += code > 65535 ? 2 : 1;
		bytes += code <= 127 ? 1 : code <= 2047 ? 2 : code <= 65535 ? 3 : 4;
		if (bytes > MAX_PAYLOAD_BYTES) return index;
	}
	return null;
}
function findCallSequences(text, matcher, structuralBoundaries = [], structuralLineBreaks, protectedRanges = []) {
	const sequences = [];
	const structuralBoundarySet = new Set(structuralBoundaries);
	let structuralBoundaryIndex = 0;
	let index = 0;
	while (index < text.length) {
		if (!(index === 0 || text[index - 1] === "\n" || text[index - 1] === "\r" || structuralBoundarySet.has(index))) {
			index += 1;
			continue;
		}
		const sequenceStart = index;
		let callStart = skipLineIndentation(text, index);
		if (isOffsetInProtectedRanges(callStart, protectedRanges)) {
			index += 1;
			continue;
		}
		let sequenceEnd = callStart;
		let hasOverCap = false;
		let activeStart;
		let callCount = 0;
		const first = scanPlainTextToolCall(text, callStart, {
			matcher,
			maxPayloadBytes: MAX_PAYLOAD_BYTES,
			structuralLineBreaks
		});
		let call = scannedCall(first);
		if (!call && first.kind === "prefix" && scanHasNamedCandidate(first)) {
			activeStart = callStart;
			callCount = 1;
			sequenceEnd = text.length;
		}
		while (call && callStart < text.length) {
			if (call.incomplete && call.overCap) {
				const overCapOffset = findUtf8OverCapOffset(text, call.payloadStart);
				while (structuralBoundaryIndex < structuralBoundaries.length && (structuralBoundaries[structuralBoundaryIndex] ?? 0) < (overCapOffset ?? Infinity)) structuralBoundaryIndex += 1;
				let boundary;
				while (structuralBoundaryIndex < structuralBoundaries.length) {
					const offset = structuralBoundaries[structuralBoundaryIndex];
					structuralBoundaryIndex += 1;
					const boundaryScan = offset === void 0 ? void 0 : scanPlainTextToolCall(text, skipLineIndentation(text, offset), {
						matcher,
						maxPayloadBytes: MAX_PAYLOAD_BYTES,
						structuralLineBreaks
					});
					if (boundaryScan && scannedCall(boundaryScan)) {
						boundary = offset;
						break;
					}
				}
				if (boundary !== void 0) {
					call.end = boundary;
					call.incomplete = false;
				}
			}
			callCount += 1;
			hasOverCap ||= call.overCap;
			sequenceEnd = consumeRemovedLineEnd(text, call.end);
			if (call.incomplete) {
				activeStart = callStart;
				break;
			}
			const nextStart = skipWhitespace(text, call.end);
			if (nextStart >= text.length) break;
			if (isOffsetInProtectedRanges(nextStart, protectedRanges)) break;
			const nextScan = scanPlainTextToolCall(text, nextStart, {
				matcher,
				maxPayloadBytes: MAX_PAYLOAD_BYTES,
				structuralLineBreaks
			});
			const next = scannedCall(nextScan);
			if (!next) {
				if (nextScan.kind === "prefix" && scanHasNamedCandidate(nextScan)) {
					activeStart = nextStart;
					sequenceEnd = text.length;
				}
				break;
			}
			callStart = nextStart;
			call = next;
		}
		if (callCount > 0) {
			const aggregateOverCap = utf8ByteLengthWithinLimit(text, sequenceStart, sequenceEnd, MAX_PAYLOAD_BYTES) === null;
			sequences.push({
				start: sequenceStart,
				end: sequenceEnd,
				...activeStart === void 0 ? {} : { activeStart },
				overCap: hasOverCap || aggregateOverCap
			});
			index = Math.max(sequenceEnd, index + 1);
			continue;
		}
		index = Math.max(index + 1, first.next);
	}
	return sequences;
}
function createCandidateScanView(candidate) {
	const boundaries = candidate.parts.slice(1).map((part) => part.start);
	return {
		boundaries,
		text: candidate.text,
		...boundaries.length > 0 ? { structuralLineBreaks: { lineBreakOffsets: new Set(boundaries) } } : {}
	};
}
function findCandidateCallSequences(candidate, matcher, resolveProtectedRanges) {
	const view = createCandidateScanView(candidate);
	return findCallSequences(view.text, matcher, view.boundaries, view.structuralLineBreaks, resolveProtectedRanges?.(view.text));
}
function createRangeRemover(ranges) {
	let rangeIndex = 0;
	return (text, offset = 0) => {
		let result = "";
		let cursor = 0;
		const endOffset = offset + text.length;
		while ((ranges[rangeIndex]?.end ?? Infinity) <= offset) rangeIndex += 1;
		for (let range = ranges[rangeIndex]; range && range.start < endOffset; range = ranges[rangeIndex]) {
			const start = Math.max(0, range.start - offset);
			const end = Math.min(text.length, range.end - offset);
			if (end > start) {
				result += text.slice(cursor, Math.max(cursor, start));
				cursor = Math.max(cursor, end);
			}
			if (range.end > endOffset) break;
			rangeIndex += 1;
		}
		return cursor ? result + text.slice(cursor) : text;
	};
}
function projectRangesOntoMessage(record, candidate, ranges, preserveEmptyTextBlocks) {
	const removeRanges = createRangeRemover(ranges);
	if (typeof record.content === "string") return {
		message: {
			...record,
			content: removeRanges(record.content)
		},
		sourceToProjectedContentIndex: /* @__PURE__ */ new Map([[0, 0]])
	};
	if (!Array.isArray(record.content)) return {
		message: record,
		sourceToProjectedContentIndex: /* @__PURE__ */ new Map()
	};
	const parts = new Map(candidate.parts.map((part) => [part.contentIndex, part]));
	const content = [];
	const sourceToProjectedContentIndex = /* @__PURE__ */ new Map();
	for (const [index, block] of record.content.entries()) {
		const part = parts.get(index);
		const blockRecord = asOptionalObjectRecord(block);
		if (!part || blockRecord?.type !== "text" || typeof blockRecord.text !== "string") {
			sourceToProjectedContentIndex.set(index, content.length);
			content.push(block);
			continue;
		}
		const text = removeRanges(blockRecord.text, part.start);
		if (text || preserveEmptyTextBlocks) {
			sourceToProjectedContentIndex.set(index, content.length);
			content.push({
				...blockRecord,
				text
			});
		}
	}
	return {
		message: {
			...record,
			content
		},
		sourceToProjectedContentIndex
	};
}
/** Scrubs unsafe or mixed calls and maps each retained source content block. */
function projectScrubbedPlainTextToolCallMessage(params) {
	const record = asOptionalObjectRecord(params.message);
	const candidate = extractStandaloneCandidate(params.message, params.requireAssistantRole === true);
	if (!record || !candidate) return;
	const sequences = findCandidateCallSequences(candidate, params.matcher, params.resolveProtectedRanges);
	const visibleOutsideCalls = Boolean(createRangeRemover(sequences)(candidate.text).trim());
	const ranges = sequences.filter((sequence) => params.forceKnownCandidates || sequence.overCap || visibleOutsideCalls || params.forceIncompleteCandidates && sequence.activeStart !== void 0);
	return ranges.length > 0 ? projectRangesOntoMessage(record, candidate, ranges, params.preserveEmptyTextBlocks === true) : void 0;
}
function findPotentialCallStart(text, atLineStart, matcher, isProtected) {
	for (let index = 0; index < text.length;) {
		if (!(index === 0 && atLineStart || text[index - 1] === "\n" || text[index - 1] === "\r")) {
			index += 1;
			continue;
		}
		const start = skipLineIndentation(text, index);
		if (isProtected?.(start)) {
			index += 1;
			continue;
		}
		const scan = scanPlainTextToolCall(text, start, {
			matcher,
			maxPayloadBytes: MAX_PAYLOAD_BYTES
		});
		if (scan.kind === "prefix" || scannedCall(scan)) return index;
		index = Math.max(index + 1, scan.next);
	}
	return null;
}
/**
* Decides whether the carried fence-state scan can be trusted for a candidate in
* `contentIndex`, reusing a cached verdict from an earlier delta in the same block when
* it is still known to apply.
*
* The scan advances in event order; `partial`'s own per-block offsets are in
* content-index order. These normally agree, but not when a provider interleaves active
* blocks (an earlier block can stream after a later one), when an earlier block was
* never streamed as its own delta at all, or when an earlier block is itself still
* actively streaming and grows between two candidate checks in a later block -- so the
* scan's state does not correspond to "everything that precedes this block" and must
* not be trusted without checking the partial's own reported preceding text.
*
* `partial` is optional on every event, so a delta can arrive with none at all -- that
* proves nothing either way and, with no cache to fall back on either, defaults to
* trusting the scan (there is nothing to contradict it with; it remains the only source
* of truth this normalizer itself built, in order).
*
* A cached verdict is reused only when a later delta's own reported preceding length
* (`part.start`) exactly matches the length the cache was validated against -- a
* still-evolving earlier block changes that length, invalidating the cache and forcing
* a fresh comparison, rather than trusting a verdict that predates the earlier block's
* own growth. A length match alone does not otherwise prove agreement -- interleaved
* blocks of the same length streamed out of order can produce the same tracked length
* from different actual text (e.g. opposite fence state) -- so a fresh, same-length,
* nonzero-length comparison still needs a real text comparison. `trackedPrefix` is
* called lazily: only an uncached (or invalidated) comparison pays for materializing it,
* and its cost is bounded by the (typically small, fixed) preceding-block size, not by
* however large the current block's own growing content is.
*/
function resolvePrecedingContextTrust(partial, contentIndex, trackedLength, trackedPrefix, cached) {
	const candidate = extractStandaloneCandidate(partial);
	const part = candidate?.parts.find((entry) => entry.contentIndex === contentIndex);
	if (!candidate || !part) return {
		cache: cached,
		trusted: cached?.trusted ?? true
	};
	if (cached && cached.precedingLength === part.start) return {
		cache: cached,
		trusted: cached.trusted
	};
	const trusted = part.start === trackedLength && (part.start === 0 || candidate.text.slice(0, part.start) === trackedPrefix());
	return {
		cache: {
			precedingLength: part.start,
			trusted
		},
		trusted
	};
}
function resolvePartialProtectionCheck(params) {
	const candidate = extractStandaloneCandidate(params.partial);
	const record = asOptionalObjectRecord(params.partial);
	if (!candidate || !record) return;
	let blockStart = 0;
	let blockText;
	if (typeof record.content === "string") {
		if (params.contentIndex !== 0) return;
		blockText = record.content;
	} else {
		const part = candidate.parts.find((entry) => entry.contentIndex === params.contentIndex);
		const block = Array.isArray(record.content) ? asOptionalObjectRecord(record.content[params.contentIndex]) : void 0;
		if (!part || block?.type !== "text" || typeof block.text !== "string") return;
		blockStart = part.start;
		blockText = block.text;
	}
	const incomingStart = params.authoritative ? 0 : blockText.length - params.incoming.length;
	if (incomingStart < 0 || (params.authoritative ? blockText !== params.incoming : !blockText.endsWith(params.incoming))) return;
	const protectedRanges = params.resolveProtectedRanges(candidate.text);
	return (offset) => isOffsetInProtectedRanges(blockStart + incomingStart + offset, protectedRanges);
}
function nextAtLineStart(previous, text) {
	if (!text) return previous;
	return text.endsWith("\n") || text.endsWith("\r");
}
function eventTemplate(event) {
	const template = { ...event };
	delete template.content;
	delete template.delta;
	delete template.partial;
	return template;
}
function createSyntheticTextDelta(template, text, partial) {
	return {
		...eventTemplate(template),
		type: "text_delta",
		delta: text,
		...partial ? { partial } : {}
	};
}
function cappedUtf8ByteLength(text) {
	return utf8ByteLengthWithinLimit(text, 0, text.length, MAX_PAYLOAD_BYTES) ?? 256001;
}
function pendingEventBytes(record) {
	const delta = typeof record.delta === "string" ? cappedUtf8ByteLength(record.delta) : 0;
	const content = typeof record.content === "string" ? cappedUtf8ByteLength(record.content) : 0;
	return Math.min(256001, delta + content);
}
function pendingQueueOverCap(pending) {
	return pending.entryBytes > MAX_PAYLOAD_BYTES || (pending.entries?.length ?? 0) > MAX_PENDING_EVENTS;
}
function createPendingState(record, text, heldStart, sequenceOverCap = false, snapshotOffset = 0) {
	const entries = [...heldStart ? [{ ...heldStart }] : [], { ...record }];
	return {
		buffer: text,
		bufferBytes: cappedUtf8ByteLength(text),
		entries,
		entryBytes: entries.reduce((total, entry) => {
			return Math.min(256001, total + pendingEventBytes(entry));
		}, 0),
		kind: "candidate",
		nextScanChars: 256,
		parts: [{
			contentIndex: eventContentIndex(record),
			start: 0,
			end: text.length
		}],
		sequenceOverCap,
		snapshotOffset,
		template: eventTemplate(record)
	};
}
function queuePendingEvent(pending, record) {
	if (!pending.entries) return;
	const event = { ...record };
	pending.entryBytes = Math.min(256001, pending.entryBytes + pendingEventBytes(event));
	const previous = pending.entries.at(-1);
	if (!(typeof previous?.delta === "string" && typeof event.delta === "string" && previous.type === event.type && eventContentIndex(previous) === eventContentIndex(event)) || !previous) {
		pending.entries.push(event);
		return;
	}
	previous.delta = previous.delta + event.delta;
	if (Object.hasOwn(event, "partial")) previous.partial = event.partial;
}
function appendPendingText(pending, text, record) {
	queuePendingEvent(pending, record);
	if (text) {
		const start = pending.buffer.length;
		const high = pending.buffer.charCodeAt(pending.buffer.length - 1);
		const low = text.charCodeAt(0);
		const joinedPair = high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343;
		pending.bufferBytes = Math.min(256001, pending.bufferBytes + cappedUtf8ByteLength(text) - (joinedPair ? 2 : 0));
		pending.buffer += text;
		const contentIndex = eventContentIndex(record);
		const previous = pending.parts.at(-1);
		if (previous?.contentIndex === contentIndex) previous.end = pending.buffer.length;
		else pending.parts.push({
			contentIndex,
			start,
			end: pending.buffer.length
		});
	}
	pending.template = eventTemplate(record);
}
function replayFalsePositiveCandidate(pending) {
	return pending.entries ?? [createSyntheticTextDelta(pending.template, pending.buffer)];
}
function projectPendingAuxEvents(pending, projection, projectPartial, retainedTextContentIndex) {
	return (pending.entries ?? []).flatMap((event) => {
		if (isTextStreamEvent(event)) {
			if (event.type !== "text_start" || eventContentIndex(event) !== retainedTextContentIndex) return [];
		}
		let eventProjection = projection ?? projectPartial?.(event.partial);
		const projectedEvent = { ...event };
		if (eventProjection && typeof event.contentIndex === "number") {
			let contentIndex = eventProjection.sourceToProjectedContentIndex.get(event.contentIndex);
			if (contentIndex === void 0 && projection) {
				const partialProjection = projectPartial?.(event.partial);
				const partialContentIndex = partialProjection?.sourceToProjectedContentIndex.get(event.contentIndex);
				if (partialProjection && partialContentIndex !== void 0) {
					eventProjection = partialProjection;
					contentIndex = partialContentIndex;
				}
			}
			if (contentIndex === void 0) return [];
			projectedEvent.contentIndex = contentIndex;
		}
		if (Object.hasOwn(projectedEvent, "partial")) {
			if (eventProjection) projectedEvent.partial = eventProjection.message;
		}
		return [projectedEvent];
	});
}
function projectEventIndex(event, projection) {
	if (typeof event.contentIndex !== "number") return event;
	const contentIndex = projection.sourceToProjectedContentIndex.get(event.contentIndex);
	return contentIndex === void 0 ? void 0 : {
		...event,
		contentIndex
	};
}
function projectedTextForEvent(event, projection) {
	const content = asOptionalObjectRecord(projection.message)?.content;
	if (typeof content === "string") return content;
	const projectedIndex = projection.sourceToProjectedContentIndex.get(eventContentIndex(event));
	const block = Array.isArray(content) && projectedIndex !== void 0 ? asOptionalObjectRecord(content[projectedIndex]) : void 0;
	return block?.type === "text" && typeof block.text === "string" ? block.text : void 0;
}
const XML_PARAMETER_CLOSE = "</parameter>";
const XML_FUNCTION_CLOSE = "</function>";
const XML_PARAMETER_OPEN = "<parameter=";
function createOverCapSuppressor(candidate, matcher, force = false) {
	const view = createCandidateScanView(candidate);
	const start = skipLineIndentation(view.text, 0);
	const { json, matches, xmlish } = scanPlainTextToolCall(view.text, start, {
		matcher,
		maxPayloadBytes: MAX_PAYLOAD_BYTES,
		structuralLineBreaks: view.structuralLineBreaks
	});
	const value = json.kind === "prefix" ? json.candidate : json.kind === "complete" ? json : void 0;
	const state = value?.json ?? (json.kind === "complete" ? {
		depth: 0,
		escaped: false,
		inString: false
	} : void 0);
	const name = value ? view.text.slice(value.name.start, value.name.end) : "";
	const jsonSuppressor = value ? {
		kind: "json",
		carry: value.payload && state?.depth === 0 ? view.text.slice(skipWhitespace(view.text, value.payload.end)) : "",
		depth: state?.depth ?? 0,
		escaped: state?.escaped ?? false,
		inString: state?.inString ?? false,
		phase: !value.payload ? "opening" : state?.depth === 0 ? "closing" : "payload",
		...value.syntax === "named-bracket" ? { requiredClosing: `[/${name}]` } : { optionalClosings: [
			HARMONY_CALL_MARKER,
			END_TOOL_REQUEST,
			`[/${name}]`
		] }
	} : void 0;
	if (force && jsonSuppressor && value?.nameComplete === true && !value.payload && matches.json) return {
		allowXml: xmlish.kind === "prefix" && matches.xmlish,
		carry: "",
		json: jsonSuppressor,
		kind: "opening"
	};
	if (xmlish.kind === "prefix" && matches.xmlish && xmlish.candidate?.payload && (force || utf8ByteLengthWithinLimit(view.text, xmlish.candidate.payload.start, xmlish.candidate.payload.end, MAX_PAYLOAD_BYTES) === null)) {
		const phase = xmlish.candidate.activeParameterOpenEnd === void 0 ? "body" : "parameter";
		const markers = phase === "parameter" ? [XML_PARAMETER_CLOSE] : [XML_PARAMETER_OPEN, XML_FUNCTION_CLOSE];
		const markerStart = view.text.lastIndexOf("<");
		return {
			kind: "xml",
			carry: markerStart !== -1 && markers.some((marker) => isAsciiMarkerPrefixIgnoreCase(view.text, markerStart, marker)) ? view.text.slice(markerStart) : "",
			phase
		};
	}
	if (!value || !jsonSuppressor || !value.nameComplete && !value.payload || !matches.json || !state && !force || !value.payload && !force || !force && value.payload && utf8ByteLengthWithinLimit(view.text, value.payload.start, value.payload.end, MAX_PAYLOAD_BYTES) !== null) return;
	return jsonSuppressor;
}
function classifyPending(pending, matcher, resolveProtectedRanges, finalize = false) {
	const candidate = {
		text: pending.buffer,
		parts: pending.parts
	};
	const view = createCandidateScanView(candidate);
	const terminalScan = scanPlainTextToolCall(view.text, skipLineIndentation(view.text, 0), {
		matcher,
		maxPayloadBytes: MAX_PAYLOAD_BYTES,
		structuralLineBreaks: view.structuralLineBreaks
	});
	const hasNamedCandidate = scanHasNamedCandidate(terminalScan);
	const sequences = findCandidateCallSequences(candidate, matcher, resolveProtectedRanges);
	const overCapRanges = sequences.filter(({ overCap }) => overCap);
	const leading = sequences[0]?.start === 0 ? sequences[0] : void 0;
	if (leading?.activeStart !== void 0 && (pending.sequenceOverCap || overCapRanges.length > 0)) {
		const activeCandidate = {
			text: candidate.text.slice(leading.activeStart),
			parts: candidate.parts.filter((part) => part.end > (leading.activeStart ?? 0)).map((part) => ({
				contentIndex: part.contentIndex,
				start: Math.max(0, part.start - (leading.activeStart ?? 0)),
				end: part.end - (leading.activeStart ?? 0)
			}))
		};
		const suppressor = createOverCapSuppressor(activeCandidate, matcher, true);
		if (suppressor) return {
			kind: "suppress",
			suppressor
		};
		if (leading.activeStart > 0) return {
			kind: "trim",
			candidate: activeCandidate
		};
	}
	if (overCapRanges.length > 0) {
		const text = createRangeRemover(overCapRanges)(candidate.text);
		const suppressor = text ? void 0 : createOverCapSuppressor(candidate, matcher);
		return suppressor ? {
			kind: "suppress",
			suppressor
		} : {
			kind: "stripped",
			text
		};
	}
	if (leading && leading.activeStart === void 0 && skipWhitespace(candidate.text, leading.end) < candidate.text.length) return {
		kind: "stripped",
		text: createRangeRemover([leading])(candidate.text)
	};
	if (leading && leading.activeStart === void 0) return pending.sequenceOverCap || pending.bufferBytes > MAX_PAYLOAD_BYTES ? {
		kind: "stripped",
		text: ""
	} : { kind: "complete" };
	if (leading?.activeStart !== void 0) return !hasNamedCandidate && finalize ? { kind: "false-positive" } : { kind: "incomplete" };
	if (terminalScan.kind === "prefix" && !hasNamedCandidate && pending.bufferBytes > MAX_PAYLOAD_BYTES) return { kind: "false-positive" };
	if (terminalScan.kind === "prefix" && (!finalize || hasNamedCandidate)) return { kind: "incomplete" };
	return pending.sequenceOverCap ? {
		kind: "stripped",
		text: candidate.text
	} : { kind: "false-positive" };
}
function consumeXmlSuppressor(suppressor, chunk) {
	const text = suppressor.carry + chunk;
	suppressor.carry = "";
	let cursor = 0;
	while (true) {
		if (suppressor.phase === "parameter") {
			const close = indexOfAsciiMarkerIgnoreCase(text, XML_PARAMETER_CLOSE, cursor);
			if (close === -1) {
				suppressor.carry = text.slice(-11);
				return { complete: false };
			}
			cursor = close + 12;
			suppressor.phase = "body";
		}
		const markerStart = skipWhitespace(text, cursor);
		if (markerStart === text.length) return { complete: false };
		if (startsWithAsciiMarkerIgnoreCase(text, markerStart, XML_FUNCTION_CLOSE)) {
			const end = consumeRemovedLineEnd(text, markerStart + 11);
			return {
				complete: true,
				suffix: text.slice(end)
			};
		}
		if (isAsciiMarkerPrefixIgnoreCase(text, markerStart, XML_FUNCTION_CLOSE) || isAsciiMarkerPrefixIgnoreCase(text, markerStart, XML_PARAMETER_OPEN)) {
			suppressor.carry = text.slice(markerStart);
			return { complete: false };
		}
		if (startsWithAsciiMarkerIgnoreCase(text, markerStart, XML_PARAMETER_OPEN)) {
			const restLength = text.length - markerStart;
			const close = text.indexOf(">", markerStart + 11);
			if (close === -1 && restLength <= 131) {
				suppressor.carry = text.slice(markerStart);
				return { complete: false };
			}
			if (close === -1) return {
				complete: true,
				suffix: text.slice(markerStart)
			};
			const name = text.slice(markerStart + 11, close);
			if (!name || name.length > MAX_TOOL_NAME_CHARS || Array.from(name).some((character) => !isXmlishNameChar(character))) return {
				complete: true,
				suffix: text.slice(markerStart)
			};
			suppressor.phase = "parameter";
			cursor = close + 1;
			continue;
		}
		return {
			complete: true,
			suffix: text.slice(markerStart)
		};
	}
}
function consumeJsonSuppressor(suppressor, chunk) {
	let text = suppressor.carry + chunk;
	suppressor.carry = "";
	let cursor = 0;
	if (suppressor.phase === "opening") {
		cursor = skipWhitespace(text, cursor);
		if (cursor === text.length) return { complete: false };
		if (text[cursor] !== "{") return {
			complete: true,
			suffix: text.slice(cursor)
		};
		suppressor.depth = 1;
		suppressor.phase = "payload";
		cursor += 1;
	}
	if (suppressor.phase === "payload") {
		for (; cursor < text.length; cursor += 1) {
			const char = text[cursor];
			if (suppressor.inString) {
				if (suppressor.escaped) suppressor.escaped = false;
				else if (char === "\\") suppressor.escaped = true;
				else if (char === "\"") suppressor.inString = false;
				continue;
			}
			if (char === "\"") suppressor.inString = true;
			else if (char === "{") suppressor.depth += 1;
			else if (char === "}") {
				suppressor.depth -= 1;
				if (suppressor.depth === 0) {
					suppressor.phase = "closing";
					cursor += 1;
					break;
				}
			}
		}
		if (suppressor.phase === "payload") return { complete: false };
		text = text.slice(cursor);
	}
	const markerStart = skipWhitespace(text, 0);
	const rest = text.slice(markerStart);
	if (suppressor.requiredClosing) {
		const markers = [suppressor.requiredClosing, END_TOOL_REQUEST];
		const closing = markers.find((marker) => rest.startsWith(marker));
		if (closing) {
			const end = consumeRemovedLineEnd(rest, closing.length);
			return {
				complete: true,
				suffix: rest.slice(end)
			};
		}
		if (markers.some((marker) => marker.startsWith(rest))) {
			suppressor.carry = rest;
			return { complete: false };
		}
		return {
			complete: true,
			suffix: rest
		};
	}
	const optionalClosing = suppressor.optionalClosings?.find((marker) => rest.startsWith(marker));
	if (optionalClosing) {
		const end = consumeRemovedLineEnd(rest, optionalClosing.length);
		return {
			complete: true,
			suffix: rest.slice(end)
		};
	}
	const optionalClosings = suppressor.optionalClosings ?? [];
	if (optionalClosings.some((marker) => marker.startsWith(rest))) {
		const maxCarryChars = Math.max(...optionalClosings.map((marker) => marker.length));
		suppressor.carry = text.slice(-maxCarryChars);
		return { complete: false };
	}
	const end = consumeRemovedLineEnd(text, 0);
	return {
		complete: true,
		suffix: text.slice(end)
	};
}
function consumeOpeningSuppressor(suppressor, chunk) {
	if (suppressor.choice) return suppressor.choice.kind === "xml" ? consumeXmlSuppressor(suppressor.choice, chunk) : consumeJsonSuppressor(suppressor.choice, chunk);
	const text = suppressor.carry + chunk;
	suppressor.carry = "";
	const start = skipWhitespace(text, 0);
	if (start === text.length) return { complete: false };
	const rest = text.slice(start);
	if (rest[0] === "{") {
		suppressor.choice = suppressor.json;
		return consumeJsonSuppressor(suppressor.choice, rest);
	}
	if (suppressor.allowXml) {
		if (isAsciiMarkerPrefixIgnoreCase(rest, 0, XML_PARAMETER_OPEN)) {
			suppressor.carry = rest;
			return { complete: false };
		}
		if (startsWithAsciiMarkerIgnoreCase(rest, 0, XML_PARAMETER_OPEN)) {
			suppressor.choice = {
				carry: "",
				kind: "xml",
				phase: "body"
			};
			return consumeXmlSuppressor(suppressor.choice, rest);
		}
	}
	return {
		complete: true,
		suffix: rest
	};
}
function consumeOverCapSuppressor(suppressor, chunk) {
	return suppressor.kind === "xml" ? consumeXmlSuppressor(suppressor, chunk) : suppressor.kind === "json" ? consumeJsonSuppressor(suppressor, chunk) : consumeOpeningSuppressor(suppressor, chunk);
}
function orderByContentIndex(events, message) {
	const contentLength = Array.isArray(message.content) ? message.content.length : 0;
	const order = (event) => {
		const index = asOptionalObjectRecord(event)?.contentIndex;
		return typeof index === "number" && Number.isInteger(index) && index >= 0 && index < contentLength ? index : contentLength;
	};
	return events.toSorted((left, right) => order(left) - order(right));
}
/** Coordinates bounded candidate buffering; terminal snapshots remain the source of truth. */
async function* normalizePlainTextToolCallStreamEvents(source, options) {
	let pending;
	let overCapSequenceOpen = false;
	let scrubFuturePartials = false;
	let forceScrubTerminal = false;
	let sawStreamStart = false;
	let preserveTerminalContentIndexes = false;
	const heldTextStarts = /* @__PURE__ */ new Map();
	const lineStarts = /* @__PURE__ */ new Map();
	const emittedTextUnits = /* @__PURE__ */ new Map();
	const protectionChunks = [];
	let protectionContextLength = 0;
	let protectionContextOverflow = false;
	let protectionBlockContentIndex;
	let protectionBlockStart = 0;
	let protectionBlockPrefixVerdict;
	let protectionScan = createProtectionScanState();
	let protectionScanAtBlockStart = createProtectionScanState();
	const beginProtectionBlock = (contentIndex) => {
		if (protectionBlockContentIndex === contentIndex) return;
		protectionBlockContentIndex = contentIndex;
		protectionBlockStart = protectionContextLength;
		protectionScanAtBlockStart = cloneProtectionScanState(protectionScan);
		protectionBlockPrefixVerdict = void 0;
	};
	const truncateProtectionContext = (length) => {
		while (protectionContextLength > length) {
			const tail = protectionChunks.at(-1);
			if (tail === void 0) {
				protectionContextLength = 0;
				return;
			}
			const retainedLength = tail.length - (protectionContextLength - length);
			if (retainedLength > 0) {
				protectionChunks[protectionChunks.length - 1] = tail.slice(0, retainedLength);
				protectionContextLength = length;
				return;
			}
			protectionChunks.pop();
			protectionContextLength -= tail.length;
		}
	};
	const advanceProtectionContext = (text, resetActiveBlock = false) => {
		if (!options.resolveProtectedRanges || protectionContextOverflow) return;
		if (resetActiveBlock) {
			truncateProtectionContext(protectionBlockStart);
			protectionScan = cloneProtectionScanState(protectionScanAtBlockStart);
		}
		if (protectionContextLength + text.length > MAX_PROTECTION_CONTEXT_CHARS) {
			protectionChunks.length = 0;
			protectionContextLength = 0;
			protectionContextOverflow = true;
			return;
		}
		if (text) {
			protectionChunks.push(text);
			advanceProtectionScanState(protectionScan, text);
		}
		protectionContextLength += text.length;
	};
	const materializeProtectionPrefix = (authoritative) => {
		if (protectionContextOverflow) return "";
		const context = protectionChunks.join("");
		return authoritative ? context.slice(0, protectionBlockStart) : context;
	};
	const materializeBoundedPrefix = (length) => {
		let result = "";
		for (const chunk of protectionChunks) {
			if (result.length >= length) break;
			result += chunk;
		}
		return result.slice(0, length);
	};
	const scrubSnapshot = (value, preserveEmptyTextBlocks = false, forceKnownCandidates = false) => {
		const forced = forceKnownCandidates ? projectScrubbedPlainTextToolCallMessage({
			forceKnownCandidates: true,
			matcher: options.matcher,
			message: value,
			preserveEmptyTextBlocks,
			resolveProtectedRanges: options.resolveProtectedRanges
		}) : void 0;
		if (forced) return forced;
		const normalized = options.normalizeTerminalMessage({
			allowPromotion: false,
			message: value,
			preserveEmptyTextBlocks,
			reason: "error"
		});
		return normalized?.kind === "scrubbed" ? normalized : void 0;
	};
	const eventKey = (record) => String(eventContentIndex(record));
	const sanitizeEventPartial = (record, forceKnownCandidates = false) => {
		if (record.partial === void 0) return record;
		const projection = scrubSnapshot(record.partial, true, forceKnownCandidates);
		if (!projection) return record;
		const projected = projectEventIndex(record, projection);
		return projected ? {
			...projected,
			partial: projection.message
		} : void 0;
	};
	const forceProjectPendingAux = (candidate, projection, retainedTextContentIndex) => projectPendingAuxEvents(candidate, projection, (message) => scrubSnapshot(message, true, true), retainedTextContentIndex);
	async function* normalizeEvents() {
		for await (const sourceEvent of source) {
			let record = asOptionalObjectRecord(sourceEvent);
			if (!record) {
				yield sourceEvent;
				continue;
			}
			const type = typeof record.type === "string" ? record.type : "";
			sawStreamStart ||= type === "start";
			if (scrubFuturePartials && !pending && type !== "done" && type !== "error" && record.partial !== void 0) {
				const projection = scrubSnapshot(record.partial, true, true);
				const projectedEvent = projection ? projectEventIndex(record, projection) : record;
				if (!projectedEvent) continue;
				record = projection ? {
					...projectedEvent,
					partial: projection.message
				} : sanitizeEventPartial(projectedEvent, true) ?? projectedEvent;
			}
			if (type === "text_start" || type === "text_delta" || type === "text_end") {
				const text = typeof record.delta === "string" ? record.delta : typeof record.content === "string" ? record.content : void 0;
				const key = eventKey(record);
				if (type === "text_start" && (text === void 0 || text === "") && !pending) {
					const previous = heldTextStarts.get(key);
					if (previous) yield previous;
					heldTextStarts.set(key, record);
					continue;
				}
				if (text === void 0) {
					if (pending?.kind === "candidate") queuePendingEvent(pending, record);
					else if (!pending) {
						const held = heldTextStarts.get(key);
						if (held) {
							yield held;
							heldTextStarts.delete(key);
						}
						yield record;
					}
					continue;
				}
				let incoming = text;
				let incomingRecord = record;
				const closesText = type === "text_end";
				let authoritative = closesText;
				let sequenceOverCap = false;
				beginProtectionBlock(eventContentIndex(record));
				while (true) {
					if (pending?.kind === "suppressing") {
						if (closesText) {
							const projection = scrubSnapshot(record.partial ?? {
								role: "assistant",
								content: incoming
							}, true, true);
							yield* forceProjectPendingAux(pending, projection);
							const novelText = (projection && projectedTextForEvent(record, projection))?.slice(emittedTextUnits.get(key) ?? 0);
							if (novelText && projection) yield createSyntheticTextDelta(record, novelText, projection.message);
							pending = void 0;
							scrubFuturePartials = true;
							break;
						}
						if (!pending.suppressor) {
							const projection = scrubSnapshot(record.partial, true, true);
							yield* forceProjectPendingAux(pending, projection);
							pending = void 0;
							continue;
						}
						const consumed = consumeOverCapSuppressor(pending.suppressor, incoming);
						if (!consumed.complete) break;
						scrubFuturePartials = true;
						overCapSequenceOpen = true;
						const partialProjection = scrubSnapshot(record.partial, true, true);
						const partial = partialProjection?.message;
						yield* forceProjectPendingAux(pending, partialProjection);
						incoming = consumed.suffix;
						sequenceOverCap = true;
						if (!incoming) {
							pending = {
								entryBytes: 0,
								kind: "suppressing"
							};
							break;
						}
						pending = void 0;
						incomingRecord = {
							...eventTemplate(record),
							type: "text_delta",
							delta: incoming,
							...partial ? { partial } : {}
						};
						authoritative = false;
					}
					if (!pending) {
						const atLineStart = authoritative || sequenceOverCap || overCapSequenceOpen || (lineStarts.get(key) ?? true);
						let callStart = findPotentialCallStart(incoming, atLineStart, options.matcher);
						if (callStart !== null && options.resolveProtectedRanges) {
							if (protectionContextOverflow) callStart = null;
							else {
								const carriedScan = authoritative ? protectionScanAtBlockStart : protectionScan;
								const precedingContextTrust = resolvePrecedingContextTrust(incomingRecord.partial, eventContentIndex(incomingRecord), protectionBlockStart, () => materializeBoundedPrefix(protectionBlockStart), protectionBlockPrefixVerdict);
								protectionBlockPrefixVerdict = precedingContextTrust.cache;
								let isProtectedAt = !!precedingContextTrust.trusted && options.protectedRangesFenceCompatible ? resolveProtectionFastPath(carriedScan, incoming) : void 0;
								if (!isProtectedAt) isProtectedAt = resolvePartialProtectionCheck({
									authoritative,
									contentIndex: eventContentIndex(incomingRecord),
									incoming,
									partial: incomingRecord.partial,
									resolveProtectedRanges: options.resolveProtectedRanges
								});
								if (!isProtectedAt) {
									const protectionPrefix = materializeProtectionPrefix(authoritative);
									const protectedRanges = options.resolveProtectedRanges(`${protectionPrefix}${incoming}`);
									isProtectedAt = (offset) => isOffsetInProtectedRanges(protectionPrefix.length + offset, protectedRanges);
								}
								callStart = findPotentialCallStart(incoming, atLineStart, options.matcher, isProtectedAt);
							}
						}
						if (callStart === null) {
							const held = heldTextStarts.get(key);
							if (held) {
								yield held;
								heldTextStarts.delete(key);
							}
							yield incomingRecord;
							if (incoming) {
								const continuesScrubbedSequence = overCapSequenceOpen;
								overCapSequenceOpen = false;
								const contentIndex = eventContentIndex(incomingRecord);
								preserveTerminalContentIndexes ||= (sequenceOverCap || continuesScrubbedSequence) && contentIndex > 0;
							}
							lineStarts.set(key, nextAtLineStart(atLineStart, incoming));
							advanceProtectionContext(incoming, authoritative);
							break;
						}
						const visiblePrefix = incoming.slice(0, callStart);
						advanceProtectionContext(visiblePrefix, authoritative);
						const emittedUnits = emittedTextUnits.get(key) ?? 0;
						const emittedPrefixUnits = authoritative ? emittedUnits : 0;
						const novelVisiblePrefix = visiblePrefix.slice(emittedPrefixUnits);
						if (novelVisiblePrefix) {
							const held = heldTextStarts.get(key);
							if (held) {
								yield held;
								heldTextStarts.delete(key);
							}
							const visibleProjection = scrubSnapshot(incomingRecord.partial, true, true);
							const visibleTemplate = visibleProjection ? projectEventIndex(incomingRecord, visibleProjection) : incomingRecord;
							if (visibleTemplate) yield createSyntheticTextDelta(visibleTemplate, novelVisiblePrefix, asOptionalObjectRecord(visibleProjection?.message));
						}
						const candidateText = incoming.slice(callStart);
						const candidateRecord = typeof incomingRecord.delta === "string" ? {
							...incomingRecord,
							delta: candidateText
						} : authoritative ? incomingRecord : {
							...incomingRecord,
							content: candidateText
						};
						const held = heldTextStarts.get(key);
						heldTextStarts.delete(key);
						pending = createPendingState(candidateRecord, candidateText, held, sequenceOverCap || overCapSequenceOpen, authoritative ? callStart : emittedUnits + callStart);
						overCapSequenceOpen = false;
					} else if (pending.kind === "candidate") {
						if (authoritative) {
							const contentIndex = eventContentIndex(incomingRecord);
							const partIndex = pending.parts.findLastIndex((part) => part.contentIndex === contentIndex);
							const part = pending.parts[partIndex];
							if (part) {
								const blockOffset = part.start === 0 ? pending.snapshotOffset : 0;
								const blockText = incoming.slice(blockOffset);
								const previousLength = part.end - part.start;
								const lengthDelta = blockText.length - previousLength;
								const candidateText = pending.buffer.slice(0, part.start) + blockText + pending.buffer.slice(part.end);
								const retained = pending.entries?.filter((event) => !isTextStreamEvent(event) || event.type === "text_start");
								pending.buffer = candidateText;
								pending.bufferBytes = cappedUtf8ByteLength(candidateText);
								pending.entries = [
									...retained ?? [],
									createSyntheticTextDelta(pending.template, candidateText, asOptionalObjectRecord(record.partial)),
									{
										...incomingRecord,
										content: incoming
									}
								];
								pending.parts = pending.parts.map((entry, index) => index < partIndex ? entry : index === partIndex ? {
									...entry,
									end: entry.start + blockText.length
								} : {
									...entry,
									start: entry.start + lengthDelta,
									end: entry.end + lengthDelta
								});
								if (part.start === 0) pending.snapshotOffset = 0;
								pending.template = eventTemplate(incomingRecord);
							} else appendPendingText(pending, incoming, incomingRecord);
						} else appendPendingText(pending, incoming, incomingRecord);
						if (!incoming && !authoritative) break;
					}
					if (pending.kind !== "candidate") break;
					if (!(authoritative || pending.bufferBytes > MAX_PAYLOAD_BYTES || pending.buffer.length <= 256 || pending.buffer.length >= pending.nextScanChars)) break;
					const classification = classifyPending(pending, options.matcher, options.resolveProtectedRanges);
					pending.nextScanChars = Math.max(pending.buffer.length + 1, pending.nextScanChars * 2);
					if (classification.kind === "complete" || classification.kind === "incomplete") break;
					if (classification.kind === "trim") {
						scrubFuturePartials = true;
						const partialProjection = scrubSnapshot(record.partial, true, true);
						yield* forceProjectPendingAux(pending, partialProjection);
						const candidate = classification.candidate;
						pending.buffer = candidate.text;
						pending.bufferBytes = cappedUtf8ByteLength(candidate.text);
						pending.entries = void 0;
						pending.entryBytes = 0;
						pending.nextScanChars = 256;
						pending.parts = candidate.parts;
						pending.sequenceOverCap = true;
						pending.snapshotOffset = 0;
						pending.template = {
							...pending.template,
							contentIndex: candidate.parts[0]?.contentIndex ?? pending.template.contentIndex
						};
						break;
					}
					if (classification.kind === "suppress") {
						const entries = pending.entries?.filter((event) => !isTextStreamEvent(event));
						scrubFuturePartials = true;
						pending = {
							entries,
							entryBytes: entries?.reduce((total, entry) => {
								return Math.min(256001, total + pendingEventBytes(entry));
							}, 0) ?? 0,
							kind: "suppressing",
							suppressor: classification.suppressor
						};
						break;
					}
					if (classification.kind === "false-positive") {
						yield* replayFalsePositiveCandidate(pending);
						const replayText = pending.buffer;
						const replayedCandidate = pending;
						pending = void 0;
						if (replayText) {
							overCapSequenceOpen = false;
							lineStarts.set(key, nextAtLineStart(lineStarts.get(key) ?? true, replayText));
							advanceProtectionContext(replayedCandidate.buffer);
						}
						break;
					}
					scrubFuturePartials = true;
					const partialProjection = scrubSnapshot(record.partial, true, true);
					const authoritativeProjection = partialProjection ?? (authoritative ? scrubSnapshot({
						role: "assistant",
						content: pending.buffer
					}, true, true) : void 0);
					const projectedText = authoritativeProjection && projectedTextForEvent(pending.template, authoritativeProjection);
					const sanitizedText = projectedText ?? classification.text;
					overCapSequenceOpen = sanitizedText.length === 0;
					const outputProjection = partialProjection;
					const contentIndex = eventContentIndex(pending.template);
					const partial = outputProjection?.message ?? (contentIndex === 0 ? {
						role: "assistant",
						content: [{
							type: "text",
							text: sanitizedText
						}]
					} : void 0);
					yield* forceProjectPendingAux(pending, outputProjection, sanitizedText ? contentIndex : void 0);
					const emittedUnits = emittedTextUnits.get(key) ?? 0;
					const novelOffset = projectedText ? emittedUnits : authoritative ? Math.max(0, emittedUnits - pending.snapshotOffset) : 0;
					const novelText = sanitizedText.slice(novelOffset);
					preserveTerminalContentIndexes ||= sanitizedText.length > 0 && contentIndex > 0;
					if (novelText) yield createSyntheticTextDelta(pending.template, novelText, partial);
					lineStarts.set(key, nextAtLineStart(lineStarts.get(key) ?? true, sanitizedText));
					advanceProtectionContext(sanitizedText);
					pending = void 0;
					break;
				}
				if (closesText) {
					emittedTextUnits.delete(key);
					protectionBlockContentIndex = void 0;
					protectionBlockStart = protectionContextLength;
				}
				continue;
			}
			if (type === "done") {
				const requestedNormalization = options.normalizeTerminalMessage({
					allowPromotion: record.reason === "stop" || record.reason === "toolUse",
					message: record.message,
					preserveEmptyTextBlocks: preserveTerminalContentIndexes,
					reason: record.reason
				});
				const forcedProjection = forceScrubTerminal ? scrubSnapshot(record.message, preserveTerminalContentIndexes, true) : void 0;
				const terminalCandidate = requestedNormalization ? void 0 : extractStandaloneCandidate(record.message, false);
				const terminalCandidateProjection = terminalCandidate && findCandidateCallSequences(terminalCandidate, options.matcher, options.resolveProtectedRanges).some((sequence) => sequence.activeStart !== void 0) ? scrubSnapshot(record.message, preserveTerminalContentIndexes, true) : void 0;
				const normalized = forcedProjection ? {
					kind: "scrubbed",
					...forcedProjection
				} : forceScrubTerminal ? void 0 : requestedNormalization ?? (terminalCandidateProjection ? {
					kind: "scrubbed",
					...terminalCandidateProjection
				} : void 0);
				if (normalized?.kind === "promoted") {
					if (!sawStreamStart) {
						yield {
							type: "start",
							partial: {
								role: "assistant",
								content: []
							}
						};
						sawStreamStart = true;
					}
					const promoted = [...options.createPromotedToolCallEvents(normalized.message)];
					const auxiliary = pending?.kind === "candidate" ? forceProjectPendingAux(pending, normalized) : [];
					yield* orderByContentIndex([...promoted, ...auxiliary], normalized.message);
					yield {
						...record,
						reason: "toolUse",
						message: normalized.message
					};
				} else if (normalized?.kind === "scrubbed") {
					if (pending?.kind === "candidate") {
						const classification = classifyPending(pending, options.matcher, options.resolveProtectedRanges, true);
						if (classification.kind === "stripped" && classification.text) {
							const template = projectEventIndex(pending.template, normalized);
							if (template) {
								const projectedText = projectedTextForEvent(pending.template, normalized);
								const sanitizedText = projectedText ?? classification.text;
								const emittedUnits = emittedTextUnits.get(eventKey(pending.template)) ?? 0;
								const novelText = sanitizedText.slice(projectedText ? emittedUnits : 0);
								if (novelText) yield createSyntheticTextDelta(template, novelText, normalized.message);
							}
						}
						yield* forceProjectPendingAux(pending, normalized);
					} else if (pending?.kind === "suppressing") yield* forceProjectPendingAux(pending, normalized);
					yield {
						...record,
						message: normalized.message
					};
				} else {
					let message = record.message;
					if (pending?.kind === "candidate") {
						if (classifyPending(pending, options.matcher, options.resolveProtectedRanges, true).kind === "false-positive") yield* replayFalsePositiveCandidate(pending);
						else {
							const projection = scrubSnapshot(record.message, true, true);
							yield* forceProjectPendingAux(pending, projection);
							message = projection?.message ?? message;
						}
					} else if (pending?.kind === "suppressing") {
						const projection = scrubSnapshot(record.message, true, true);
						yield* forceProjectPendingAux(pending, projection);
						message = projection?.message ?? message;
					}
					yield message === record.message ? record : {
						...record,
						message
					};
				}
				pending = void 0;
				forceScrubTerminal = false;
				heldTextStarts.clear();
				emittedTextUnits.clear();
				protectionChunks.length = 0;
				protectionContextLength = 0;
				protectionContextOverflow = false;
				protectionBlockContentIndex = void 0;
				protectionBlockStart = 0;
				protectionScan = createProtectionScanState();
				protectionScanAtBlockStart = createProtectionScanState();
				if (options.stopAfterDone) return;
				continue;
			}
			if (type === "error") {
				const knownCandidate = pending?.kind === "suppressing" || pending?.kind === "candidate" && classifyPending(pending, options.matcher, options.resolveProtectedRanges, true).kind !== "false-positive";
				if (pending?.kind === "candidate" && !knownCandidate) yield* replayFalsePositiveCandidate(pending);
				const streamedPartial = scrubSnapshot(record.partial, true, knownCandidate);
				const streamedError = scrubSnapshot(record.error, preserveTerminalContentIndexes, knownCandidate);
				const projection = streamedPartial ?? streamedError;
				if (pending?.kind === "candidate" && knownCandidate) yield* forceProjectPendingAux(pending, projection);
				else if (pending?.kind === "suppressing") yield* forceProjectPendingAux(pending, projection);
				yield {
					...record,
					...streamedPartial ? { partial: streamedPartial.message } : {},
					...streamedError ? { error: streamedError.message } : {}
				};
				return;
			}
			if (pending?.kind === "suppressing") {
				if (!pending.entries) {
					const sanitized = sanitizeEventPartial(record, true);
					if (sanitized) yield sanitized;
					continue;
				}
				queuePendingEvent(pending, record);
				if (pendingQueueOverCap(pending)) {
					forceScrubTerminal = true;
					if (!sawStreamStart) {
						yield {
							type: "start",
							partial: {
								role: "assistant",
								content: []
							}
						};
						sawStreamStart = true;
					}
					yield* forceProjectPendingAux(pending);
					pending.entries = void 0;
					pending.entryBytes = 0;
				}
			} else if (pending?.kind === "candidate") {
				if (!pending.entries) {
					const sanitized = sanitizeEventPartial(record, true);
					if (sanitized) yield sanitized;
					continue;
				}
				queuePendingEvent(pending, record);
				if (pendingQueueOverCap(pending)) {
					const classification = classifyPending(pending, options.matcher, options.resolveProtectedRanges);
					if (classification.kind === "false-positive") {
						yield* replayFalsePositiveCandidate(pending);
						advanceProtectionContext(pending.buffer);
						pending = void 0;
						continue;
					}
					forceScrubTerminal = true;
					scrubFuturePartials = true;
					if (!sawStreamStart) {
						yield {
							type: "start",
							partial: {
								role: "assistant",
								content: []
							}
						};
						sawStreamStart = true;
					}
					yield* forceProjectPendingAux(pending);
					pending.entries = void 0;
					pending.entryBytes = 0;
					if (classification.kind === "suppress") pending = {
						entryBytes: 0,
						kind: "suppressing",
						suppressor: classification.suppressor
					};
				}
			} else {
				for (const held of heldTextStarts.values()) yield held;
				heldTextStarts.clear();
				yield record;
			}
		}
		if (pending?.kind === "candidate") {
			if (classifyPending(pending, options.matcher, options.resolveProtectedRanges, true).kind === "false-positive") yield* replayFalsePositiveCandidate(pending);
			else yield* forceProjectPendingAux(pending);
		} else if (pending?.kind === "suppressing") yield* forceProjectPendingAux(pending);
		for (const held of heldTextStarts.values()) yield held;
	}
	for await (const event of normalizeEvents()) {
		const record = asOptionalObjectRecord(event);
		if (record?.type === "text_delta" && typeof record.delta === "string") {
			const key = eventKey(record);
			const previous = emittedTextUnits.get(key) ?? 0;
			emittedTextUnits.set(key, previous + record.delta.length);
		}
		yield event;
	}
}
//#endregion
//#region packages/tool-call-repair/src/promote.ts
/** Builds the shared assistant-message shape for a repaired text tool call. */
function createPromotedPlainTextToolCallBlock(block, name) {
	return {
		type: "toolCall",
		id: `call_${crypto.randomUUID().replace(/-/g, "").slice(0, 24)}`,
		name,
		arguments: block.arguments,
		partialArgs: JSON.stringify(block.arguments)
	};
}
/** Emits the complete provider-neutral lifecycle for promoted tool-call blocks. */
function createPromotedPlainTextToolCallEvents(message) {
	return (Array.isArray(message.content) ? message.content : []).flatMap((block, contentIndex) => {
		const toolCall = asOptionalObjectRecord(block);
		if (toolCall?.type !== "toolCall") return [];
		return [
			{
				type: "toolcall_start",
				contentIndex,
				partial: message
			},
			{
				type: "toolcall_delta",
				contentIndex,
				delta: typeof toolCall.partialArgs === "string" ? toolCall.partialArgs : "{}",
				partial: message
			},
			{
				type: "toolcall_end",
				contentIndex,
				toolCall,
				partial: message
			}
		];
	});
}
function resolveExactToolName(rawName, allowedToolNames) {
	return allowedToolNames.has(rawName) ? rawName : null;
}
function createPromotedToolCallBlocks(text, options, lineBreakOffsets) {
	const parsedBlocks = parseStandalonePlainTextToolCallBlocks(text, void 0, lineBreakOffsets ? { lineBreakOffsets } : void 0);
	if (!parsedBlocks) return;
	const protectedRanges = options.resolveProtectedRanges?.(text) ?? [];
	if (parsedBlocks.some((block) => protectedRanges.some((range) => block.start >= range.start && block.start < range.end))) return;
	const resolveToolName = options.resolveToolName ?? resolveExactToolName;
	const toolCalls = [];
	for (const block of parsedBlocks) {
		const resolvedName = resolveToolName(block.name, options.allowedToolNames);
		if (!resolvedName) return;
		toolCalls.push(options.createToolCallBlock(block, resolvedName));
	}
	return toolCalls;
}
function createPromotedToolCallBlocksFromTextParts(textParts, options) {
	const text = textParts.join("");
	if (!text.trim()) return [];
	let offset = 0;
	const lineBreakOffsets = new Set(textParts.slice(0, -1).map((part) => {
		offset += part.length;
		return offset;
	}));
	if (lineBreakOffsets.has(text.length)) lineBreakOffsets.delete(text.length);
	return createPromotedToolCallBlocks(text, options, lineBreakOffsets);
}
/** Promotes text calls and maps source blocks retained in the projected message. */
function projectStandalonePlainTextToolCallMessage(options) {
	const messageRecord = asOptionalObjectRecord(options.message);
	if (!messageRecord || options.allowedToolNames.size === 0 || options.requireAssistantRole && messageRecord.role !== "assistant" || options.allowedStopReasons && !options.allowedStopReasons.has(messageRecord.stopReason)) return;
	const originalContent = messageRecord.content;
	if (typeof originalContent === "string") {
		const toolCalls = createPromotedToolCallBlocks(originalContent, options);
		if (!toolCalls) return;
		return {
			message: {
				...messageRecord,
				content: toolCalls,
				stopReason: "toolUse"
			},
			sourceToProjectedContentIndex: /* @__PURE__ */ new Map()
		};
	}
	if (!Array.isArray(originalContent)) return;
	const content = [];
	const sourceToProjectedContentIndex = /* @__PURE__ */ new Map();
	let promotedTextBlock = false;
	let textParts = [];
	const flushTextParts = () => {
		const toolCalls = createPromotedToolCallBlocksFromTextParts(textParts, options);
		textParts = [];
		if (!toolCalls) return false;
		content.push(...toolCalls);
		promotedTextBlock ||= toolCalls.length > 0;
		return true;
	};
	for (const [sourceIndex, block] of originalContent.entries()) {
		const blockRecord = asOptionalObjectRecord(block);
		if (!blockRecord) return;
		if (blockRecord.type === "text") {
			if (typeof blockRecord.text !== "string") return;
			textParts.push(blockRecord.text);
			continue;
		}
		if (!flushTextParts()) return;
		if (options.isRetainableNonTextBlock?.(blockRecord)) {
			sourceToProjectedContentIndex.set(sourceIndex, content.length);
			content.push(blockRecord);
			continue;
		}
		return;
	}
	if (!flushTextParts()) return;
	if (!promotedTextBlock) return;
	return {
		message: {
			...messageRecord,
			content,
			stopReason: "toolUse"
		},
		sourceToProjectedContentIndex
	};
}
//#endregion
export { projectScrubbedPlainTextToolCallMessage as a, consumeLineBreak as c, normalizePlainTextToolCallStreamEvents as i, skipHorizontalWhitespace as l, createPromotedPlainTextToolCallEvents as n, parseStandalonePlainTextToolCallBlocks as o, projectStandalonePlainTextToolCallMessage as r, stripPlainTextToolCallBlocks as s, createPromotedPlainTextToolCallBlock as t, skipWhitespace as u };