import { a as getChildLogger } from "./logger-Dv_84D2e.js";
import { i as openOpenClawStateDatabase } from "./openclaw-state-db-Cb4e1jdd.js";
import { d as loadSessionEntry } from "./session-accessor.sqlite-entry-CoLie3L_.js";
import { p as runExclusiveSessionLifecycleMutation } from "./session-lifecycle-admission-DPRIiIqj.js";
import { d as collectActiveSessionWorkAdmissionKeys } from "./types-ukra4D97.js";
import "./session-accessor-Dwfzn6Ej.js";
import { d as runGit } from "./git-DsbcE8dM.js";
import { n as prepareSessionWorkerPlacementMutationCheck } from "./session-placement-lifecycle-7DQHpaT9.js";
import { f as getRegistryWorktree } from "./registry-VkURAuz0.js";
import { c as classifyWorktreeRemovalError, l as managedWorktrees, n as ManagedWorktreeService } from "./service-DDsZDT_O.js";
import { t as createWorkerSessionPlacementStore } from "./placement-store-BXCAxO43.js";
import { existsSync } from "node:fs";
import { isDeepStrictEqual } from "node:util";
//#region src/sessions/session-worktree-lifecycle.ts
var SessionWorktreeLifecycleError = class extends Error {
	constructor(message, reason) {
		super(message);
		this.reason = reason;
	}
};
function serviceFor(env) {
	return env ? new ManagedWorktreeService({ env }) : managedWorktrees;
}
function belongsToSession(record, sessionKey) {
	return record.ownerKind === "session" && record.ownerId === sessionKey;
}
/** The session lifecycle fence remains held until this exact bound checkout finishes cleanup. */
async function removeSessionWorktree(params) {
	if (!params.id) return;
	const env = params.env ?? process.env;
	const record = getRegistryWorktree(env, params.id);
	if (!record || record.removedAt !== void 0) return;
	const preserved = (current, reason) => ({
		id: current.id,
		branch: current.branch,
		path: current.path,
		reason
	});
	const assertCurrent = () => {
		params.commitGuard?.();
		const current = getRegistryWorktree(env, record.id);
		if (current && !belongsToSession(current, params.sessionKey)) throw new SessionWorktreeLifecycleError("Session worktree ownership changed; retry cleanup.", "owner-mismatch");
	};
	try {
		assertCurrent();
		await serviceFor(params.env).remove({
			id: record.id,
			reason: params.reason,
			commitGuard: assertCurrent
		});
	} catch (error) {
		params.commitGuard?.();
		const current = getRegistryWorktree(env, record.id);
		if (current && current.removedAt === void 0) {
			const reason = error instanceof SessionWorktreeLifecycleError && error.reason === "owner-mismatch" ? error.reason : classifyWorktreeRemovalError(error);
			getChildLogger({ subsystem: "session-worktree" }).warn("Session worktree preserved", {
				worktreeId: record.id,
				sessionKey: params.sessionKey,
				reason
			});
			return preserved(current, reason);
		}
	}
}
/** Snapshot removal and restore never change conversation or session metadata themselves. */
async function synchronizeSessionWorktreeArchive(params) {
	const { entry, scope } = params;
	const id = entry.worktree?.id;
	if (!id) return;
	const assertCurrent = () => {
		params.commitGuard?.();
		const current = loadSessionEntry(scope);
		if (current?.sessionId !== entry.sessionId || current?.lifecycleRevision !== entry.lifecycleRevision || current?.archivedAt !== entry.archivedAt || !isDeepStrictEqual(current?.worktree, entry.worktree)) throw new SessionWorktreeLifecycleError("Session changed while preparing its worktree; retry the request.", "session-changed");
		const record = getRegistryWorktree(scope.env ?? process.env, id);
		if (record && !belongsToSession(record, scope.sessionKey)) throw new SessionWorktreeLifecycleError("Session worktree has a different owner; restore the correct binding before retrying.", "owner-mismatch");
	};
	assertCurrent();
	if (params.archived) {
		const preserved = await removeSessionWorktree({
			id,
			sessionKey: scope.sessionKey,
			reason: "session-archive",
			env: scope.env,
			commitGuard: assertCurrent
		});
		if (preserved) throw new SessionWorktreeLifecycleError(`Session worktree was preserved (${preserved.reason}); resolve the cleanup condition and retry the archive.`, preserved.reason);
	} else {
		const record = getRegistryWorktree(scope.env ?? process.env, id);
		if (!record || record.removedAt !== void 0 && !record.snapshotRef) throw new SessionWorktreeLifecycleError("Session worktree snapshot is missing or expired. The conversation is preserved; start a new worktree task from the source repository to continue.", "restore-failed");
		if (record.removedAt !== void 0) try {
			await serviceFor(scope.env).restore({
				id,
				commitGuard: assertCurrent
			});
		} catch (error) {
			assertCurrent();
			if (error instanceof SessionWorktreeLifecycleError) throw error;
			if (!existsSync(record.repoRoot)) throw new SessionWorktreeLifecycleError("Session worktree source repository is missing. Restore the original repository and its snapshot refs, then retry; otherwise start a new worktree task. The conversation is preserved.", "restore-failed");
			const snapshot = await runGit(record.repoRoot, [
				"rev-parse",
				"--verify",
				"--end-of-options",
				`${record.snapshotRef}^{commit}`
			]);
			assertCurrent();
			if (snapshot.code !== 0) throw new SessionWorktreeLifecycleError("Session worktree snapshot is missing or unavailable. Restore the original repository snapshot, or start a new worktree task. The conversation is preserved.", "restore-failed");
			throw new SessionWorktreeLifecycleError("Session worktree could not be restored. Free disk space or an unused worktree slot, check the source repository, then retry. The conversation and snapshot are preserved.", "restore-failed");
		}
	}
	assertCurrent();
}
/** Maintenance commits archive metadata first; its released writer lane must never retain Git work. */
async function cleanUpAutomaticallyArchivedWorktrees(scope, targets) {
	for (const target of targets) try {
		await runExclusiveSessionLifecycleMutation({
			scope: target.storePath,
			identities: [target.sessionKey, target.entry.sessionId],
			run: async () => {
				const assertPlacementCurrent = prepareSessionWorkerPlacementMutationCheck({
					context: { workerSessionPlacementService: createWorkerSessionPlacementStore({ database: openOpenClawStateDatabase({ env: scope.env }) }) },
					sessionId: target.entry.sessionId
				});
				await synchronizeSessionWorktreeArchive({
					archived: true,
					entry: target.entry,
					scope: {
						...scope,
						sessionKey: target.sessionKey,
						storePath: target.storePath
					},
					commitGuard: () => {
						assertPlacementCurrent();
						if (collectActiveSessionWorkAdmissionKeys({
							storePath: target.storePath,
							store: { [target.sessionKey]: target.entry }
						})?.has(target.sessionKey)) throw new SessionWorktreeLifecycleError("Session worktree is still active; cleanup is deferred.", "busy");
					}
				});
			}
		});
	} catch (error) {
		getChildLogger({ subsystem: "session-worktree" }).warn("Automatic archive worktree cleanup deferred", {
			worktreeId: target.entry.worktree?.id,
			reason: error instanceof SessionWorktreeLifecycleError ? error.reason : "cleanup-failed"
		});
	}
}
//#endregion
export { synchronizeSessionWorktreeArchive as i, cleanUpAutomaticallyArchivedWorktrees as n, removeSessionWorktree as r, SessionWorktreeLifecycleError as t };
