import { createLogger } from "../../utils/logger/index.js";
import { init_logger } from "../../utils/logger.js";
import { getDefaultModelSync, init_providers, resolveModel } from "../../providers/index.js";
import { getOrLoadSessionSearchIndex, invalidateSessionSearchIndexCache } from "../../session/search-index-cache.js";
import { readAgentMessageContent } from "../memory/agent-message-access.js";
import { complete } from "@earendil-works/pi-ai";
import { Type } from "@sinclair/typebox";
//#region src/agent/tools/session-search-tool.ts
init_providers();
init_logger();
const log = createLogger("session-search-tool");
const MAX_SUMMARY_CHARS = 2e4;
function resolveSummaryModel(getConfig) {
	const envRef = process.env.XOPC_SESSION_SEARCH_MODEL?.trim();
	const configRef = getConfig?.()?.agents?.defaults?.sessionSearch?.summaryModel?.trim();
	const ref = envRef || configRef;
	if (ref) try {
		return resolveModel(ref);
	} catch (err) {
		log.warn({
			err,
			ref
		}, "session_search: summary model resolve failed, using fallback");
	}
	try {
		return resolveModel("openai/gpt-4o-mini");
	} catch {
		return resolveModel(getDefaultModelSync(getConfig?.()));
	}
}
function extractTextFromContent(content) {
	if (typeof content === "string") return content;
	if (!Array.isArray(content)) return "";
	const parts = [];
	for (const item of content) {
		if (typeof item !== "object" || item === null || !("type" in item)) continue;
		const c = item;
		if (c.type === "text" && typeof c.text === "string") parts.push(c.text);
	}
	return parts.join(" ");
}
function formatMessagesForSummary(messages) {
	const lines = [];
	let total = 0;
	for (const msg of messages) {
		const line = `${String(msg.role || "unknown").toUpperCase()}: ${extractTextFromContent(readAgentMessageContent(msg))}`;
		if (total + line.length > MAX_SUMMARY_CHARS) {
			lines.push("… [truncated]");
			break;
		}
		lines.push(line);
		total += line.length + 1;
	}
	return lines.join("\n\n");
}
async function summarizeSession(messages, query, getConfig, signal, logMeta) {
	if (messages.length === 0) return "No messages in session.";
	const userMsg = {
		role: "user",
		content: `Summarize this conversation for someone searching with: "${query}". Focus on facts, decisions, and names. Max 200 words. Use the same language as the conversation when possible.

Conversation:
${formatMessagesForSummary(messages)}`,
		timestamp: Date.now()
	};
	const model = resolveSummaryModel(getConfig);
	try {
		const result = await complete(model, { messages: [userMsg] }, {
			maxTokens: 400,
			temperature: .15,
			signal
		});
		let text = "";
		if (Array.isArray(result.content)) {
			for (const c of result.content) if (c && typeof c === "object" && c.type === "text") text += String(c.text || "");
		}
		return text.trim() || "[Empty summary]";
	} catch (err) {
		const msg = err instanceof Error ? err.message : String(err);
		log.warn({
			err,
			summarizingSessionKey: logMeta?.summarizingSessionKey,
			queryLength: query.length,
			messageCount: messages.length
		}, `session_search LLM summarization failed${logMeta?.summarizingSessionKey ? ` (session ${logMeta.summarizingSessionKey})` : ""}: ${msg}`);
		return `[Summarization failed: ${msg}]`;
	}
}
const SessionSearchSchema = Type.Object({
	query: Type.Optional(Type.String({ description: "Keyword search over past sessions. Omit to list recent sessions." })),
	roleFilter: Type.Optional(Type.Union([
		Type.Literal("user"),
		Type.Literal("assistant"),
		Type.Literal("system"),
		Type.Literal("tool"),
		Type.Literal("toolResult")
	])),
	limit: Type.Optional(Type.Number({
		minimum: 1,
		maximum: 15
	})),
	excludeSessionKey: Type.Optional(Type.String({ description: "Exclude this session key from keyword results (default: current chat)." }))
});
function createSessionSearchTool(deps) {
	return {
		name: "session_search",
		label: "Session search",
		description: "Search other chat sessions by keywords and get short summaries, or omit `query` to list recent sessions (no LLM cost). Uses the same session store as the gateway. Narrow with roleFilter if needed.",
		parameters: SessionSearchSchema,
		async execute(_toolCallId, params, signal) {
			const p = params;
			const store = deps.getSessionStore();
			const limit = Math.min(15, Math.max(1, p.limit ?? 5));
			const query = p.query?.trim() ?? "";
			try {
				if (!query) {
					const listed = await store.list({
						limit: limit + 5,
						sortBy: "updatedAt",
						sortOrder: "desc"
					});
					const items = listed.items.slice(0, limit).map((s) => ({
						key: s.key,
						name: s.name,
						updatedAt: s.updatedAt,
						messageCount: s.messageCount,
						sourceChannel: s.sourceChannel
					}));
					return {
						content: [{
							type: "text",
							text: JSON.stringify({
								success: true,
								mode: "recent",
								results: items,
								total: listed.total
							}, null, 2)
						}],
						details: {
							mode: "recent",
							items
						}
					};
				}
				const index = await getOrLoadSessionSearchIndex(store.getSessionsRoot());
				let matches = index.search(query, 80);
				const exclude = p.excludeSessionKey?.trim() || deps.getCurrentSessionKey?.() || "";
				if (exclude) matches = matches.filter((m) => m.key !== exclude);
				const top = matches.slice(0, limit);
				const summaries = await Promise.all(top.map(async ({ key, score }) => {
					let messages = index.getSessionMessages(key);
					if (messages.length === 0) messages = await store.load(key);
					if (p.roleFilter) messages = messages.filter((m) => m.role === p.roleFilter);
					return {
						sessionKey: key,
						score,
						summary: await summarizeSession(messages, query, deps.getConfig, signal, { summarizingSessionKey: key })
					};
				}));
				return {
					content: [{
						type: "text",
						text: JSON.stringify({
							success: true,
							mode: "keyword",
							query,
							results: summaries,
							count: summaries.length
						}, null, 2)
					}],
					details: {
						query,
						summaries
					}
				};
			} catch (err) {
				const message = err instanceof Error ? err.message : String(err);
				return {
					content: [{
						type: "text",
						text: `session_search error: ${message}`
					}],
					details: { error: message }
				};
			}
		}
	};
}
//#endregion
export { createSessionSearchTool, invalidateSessionSearchIndexCache };

//# sourceMappingURL=session-search-tool.js.map