import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { l as asPositiveSafeInteger } from "./number-coercion-CJQ8TR--.js";
import "./agent-scope-BhR6l0A-.js";
import { u as normalizeAgentId } from "./session-key-B_NoIfpX.js";
import { c as resolveDefaultAgentId } from "./agent-scope-config-BkE-30yC.js";
import { i as getRuntimeConfig } from "./io-CHZy8A_F.js";
import { t as loadCombinedSessionStoreForGateway } from "./combined-store-gateway-CIK79XwB.js";
import { t as attachOpenClawTranscriptMeta } from "./session-utils.fs-By4_kWbs.js";
import { i as resolveSessionTranscriptCandidates } from "./session-transcript-files.fs-CETU3kxU.js";
import { a as readSessionMessageCountAsync } from "./session-transcript-readers-Owt8R_3p.js";
import { g as resolveGatewaySessionStoreTarget, l as loadGatewaySessionRow, u as loadSessionEntry } from "./session-utils-CnvO9oEi.js";
import { t as resolvePreferredSessionKeyForSessionIdMatches } from "./session-id-resolution-CcSU7jfY.js";
import { a as projectChatDisplayMessage, t as resolveTranscriptPathForComparison } from "./session-transcript-path-Cx9u2ERr.js";
import { t as hasTrackedActiveSessionRun } from "./session-active-runs-lGkDgO9V.js";
//#region src/gateway/session-transcript-key.ts
const TRANSCRIPT_SESSION_KEY_CACHE = /* @__PURE__ */ new Map();
const TRANSCRIPT_SESSION_KEY_CACHE_MAX = 256;
function sessionKeyMatchesTranscriptPath(params) {
	const entry = params.store[params.key];
	if (!entry?.sessionId) return false;
	const target = resolveGatewaySessionStoreTarget({
		cfg: params.cfg,
		key: params.key,
		scanLegacyKeys: false,
		store: params.store
	});
	const sessionAgentId = normalizeAgentId(target.agentId);
	return resolveSessionTranscriptCandidates(entry.sessionId, target.storePath, entry.sessionFile, sessionAgentId).some((candidate) => resolveTranscriptPathForComparison(candidate) === params.targetPath);
}
/** Resolve the most likely Gateway session key for a transcript file path. */
function resolveSessionKeyForTranscriptFile(sessionFile) {
	const targetPath = resolveTranscriptPathForComparison(sessionFile);
	if (!targetPath) return;
	const cfg = getRuntimeConfig();
	const { store } = loadCombinedSessionStoreForGateway(cfg);
	const cachedKey = TRANSCRIPT_SESSION_KEY_CACHE.get(targetPath);
	if (cachedKey && sessionKeyMatchesTranscriptPath({
		cfg,
		store,
		key: cachedKey,
		targetPath
	})) return cachedKey;
	const matchingEntries = [];
	for (const [key, entry] of Object.entries(store)) {
		if (!entry?.sessionId || key === cachedKey) continue;
		if (sessionKeyMatchesTranscriptPath({
			cfg,
			store,
			key,
			targetPath
		})) matchingEntries.push([key, entry]);
	}
	if (matchingEntries.length > 0) {
		const matchesBySessionId = /* @__PURE__ */ new Map();
		for (const entry of matchingEntries) {
			const sessionId = entry[1].sessionId;
			if (!sessionId) continue;
			const group = matchesBySessionId.get(sessionId);
			if (group) group.push(entry);
			else matchesBySessionId.set(sessionId, [entry]);
		}
		const resolvedMatches = Array.from(matchesBySessionId.entries()).map(([sessionId, matches]) => {
			const resolvedKey = resolvePreferredSessionKeyForSessionIdMatches(matches, sessionId) ?? matches[0]?.[0];
			const resolvedEntry = resolvedKey ? matches.find(([key]) => key === resolvedKey)?.[1] : void 0;
			return resolvedKey && resolvedEntry ? {
				key: resolvedKey,
				updatedAt: resolvedEntry.updatedAt ?? 0
			} : void 0;
		}).filter((match) => match !== void 0);
		const [freshestMatch, secondFreshestMatch] = [...resolvedMatches].toSorted((a, b) => b.updatedAt - a.updatedAt);
		const resolvedKey = resolvedMatches.length === 1 ? freshestMatch?.key : (freshestMatch?.updatedAt ?? 0) > (secondFreshestMatch?.updatedAt ?? 0) ? freshestMatch?.key : void 0;
		if (resolvedKey) {
			if (!TRANSCRIPT_SESSION_KEY_CACHE.has(targetPath) && TRANSCRIPT_SESSION_KEY_CACHE.size >= TRANSCRIPT_SESSION_KEY_CACHE_MAX) {
				const oldest = TRANSCRIPT_SESSION_KEY_CACHE.keys().next().value;
				if (oldest !== void 0) TRANSCRIPT_SESSION_KEY_CACHE.delete(oldest);
			}
			TRANSCRIPT_SESSION_KEY_CACHE.set(targetPath, resolvedKey);
			return resolvedKey;
		}
	}
	TRANSCRIPT_SESSION_KEY_CACHE.delete(targetPath);
}
//#endregion
//#region src/gateway/server-session-events.ts
function resolveSessionMessageBroadcastKeys(sessionKey, agentId) {
	const normalizedAgentId = normalizeOptionalString(agentId);
	if (sessionKey === "global") {
		const defaultAgentId = normalizeAgentId(resolveDefaultAgentId(getRuntimeConfig()));
		if (normalizedAgentId) {
			const scopedKey = `agent:${normalizeAgentId(normalizedAgentId)}:global`;
			return normalizeAgentId(normalizedAgentId) === defaultAgentId ? [scopedKey, sessionKey] : [scopedKey];
		}
		return [`agent:${defaultAgentId}:global`, sessionKey];
	}
	return [sessionKey];
}
function buildGatewaySessionSnapshot(params) {
	const { sessionRow } = params;
	if (!sessionRow) return {};
	const omitUnscopedGlobalGoal = sessionRow.key === "global" && !params.agentId;
	const session = params.includeSession ? { ...sessionRow } : void 0;
	if (session && omitUnscopedGlobalGoal) delete session.goal;
	if (session && params.hasActiveRun !== void 0) session.hasActiveRun = params.hasActiveRun;
	return {
		...session ? { session } : {},
		updatedAt: sessionRow.updatedAt ?? void 0,
		sessionId: sessionRow.sessionId,
		kind: sessionRow.kind,
		channel: sessionRow.channel,
		subject: sessionRow.subject,
		groupChannel: sessionRow.groupChannel,
		space: sessionRow.space,
		chatType: sessionRow.chatType,
		origin: sessionRow.origin,
		spawnedBy: sessionRow.spawnedBy,
		spawnedWorkspaceDir: sessionRow.spawnedWorkspaceDir,
		spawnedCwd: sessionRow.spawnedCwd,
		forkedFromParent: sessionRow.forkedFromParent,
		spawnDepth: sessionRow.spawnDepth,
		subagentRole: sessionRow.subagentRole,
		subagentControlScope: sessionRow.subagentControlScope,
		label: params.label ?? sessionRow.label,
		displayName: params.displayName ?? sessionRow.displayName,
		deliveryContext: sessionRow.deliveryContext,
		parentSessionKey: params.parentSessionKey ?? sessionRow.parentSessionKey,
		childSessions: sessionRow.childSessions,
		thinkingLevel: sessionRow.thinkingLevel,
		fastMode: sessionRow.fastMode,
		verboseLevel: sessionRow.verboseLevel,
		reasoningLevel: sessionRow.reasoningLevel,
		elevatedLevel: sessionRow.elevatedLevel,
		sendPolicy: sessionRow.sendPolicy,
		systemSent: sessionRow.systemSent,
		abortedLastRun: sessionRow.abortedLastRun,
		inputTokens: sessionRow.inputTokens,
		outputTokens: sessionRow.outputTokens,
		lastChannel: sessionRow.lastChannel,
		lastTo: sessionRow.lastTo,
		lastAccountId: sessionRow.lastAccountId,
		lastThreadId: sessionRow.lastThreadId,
		totalTokens: sessionRow.totalTokens,
		totalTokensFresh: sessionRow.totalTokensFresh,
		...omitUnscopedGlobalGoal ? {} : { goal: sessionRow.goal ?? null },
		contextTokens: sessionRow.contextTokens,
		estimatedCostUsd: sessionRow.estimatedCostUsd,
		responseUsage: sessionRow.responseUsage,
		modelProvider: sessionRow.modelProvider,
		model: sessionRow.model,
		status: sessionRow.status,
		...params.hasActiveRun === void 0 ? {} : { hasActiveRun: params.hasActiveRun },
		subagentRunState: sessionRow.subagentRunState,
		hasActiveSubagentRun: sessionRow.hasActiveSubagentRun,
		startedAt: sessionRow.startedAt,
		endedAt: sessionRow.endedAt,
		runtimeMs: sessionRow.runtimeMs,
		compactionCheckpointCount: sessionRow.compactionCheckpointCount,
		latestCompactionCheckpoint: sessionRow.latestCompactionCheckpoint
	};
}
/** Creates a serialized transcript-update broadcaster for session websocket clients. */
function createTranscriptUpdateBroadcastHandler(params) {
	let broadcastQueue = Promise.resolve();
	return (update) => {
		broadcastQueue = broadcastQueue.then(() => handleTranscriptUpdateBroadcast(params, update)).catch(() => void 0);
	};
}
async function handleTranscriptUpdateBroadcast(params, update) {
	const sessionKey = update.sessionKey ?? resolveSessionKeyForTranscriptFile(update.sessionFile);
	if (!sessionKey || update.message === void 0) return;
	const effectiveAgentId = update.agentId;
	const defaultGlobalAgentId = sessionKey === "global" ? normalizeAgentId(resolveDefaultAgentId(getRuntimeConfig())) : void 0;
	const visibleAgentId = update.agentId ?? (effectiveAgentId && effectiveAgentId !== defaultGlobalAgentId ? effectiveAgentId : void 0);
	const connIds = /* @__PURE__ */ new Set();
	for (const connId of params.sessionEventSubscribers.getAll()) connIds.add(connId);
	for (const broadcastKey of resolveSessionMessageBroadcastKeys(sessionKey, effectiveAgentId)) for (const connId of params.sessionMessageSubscribers.get(broadcastKey)) connIds.add(connId);
	if (connIds.size === 0) return;
	let messageSeq = asPositiveSafeInteger(update.messageSeq);
	if (messageSeq === void 0) {
		const { entry, storePath } = loadSessionEntry(sessionKey, { agentId: visibleAgentId });
		messageSeq = entry?.sessionId ? asPositiveSafeInteger(await readSessionMessageCountAsync({
			agentId: visibleAgentId,
			sessionEntry: entry,
			sessionId: entry.sessionId,
			sessionKey,
			storePath
		})) : void 0;
	}
	const sessionRow = loadGatewaySessionRow(sessionKey, {
		agentId: visibleAgentId,
		transcriptUsageMaxBytes: 64 * 1024
	});
	const sessionSnapshot = buildGatewaySessionSnapshot({
		sessionRow,
		agentId: visibleAgentId,
		includeSession: true,
		hasActiveRun: sessionRow ? hasTrackedActiveSessionRun({
			context: params,
			requestedKey: sessionKey,
			canonicalKey: sessionRow.key,
			...sessionRow.key === "global" && visibleAgentId ? { agentId: visibleAgentId } : {},
			defaultAgentId: normalizeAgentId(resolveDefaultAgentId(getRuntimeConfig()))
		}) : false
	});
	const message = projectChatDisplayMessage(attachOpenClawTranscriptMeta(update.message, {
		...typeof update.messageId === "string" ? { id: update.messageId } : {},
		...messageSeq !== void 0 ? { seq: messageSeq } : {}
	}));
	if (message) {
		params.broadcastToConnIds("session.message", {
			sessionKey,
			...visibleAgentId ? { agentId: visibleAgentId } : {},
			message,
			...typeof update.messageId === "string" ? { messageId: update.messageId } : {},
			...messageSeq !== void 0 ? { messageSeq } : {},
			...sessionSnapshot
		}, connIds, { dropIfSlow: true });
		return;
	}
	const sessionEventConnIds = params.sessionEventSubscribers.getAll();
	if (sessionEventConnIds.size === 0) return;
	params.broadcastToConnIds("sessions.changed", {
		sessionKey,
		...visibleAgentId ? { agentId: visibleAgentId } : {},
		phase: "message",
		ts: Date.now(),
		...typeof update.messageId === "string" ? { messageId: update.messageId } : {},
		...messageSeq !== void 0 ? { messageSeq } : {},
		...sessionSnapshot
	}, sessionEventConnIds, { dropIfSlow: true });
}
/** Creates a lifecycle-event broadcaster for session list refreshes. */
function createLifecycleEventBroadcastHandler(params) {
	return (event) => {
		const connIds = params.sessionEventSubscribers.getAll();
		if (connIds.size === 0) return;
		params.broadcastToConnIds("sessions.changed", {
			sessionKey: event.sessionKey,
			reason: event.reason,
			parentSessionKey: event.parentSessionKey,
			label: event.label,
			displayName: event.displayName,
			ts: Date.now(),
			...buildGatewaySessionSnapshot({
				sessionRow: loadGatewaySessionRow(event.sessionKey),
				label: event.label,
				displayName: event.displayName,
				parentSessionKey: event.parentSessionKey
			})
		}, connIds, { dropIfSlow: true });
	};
}
//#endregion
export { createLifecycleEventBroadcastHandler, createTranscriptUpdateBroadcastHandler };
