import { r as formatErrorMessage } from "./errors-Z0lU1pH3.js";
import { h as clearNodeSqliteKyselyCacheForDatabase, r as resolveImmutableSqliteFileUri, t as openNodeSqliteDatabase } from "./node-sqlite-GN5_mcoB.js";
import { a as OPENCLAW_DATABASE_SCHEMA_DOCS_URL, o as OPENCLAW_SQLITE_BUSY_TIMEOUT_MS } from "./openclaw-state-db-contract-BYGU51qd.js";
import { a as readSqliteUserVersion, r as describeRunningOpenClawBuild, t as SqliteSchemaVersionError } from "./sqlite-user-version-CdJndp9E.js";
import { C as isOpenClawStateFirstUseSchemaIssue, G as resolveOpenClawRegisteredAgentDatabasePath, I as collectSqliteSchemaIssues, J as resolveOpenClawStateSqlitePath, S as getOpenClawStateRuntimeSchema, T as OPENCLAW_STATE_SCHEMA_SQL, b as OPENCLAW_STATE_MAINTENANCE_SCHEMA_COMPATIBILITY, d as assertOpenClawStateDatabaseForMaintenance, f as assertOpenClawStateDatabaseOwner, w as isOpenClawStateStartupRepairableSchemaIssue, x as STATE_PERSISTENT_SCHEMA_COMPATIBILITY } from "./openclaw-state-db-readonly-YhcJ4nbp.js";
import { Jt as getNodeSqliteKysely, Kt as executeSqliteQuerySync, g as inspectOpenClawStateOwnershipFromDatabase } from "./openclaw-state-db-Cb4e1jdd.js";
import { t as assertSqliteIntegrity } from "./sqlite-integrity-DUFG60T3.js";
import { t as assertOpenClawAgentDatabaseForMaintenance } from "./openclaw-agent-db-maintenance-C4D8xHaF.js";
import "./openclaw-agent-db-contract-CI-wBXqB.js";
import { t as discoverAgentDatabaseMigrationTargets } from "./state-migrations.media-persistence-targets-CXLKvWg2.js";
import { existsSync, realpathSync } from "node:fs";
import path from "node:path";
//#region src/state/openclaw-database-preflight.ts
function formatDoctorIncompatibleDatabase(database) {
	const agent = database.agentId ? ` for agent ${database.agentId}` : "";
	const writer = database.writerAppVersion ? `; writer build ${database.writerAppVersion}` : "";
	return `${database.kind} database${agent} ${database.path} uses schema ${database.foundVersion}; this build supports ${database.supportedVersion}${writer}.`;
}
/** Fatal refusal when persisted schemas were written by a newer build. */
var OpenClawDatabaseSchemaPreflightError = class extends SqliteSchemaVersionError {
	constructor(incompatibleDatabases, options = {}) {
		const operation = options.operation ?? "gateway-startup";
		const prefix = operation === "doctor" ? "Doctor refused to continue" : operation === "gateway-restart" ? "Gateway refused restart" : "Gateway refused startup";
		const doctorGuidance = operation === "doctor" ? ` ${incompatibleDatabases.map(formatDoctorIncompatibleDatabase).join(" ")} Run Doctor with the OpenClaw install that wrote this state (typically the active Gateway install), or another build that supports these schemas.` : "";
		super(`${prefix} because ${incompatibleDatabases.length} OpenClaw database schema(s) are newer than this build. Refused by ${describeRunningOpenClawBuild()}.${doctorGuidance} See ${OPENCLAW_DATABASE_SCHEMA_DOCS_URL}.`);
		this.incompatibleDatabases = incompatibleDatabases;
		this.name = "OpenClawDatabaseSchemaPreflightError";
	}
};
/** Verify persisted runtime schemas before certifying repair or accepting restart. */
function assertOpenClawDatabasesReady(options) {
	const schemas = preflightOpenClawDatabaseSchemas({
		env: options.env,
		supportedVersions: {
			state: 15,
			agent: 19
		},
		verifyCurrentSchemaShape: true,
		...options.operation === "doctor" ? { configuredAgentDatabaseTargets: options.configuredAgentDatabaseTargets } : {}
	});
	if (schemas.incompatible.length > 0) throw new OpenClawDatabaseSchemaPreflightError(schemas.incompatible, { operation: options.operation });
	if (schemas.indeterminate.length === 0) return;
	const shown = schemas.indeterminate.slice(0, 3).map((database) => `${database.kind} ${database.path}: ${database.reason}`);
	const omitted = schemas.indeterminate.length - shown.length;
	const action = options.operation === "doctor" ? "Doctor could not complete repair" : "Gateway refused restart";
	throw new Error(`${action} because persisted database readiness could not be verified: ${shown.join("; ")}${omitted > 0 ? `; +${omitted} more` : ""}. Stop the Gateway and other OpenClaw processes, run openclaw doctor --fix, then retry.`);
}
function readWriterAppVersion(database) {
	try {
		const row = database.prepare("SELECT app_version FROM schema_meta WHERE meta_key = 'primary' LIMIT 1").get();
		return typeof row?.app_version === "string" && row.app_version.length > 0 ? row.app_version : void 0;
	} catch {
		return;
	}
}
function readRegisteredAgentDatabases(database, registryPath) {
	if (!database.prepare("SELECT 1 AS ok FROM sqlite_master WHERE type = 'table' AND name = 'agent_databases'").get()) return [];
	return executeSqliteQuerySync(database, getNodeSqliteKysely(database).selectFrom("agent_databases").select(["agent_id", "path"])).rows.flatMap((row) => typeof row.agent_id === "string" && typeof row.path === "string" ? [{
		agentId: row.agent_id,
		path: resolveOpenClawRegisteredAgentDatabasePath(registryPath, row.path)
	}] : []);
}
function deduplicateSchemaIssues(issues) {
	return [...new Map(issues.map((issue) => [`${issue.code}\0${issue.objectName}`, issue])).values()];
}
/** Compare one explicit SQLite file with this release's canonical shared-state schema. */
async function preflightOpenClawStateDatabasePath(databasePath) {
	const resolvedPath = path.resolve(databasePath);
	const base = {
		schema: "openclaw.state-schema-preflight.v1",
		databasePath: resolvedPath,
		targetVersion: 15
	};
	let database;
	let foundVersion = null;
	let ownership = null;
	const result = (status, details = {}) => ({
		...base,
		foundVersion,
		ownership,
		issues: details.issues ?? [],
		status,
		requiresWrite: details.requiresWrite ?? false,
		...details.reason ? { reason: details.reason } : {}
	});
	try {
		const inspectionPath = realpathSync.native(resolvedPath);
		const sidecars = [
			"-wal",
			"-shm",
			"-journal"
		].filter((suffix) => existsSync(`${inspectionPath}${suffix}`));
		if (sidecars.length > 0) throw new Error(`SQLite preflight requires a consolidated snapshot with no sidecars; found ${sidecars.join(", ")}. Create a WAL-aware online backup and preflight the resulting standalone file.`);
		database = openNodeSqliteDatabase(resolveImmutableSqliteFileUri(inspectionPath), { readOnly: true });
		database.exec(`PRAGMA busy_timeout = ${OPENCLAW_SQLITE_BUSY_TIMEOUT_MS}; PRAGMA query_only = ON; PRAGMA trusted_schema = OFF;`);
		assertSqliteIntegrity(database, resolvedPath);
		foundVersion = readSqliteUserVersion(database);
		if (!Number.isSafeInteger(foundVersion) || foundVersion < 0) throw new Error(`OpenClaw state database ${resolvedPath} has invalid schema version metadata.`);
		if (foundVersion > 15) {
			try {
				ownership = inspectOpenClawStateOwnershipFromDatabase(database, resolvedPath);
			} catch {}
			return result("incompatible");
		}
		ownership = inspectOpenClawStateOwnershipFromDatabase(database, resolvedPath);
		if (foundVersion < 15) return result("migration-required", { requiresWrite: true });
		assertOpenClawStateDatabaseOwner(database, { pathname: resolvedPath });
		const metadata = database.prepare("SELECT schema_version FROM schema_meta WHERE meta_key = 'primary' LIMIT 1").get();
		if (metadata?.schema_version !== foundVersion) throw new Error(`OpenClaw state database ${resolvedPath} metadata schema version ${typeof metadata?.schema_version === "number" ? metadata.schema_version : "invalid"} does not match ${foundVersion}.`);
		const maintenanceIssues = collectSqliteSchemaIssues(database, OPENCLAW_STATE_SCHEMA_SQL, OPENCLAW_STATE_MAINTENANCE_SCHEMA_COMPATIBILITY);
		const blockingIssues = maintenanceIssues.filter((issue) => !isOpenClawStateStartupRepairableSchemaIssue(issue) && !isOpenClawStateFirstUseSchemaIssue(issue));
		if (blockingIssues.length > 0) return result("incompatible", { issues: deduplicateSchemaIssues(blockingIssues) });
		const projectedRuntimeIssues = collectSqliteSchemaIssues(database, getOpenClawStateRuntimeSchema({ includeVersionLazyAdditiveTables: false }), STATE_PERSISTENT_SCHEMA_COMPATIBILITY);
		const projectedRuntimeBlockingIssues = projectedRuntimeIssues.filter((issue) => !isOpenClawStateStartupRepairableSchemaIssue(issue) && !isOpenClawStateFirstUseSchemaIssue(issue));
		if (projectedRuntimeBlockingIssues.length > 0) return result("incompatible", { issues: deduplicateSchemaIssues(projectedRuntimeBlockingIssues) });
		const startupRepairableIssues = deduplicateSchemaIssues([...maintenanceIssues.filter(isOpenClawStateStartupRepairableSchemaIssue), ...projectedRuntimeIssues.filter(isOpenClawStateStartupRepairableSchemaIssue)]);
		return result(startupRepairableIssues.length > 0 ? "startup-repairable" : "exact", {
			issues: startupRepairableIssues,
			requiresWrite: startupRepairableIssues.length > 0
		});
	} catch (error) {
		return result("indeterminate", { reason: formatErrorMessage(error) });
	} finally {
		database?.close();
	}
}
/** Read schema headers and optionally verify current schema shape without repairing it. */
function preflightOpenClawDatabaseSchemas(options) {
	const result = {
		incompatible: [],
		indeterminate: []
	};
	const statePath = path.resolve(resolveOpenClawStateSqlitePath(options.env));
	let registeredDatabases = [];
	let stateDatabase;
	try {
		if (existsSync(statePath)) {
			stateDatabase = openNodeSqliteDatabase(statePath, { readOnly: true });
			stateDatabase.exec(`PRAGMA busy_timeout = ${OPENCLAW_SQLITE_BUSY_TIMEOUT_MS};`);
			const stateVersion = readSqliteUserVersion(stateDatabase);
			if (stateVersion > options.supportedVersions.state) {
				const writerAppVersion = readWriterAppVersion(stateDatabase);
				result.incompatible.push({
					kind: "state",
					path: statePath,
					foundVersion: stateVersion,
					supportedVersion: options.supportedVersions.state,
					...writerAppVersion ? { writerAppVersion } : {}
				});
			}
			if (options.verifyCurrentSchemaShape === true && stateVersion === 15) try {
				assertOpenClawStateDatabaseForMaintenance(stateDatabase, { pathname: statePath });
			} catch (error) {
				result.indeterminate.push({
					kind: "state",
					path: statePath,
					reason: formatErrorMessage(error)
				});
			}
			try {
				registeredDatabases = readRegisteredAgentDatabases(stateDatabase, statePath);
			} catch (error) {
				result.indeterminate.push({
					kind: "state",
					path: statePath,
					reason: `agent database registry query failed: ${formatErrorMessage(error)}`
				});
				return result;
			}
		}
	} catch (error) {
		result.indeterminate.push({
			kind: "state",
			path: statePath,
			reason: formatErrorMessage(error)
		});
		return result;
	} finally {
		if (stateDatabase) {
			clearNodeSqliteKyselyCacheForDatabase(stateDatabase);
			stateDatabase.close();
		}
	}
	let agentTargets = registeredDatabases;
	if (options.configuredAgentDatabaseTargets !== void 0) {
		const discovery = discoverAgentDatabaseMigrationTargets({
			env: options.env,
			configuredAgentDatabaseTargets: options.configuredAgentDatabaseTargets,
			registeredAgentDatabases: registeredDatabases
		});
		agentTargets = discovery.targets;
		for (const failure of discovery.failures) result.indeterminate.push({
			kind: "agent",
			...failure
		});
	}
	for (const row of agentTargets) {
		const agentPath = row.path;
		if (!existsSync(agentPath)) continue;
		let agentDatabase;
		try {
			agentDatabase = openNodeSqliteDatabase(agentPath, { readOnly: true });
			agentDatabase.exec(`PRAGMA busy_timeout = ${OPENCLAW_SQLITE_BUSY_TIMEOUT_MS};`);
			const agentVersion = readSqliteUserVersion(agentDatabase);
			if (agentVersion <= options.supportedVersions.agent) {
				if (options.verifyCurrentSchemaShape === true) assertOpenClawAgentDatabaseForMaintenance(agentDatabase, {
					agentId: row.agentId,
					pathname: agentPath
				});
				continue;
			}
			const writerAppVersion = readWriterAppVersion(agentDatabase);
			result.incompatible.push({
				kind: "agent",
				path: agentPath,
				agentId: row.agentId,
				foundVersion: agentVersion,
				supportedVersion: options.supportedVersions.agent,
				...writerAppVersion ? { writerAppVersion } : {}
			});
		} catch (error) {
			result.indeterminate.push({
				kind: "agent",
				path: agentPath,
				reason: formatErrorMessage(error)
			});
		} finally {
			agentDatabase?.close();
		}
	}
	return result;
}
//#endregion
export { preflightOpenClawStateDatabasePath as i, assertOpenClawDatabasesReady as n, preflightOpenClawDatabaseSchemas as r, OpenClawDatabaseSchemaPreflightError as t };
