//#region src/stream/message-reconciliation.ts
/**
* Merge an authoritative `values.messages` snapshot with the current streamed
* message projection.
*
* Values remain authoritative for ordering and removals. Streamed messages
* remain authoritative for in-flight content until the server echoes them in a
* values snapshot, and stream-only messages are preserved until they either
* appear in values or are known to have been removed.
*/
function reconcileMessagesFromValues({ valueMessages, currentMessages, currentIndexById, previousValueMessageIds, streamedMessageIds, preferValuesMessage, addOnly }) {
	const valueMessageIds = /* @__PURE__ */ new Set();
	const merged = [];
	for (const valuesMessage of valueMessages) {
		const id = normalizedMessageId(valuesMessage);
		if (id == null) {
			merged.push(valuesMessage);
			continue;
		}
		valueMessageIds.add(id);
		const streamIdx = currentIndexById.get(id);
		const streamedMessage = streamIdx != null && (streamedMessageIds == null || streamedMessageIds.has(id)) ? currentMessages[streamIdx] : void 0;
		if (streamedMessage != null && preferValuesMessage?.(valuesMessage, streamedMessage) !== true) merged.push(streamedMessage);
		else merged.push(valuesMessage);
	}
	for (const existing of currentMessages) {
		const id = normalizedMessageId(existing);
		if (id == null) continue;
		if (valueMessageIds.has(id)) continue;
		if (!addOnly && previousValueMessageIds.has(id)) continue;
		if (streamedMessageIds != null && !streamedMessageIds.has(id)) continue;
		merged.push(existing);
	}
	return {
		messages: messagesEqualList(currentMessages, merged) ? currentMessages : merged,
		valueMessageIds
	};
}
/**
* Build a position index for keyed messages.
*/
function buildMessageIndex(messages) {
	const index = /* @__PURE__ */ new Map();
	messages.forEach((message, idx) => {
		const id = normalizedMessageId(message);
		if (id != null) index.set(id, idx);
	});
	return index;
}
/**
* Decide whether a values message carries tool-call data missing from the
* streamed message.
*/
function shouldPreferValuesMessageForToolCalls(valuesMessage, streamedMessage) {
	const valuesToolCalls = getMessageToolCalls(valuesMessage);
	if (valuesToolCalls.length === 0) return false;
	const streamedToolCalls = getMessageToolCalls(streamedMessage);
	if (streamedToolCalls.length < valuesToolCalls.length) return true;
	const streamedIds = new Set(streamedToolCalls.map((toolCall) => toolCall.id).filter((id) => typeof id === "string" && id.length > 0));
	if (valuesToolCalls.some((toolCall) => {
		return typeof toolCall.id === "string" && !streamedIds.has(toolCall.id);
	})) return true;
	return valuesToolCalls.some((valuesToolCall) => {
		const streamedToolCall = streamedToolCalls.find((candidate) => typeof valuesToolCall.id === "string" && candidate.id === valuesToolCall.id);
		return streamedToolCall != null && hasMeaningfulArgs(valuesToolCall.args) && !jsonishEqual(valuesToolCall.args, streamedToolCall.args);
	});
}
function hasMeaningfulArgs(args) {
	if (args == null) return false;
	if (typeof args === "string") return args.length > 0;
	if (typeof args === "object") return Object.keys(args).length > 0;
	return true;
}
function messagesEqualList(previous, next) {
	if (previous === next) return true;
	if (previous.length !== next.length) return false;
	for (let i = 0; i < previous.length; i += 1) if (!messagesEqual(previous[i], next[i])) return false;
	return true;
}
function messagesEqual(previous, next) {
	if (previous === next) return true;
	if (previous == null || next == null) return false;
	const previousRecord = previous;
	const nextRecord = next;
	const previousType = typeof previous.getType === "function" ? previous.getType() : previousRecord.type;
	const nextType = typeof next.getType === "function" ? next.getType() : nextRecord.type;
	return previous.id === next.id && previousType === nextType && jsonishEqual(previous.content, next.content) && previousRecord.tool_call_id === nextRecord.tool_call_id && previousRecord.status === nextRecord.status && jsonishEqual(previousRecord.additional_kwargs, nextRecord.additional_kwargs) && jsonishEqual(previousRecord.response_metadata, nextRecord.response_metadata) && jsonishEqual(previousRecord.tool_calls, nextRecord.tool_calls) && jsonishEqual(previousRecord.tool_call_chunks, nextRecord.tool_call_chunks) && jsonishEqual(previousRecord.usage_metadata, nextRecord.usage_metadata);
}
function normalizedMessageId(message) {
	return typeof message.id === "string" && message.id.length > 0 ? message.id : void 0;
}
function getMessageToolCalls(message) {
	const raw = message.tool_calls;
	if (!Array.isArray(raw)) return [];
	return raw.filter((toolCall) => toolCall != null && typeof toolCall === "object");
}
function jsonishEqual(previous, next) {
	return jsonishEqualAtDepth(previous, next, 0);
}
function jsonishEqualAtDepth(previous, next, depth) {
	if (Object.is(previous, next)) return true;
	if (previous == null || next == null) return false;
	if (typeof previous !== "object" || typeof next !== "object") return false;
	if (depth >= 4) return false;
	if (Array.isArray(previous) || Array.isArray(next)) {
		if (!Array.isArray(previous) || !Array.isArray(next)) return false;
		if (previous.length !== next.length) return false;
		for (let i = 0; i < previous.length; i += 1) if (!jsonishEqualAtDepth(previous[i], next[i], depth + 1)) return false;
		return true;
	}
	const previousRecord = previous;
	const nextRecord = next;
	const previousKeys = Object.keys(previousRecord).filter((key) => typeof previousRecord[key] !== "function");
	const nextKeys = Object.keys(nextRecord).filter((key) => typeof nextRecord[key] !== "function");
	if (previousKeys.length !== nextKeys.length) return false;
	for (const key of previousKeys) {
		if (!Object.prototype.hasOwnProperty.call(nextRecord, key)) return false;
		if (!jsonishEqualAtDepth(previousRecord[key], nextRecord[key], depth + 1)) return false;
	}
	return true;
}
//#endregion
exports.buildMessageIndex = buildMessageIndex;
exports.messagesEqual = messagesEqual;
exports.reconcileMessagesFromValues = reconcileMessagesFromValues;
exports.shouldPreferValuesMessageForToolCalls = shouldPreferValuesMessageForToolCalls;

//# sourceMappingURL=message-reconciliation.cjs.map