import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js";
import { c as isRecord } from "./utils-BrVIyjFt.js";
import { d as executeSqliteQuerySync, f as executeSqliteQueryTakeFirstSync, i as openOpenClawStateDatabase, o as runOpenClawStateWriteTransaction, p as getNodeSqliteKysely } from "./openclaw-state-db-x4Za4WWq.js";
import { c as parseAgentSessionKey } from "./session-key-utils-Bx3apsJ3.js";
import { i as getRuntimeConfig } from "./io-CVFQolkW.js";
import "./config-BzUsatE2.js";
import { u as resolveStorePath } from "./paths-Dvr__F-x.js";
import { t as loadSessionStore } from "./store-load-I8kTMilv.js";
import { r as mergeSessionEntry } from "./types-D8S_uNvu.js";
//#region src/acp/runtime/session-meta.ts
let sessionStoreRuntimePromise;
function loadSessionStoreRuntime() {
	sessionStoreRuntimePromise ??= import("./store.runtime.js");
	return sessionStoreRuntimePromise;
}
function resolveStoreSessionKey(store, sessionKey) {
	const normalized = sessionKey.trim();
	if (!normalized) return "";
	if (store[normalized]) return normalized;
	const lower = normalizeLowercaseStringOrEmpty(normalized);
	if (store[lower]) return lower;
	for (const key of Object.keys(store)) if (normalizeLowercaseStringOrEmpty(key) === lower) return key;
	return lower;
}
/** Resolves the session store path that owns an ACP session key. */
function resolveSessionStorePathForAcp(params) {
	const cfg = params.cfg ?? getRuntimeConfig();
	const parsed = parseAgentSessionKey(params.sessionKey);
	return {
		cfg,
		storePath: resolveStorePath(cfg.session?.store, {
			agentId: parsed?.agentId,
			env: params.env
		})
	};
}
function getAcpSessionKysely(db) {
	return getNodeSqliteKysely(db);
}
function parseOptionalJsonRecord(raw) {
	if (raw == null || raw === "") return;
	try {
		const parsed = JSON.parse(raw);
		return isRecord(parsed) ? parsed : void 0;
	} catch {
		return;
	}
}
function rowToAcpSessionMeta(row) {
	const identity = parseOptionalJsonRecord(row.identity_json);
	const runtimeOptions = parseOptionalJsonRecord(row.runtime_options_json);
	return {
		backend: row.backend,
		agent: row.agent,
		runtimeSessionName: row.runtime_session_name,
		...identity ? { identity } : {},
		mode: row.mode === "oneshot" ? "oneshot" : "persistent",
		...runtimeOptions ? { runtimeOptions } : {},
		...row.cwd != null ? { cwd: row.cwd } : {},
		state: row.state === "running" || row.state === "error" ? row.state : "idle",
		lastActivityAt: row.last_activity_at,
		...row.last_error != null ? { lastError: row.last_error } : {}
	};
}
function bindAcpSessionMeta(params) {
	return {
		session_key: params.sessionKey,
		session_id: params.sessionId ?? null,
		backend: params.meta.backend,
		agent: params.meta.agent,
		runtime_session_name: params.meta.runtimeSessionName,
		identity_json: params.meta.identity ? JSON.stringify(params.meta.identity) : null,
		mode: params.meta.mode,
		runtime_options_json: params.meta.runtimeOptions ? JSON.stringify(params.meta.runtimeOptions) : null,
		cwd: params.meta.cwd ?? null,
		state: params.meta.state,
		last_activity_at: params.meta.lastActivityAt,
		last_error: params.meta.lastError ?? null,
		updated_at: params.updatedAt
	};
}
function selectAcpSessionRow(db, sessionKey) {
	return executeSqliteQueryTakeFirstSync(db, getAcpSessionKysely(db).selectFrom("acp_sessions").selectAll().where("session_key", "=", sessionKey));
}
function acpSessionRowMatchesEntry(row, entry) {
	return row.session_id == null || row.session_id === entry?.sessionId;
}
function readAcpSessionMeta(params) {
	const sessionKey = params.sessionKey.trim();
	if (!sessionKey) return;
	const storeEntry = readSessionEntryFromStore({
		sessionKey,
		cfg: params.cfg,
		env: params.env,
		clone: false
	});
	const row = selectAcpSessionRow(openOpenClawStateDatabase({
		env: params.env,
		path: params.databasePath
	}).db, storeEntry.storeSessionKey);
	if (!row || !acpSessionRowMatchesEntry(row, storeEntry.entry)) return;
	return rowToAcpSessionMeta(row);
}
function readAcpSessionMetaForEntry(params) {
	const sessionKey = params.sessionKey.trim();
	if (!sessionKey) return;
	const row = selectAcpSessionRow(openOpenClawStateDatabase({
		env: params.env,
		path: params.databasePath
	}).db, sessionKey);
	if (!row || !acpSessionRowMatchesEntry(row, params.entry)) return;
	return rowToAcpSessionMeta(row);
}
function selectAcpSessionRows(options = {}) {
	const database = openOpenClawStateDatabase(options);
	return executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).selectFrom("acp_sessions").selectAll().orderBy("last_activity_at", "desc").orderBy("session_key", "asc")).rows;
}
function writeAcpSessionMetaForMigration(params) {
	const sessionKey = params.sessionKey.trim();
	if (!sessionKey) return;
	const row = bindAcpSessionMeta({
		sessionKey,
		sessionId: params.sessionId,
		meta: params.meta,
		updatedAt: params.now?.() ?? Date.now()
	});
	runOpenClawStateWriteTransaction((database) => {
		upsertAcpSessionMetaRow(database.db, row);
	}, {
		env: params.env,
		path: params.databasePath
	});
}
function repairAcpSessionMetaKeyForMigration(params) {
	const sessionKey = params.sessionKey.trim();
	if (!sessionKey) return false;
	let repaired = false;
	runOpenClawStateWriteTransaction((database) => {
		const currentRow = selectAcpSessionRow(database.db, sessionKey);
		if (currentRow && acpSessionRowMatchesEntry(currentRow, params.entry)) return;
		const normalizedSessionKey = normalizeLowercaseStringOrEmpty(sessionKey);
		const candidateKeys = /* @__PURE__ */ new Set();
		candidateKeys.add(normalizedSessionKey);
		for (const candidate of params.candidateSessionKeys ?? []) {
			const trimmed = typeof candidate === "string" ? candidate.trim() : "";
			if (trimmed && trimmed !== sessionKey && normalizeLowercaseStringOrEmpty(trimmed) === normalizedSessionKey) candidateKeys.add(trimmed);
		}
		let row;
		for (const candidateKey of candidateKeys) {
			const candidateRow = selectAcpSessionRow(database.db, candidateKey);
			if (candidateRow && acpSessionRowMatchesEntry(candidateRow, params.entry)) {
				row = candidateRow;
				break;
			}
		}
		row ??= executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).selectFrom("acp_sessions").selectAll().where((eb) => eb.fn("lower", ["session_key"]), "=", normalizedSessionKey).orderBy("last_activity_at", "desc").orderBy("session_key", "asc")).rows.find((candidate) => candidate.session_key !== sessionKey && acpSessionRowMatchesEntry(candidate, params.entry));
		if (!row) return;
		upsertAcpSessionMetaRow(database.db, {
			...row,
			session_key: sessionKey,
			updated_at: params.now?.() ?? Date.now()
		});
		executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).deleteFrom("acp_sessions").where("session_key", "=", row.session_key));
		repaired = true;
	}, {
		env: params.env,
		path: params.databasePath
	});
	return repaired;
}
function upsertAcpSessionMetaRow(db, row) {
	executeSqliteQuerySync(db, getAcpSessionKysely(db).insertInto("acp_sessions").values(row).onConflict((conflict) => conflict.column("session_key").doUpdateSet({
		session_id: (eb) => eb.ref("excluded.session_id"),
		backend: (eb) => eb.ref("excluded.backend"),
		agent: (eb) => eb.ref("excluded.agent"),
		runtime_session_name: (eb) => eb.ref("excluded.runtime_session_name"),
		identity_json: (eb) => eb.ref("excluded.identity_json"),
		mode: (eb) => eb.ref("excluded.mode"),
		runtime_options_json: (eb) => eb.ref("excluded.runtime_options_json"),
		cwd: (eb) => eb.ref("excluded.cwd"),
		state: (eb) => eb.ref("excluded.state"),
		last_activity_at: (eb) => eb.ref("excluded.last_activity_at"),
		last_error: (eb) => eb.ref("excluded.last_error"),
		updated_at: (eb) => eb.ref("excluded.updated_at")
	})));
}
function readSessionEntryFromStore(params) {
	const { cfg, storePath } = resolveSessionStorePathForAcp({
		sessionKey: params.sessionKey,
		cfg: params.cfg,
		env: params.env
	});
	try {
		const store = loadSessionStore(storePath, params.clone === false ? { clone: false } : void 0);
		const storeSessionKey = resolveStoreSessionKey(store, params.sessionKey);
		return {
			cfg,
			storePath,
			storeSessionKey,
			entry: store[storeSessionKey]
		};
	} catch {
		return {
			cfg,
			storePath,
			storeSessionKey: normalizeLowercaseStringOrEmpty(params.sessionKey),
			storeReadFailed: true
		};
	}
}
function readAcpSessionEntry(params) {
	const sessionKey = params.sessionKey.trim();
	if (!sessionKey) return null;
	const storeEntry = readSessionEntryFromStore(params);
	const row = selectAcpSessionRow(openOpenClawStateDatabase({
		env: params.env,
		path: params.databasePath
	}).db, storeEntry.storeSessionKey);
	const acp = row && acpSessionRowMatchesEntry(row, storeEntry.entry) ? rowToAcpSessionMeta(row) : void 0;
	return {
		cfg: storeEntry.cfg,
		storePath: storeEntry.storePath,
		sessionKey,
		storeSessionKey: storeEntry.storeSessionKey,
		entry: storeEntry.entry,
		acp,
		storeReadFailed: storeEntry.storeReadFailed
	};
}
async function listAcpSessionEntries(params) {
	const cfg = params.cfg ?? getRuntimeConfig();
	const rows = selectAcpSessionRows({
		env: params.env,
		path: params.databasePath
	});
	const entries = [];
	for (const row of rows) {
		const sessionKey = row.session_key;
		const { storePath } = resolveSessionStorePathForAcp({
			sessionKey,
			cfg,
			env: params.env
		});
		let store;
		try {
			store = loadSessionStore(storePath, params.clone === false ? { clone: false } : void 0);
		} catch {
			continue;
		}
		const storeSessionKey = resolveStoreSessionKey(store, sessionKey);
		const entry = store[storeSessionKey];
		if (!entry || !acpSessionRowMatchesEntry(row, entry)) continue;
		entries.push({
			cfg,
			storePath,
			sessionKey,
			storeSessionKey,
			entry,
			acp: rowToAcpSessionMeta(row)
		});
	}
	return entries;
}
function mergeAcpForReturn(entry, acp) {
	return mergeSessionEntry(entry, { acp });
}
function sessionStoreUpdateOptions(params) {
	return {
		activeSessionKey: normalizeLowercaseStringOrEmpty(params.sessionKey),
		...params.skipMaintenance === true ? { skipMaintenance: true } : {},
		...params.takeCacheOwnership === true ? { takeCacheOwnership: true } : {}
	};
}
async function upsertAcpSessionMeta(params) {
	const sessionKey = params.sessionKey.trim();
	if (!sessionKey) return null;
	const storeEntry = readSessionEntryFromStore({
		sessionKey,
		cfg: params.cfg,
		env: params.env,
		clone: false
	});
	const { entry } = storeEntry;
	const storageSessionKey = storeEntry.storeSessionKey;
	let current;
	let nextMeta;
	let preparedEntry;
	const updatedAt = params.now?.() ?? Date.now();
	runOpenClawStateWriteTransaction((database) => {
		const currentRow = selectAcpSessionRow(database.db, storageSessionKey);
		current = currentRow && acpSessionRowMatchesEntry(currentRow, entry) ? rowToAcpSessionMeta(currentRow) : void 0;
		preparedEntry = mergeSessionEntry(entry, { updatedAt });
		nextMeta = params.mutate(current, current ? mergeAcpForReturn(preparedEntry, current) : entry);
		if (nextMeta === null) {
			executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).deleteFrom("acp_sessions").where("session_key", "=", storageSessionKey));
			return;
		}
		if (nextMeta !== void 0) upsertAcpSessionMetaRow(database.db, bindAcpSessionMeta({
			sessionKey: storageSessionKey,
			sessionId: preparedEntry.sessionId,
			meta: nextMeta,
			updatedAt
		}));
	}, {
		env: params.env,
		path: params.databasePath
	});
	if (nextMeta === void 0) return current ? mergeAcpForReturn(entry, current) : entry ?? null;
	if (nextMeta === null) {
		if (!entry) return null;
		const { updateSessionStore } = await loadSessionStoreRuntime();
		return await updateSessionStore(storeEntry.storePath, (store) => {
			const storeSessionKey = resolveStoreSessionKey(store, storageSessionKey);
			const next = { ...store[storeSessionKey] ?? entry };
			delete next.acp;
			store[storeSessionKey] = next;
			return next;
		}, sessionStoreUpdateOptions({
			...params,
			sessionKey: storageSessionKey
		}));
	}
	const { updateSessionStore } = await loadSessionStoreRuntime();
	return mergeAcpForReturn(await updateSessionStore(storeEntry.storePath, (store) => {
		const storeSessionKey = resolveStoreSessionKey(store, storageSessionKey);
		const next = mergeSessionEntry(store[storeSessionKey], {
			sessionId: preparedEntry?.sessionId,
			updatedAt
		});
		delete next.acp;
		store[storeSessionKey] = next;
		return next;
	}, sessionStoreUpdateOptions({
		...params,
		sessionKey: storageSessionKey
	})), nextMeta);
}
//#endregion
export { repairAcpSessionMetaKeyForMigration as a, readAcpSessionMetaForEntry as i, readAcpSessionEntry as n, upsertAcpSessionMeta as o, readAcpSessionMeta as r, writeAcpSessionMetaForMigration as s, listAcpSessionEntries as t };
