import { i as writeTextAtomic } from "./json-files-kvH2Eboc.js";
import { r as hasInterSessionUserProvenance } from "./input-provenance-D4kqK5-a.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/hooks/bundled/session-memory/transcript.ts
function extractTextMessageContent(content) {
	if (typeof content === "string") return content;
	if (!Array.isArray(content)) return;
	for (const block of content) {
		if (!block || typeof block !== "object") continue;
		const candidate = block;
		if (candidate.type === "text" && typeof candidate.text === "string") return candidate.text;
	}
}
const MESSAGE_ID_LINE_RE = /\[message_id:\s*\S+\]\s*\n/;
const SENDER_ID_PREFIX_RE = /^ou_\w+:\s*/;
function stripMessageMetadata(text) {
	if (!text.includes("Conversation info (untrusted metadata):")) return text;
	let cleaned = text;
	const messageIdMatch = MESSAGE_ID_LINE_RE.exec(cleaned);
	if (messageIdMatch) cleaned = cleaned.slice(messageIdMatch.index + messageIdMatch[0].length);
	return cleaned.replace(SENDER_ID_PREFIX_RE, "");
}
/**
* Synchronous transcript preprocessor. Parses raw JSONL content and extracts
* clean conversation text, stripping metadata, tool results, and thinking blocks.
*/
function preprocessSessionTranscript(rawContent, opts) {
	const maxMessages = opts?.maxMessages ?? 500;
	const lines = rawContent.trim().split("\n");
	const allMessages = [];
	for (const line of lines) {
		let entry;
		try {
			entry = JSON.parse(line);
		} catch {
			continue;
		}
		if (entry.type !== "message" || !entry.message) continue;
		const msg = entry.message;
		const role = msg.role;
		if (role !== "user" && role !== "assistant") continue;
		if (!("content" in msg) || !msg.content) continue;
		if (role === "user" && hasInterSessionUserProvenance(msg)) continue;
		const rawText = extractTextMessageContent(msg.content);
		if (!rawText) continue;
		const text = role === "user" ? stripMessageMetadata(rawText) : rawText;
		if (!text || text.startsWith("/")) continue;
		if (!opts?.excludeToolAnnotations && role === "assistant" && Array.isArray(msg.content)) {
			const toolNames = [];
			for (const block of msg.content) if (block && typeof block === "object" && block.type === "toolCall" && typeof block.name === "string") toolNames.push(block.name);
			if (toolNames.length > 0) {
				allMessages.push(`assistant: [tool: ${toolNames.join(", ")}] ${text}`);
				continue;
			}
		}
		allMessages.push(`${role}: ${text}`);
	}
	if (allMessages.length === 0) return null;
	return allMessages.slice(-maxMessages).join("\n");
}
async function getCleanDialogueContent(sessionFilePath) {
	try {
		return preprocessSessionTranscript(await fs.readFile(sessionFilePath, "utf-8"), {
			excludeToolAnnotations: true,
			maxMessages: Number.MAX_SAFE_INTEGER
		});
	} catch {
		return null;
	}
}
function mergeJsonlContents(existing, incoming) {
	const seenIds = /* @__PURE__ */ new Set();
	const keptLines = [];
	for (const line of existing.trim().split("\n")) {
		if (!line.trim()) continue;
		try {
			const entry = JSON.parse(line);
			if (entry.id) seenIds.add(entry.id);
		} catch {}
		keptLines.push(line);
	}
	for (const line of incoming.trim().split("\n")) {
		if (!line.trim()) continue;
		try {
			const entry = JSON.parse(line);
			if (entry.id && seenIds.has(entry.id)) continue;
			if (entry.id) seenIds.add(entry.id);
		} catch {}
		keptLines.push(line);
	}
	return keptLines.join("\n") + "\n";
}
async function updateDialogueStaging(stagingPath, sessionFilePath) {
	const currentRaw = await fs.readFile(sessionFilePath, "utf-8");
	let stagingExists = false;
	try {
		await fs.access(stagingPath);
		stagingExists = true;
	} catch {}
	if (!stagingExists) {
		await writeTextAtomic(stagingPath, currentRaw, { ensureDirMode: 493 });
		return;
	}
	await writeTextAtomic(stagingPath, mergeJsonlContents(await fs.readFile(stagingPath, "utf-8"), currentRaw), { appendTrailingNewline: false });
}
/**
* Resolve the session file path to a readable file.
* When /new or /reset fires, the JSONL may already be renamed to `.reset.XXX`.
* This mirrors the fallback in getRecentSessionContentWithResetFallback.
*/
async function resolveReadableSessionFile(sessionFilePath) {
	try {
		await fs.access(sessionFilePath);
		return sessionFilePath;
	} catch {}
	try {
		const dir = path.dirname(sessionFilePath);
		const resetPrefix = `${path.basename(sessionFilePath)}.reset.`;
		const resetCandidates = (await fs.readdir(dir)).filter((name) => name.startsWith(resetPrefix)).toSorted();
		if (resetCandidates.length === 0) return null;
		return path.join(dir, resetCandidates[resetCandidates.length - 1]);
	} catch {
		return null;
	}
}
async function getDialogueWithStaging(stagingPath, sessionFilePath) {
	let stagingExists = false;
	try {
		await fs.access(stagingPath);
		stagingExists = true;
	} catch {}
	const resolvedFile = await resolveReadableSessionFile(sessionFilePath);
	if (!resolvedFile) return null;
	if (!stagingExists) return getCleanDialogueContent(resolvedFile);
	const [stagingRaw, currentRaw] = await Promise.all([fs.readFile(stagingPath, "utf-8"), fs.readFile(resolvedFile, "utf-8")]);
	return preprocessSessionTranscript(mergeJsonlContents(stagingRaw, currentRaw), {
		excludeToolAnnotations: true,
		maxMessages: Number.MAX_SAFE_INTEGER
	});
}
async function getRecentSessionContent(sessionFilePath, messageCount = 15) {
	try {
		return preprocessSessionTranscript(await fs.readFile(sessionFilePath, "utf-8"), { maxMessages: messageCount });
	} catch {
		return null;
	}
}
async function getRecentSessionContentWithResetFallback(sessionFilePath, messageCount = 15) {
	const primary = await getRecentSessionContent(sessionFilePath, messageCount);
	if (primary) return primary;
	try {
		const dir = path.dirname(sessionFilePath);
		const resetPrefix = `${path.basename(sessionFilePath)}.reset.`;
		const resetCandidates = (await fs.readdir(dir)).filter((name) => name.startsWith(resetPrefix)).toSorted();
		if (resetCandidates.length === 0) return primary;
		return await getRecentSessionContent(path.join(dir, resetCandidates[resetCandidates.length - 1]), messageCount) || primary;
	} catch {
		return primary;
	}
}
function stripResetSuffix(fileName) {
	const resetIndex = fileName.indexOf(".reset.");
	return resetIndex === -1 ? fileName : fileName.slice(0, resetIndex);
}
async function findPreviousSessionFile(params) {
	try {
		const files = await fs.readdir(params.sessionsDir);
		const fileSet = new Set(files);
		const baseFromReset = params.currentSessionFile ? stripResetSuffix(path.basename(params.currentSessionFile)) : void 0;
		if (baseFromReset && fileSet.has(baseFromReset)) return path.join(params.sessionsDir, baseFromReset);
		const trimmedSessionId = params.sessionId?.trim();
		if (trimmedSessionId) {
			const canonicalFile = `${trimmedSessionId}.jsonl`;
			if (fileSet.has(canonicalFile)) return path.join(params.sessionsDir, canonicalFile);
			const topicVariants = files.filter((name) => name.startsWith(`${trimmedSessionId}-topic-`) && name.endsWith(".jsonl") && !name.includes(".reset.")).toSorted().toReversed();
			if (topicVariants.length > 0) return path.join(params.sessionsDir, topicVariants[0]);
		}
		if (!params.currentSessionFile) return;
		const nonResetJsonl = files.filter((name) => name.endsWith(".jsonl") && !name.includes(".reset.")).toSorted().toReversed();
		if (nonResetJsonl.length > 0) return path.join(params.sessionsDir, nonResetJsonl[0]);
	} catch {}
}
//#endregion
export { getRecentSessionContentWithResetFallback as a, stripMessageMetadata as c, getRecentSessionContent as i, stripResetSuffix as l, getCleanDialogueContent as n, mergeJsonlContents as o, getDialogueWithStaging as r, preprocessSessionTranscript as s, findPreviousSessionFile as t, updateDialogueStaging as u };
