import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { l as mimeTypeFromFilePath } from "./mime-C8mVE2Bw.js";
import { s as appendSessionTranscriptMessage } from "./transcript-DTK-W1ir.js";
import { t as emitSessionTranscriptUpdate } from "./transcript-events-DTn-thXR.js";
import { O as resolveProviderReasoningOutputModeWithPlugin } from "./provider-runtime-BElGfwPJ.js";
import { r as applyInputProvenanceToUserMessage, s as normalizeInputProvenance } from "./input-provenance-CYQvfQZP.js";
import path from "node:path";
//#region src/utils/provider-utils.ts
/**
* Provider behavior helpers shared by reply runners, embedded agents, and provider plugins.
* Keep policy here generic; provider-specific reasoning rules belong in provider runtime hooks.
*/
/**
* Resolves whether a provider should emit reasoning via native fields or tagged text,
* using provider runtime hooks when available and defaulting to native output.
*/
function resolveReasoningOutputMode(params) {
	const provider = normalizeOptionalString(params.provider);
	if (!provider) return "native";
	const pluginMode = resolveProviderReasoningOutputModeWithPlugin({
		provider,
		config: params.config,
		workspaceDir: params.workspaceDir,
		env: params.env,
		runtimeHandle: params.runtimeHandle,
		context: {
			config: params.config,
			workspaceDir: params.workspaceDir,
			env: params.env,
			provider,
			modelId: params.modelId,
			modelApi: params.modelApi,
			model: params.model
		}
	});
	if (pluginMode) return pluginMode;
	return "native";
}
/**
* Returns true if the provider requires reasoning to be wrapped in tags
* (e.g. <think> and <final>) in the text stream, rather than using native
* API fields for reasoning/thinking.
*/
function isReasoningTagProvider(provider, options) {
	return resolveReasoningOutputMode({
		provider,
		config: options?.config,
		workspaceDir: options?.workspaceDir,
		env: options?.env,
		modelId: options?.modelId,
		modelApi: options?.modelApi,
		model: options?.model,
		runtimeHandle: options?.runtimeHandle
	}) === "tagged";
}
//#endregion
//#region src/sessions/user-turn-transcript.ts
function normalizeOptionalText(value) {
	const normalized = value?.trim();
	return normalized ? normalized : void 0;
}
function normalizeTranscriptText(value) {
	return value ?? "";
}
const CHANNEL_MEDIA_PLACEHOLDER_PATTERN = /^<media:[a-z0-9_-]+>(?:\s+\([^)]*\))?$/i;
function resolvePersistedUserTurnText(value, options = {}) {
	const normalized = normalizeOptionalText(value);
	if (!normalized) return;
	if (options.hasMedia === true && CHANNEL_MEDIA_PLACEHOLDER_PATTERN.test(normalized)) return;
	return normalized;
}
function mediaTypeForTranscript(media) {
	return normalizeOptionalText(media.contentType) ?? normalizeOptionalText(media.kind) ?? "application/octet-stream";
}
function normalizeMediaEntryForTranscript(media) {
	const pathLocal = normalizeOptionalText(media.path) ?? normalizeOptionalText(media.url);
	if (!pathLocal) return;
	return {
		path: pathLocal,
		type: mediaTypeForTranscript(media)
	};
}
function normalizeOptionalTextArray(values) {
	return values?.map(normalizeOptionalText).filter((value) => Boolean(value)) ?? [];
}
const URL_LIKE_MEDIA_PATH_PATTERN = /^[a-z][a-z0-9+.-]*:/i;
function resolveTranscriptMediaPath(pathValue, workspaceDir) {
	if (!workspaceDir || path.isAbsolute(pathValue) || URL_LIKE_MEDIA_PATH_PATTERN.test(pathValue)) return pathValue;
	return path.join(workspaceDir, pathValue);
}
function resolveTranscriptMediaType(params) {
	return params.explicitType ?? mimeTypeFromFilePath(params.mediaPath ?? params.mediaUrl);
}
function buildPersistedUserTurnMediaInputsFromFields(fields) {
	if (!fields) return [];
	const paths = normalizeOptionalTextArray(fields.MediaPaths);
	const urls = normalizeOptionalTextArray(fields.MediaUrls);
	const types = normalizeOptionalTextArray(fields.MediaTypes);
	const singlePath = normalizeOptionalText(fields.MediaPath);
	const singleUrl = normalizeOptionalText(fields.MediaUrl);
	const singleType = normalizeOptionalText(fields.MediaType);
	const workspaceDir = normalizeOptionalText(fields.MediaWorkspaceDir);
	const mediaCount = Math.max(paths.length, urls.length, singlePath || singleUrl ? 1 : 0);
	const media = [];
	for (let index = 0; index < mediaCount; index += 1) {
		const rawPath = paths[index] ?? (index === 0 ? singlePath : void 0);
		const mediaPath = rawPath ? resolveTranscriptMediaPath(rawPath, workspaceDir) : void 0;
		const url = urls[index] ?? (index === 0 ? singleUrl : void 0);
		if (!mediaPath && !url) continue;
		media.push({
			...mediaPath ? { path: mediaPath } : {},
			...url ? { url } : {},
			contentType: resolveTranscriptMediaType({
				explicitType: types[index] ?? (index === 0 ? singleType : void 0),
				mediaPath,
				mediaUrl: url
			})
		});
	}
	return media;
}
function buildPersistedUserTurnMediaFields(media) {
	const normalized = (Array.isArray(media) ? media : []).map(normalizeMediaEntryForTranscript).filter((entry) => entry !== void 0);
	const paths = normalized.map((entry) => entry.path);
	if (paths.length === 0) return {};
	const types = normalized.map((entry) => entry.type);
	return {
		MediaPath: paths[0],
		MediaPaths: paths,
		MediaType: types[0],
		MediaTypes: types
	};
}
function buildPersistedUserTurnMessage(params) {
	const mediaFields = buildPersistedUserTurnMediaFields(params.media);
	const hasMedia = Boolean(mediaFields.MediaPath);
	return applyInputProvenanceToUserMessage({
		role: "user",
		content: normalizeTranscriptText(params.text) || (hasMedia ? params.mediaOnlyText ?? "" : ""),
		timestamp: params.timestamp ?? Date.now(),
		...params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : {},
		...mediaFields
	}, params.provenance);
}
function resolvePersistedUserTurnMessage(params) {
	if (params.message) return params.message;
	if (!params.input) return;
	return buildPersistedUserTurnMessage(params.input);
}
function isUserMessage(message) {
	return message.role === "user";
}
function isBeforeAgentRunBlockedMessage(message) {
	return message["__openclaw"]?.beforeAgentRunBlocked !== void 0;
}
function mergePreparedUserTurnMessageForRuntime(params) {
	if (!params.preparedMessage || !isUserMessage(params.runtimeMessage) || isBeforeAgentRunBlockedMessage(params.runtimeMessage)) return params.runtimeMessage;
	return {
		...params.runtimeMessage,
		...params.preparedMessage
	};
}
function applyBeforeMessageWriteToUserTurn(message, params) {
	if (!params.beforeMessageWrite) return message;
	const originalMessage = message;
	const idempotencyKey = typeof originalMessage.idempotencyKey === "string" ? originalMessage.idempotencyKey : void 0;
	const provenance = normalizeInputProvenance(message.provenance);
	const nextMessage = params.beforeMessageWrite({
		message,
		...params.agentId ? { agentId: params.agentId } : {},
		...params.sessionKey ? { sessionKey: params.sessionKey } : {}
	});
	if (nextMessage?.role !== "user") return;
	const nextUserMessage = provenance ? applyInputProvenanceToUserMessage(nextMessage, provenance) : nextMessage;
	return idempotencyKey ? {
		...nextUserMessage,
		idempotencyKey
	} : nextUserMessage;
}
async function appendUserTurnTranscriptMessage(params) {
	const resolvedMessage = resolvePersistedUserTurnMessage(params);
	if (!resolvedMessage) return;
	const appended = await appendSessionTranscriptMessage({
		transcriptPath: params.transcriptPath,
		...params.sessionId ? { sessionId: params.sessionId } : {},
		...params.cwd ? { cwd: params.cwd } : {},
		...params.config ? { config: params.config } : {},
		message: resolvedMessage,
		idempotencyLookup: "scan",
		prepareMessageAfterIdempotencyCheck: (message) => applyBeforeMessageWriteToUserTurn(message, params)
	});
	if (!appended) return;
	switch (params.updateMode ?? "inline") {
		case "inline":
			if (appended.appended) emitSessionTranscriptUpdate({
				sessionFile: params.transcriptPath,
				...params.sessionKey ? { sessionKey: params.sessionKey } : {},
				...params.agentId ? { agentId: params.agentId } : {},
				message: appended.message,
				messageId: appended.messageId
			});
			break;
		case "none": break;
	}
	return {
		sessionFile: params.transcriptPath,
		messageId: appended.messageId,
		message: appended.message
	};
}
async function persistUserTurnTranscript(params) {
	const message = resolvePersistedUserTurnMessage(params);
	if (!message) return;
	const { resolveSessionTranscriptFile } = await import("./transcript-FyOcEi-1.js");
	const { sessionFile, sessionEntry } = await resolveSessionTranscriptFile({
		sessionId: params.sessionId,
		sessionKey: params.sessionKey,
		sessionEntry: params.sessionEntry,
		...params.sessionStore ? { sessionStore: params.sessionStore } : {},
		...params.storePath ? { storePath: params.storePath } : {},
		agentId: params.agentId,
		...params.threadId !== void 0 ? { threadId: params.threadId } : {}
	});
	const appended = await appendUserTurnTranscriptMessage({
		transcriptPath: sessionFile,
		message,
		sessionId: params.sessionId,
		agentId: params.agentId,
		sessionKey: params.sessionKey,
		...params.cwd ? { cwd: params.cwd } : {},
		...params.config ? { config: params.config } : {},
		...params.updateMode ? { updateMode: params.updateMode } : {},
		...params.beforeMessageWrite ? { beforeMessageWrite: params.beforeMessageWrite } : {}
	});
	if (!appended) return;
	return {
		...appended,
		sessionEntry
	};
}
async function resolveUserTurnTranscriptTarget(target) {
	return typeof target === "function" ? await target() : target;
}
function isUserTurnTranscriptFileTarget(target) {
	return "transcriptPath" in target;
}
function createUserTurnTranscriptRecorder(params) {
	const message = resolvePersistedUserTurnMessage(params);
	let blocked = false;
	let persisted = false;
	let persistedResult;
	let runtimePersistencePromise;
	let selfPersistencePromise;
	let resolvedMessagePromise;
	const handlePersistenceError = (error) => {
		if (params.onPersistenceError) {
			params.onPersistenceError(error);
			return;
		}
		import("./globals-U0xzF3oh.js").then(({ logVerbose }) => {
			logVerbose(`failed to persist ${params.errorContext ?? "user turn transcript"}: ${String(error)}`);
		}).catch(() => void 0);
	};
	const resolveMessageForPersistence = async () => {
		if (params.message) return params.message;
		if (!params.resolveInput) return message;
		if (!resolvedMessagePromise) resolvedMessagePromise = (async () => {
			try {
				const resolvedInput = await params.resolveInput?.();
				return resolvePersistedUserTurnMessage({
					message: params.message,
					input: resolvedInput ?? params.input
				}) ?? message;
			} catch (error) {
				handlePersistenceError(error);
				return message;
			}
		})();
		return await resolvedMessagePromise;
	};
	const waitForRuntimePersistence = async () => {
		if (!runtimePersistencePromise) return;
		try {
			await runtimePersistencePromise;
		} catch (error) {
			handlePersistenceError(error);
		}
	};
	const persistPrepared = async (options) => {
		if (persisted) return persistedResult;
		if (options.skipWhenBlocked && blocked) return;
		if (!message && !params.resolveInput) return;
		if (options.waitForRuntime) {
			await waitForRuntimePersistence();
			if (persisted) return persistedResult;
		}
		if (selfPersistencePromise) return await selfPersistencePromise;
		selfPersistencePromise = (async () => {
			const resolvedMessage = await resolveMessageForPersistence();
			if (!resolvedMessage) return;
			const target = await resolveUserTurnTranscriptTarget(options.target ?? params.target);
			if (!target) return;
			const updateMode = options.updateMode ?? params.updateMode ?? "inline";
			const result = isUserTurnTranscriptFileTarget(target) ? await appendUserTurnTranscriptMessage({
				...target,
				message: resolvedMessage,
				updateMode,
				...params.beforeMessageWrite ? { beforeMessageWrite: params.beforeMessageWrite } : {}
			}).then((appended) => appended ? {
				...appended,
				sessionEntry: void 0
			} : void 0) : await persistUserTurnTranscript({
				...target,
				message: resolvedMessage,
				updateMode,
				...params.beforeMessageWrite ? { beforeMessageWrite: params.beforeMessageWrite } : {}
			});
			if (result) {
				persisted = true;
				persistedResult = result;
			}
			return result;
		})();
		try {
			return await selfPersistencePromise;
		} catch (error) {
			handlePersistenceError(error);
			throw error;
		}
	};
	return {
		message,
		resolveMessage: resolveMessageForPersistence,
		markRuntimePersistencePending: (pending) => {
			runtimePersistencePromise = pending;
		},
		markRuntimePersisted: (persistedMessage) => {
			persisted = true;
			if (persistedMessage && persistedResult) persistedResult = {
				...persistedResult,
				message: persistedMessage
			};
		},
		markBlocked: () => {
			blocked = true;
		},
		hasPersisted: () => persisted,
		isBlocked: () => blocked,
		hasRuntimePersistencePending: () => runtimePersistencePromise !== void 0,
		waitForRuntimePersistence,
		persistApproved: async (options) => await persistPrepared({
			waitForRuntime: false,
			skipWhenBlocked: true,
			target: options?.target,
			updateMode: options?.updateMode
		}),
		persistFallback: async (options) => await persistPrepared({
			waitForRuntime: true,
			skipWhenBlocked: true,
			target: options?.target,
			updateMode: options?.updateMode
		})
	};
}
//#endregion
export { resolvePersistedUserTurnText as a, mergePreparedUserTurnMessageForRuntime as i, buildPersistedUserTurnMediaInputsFromFields as n, isReasoningTagProvider as o, createUserTurnTranscriptRecorder as r, appendUserTurnTranscriptMessage as t };
