import { g as readStringValue, l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { a as asOptionalRecord, c as isRecord } from "./record-coerce-DItp3I4t.js";
import { s as asFiniteNumber } from "./number-coercion-CLj0HTDM.js";
import { c as normalizeProviderId } from "./model-ref-shared-DflvAb3G.js";
import { it as stripInboundMetadata } from "./openclaw-state-db-Cb4e1jdd.js";
import { f as readPersistedMediaFacts, s as isImageMediaFact } from "./media-facts-Bd6apMSF.js";
import { i as normalizeCliSessionReseedReceipt, n as getCliSessionBinding } from "./cli-session-binding-CBcssoqj.js";
import { a as stripInlineDirectiveTagsForDisplay } from "./directive-tags-C_zJMvJq.js";
import "./model-selection-CtjI2iP7.js";
import { r as isOpenClawCliImageCachePath } from "./images.media-refs-CkDEKTeH.js";
import { n as hashCliImageTurnEntryId, r as readCliImageTurnContext } from "./cli-image-turn-correlation-ByDmAKG3.js";
import { a as parseClaudeCliHistoryEntry, c as redactClaudeCliHistoryMessage, d as resolveClaudeCliSessionFilePath, i as decodeClaudeCliProjectEntry, n as appendCoalescedClaudeCliToolMessage, r as createClaudeReseedImportState, s as readClaudeCliSessionMessages, t as CLAUDE_CLI_PROVIDER } from "./cli-session-history.claude-B58h1uOv.js";
import fs from "node:fs";
import { Worker } from "node:worker_threads";
import { setImmediate } from "node:timers/promises";
import readline from "node:readline";
//#region src/gateway/cli-session-history.claude-snapshot.ts
const YIELD_BYTES = 256 * 1024;
const OFFTHREAD_JSONL_LINE_CHARS = 1024 * 1024;
const OVERSIZED_HISTORY_PLACEHOLDER = "[Claude CLI history record omitted from context because it exceeded 1 MiB.]";
const OVERSIZED_ENTRY_WORKER_SOURCE = `
  const { parentPort, workerData } = require("node:worker_threads");
  const boundedString = (value, max) =>
    typeof value === "string" && value.length <= max ? value : undefined;
  try {
    const entry = JSON.parse(workerData);
    const type = entry?.type;
    const message = entry?.message;
    if ((type !== "user" && type !== "assistant") || !message || message.role !== type) {
      parentPort.postMessage(null);
    } else {
      const rawUsage = message.usage;
      const usage = rawUsage && typeof rawUsage === "object"
        ? Object.fromEntries(
            ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"]
              .flatMap((key) => Number.isFinite(rawUsage[key]) ? [[key, rawUsage[key]]] : []),
          )
        : undefined;
      parentPort.postMessage({
        type,
        timestamp: boundedString(entry.timestamp, 128),
        uuid: boundedString(entry.uuid, 1_024),
        isSidechain: entry.isSidechain === true,
        isMeta: entry.isMeta === true,
        isCompactSummary: entry.isCompactSummary === true,
        isVisibleInTranscriptOnly: entry.isVisibleInTranscriptOnly === true,
        message: {
          role: type,
          content: ${JSON.stringify(OVERSIZED_HISTORY_PLACEHOLDER)},
          model: boundedString(message.model, 256),
          stop_reason: boundedString(message.stop_reason, 128),
          usage,
        },
      });
    }
  } catch {
    parentPort.postMessage(null);
  }
`;
let snapshotCache;
function normalizeOversizedEntry(value) {
	if (!isRecord(value) || value.type !== "user" && value.type !== "assistant") return null;
	const message = value.message;
	if (!isRecord(message) || message.role !== value.type) return null;
	const usage = isRecord(message.usage) ? message.usage : void 0;
	return {
		type: value.type,
		...typeof value.timestamp === "string" ? { timestamp: value.timestamp } : {},
		...typeof value.uuid === "string" ? { uuid: value.uuid } : {},
		...value.isSidechain === true ? { isSidechain: true } : {},
		...value.isMeta === true ? { isMeta: true } : {},
		...value.isCompactSummary === true ? { isCompactSummary: true } : {},
		...value.isVisibleInTranscriptOnly === true ? { isVisibleInTranscriptOnly: true } : {},
		message: {
			role: value.type,
			content: OVERSIZED_HISTORY_PLACEHOLDER,
			...typeof message.model === "string" ? { model: message.model } : {},
			...typeof message.stop_reason === "string" ? { stop_reason: message.stop_reason } : {},
			...usage ? { usage: {
				input_tokens: usage.input_tokens,
				output_tokens: usage.output_tokens,
				cache_read_input_tokens: usage.cache_read_input_tokens,
				cache_creation_input_tokens: usage.cache_creation_input_tokens
			} } : {}
		}
	};
}
async function decodeOversizedClaudeEntry(line) {
	let worker;
	try {
		worker = new Worker(OVERSIZED_ENTRY_WORKER_SOURCE, {
			eval: true,
			workerData: line
		});
	} catch {
		return null;
	}
	return await new Promise((resolve) => {
		let settled = false;
		const finish = (value) => {
			if (settled) return;
			settled = true;
			resolve(normalizeOversizedEntry(value));
		};
		worker.once("message", finish);
		worker.once("error", () => finish(null));
		worker.once("exit", () => finish(null));
	});
}
function fingerprint(stats) {
	return [
		stats.dev,
		stats.ino,
		stats.size,
		stats.mtimeMs,
		stats.ctimeMs
	].join(":");
}
async function resolveSource(params) {
	const candidate = resolveClaudeCliSessionFilePath(params);
	if (!candidate) return;
	try {
		const filePath = await fs.promises.realpath(candidate);
		const sourceFingerprint = fingerprint(await fs.promises.stat(filePath));
		return [filePath, JSON.stringify([
			filePath,
			sourceFingerprint,
			params.cliSessionId,
			params.localSessionId?.trim() || null,
			normalizeCliSessionReseedReceipt(params.reseedReceipt)
		])];
	} catch {
		return;
	}
}
async function parseSnapshot(filePath, params) {
	const messages = [];
	const toolNames = /* @__PURE__ */ new Map();
	const lines = readline.createInterface({
		input: fs.createReadStream(filePath, { encoding: "utf8" }),
		crlfDelay: Number.POSITIVE_INFINITY
	});
	const reseedState = createClaudeReseedImportState(params);
	let bytesSinceYield = 0;
	let lineNumber = 0;
	for await (const line of lines) {
		lineNumber += 1;
		const oversized = line.length > OFFTHREAD_JSONL_LINE_CHARS;
		if (oversized) bytesSinceYield = 0;
		else {
			bytesSinceYield += Buffer.byteLength(line, "utf8") + 1;
			if (bytesSinceYield >= YIELD_BYTES) {
				bytesSinceYield = 0;
				await setImmediate();
			}
			if (!line.trim()) continue;
		}
		try {
			const entry = oversized ? await decodeOversizedClaudeEntry(line) : decodeClaudeCliProjectEntry(line);
			if (!entry) continue;
			const message = parseClaudeCliHistoryEntry(entry, params.cliSessionId, lineNumber, toolNames, {
				reseedMode: "recover",
				reseedState
			});
			if (message) appendCoalescedClaudeCliToolMessage(messages, message);
		} catch {}
	}
	const redacted = [];
	for (const [index, message] of messages.entries()) {
		if (index % 32 === 0) await setImmediate();
		redacted.push(redactClaudeCliHistoryMessage(message));
	}
	return Object.freeze(redacted);
}
async function readClaudeCliSessionMessagesAsync(params) {
	const source = await resolveSource(params);
	if (!source) return [];
	const [filePath, cacheKey] = source;
	if (snapshotCache?.key !== cacheKey) snapshotCache = {
		key: cacheKey,
		pending: parseSnapshot(filePath, params)
	};
	const pending = snapshotCache.pending;
	let snapshot;
	try {
		snapshot = await pending;
	} catch {
		if (snapshotCache?.pending === pending) snapshotCache = void 0;
		return [];
	}
	const messages = [];
	for (const [index, message] of snapshot.entries()) {
		if (index % 32 === 0) await setImmediate();
		messages.push(structuredClone(message));
	}
	return messages;
}
//#endregion
//#region src/gateway/cli-session-history.merge.ts
const DEDUPE_TIMESTAMP_WINDOW_MS = 300 * 1e3;
function stripTrailingCliImageMentions(text) {
	const lines = text.split("\n");
	let end = lines.length;
	while (end > 0) {
		const line = lines[end - 1]?.trim() ?? "";
		if (!line.startsWith("@") || !isOpenClawCliImageCachePath(line.slice(1))) break;
		end -= 1;
	}
	return end === lines.length ? {
		text,
		stripped: false
	} : {
		text: lines.slice(0, end).join("\n").trimEnd(),
		stripped: true
	};
}
function isClaudeCliImportedUserMessage(message, role) {
	if (role !== "user") return false;
	return normalizeOptionalString(asOptionalRecord(asOptionalRecord(message)?.["__openclaw"])?.importedFrom) === "claude-cli";
}
function extractComparableText(message, role) {
	if (!message || typeof message !== "object") return { hasCliImageMentions: false };
	const record = message;
	const parts = [];
	const text = readStringValue(record.text);
	if (text !== void 0) parts.push(text);
	const rawContent = record.content;
	const content = readStringValue(rawContent);
	if (content !== void 0) parts.push(content);
	else if (Array.isArray(rawContent)) {
		for (const block of rawContent) if (block && typeof block === "object" && "text" in block) {
			const blockText = readStringValue(block.text);
			if (blockText !== void 0) parts.push(blockText);
		}
	}
	if (parts.length === 0) return { hasCliImageMentions: false };
	const joined = parts.join("\n").trim();
	if (!joined) return { hasCliImageMentions: false };
	const stripResult = isClaudeCliImportedUserMessage(message, role) ? stripTrailingCliImageMentions(joined) : {
		text: joined,
		stripped: false
	};
	const normalized = stripInlineDirectiveTagsForDisplay(role === "user" ? stripInboundMetadata(stripResult.text) : stripResult.text).text.replace(/\s+/g, " ").trim();
	const storedImageTurnKey = normalizeOptionalString(asOptionalRecord(asOptionalRecord(message)?.["__openclaw"])?.cliImageTurnKey);
	return {
		hasCliImageMentions: stripResult.stripped,
		...stripResult.stripped && isClaudeCliImportedUserMessage(message, role) ? { cliImageTurnKey: storedImageTurnKey ?? readCliImageTurnContext(joined) } : {},
		...normalized ? { text: normalized } : {}
	};
}
function prepareComparableMessage(message, order) {
	if (!message || typeof message !== "object") return {
		message,
		order,
		hasCliImageMentions: false
	};
	const record = message;
	const role = readStringValue(record.role);
	const comparableText = extractComparableText(message, role);
	return {
		message,
		order,
		externalIdentityKey: resolveImportedExternalIdentityKey(message),
		hasCliImageMentions: comparableText.hasCliImageMentions,
		...comparableText.cliImageTurnKey ? { cliImageTurnKey: comparableText.cliImageTurnKey } : {},
		role,
		text: comparableText.text,
		timestamp: asFiniteNumber(record.timestamp)
	};
}
function resolveImportedExternalIdentityKey(message) {
	if (!message || typeof message !== "object") return;
	const rawMeta = message["__openclaw"];
	if (!rawMeta || typeof rawMeta !== "object") return;
	const externalId = normalizeOptionalString(rawMeta.externalId);
	return externalId ? JSON.stringify([
		externalId,
		normalizeOptionalString(rawMeta.importedFrom),
		normalizeOptionalString(rawMeta.cliSessionId)
	]) : void 0;
}
function addTimestampToSummary(summary, timestamp) {
	if (timestamp === void 0) {
		summary.hasMissingTimestamp = true;
		return;
	}
	const bucketKey = Math.floor(timestamp / DEDUPE_TIMESTAMP_WINDOW_MS);
	const bucket = summary.buckets.get(bucketKey);
	if (bucket) {
		bucket.min = Math.min(bucket.min, timestamp);
		bucket.max = Math.max(bucket.max, timestamp);
	} else summary.buckets.set(bucketKey, {
		min: timestamp,
		max: timestamp
	});
}
function summaryHasTimestampMatch(summary, timestamp) {
	if (!summary || timestamp === void 0) return false;
	const bucketKey = Math.floor(timestamp / DEDUPE_TIMESTAMP_WINDOW_MS);
	if (summary.buckets.has(bucketKey)) return true;
	const previous = summary.buckets.get(bucketKey - 1);
	if (previous && previous.max >= timestamp - DEDUPE_TIMESTAMP_WINDOW_MS) return true;
	const next = summary.buckets.get(bucketKey + 1);
	return next !== void 0 && next.min <= timestamp + DEDUPE_TIMESTAMP_WINDOW_MS;
}
function summaryMatchesTimestamp(summary, timestamp) {
	return Boolean(summary && (timestamp === void 0 || summary.hasMissingTimestamp)) || summaryHasTimestampMatch(summary, timestamp);
}
function addRoleTextCandidate(index, entry) {
	if (!entry.role || !entry.text) return;
	let byText = index.get(entry.role);
	if (!byText) {
		byText = /* @__PURE__ */ new Map();
		index.set(entry.role, byText);
	}
	let summary = byText.get(entry.text);
	if (!summary) {
		summary = {
			hasMissingTimestamp: false,
			buckets: /* @__PURE__ */ new Map()
		};
		byText.set(entry.text, summary);
	}
	addTimestampToSummary(summary, entry.timestamp);
}
function hasRoleTextCandidate(index, entry) {
	if (!entry.role || !entry.text) return false;
	return summaryMatchesTimestamp(index.get(entry.role)?.get(entry.text), entry.timestamp);
}
function hasLocalImageMediaFacts(entry) {
	if (entry.role !== "user") return false;
	const message = asOptionalRecord(entry.message);
	return message ? (readPersistedMediaFacts(message) ?? []).some(isImageMediaFact) : false;
}
function findLocalImageMediaMatch(candidates, imported) {
	if (!imported.hasCliImageMentions || !imported.cliImageTurnKey) return -1;
	return candidates.findIndex((candidate) => candidate.cliImageTurnKey === imported.cliImageTurnKey);
}
function compareHistoryMessages(a, b) {
	if (a.timestamp !== void 0 && b.timestamp !== void 0 && a.timestamp !== b.timestamp) return a.timestamp - b.timestamp;
	return a.order - b.order;
}
/** Merges imported CLI transcript messages into local history without duplicating overlaps. */
function mergeImportedChatHistoryMessages(params) {
	if (params.importedMessages.length === 0) return params.localMessages;
	const merged = params.localMessages.map(prepareComparableMessage);
	const exactExternalIdentityIndex = /* @__PURE__ */ new Set();
	const allMessageRoleTextIndex = /* @__PURE__ */ new Map();
	const identitylessRoleTextIndex = /* @__PURE__ */ new Map();
	const localImageMediaCandidates = [];
	const indexEntry = (entry) => {
		if (entry.externalIdentityKey) exactExternalIdentityIndex.add(entry.externalIdentityKey);
		else addRoleTextCandidate(identitylessRoleTextIndex, entry);
		addRoleTextCandidate(allMessageRoleTextIndex, entry);
	};
	for (const entry of merged) {
		const localEntryId = normalizeOptionalString(asOptionalRecord(asOptionalRecord(entry.message)?.["__openclaw"])?.id);
		if (localEntryId) entry.cliImageTurnKey = hashCliImageTurnEntryId(localEntryId);
		indexEntry(entry);
		if (hasLocalImageMediaFacts(entry)) localImageMediaCandidates.push(entry);
	}
	let nextOrder = merged.length;
	for (const message of params.importedMessages) {
		const imported = prepareComparableMessage(message, nextOrder);
		if (imported.externalIdentityKey && exactExternalIdentityIndex.has(imported.externalIdentityKey)) continue;
		const localImageMediaIndex = findLocalImageMediaMatch(localImageMediaCandidates, imported);
		if (localImageMediaIndex !== -1) {
			localImageMediaCandidates.splice(localImageMediaIndex, 1);
			continue;
		}
		const duplicate = imported.externalIdentityKey ? hasRoleTextCandidate(identitylessRoleTextIndex, imported) : hasRoleTextCandidate(allMessageRoleTextIndex, imported);
		if (!imported.hasCliImageMentions && duplicate) continue;
		merged.push(imported);
		indexEntry(imported);
		nextOrder += 1;
	}
	merged.sort(compareHistoryMessages);
	return merged.map((entry) => entry.message);
}
//#endregion
//#region src/gateway/cli-session-history.ts
const ANTHROPIC_PROVIDER = "anthropic";
function resolveEligibleCliSessionBinding(params) {
	const binding = getCliSessionBinding(params.entry, CLAUDE_CLI_PROVIDER);
	const provider = normalizeProviderId(params.provider ?? "");
	const eligible = !provider || params.localMessages.length === 0 || provider === "claude-cli" || provider === ANTHROPIC_PROVIDER;
	return binding?.sessionId && eligible ? binding : void 0;
}
/** Resolves chat history plus whether a bound external transcript was actually incorporated. */
function resolveChatHistoryWithCliSessionImports(params) {
	const binding = resolveEligibleCliSessionBinding(params);
	if (!binding) return {
		messages: params.localMessages,
		imported: false
	};
	const importedMessages = params.preparedImportedMessages ?? readClaudeCliSessionMessages({
		cliSessionId: binding.sessionId,
		homeDir: params.homeDir,
		localSessionId: params.entry?.sessionId,
		reseedReceipt: binding.reseedReceipt
	});
	if (importedMessages.length === 0) return {
		messages: params.localMessages,
		imported: false
	};
	const messages = mergeImportedChatHistoryMessages({
		localMessages: params.localMessages,
		importedMessages
	});
	return messages.length > params.localMessages.length ? {
		messages,
		imported: true
	} : {
		messages: params.localMessages,
		imported: false
	};
}
/** Acquires one request-local redacted view of the process-owned external snapshot. */
async function readChatHistoryCliSessionImportSnapshot(params) {
	const binding = resolveEligibleCliSessionBinding(params);
	return binding?.sessionId ? await readClaudeCliSessionMessagesAsync({
		cliSessionId: binding.sessionId,
		homeDir: params.homeDir,
		localSessionId: params.entry?.sessionId,
		reseedReceipt: binding.reseedReceipt
	}) : [];
}
//#endregion
export { resolveChatHistoryWithCliSessionImports as n, readChatHistoryCliSessionImportSnapshot as t };
