import { T as TERMINAL_TASK_STATUSES, b as getLogger } from './paths-2M_UxD_H.js';
import { w as withWriteRetry } from './dual-scope-db-ATrwq0LH.js';
import { f as tasksEvidenceAcBindings, g as tasksTaskAcceptanceCriteriaHistory, h as tasksTaskAcceptanceCriteria, i as tasksTaskRelations, c as tasksTasks, d as tasksTaskDependencies, e as tasksTaskLabels, t as tasksSessions, b as auditLog, s as schemaMeta } from './conduit-Ca_OFWR_.js';
import { a as getDb, c as closeDb, b as getNativeTasksDb } from './sqlite-Dsbe0wec.js';
import { d as rowToTask, t as taskToRow, f as resolveCurrentSession, h as rowToSession, i as archivedTaskToRow } from './session-store-OwQWMSf_.js';
import { c as cleanupBrainRefsOnSessionDelete } from './cross-db-cleanup-C_FLyd-6.js';
import { s as sql } from './utils-BvHPJOVk.js';
import { i as inArray, e as eq, a as and, n as ne, b as notInArray, c as isNull, o as or, l as like } from './logger-ChCwlV5k.js';

//#region ../core/dist/store/db-helpers.js
/**
* Shared database helper functions for SQLite store modules.
*
* Consolidates upsert and dependency patterns used across
* sqlite-data-accessor.ts, tasks-sqlite.ts, and session-store.ts.
*
* @epic T4454
*/
var log = getLogger("db-helpers");
/**
* Upsert a single task row into the tasks table.
* Handles both active task upsert and archived task upsert via optional archiveFields.
*
* When `allowOrphanParent` is true (bulk/migration mode, T5034): silently nulls out
* parentId if the referenced parent does not exist, preventing FK violations.
* When false (normal single-task writes, default): logs a warning but still proceeds
* so that FK enforcement at the DB level provides the final safety net.
*
* Callers that perform bulk imports or archive restoration should pass
* `allowOrphanParent: true` to enable the lenient behavior.
*/
async function upsertTask(db, row, archiveFields, allowOrphanParent = false) {
	if (row.parentId) {
		if ((await db.select({ id: tasksTasks.id }).from(tasksTasks).where(eq(tasksTasks.id, row.parentId)).limit(1).all()).length === 0) if (allowOrphanParent) row = {
			...row,
			parentId: null
		};
		else log.warn({
			taskId: row.id,
			parentId: row.parentId
		}, "upsertTask: parentId references a non-existent task — parent relationship may be lost");
	}
	const values = archiveFields ? {
		...row,
		...archiveFields,
		status: "archived"
	} : row;
	const set = {
		title: row.title,
		description: row.description,
		status: archiveFields ? "archived" : row.status,
		priority: row.priority,
		type: row.type,
		kind: row.kind,
		scope: row.scope,
		severity: row.severity,
		parentId: row.parentId,
		phase: row.phase,
		size: row.size,
		position: row.position,
		positionVersion: row.positionVersion,
		labelsJson: row.labelsJson,
		notesJson: row.notesJson,
		acceptanceJson: row.acceptanceJson,
		filesJson: row.filesJson,
		origin: row.origin,
		blockedBy: row.blockedBy,
		epicLifecycle: row.epicLifecycle,
		noAutoComplete: row.noAutoComplete,
		createdAt: row.createdAt,
		updatedAt: row.updatedAt,
		completedAt: row.completedAt,
		cancelledAt: row.cancelledAt,
		cancellationReason: row.cancellationReason,
		verificationJson: row.verificationJson,
		createdBy: row.createdBy,
		modifiedBy: row.modifiedBy,
		sessionId: row.sessionId,
		pipelineStage: row.pipelineStage ?? null,
		assignee: row.assignee ?? null,
		archivedAt: archiveFields?.archivedAt ?? null,
		archiveReason: archiveFields?.archiveReason ?? null,
		cycleTimeDays: archiveFields?.cycleTimeDays ?? null
	};
	await db.insert(tasksTasks).values(values).onConflictDoUpdate({
		target: tasksTasks.id,
		set
	}).run();
	await updateTaskLabels(db, row.id, parseLabels(row.labelsJson));
}
/**
* Parse a `labels_json` text column into a deduplicated string-array.
*
* Invalid / non-array JSON yields an empty list — the junction is then emptied
* for that task, matching the "no labels" state.
*
* @param labelsJson - The serialized JSON array from `tasks.labels_json`.
* @returns Deduplicated, non-empty label strings.
*/
function parseLabels(labelsJson) {
	if (!labelsJson) return [];
	try {
		const parsed = JSON.parse(labelsJson);
		if (!Array.isArray(parsed)) return [];
		const seen = /* @__PURE__ */ new Set();
		for (const l of parsed) if (typeof l === "string" && l.length > 0) seen.add(l);
		return [...seen];
	} catch {
		return [];
	}
}
/**
* Replace the {@link schema.taskLabels} junction rows for one task so they
* exactly mirror its label set (T11356).
*
* Delete-then-insert keeps the junction authoritative without read-modify-write
* races: the prior label set is dropped and the supplied set re-inserted. Called
* from {@link upsertTask} and from raw-SQL proposal inserters that bypass it.
*
* @param db - Drizzle tasks.db handle.
* @param taskId - The owning task id.
* @param labels - The full label set the junction should reflect.
*/
async function updateTaskLabels(db, taskId, labels) {
	await db.delete(tasksTaskLabels).where(eq(tasksTaskLabels.taskId, taskId)).run();
	if (labels.length === 0) return;
	await db.insert(tasksTaskLabels).values(labels.map((label) => ({
		taskId,
		label
	}))).onConflictDoNothing().run();
}
/**
* Upsert a single session row into the sessions table.
*/
async function upsertSession(db, session) {
	const sessionName = session.name || `session-${session.id}`;
	const values = {
		id: session.id,
		name: sessionName,
		status: session.status,
		scopeJson: JSON.stringify(session.scope ?? { type: "global" }),
		currentTask: session.taskWork?.taskId ?? null,
		taskStartedAt: session.taskWork?.setAt ?? null,
		agent: session.agent ?? null,
		notesJson: session.notes ? JSON.stringify(session.notes) : "[]",
		tasksCompletedJson: session.tasksCompleted ? JSON.stringify(session.tasksCompleted) : "[]",
		tasksCreatedJson: session.tasksCreated ? JSON.stringify(session.tasksCreated) : "[]",
		handoffJson: session.handoffJson ?? null,
		startedAt: session.startedAt,
		endedAt: session.endedAt ?? null,
		previousSessionId: session.previousSessionId ?? null,
		nextSessionId: session.nextSessionId ?? null,
		parentSessionId: session.parentSessionId ?? null,
		agentIdentifier: session.agentIdentifier ?? null,
		handoffConsumedAt: session.handoffConsumedAt ?? null,
		handoffConsumedBy: session.handoffConsumedBy ?? null,
		debriefJson: session.debriefJson ?? null,
		statsJson: session.stats ? JSON.stringify(session.stats) : null,
		resumeCount: session.resumeCount ?? null,
		gradeMode: session.gradeMode ? true : null,
		agentHandle: session.agentHandle ?? null,
		scopeKind: session.scopeKind ?? null,
		scopeId: session.scopeId ?? null,
		lastActivity: session.lastActivity ?? null
	};
	const { id: _id, ...setFields } = values;
	await db.insert(tasksSessions).values(values).onConflictDoUpdate({
		target: tasksSessions.id,
		set: setFields
	}).run();
}
/**
* Update dependencies for a task: delete existing, then re-insert.
* Optionally filters by a set of valid IDs.
*/
async function updateDependencies(db, taskId, depends, validIds) {
	await db.delete(tasksTaskDependencies).where(eq(tasksTaskDependencies.taskId, taskId)).run();
	for (const depId of depends) await db.insert(tasksTaskDependencies).values({
		taskId,
		dependsOn: depId
	}).onConflictDoNothing().run();
}
/**
* Batch-update dependencies for multiple tasks in two bulk SQL operations.
* Replaces per-task updateDependencies() loops with:
* 1. Single DELETE for all task IDs
* 2. Single INSERT for all dependency rows
*
* Callers are responsible for wrapping this in a transaction if needed.
*/
async function batchUpdateDependencies(db, tasks, validIds) {
	if (tasks.length === 0) return;
	const allTaskIds = tasks.map((t) => t.taskId);
	await db.delete(tasksTaskDependencies).where(inArray(tasksTaskDependencies.taskId, allTaskIds)).run();
	const allDepRows = [];
	for (const { taskId, deps } of tasks) for (const depId of deps) if (!validIds || validIds.has(depId)) allDepRows.push({
		taskId,
		dependsOn: depId
	});
	if (allDepRows.length > 0) await db.insert(tasksTaskDependencies).values(allDepRows).onConflictDoNothing().run();
}
/**
* Batch-load dependencies for a list of tasks and apply them in-place.
* Uses inArray for efficient querying. Optionally filters by a set of valid IDs.
*/
async function loadDependenciesForTasks(db, tasks, validationIds) {
	if (tasks.length === 0) return;
	const taskIds = tasks.map((t) => t.id);
	const taskIdSet = validationIds ?? new Set(taskIds);
	const allDeps = await db.select().from(tasksTaskDependencies).where(inArray(tasksTaskDependencies.taskId, taskIds)).all();
	const depMap = /* @__PURE__ */ new Map();
	for (const dep of allDeps) if (taskIdSet.has(dep.dependsOn)) {
		let arr = depMap.get(dep.taskId);
		if (!arr) {
			arr = [];
			depMap.set(dep.taskId, arr);
		}
		arr.push(dep.dependsOn);
	}
	for (const task of tasks) {
		const deps = depMap.get(task.id);
		if (deps && deps.length > 0) task.depends = deps;
	}
}
/**
* Batch-load relations for a list of tasks and apply them in-place.
* Mirrors loadDependenciesForTasks pattern for task_relations table (T5168).
*/
async function loadRelationsForTasks(db, tasks) {
	if (tasks.length === 0) return;
	const taskIds = tasks.map((t) => t.id);
	const allRels = await db.select().from(tasksTaskRelations).where(inArray(tasksTaskRelations.taskId, taskIds)).all();
	const relMap = /* @__PURE__ */ new Map();
	for (const rel of allRels) {
		let arr = relMap.get(rel.taskId);
		if (!arr) {
			arr = [];
			relMap.set(rel.taskId, arr);
		}
		arr.push({
			taskId: rel.relatedTo,
			type: rel.relationType,
			reason: rel.reason ?? void 0
		});
	}
	for (const task of tasks) {
		const relations = relMap.get(task.id);
		task.relates = relations && relations.length > 0 ? relations : [];
	}
}
//#endregion
//#region ../core/dist/store/sqlite-data-accessor.js
/**
* SQLite-based implementation of the DataAccessor interface.
*
* Materializes TaskFile/ArchiveFile/SessionsFile from SQLite tables,
* allowing core modules to continue using whole-file data structures
* while storage is backed by the relational database.
*
* Uses existing sqlite.ts engine (node:sqlite / drizzle-orm) and
* tasks-sqlite.ts / session-store.ts for row-level operations.
*
* @epic T4454
*/
/**
* Per-process monotonic counter for unique nested SAVEPOINT names (T9814).
* Only used when DataAccessor.transaction() is called from within an
* already-open outer transaction (depth > 0). Outer transactions use
* BEGIN IMMEDIATE/COMMIT/ROLLBACK (depth = 0).
*/
var _txSavepointCounter = 0;
/**
* Generate a unique audit log entry ID.
* @task T4837
*/
function generateAuditLogId() {
	return `log-${Math.floor(Date.now() / 1e3)}-${Math.random().toString(36).slice(2, 8)}`;
}
/**
* Run `fn` inside an outermost `BEGIN IMMEDIATE` transaction, wrapped
* in {@link withWriteRetry} so SQLITE_BUSY contention is absorbed at the
* transaction boundary rather than mid-statement.
*
* - Acquires a RESERVED lock immediately so concurrent writers queue
*   politely (or fall through to retry after `busy_timeout` expires).
* - Commits on success; rolls back on any thrown error before
*   propagating it. If the thrown error is SQLITE_BUSY,
*   {@link withWriteRetry} re-runs the WHOLE function (BEGIN included)
*   after backoff — better-sqlite3 / node:sqlite forbids nesting
*   `BEGIN IMMEDIATE` inside an open transaction, so retry MUST start
*   from this outer boundary.
*
* DO NOT call this from inside an already-open transaction. For nested
* writes use `accessor.transaction()` which uses SAVEPOINTs (T9814).
*
* @bug gh-391 — SQLITE_BUSY contention on parallel writes.
* @task T9839
*/
async function runInImmediateTx(nativeDb, fn) {
	return withWriteRetry(async () => {
		nativeDb.prepare("BEGIN IMMEDIATE").run();
		try {
			const result = await fn();
			nativeDb.prepare("COMMIT").run();
			return result;
		} catch (err) {
			try {
				nativeDb.prepare("ROLLBACK").run();
			} catch {}
			throw err;
		}
	});
}
/** Read a JSON blob from the schema_meta table by key. */
async function getMetaValue(cwd, key) {
	const rows = await (await getDb(cwd)).select().from(schemaMeta).where(eq(schemaMeta.key, key)).all();
	if (rows.length === 0 || !rows[0]) return null;
	try {
		return JSON.parse(rows[0].value);
	} catch {
		return null;
	}
}
/** Write a JSON blob to the schema_meta table by key. */
async function setMetaValue(cwd, key, value) {
	const db = await getDb(cwd);
	const json = JSON.stringify(value);
	await db.insert(schemaMeta).values({
		key,
		value: json
	}).onConflictDoUpdate({
		target: schemaMeta.key,
		set: { value: json }
	}).run();
}
/**
* Create a SQLite-backed DataAccessor.
*
* Opens (or creates) the SQLite database at `.cleo/tasks.db` and returns
* a DataAccessor that materializes/dematerializes whole-file structures
* from the relational tables.
*
* @param cwd - Working directory for path resolution (defaults to process.cwd())
*/
async function createSqliteDataAccessor(cwd) {
	await getDb(cwd);
	/** Load all task IDs for cross-task dependency validation. */
	async function getAllTaskIds() {
		const rows = await (await getDb(cwd)).select({ id: tasksTasks.id }).from(tasksTasks).all();
		return new Set(rows.map((r) => r.id));
	}
	let _txDepth = 0;
	const accessor = {
		engine: "sqlite",
		async loadArchive() {
			const db = await getDb(cwd);
			const archivedRows = await db.select().from(tasksTasks).where(eq(tasksTasks.status, "archived")).all();
			if (archivedRows.length === 0) return null;
			const archivedTasks = archivedRows.map((row) => {
				return {
					...rowToTask(row),
					archivedAt: row.archivedAt ?? void 0,
					archiveReason: row.archiveReason ?? void 0,
					cycleTimeDays: row.cycleTimeDays ?? void 0
				};
			});
			if (archivedTasks.length > 0) {
				const activeRows = await db.select({ id: tasksTasks.id }).from(tasksTasks).where(ne(tasksTasks.status, "archived")).all();
				await loadDependenciesForTasks(db, archivedTasks, new Set([...archivedTasks.map((t) => t.id), ...activeRows.map((r) => r.id)]));
				await loadRelationsForTasks(db, archivedTasks);
			}
			return {
				archivedTasks,
				version: "1.0.0"
			};
		},
		async saveArchive(data) {
			const db = await getDb(cwd);
			const archiveIds = new Set(data.archivedTasks.map((t) => t.id));
			const activeRows = await db.select({ id: tasksTasks.id }).from(tasksTasks).where(ne(tasksTasks.status, "archived")).all();
			const validDepIds = new Set([...archiveIds, ...activeRows.map((r) => r.id)]);
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) throw new Error("Native database not initialized");
			await runInImmediateTx(nativeDb, async () => {
				const depBatch = [];
				for (const task of data.archivedTasks) {
					const row = archivedTaskToRow(task);
					const taskAny = task;
					await upsertTask(db, row, {
						archivedAt: taskAny.archivedAt ?? row.completedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
						archiveReason: taskAny.archiveReason ?? "completed-unverified",
						cycleTimeDays: taskAny.cycleTimeDays ?? null
					}, true);
					depBatch.push({
						taskId: task.id,
						deps: task.depends ?? []
					});
				}
				await batchUpdateDependencies(db, depBatch, validDepIds);
			});
		},
		async loadSessions() {
			return (await (await getDb(cwd)).select().from(tasksSessions).all()).map(rowToSession);
		},
		async saveSessions(sessions) {
			const db = await getDb(cwd);
			const existingRows = await db.select({ id: tasksSessions.id }).from(tasksSessions).all();
			const existingIds = new Set(existingRows.map((r) => r.id));
			const incomingIds = new Set(sessions.map((s) => s.id));
			for (const eid of existingIds) if (!incomingIds.has(eid)) await db.delete(tasksSessions).where(eq(tasksSessions.id, eid)).run();
			for (const session of sessions) await upsertSession(db, session);
		},
		async appendLog(entry) {
			const db = await getDb(cwd);
			await withWriteRetry(() => db.insert(auditLog).values({
				id: entry.id ?? generateAuditLogId(),
				timestamp: entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
				action: entry.action ?? entry.operation ?? "unknown",
				taskId: entry.taskId ?? "unknown",
				actor: entry.actor ?? "system",
				detailsJson: entry.details ? JSON.stringify(entry.details) : "{}",
				beforeJson: entry.before ? JSON.stringify(entry.before) : null,
				afterJson: entry.after ? JSON.stringify(entry.after) : null
			}).run());
		},
		async queryAuditLog(query) {
			const db = await getDb(cwd);
			const conditions = [];
			if (query.taskIds && query.taskIds.length > 0) conditions.push(inArray(auditLog.taskId, [...query.taskIds]));
			if (query.actions && query.actions.length > 0) conditions.push(inArray(auditLog.action, [...query.actions]));
			if (query.since) conditions.push(sql`${auditLog.timestamp} >= ${query.since}`);
			const limit = Math.max(1, Math.floor(query.limit ?? 100));
			return (await db.select({
				id: auditLog.id,
				timestamp: auditLog.timestamp,
				action: auditLog.action,
				taskId: auditLog.taskId,
				actor: auditLog.actor,
				detailsJson: auditLog.detailsJson,
				beforeJson: auditLog.beforeJson,
				afterJson: auditLog.afterJson
			}).from(auditLog).where(conditions.length > 0 ? and(...conditions) : void 0).orderBy(sql`${auditLog.timestamp} DESC`).limit(limit).all()).map((row) => ({
				id: row.id,
				timestamp: row.timestamp,
				action: row.action,
				taskId: row.taskId,
				actor: row.actor,
				detailsJson: row.detailsJson ?? null,
				beforeJson: row.beforeJson ?? null,
				afterJson: row.afterJson ?? null
			}));
		},
		async upsertSingleTask(task) {
			const db = await getDb(cwd);
			const row = taskToRow(task);
			await withWriteRetry(async () => {
				await upsertTask(db, row);
				await updateDependencies(db, task.id, task.depends ?? []);
			});
		},
		async addRelation(taskId, relatedTo, relationType, reason) {
			const db = await getDb(cwd);
			const validTypes = [
				"related",
				"blocks",
				"duplicates",
				"absorbs",
				"fixes",
				"extends",
				"supersedes",
				"groups"
			];
			if (!validTypes.includes(relationType)) throw new Error(`Invalid relation type: ${relationType}. Valid types: ${validTypes.join(", ")}`);
			await withWriteRetry(() => db.insert(tasksTaskRelations).values({
				taskId,
				relatedTo,
				relationType,
				reason: reason ?? null
			}).onConflictDoNothing().run());
		},
		async removeRelation(taskId, relatedTo, relationType) {
			const db = await getDb(cwd);
			const conditions = [eq(tasksTaskRelations.taskId, taskId), eq(tasksTaskRelations.relatedTo, relatedTo)];
			if (relationType !== void 0) {
				const validTypes = [
					"related",
					"blocks",
					"duplicates",
					"absorbs",
					"fixes",
					"extends",
					"supersedes",
					"groups"
				];
				if (!validTypes.includes(relationType)) throw new Error(`Invalid relation type: ${relationType}. Valid types: ${validTypes.join(", ")}`);
				conditions.push(eq(tasksTaskRelations.relationType, relationType));
			}
			await withWriteRetry(() => db.delete(tasksTaskRelations).where(and(...conditions)).run());
		},
		async getAcRows(taskId) {
			return (await (await getDb(cwd)).select({
				id: tasksTaskAcceptanceCriteria.id,
				taskId: tasksTaskAcceptanceCriteria.taskId,
				ordinal: tasksTaskAcceptanceCriteria.ordinal,
				kind: tasksTaskAcceptanceCriteria.kind,
				sourceKey: tasksTaskAcceptanceCriteria.sourceKey,
				targetTaskId: tasksTaskAcceptanceCriteria.targetTaskId,
				projection: tasksTaskAcceptanceCriteria.projection,
				text: tasksTaskAcceptanceCriteria.text,
				createdAt: tasksTaskAcceptanceCriteria.createdAt,
				updatedAt: tasksTaskAcceptanceCriteria.updatedAt,
				contentHash: tasksTaskAcceptanceCriteria.contentHash
			}).from(tasksTaskAcceptanceCriteria).where(eq(tasksTaskAcceptanceCriteria.taskId, taskId)).orderBy(tasksTaskAcceptanceCriteria.ordinal).all()).map((r) => ({
				id: r.id,
				taskId: r.taskId,
				ordinal: r.ordinal,
				kind: r.kind,
				sourceKey: r.sourceKey ?? `text:${r.ordinal}`,
				targetTaskId: r.targetTaskId ?? null,
				projection: r.projection,
				text: r.text,
				createdAt: r.createdAt,
				updatedAt: r.updatedAt ?? null,
				contentHash: r.contentHash ?? null
			}));
		},
		async getAcBindings(acIds) {
			if (acIds.length === 0) return [];
			return (await (await getDb(cwd)).select({
				id: tasksEvidenceAcBindings.id,
				evidenceAtomId: tasksEvidenceAcBindings.evidenceAtomId,
				acId: tasksEvidenceAcBindings.acId,
				bindingType: tasksEvidenceAcBindings.bindingType,
				createdAt: tasksEvidenceAcBindings.createdAt
			}).from(tasksEvidenceAcBindings).where(inArray(tasksEvidenceAcBindings.acId, acIds)).all()).map((r) => ({
				id: r.id,
				evidenceAtomId: r.evidenceAtomId,
				acId: r.acId,
				bindingType: r.bindingType,
				createdAt: r.createdAt
			}));
		},
		async archiveSingleTask(taskId, fields) {
			const db = await getDb(cwd);
			if ((await db.select({ id: tasksTasks.id }).from(tasksTasks).where(eq(tasksTasks.id, taskId)).all()).length === 0) return;
			await withWriteRetry(() => db.update(tasksTasks).set({
				status: "archived",
				archivedAt: fields.archivedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
				archiveReason: fields.archiveReason ?? "completed-unverified",
				cycleTimeDays: fields.cycleTimeDays ?? null,
				updatedAt: (/* @__PURE__ */ new Date()).toISOString()
			}).where(eq(tasksTasks.id, taskId)).run());
		},
		async removeSingleTask(taskId) {
			const db = await getDb(cwd);
			await withWriteRetry(async () => {
				await db.delete(tasksTaskDependencies).where(eq(tasksTaskDependencies.taskId, taskId)).run();
				await db.delete(tasksTaskDependencies).where(eq(tasksTaskDependencies.dependsOn, taskId)).run();
				await db.delete(tasksTasks).where(eq(tasksTasks.id, taskId)).run();
			});
		},
		async loadSingleTask(taskId) {
			const db = await getDb(cwd);
			const rows = await db.select().from(tasksTasks).where(eq(tasksTasks.id, taskId)).limit(1).all();
			if (rows.length === 0 || !rows[0]) return null;
			const task = rowToTask(rows[0]);
			const allIdRows = await db.select({ id: tasksTasks.id }).from(tasksTasks).all();
			const allIds = new Set(allIdRows.map((r) => r.id));
			await loadDependenciesForTasks(db, [task], allIds);
			await loadRelationsForTasks(db, [task]);
			return task;
		},
		async getActiveSession() {
			const rows = await (await getDb(cwd)).select().from(tasksSessions).where(eq(tasksSessions.status, "active")).orderBy(sql`${tasksSessions.startedAt} DESC`).limit(1).all();
			if (rows.length === 0 || !rows[0]) return null;
			return rowToSession(rows[0]);
		},
		async resolveCurrentSession() {
			return resolveCurrentSession(cwd);
		},
		async upsertSingleSession(session) {
			await upsertSession(await getDb(cwd), session);
		},
		async removeSingleSession(sessionId) {
			await (await getDb(cwd)).delete(tasksSessions).where(eq(tasksSessions.id, sessionId)).run();
			cleanupBrainRefsOnSessionDelete(sessionId, cwd);
		},
		async queryTasks(filters) {
			const db = await getDb(cwd);
			const conditions = [];
			if (filters.status) {
				const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
				if (!statuses.includes("archived")) conditions.push(ne(tasksTasks.status, "archived"));
				conditions.push(inArray(tasksTasks.status, statuses));
			} else if (filters.excludeStatus) {
				const excluded = Array.isArray(filters.excludeStatus) ? filters.excludeStatus : [filters.excludeStatus];
				for (const s of excluded) conditions.push(ne(tasksTasks.status, s));
			} else conditions.push(ne(tasksTasks.status, "archived"));
			if (filters.priority) conditions.push(eq(tasksTasks.priority, filters.priority));
			if (filters.type) conditions.push(eq(tasksTasks.type, filters.type));
			if (filters.phase) conditions.push(eq(tasksTasks.phase, filters.phase));
			if (filters.parentId !== void 0) if (filters.parentId === null) conditions.push(isNull(tasksTasks.parentId));
			else conditions.push(eq(tasksTasks.parentId, filters.parentId));
			if (filters.search) {
				const pattern = `%${filters.search}%`;
				conditions.push(or(like(tasksTasks.title, pattern), like(tasksTasks.description, pattern), like(tasksTasks.id, pattern)));
			}
			if (filters.label) {
				const labelMatchIds = db.select({ taskId: tasksTaskLabels.taskId }).from(tasksTaskLabels).where(eq(tasksTaskLabels.label, filters.label));
				conditions.push(inArray(tasksTasks.id, labelMatchIds));
			}
			const where = conditions.length > 0 ? and(...conditions) : void 0;
			const total = (await db.select({ count: sql`count(*)` }).from(tasksTasks).where(where).get())?.count ?? 0;
			let orderClause;
			switch (filters.orderBy) {
				case "createdAt":
					orderClause = sql`${tasksTasks.createdAt} ASC`;
					break;
				case "updatedAt":
					orderClause = sql`${tasksTasks.updatedAt} DESC NULLS LAST`;
					break;
				case "priority":
					orderClause = sql`CASE ${tasksTasks.priority}
            WHEN 'critical' THEN 0 WHEN 'high' THEN 1
            WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC`;
					break;
				default: orderClause = sql`${tasksTasks.position} ASC, ${tasksTasks.createdAt} ASC`;
			}
			let query = db.select().from(tasksTasks).where(where).orderBy(orderClause);
			if (filters.limit !== void 0) query = query.limit(filters.limit);
			if (filters.offset !== void 0) query = query.offset(filters.offset);
			const tasks = (await query.all()).map(rowToTask);
			if (tasks.length > 0) {
				await loadDependenciesForTasks(db, tasks, await getAllTaskIds());
				await loadRelationsForTasks(db, tasks);
			}
			return {
				tasks,
				total
			};
		},
		async countTasks(filters) {
			const db = await getDb(cwd);
			const conditions = [];
			if (filters?.status) {
				const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
				conditions.push(inArray(tasksTasks.status, statuses));
			} else conditions.push(ne(tasksTasks.status, "archived"));
			if (filters?.parentId) conditions.push(eq(tasksTasks.parentId, filters.parentId));
			const where = conditions.length > 0 ? and(...conditions) : void 0;
			return (await db.select({ count: sql`count(*)` }).from(tasksTasks).where(where).get())?.count ?? 0;
		},
		async getChildren(parentId) {
			const db = await getDb(cwd);
			const tasks = (await db.select().from(tasksTasks).where(and(eq(tasksTasks.parentId, parentId), ne(tasksTasks.status, "archived"))).orderBy(sql`${tasksTasks.position} ASC, ${tasksTasks.createdAt} ASC`).all()).map(rowToTask);
			if (tasks.length > 0) {
				await loadDependenciesForTasks(db, tasks, await getAllTaskIds());
				await loadRelationsForTasks(db, tasks);
			}
			return tasks;
		},
		async countChildren(parentId) {
			return (await (await getDb(cwd)).select({ count: sql`count(*)` }).from(tasksTasks).where(and(eq(tasksTasks.parentId, parentId), ne(tasksTasks.status, "archived"))).get())?.count ?? 0;
		},
		async countActiveChildren(parentId) {
			return (await (await getDb(cwd)).select({ count: sql`count(*)` }).from(tasksTasks).where(and(eq(tasksTasks.parentId, parentId), notInArray(tasksTasks.status, [...TERMINAL_TASK_STATUSES]))).get())?.count ?? 0;
		},
		async getAncestorChain(taskId) {
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) return [];
			const idRows = nativeDb.prepare(`WITH RECURSIVE ancestor_ids(id, depth) AS (
            SELECT parent_id, 0 FROM tasks_tasks WHERE id = ? AND parent_id IS NOT NULL
            UNION ALL
            SELECT t.parent_id, a.depth + 1 FROM tasks_tasks t
            JOIN ancestor_ids a ON t.id = a.id
            WHERE t.parent_id IS NOT NULL
          )
          SELECT id FROM ancestor_ids ORDER BY depth DESC`).all(taskId);
			if (idRows.length === 0) return [];
			const db = await getDb(cwd);
			const ids = idRows.map((r) => r.id);
			const tasks = (await db.select().from(tasksTasks).where(inArray(tasksTasks.id, ids)).all()).map(rowToTask);
			if (tasks.length > 0) {
				await loadDependenciesForTasks(db, tasks, await getAllTaskIds());
				await loadRelationsForTasks(db, tasks);
			}
			const orderMap = new Map(ids.map((id, i) => [id, i]));
			tasks.sort((a, b) => (orderMap.get(a.id) ?? 0) - (orderMap.get(b.id) ?? 0));
			return tasks;
		},
		async getSubtree(rootId) {
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) return [];
			const idRows = nativeDb.prepare(`WITH RECURSIVE subtree AS (
            SELECT id FROM tasks_tasks WHERE id = ?
            UNION ALL
            SELECT t.id FROM tasks_tasks t
            JOIN subtree s ON t.parent_id = s.id
          )
          SELECT id FROM subtree`).all(rootId);
			if (idRows.length === 0) return [];
			const db = await getDb(cwd);
			const ids = idRows.map((r) => r.id);
			const tasks = (await db.select().from(tasksTasks).where(inArray(tasksTasks.id, ids)).all()).map(rowToTask);
			if (tasks.length > 0) {
				await loadDependenciesForTasks(db, tasks, await getAllTaskIds());
				await loadRelationsForTasks(db, tasks);
			}
			return tasks;
		},
		async getDependents(taskId) {
			const db = await getDb(cwd);
			const depRows = await db.select().from(tasksTaskDependencies).where(eq(tasksTaskDependencies.dependsOn, taskId)).all();
			if (depRows.length === 0) return [];
			const dependentIds = depRows.map((r) => r.taskId);
			const tasks = (await db.select().from(tasksTasks).where(inArray(tasksTasks.id, dependentIds)).all()).map(rowToTask);
			if (tasks.length > 0) {
				await loadDependenciesForTasks(db, tasks, await getAllTaskIds());
				await loadRelationsForTasks(db, tasks);
			}
			return tasks;
		},
		async getDependencyChain(taskId) {
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) return [];
			return nativeDb.prepare(`WITH RECURSIVE dep_chain(id) AS (
            SELECT depends_on FROM tasks_task_dependencies WHERE task_id = ?
            UNION
            SELECT td.depends_on FROM tasks_task_dependencies td
            JOIN dep_chain dc ON td.task_id = dc.id
          )
          SELECT id FROM dep_chain`).all(taskId).map((r) => r.id);
		},
		async taskExists(taskId) {
			return !!await (await getDb(cwd)).select({ id: tasksTasks.id }).from(tasksTasks).where(eq(tasksTasks.id, taskId)).limit(1).get();
		},
		async loadTasks(taskIds) {
			if (taskIds.length === 0) return [];
			const db = await getDb(cwd);
			const tasks = (await db.select().from(tasksTasks).where(inArray(tasksTasks.id, taskIds)).all()).map(rowToTask);
			if (tasks.length > 0) {
				await loadDependenciesForTasks(db, tasks, await getAllTaskIds());
				await loadRelationsForTasks(db, tasks);
			}
			return tasks;
		},
		async getNextPosition(parentId) {
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) throw new Error("Native database not initialized");
			return (parentId === null ? nativeDb.prepare(`SELECT COALESCE(MAX(position), 0) + 1 AS next_pos FROM tasks_tasks WHERE parent_id IS NULL AND status != 'archived'`).get() : nativeDb.prepare(`SELECT COALESCE(MAX(position), 0) + 1 AS next_pos FROM tasks_tasks WHERE parent_id = ? AND status != 'archived'`).get(parentId))?.next_pos ?? 1;
		},
		async shiftPositions(parentId, fromPosition, delta) {
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) throw new Error("Native database not initialized");
			if (parentId === null) nativeDb.prepare(`UPDATE tasks_tasks SET position = position + ?, position_version = position_version + 1, updated_at = ? WHERE parent_id IS NULL AND position >= ? AND status != 'archived'`).run(delta, (/* @__PURE__ */ new Date()).toISOString(), fromPosition);
			else nativeDb.prepare(`UPDATE tasks_tasks SET position = position + ?, position_version = position_version + 1, updated_at = ? WHERE parent_id = ? AND position >= ? AND status != 'archived'`).run(delta, (/* @__PURE__ */ new Date()).toISOString(), parentId, fromPosition);
		},
		async updateTaskFields(taskId, fields) {
			const db = await getDb(cwd);
			const updateRow = { updatedAt: fields.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString() };
			for (const [key, col] of [
				["title", "title"],
				["description", "description"],
				["status", "status"],
				["priority", "priority"],
				["type", "type"],
				["parentId", "parentId"],
				["phase", "phase"],
				["size", "size"],
				["position", "position"],
				["positionVersion", "positionVersion"],
				["labelsJson", "labelsJson"],
				["notesJson", "notesJson"],
				["acceptanceJson", "acceptanceJson"],
				["filesJson", "filesJson"],
				["origin", "origin"],
				["blockedBy", "blockedBy"],
				["epicLifecycle", "epicLifecycle"],
				["noAutoComplete", "noAutoComplete"],
				["completedAt", "completedAt"],
				["cancelledAt", "cancelledAt"],
				["cancellationReason", "cancellationReason"],
				["verificationJson", "verificationJson"],
				["createdBy", "createdBy"],
				["modifiedBy", "modifiedBy"],
				["sessionId", "sessionId"],
				["assignee", "assignee"],
				["pipelineStage", "pipelineStage"]
			]) if (fields[key] !== void 0) updateRow[col] = fields[key];
			await withWriteRetry(() => db.update(tasksTasks).set(updateRow).where(eq(tasksTasks.id, taskId)).run());
		},
		async transaction(fn) {
			const db = await getDb(cwd);
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) throw new Error("Native database not initialized");
			const isOuter = _txDepth === 0;
			const spName = isOuter ? null : `_cleo_tx_${(_txSavepointCounter++).toString(36)}`;
			if (isOuter) nativeDb.prepare("BEGIN IMMEDIATE").run();
			else nativeDb.prepare(`SAVEPOINT ${spName}`).run();
			_txDepth++;
			try {
				const result = await fn({
					async upsertSingleTask(task) {
						await upsertTask(db, taskToRow(task));
						await updateDependencies(db, task.id, task.depends ?? []);
					},
					async archiveSingleTask(taskId, fields) {
						await db.update(tasksTasks).set({
							status: "archived",
							archivedAt: fields.archivedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
							archiveReason: fields.archiveReason ?? "completed-unverified",
							cycleTimeDays: fields.cycleTimeDays ?? null,
							updatedAt: (/* @__PURE__ */ new Date()).toISOString()
						}).where(eq(tasksTasks.id, taskId)).run();
					},
					async removeSingleTask(taskId) {
						await db.delete(tasksTaskDependencies).where(eq(tasksTaskDependencies.taskId, taskId)).run();
						await db.delete(tasksTaskDependencies).where(eq(tasksTaskDependencies.dependsOn, taskId)).run();
						await db.delete(tasksTasks).where(eq(tasksTasks.id, taskId)).run();
					},
					async setMetaValue(key, value) {
						await setMetaValue(cwd, key, value);
					},
					async updateTaskFields(taskId, flds) {
						await accessor.updateTaskFields(taskId, flds);
					},
					async getChildren(parentId) {
						return (await db.select().from(tasksTasks).where(and(eq(tasksTasks.parentId, parentId), ne(tasksTasks.status, "archived"))).orderBy(sql`${tasksTasks.position} ASC, ${tasksTasks.createdAt} ASC`).all()).map(rowToTask);
					},
					async appendLog(entry) {
						await accessor.appendLog(entry);
					},
					async addRelation(taskId, relatedTo, relationType, reason) {
						await accessor.addRelation(taskId, relatedTo, relationType, reason);
					},
					async removeRelation(taskId, relatedTo, relationType) {
						await accessor.removeRelation(taskId, relatedTo, relationType);
					},
					async clearRelations(taskId) {
						await db.delete(tasksTaskRelations).where(eq(tasksTaskRelations.taskId, taskId)).run();
					},
					async insertAcRows(rows) {
						if (rows.length === 0) return;
						await db.insert(tasksTaskAcceptanceCriteria).values(rows.map((r) => ({
							id: r.id,
							taskId: r.taskId,
							ordinal: r.ordinal,
							kind: r.kind ?? "text",
							sourceKey: r.sourceKey ?? `text:${r.ordinal}`,
							targetTaskId: r.targetTaskId ?? null,
							projection: r.projection ?? "legacy",
							text: r.text,
							contentHash: r.contentHash ?? null
						}))).run();
					},
					async getAcRows(taskId) {
						return (await db.select({
							id: tasksTaskAcceptanceCriteria.id,
							taskId: tasksTaskAcceptanceCriteria.taskId,
							ordinal: tasksTaskAcceptanceCriteria.ordinal,
							kind: tasksTaskAcceptanceCriteria.kind,
							sourceKey: tasksTaskAcceptanceCriteria.sourceKey,
							targetTaskId: tasksTaskAcceptanceCriteria.targetTaskId,
							projection: tasksTaskAcceptanceCriteria.projection,
							text: tasksTaskAcceptanceCriteria.text,
							createdAt: tasksTaskAcceptanceCriteria.createdAt,
							updatedAt: tasksTaskAcceptanceCriteria.updatedAt,
							contentHash: tasksTaskAcceptanceCriteria.contentHash
						}).from(tasksTaskAcceptanceCriteria).where(eq(tasksTaskAcceptanceCriteria.taskId, taskId)).orderBy(tasksTaskAcceptanceCriteria.ordinal).all()).map((r) => ({
							id: r.id,
							taskId: r.taskId,
							ordinal: r.ordinal,
							kind: r.kind,
							sourceKey: r.sourceKey ?? `text:${r.ordinal}`,
							targetTaskId: r.targetTaskId ?? null,
							projection: r.projection,
							text: r.text,
							createdAt: r.createdAt,
							updatedAt: r.updatedAt ?? null,
							contentHash: r.contentHash ?? null
						}));
					},
					async deleteAcRowsForTask(taskId) {
						await db.delete(tasksTaskAcceptanceCriteria).where(eq(tasksTaskAcceptanceCriteria.taskId, taskId)).run();
					},
					async appendAcHistory(rows) {
						if (rows.length === 0) return;
						await db.insert(tasksTaskAcceptanceCriteriaHistory).values(rows.map((r) => ({
							acId: r.acId,
							previousText: r.previousText,
							reason: r.reason
						}))).run();
					},
					async getAcBindings(acIds) {
						if (acIds.length === 0) return [];
						return (await db.select({
							id: tasksEvidenceAcBindings.id,
							evidenceAtomId: tasksEvidenceAcBindings.evidenceAtomId,
							acId: tasksEvidenceAcBindings.acId,
							bindingType: tasksEvidenceAcBindings.bindingType,
							createdAt: tasksEvidenceAcBindings.createdAt
						}).from(tasksEvidenceAcBindings).where(inArray(tasksEvidenceAcBindings.acId, acIds)).all()).map((r) => ({
							id: r.id,
							evidenceAtomId: r.evidenceAtomId,
							acId: r.acId,
							bindingType: r.bindingType,
							createdAt: r.createdAt
						}));
					},
					async insertAcBindings(rows) {
						if (rows.length === 0) return;
						await db.insert(tasksEvidenceAcBindings).values(rows.map((r) => ({
							id: r.id,
							evidenceAtomId: r.evidenceAtomId,
							acId: r.acId,
							bindingType: r.bindingType
						}))).onConflictDoNothing({ target: [
							tasksEvidenceAcBindings.evidenceAtomId,
							tasksEvidenceAcBindings.acId,
							tasksEvidenceAcBindings.bindingType
						] }).run();
					}
				});
				_txDepth--;
				if (isOuter) nativeDb.prepare("COMMIT").run();
				else nativeDb.prepare(`RELEASE SAVEPOINT ${spName}`).run();
				return result;
			} catch (err) {
				_txDepth--;
				try {
					if (isOuter) nativeDb.prepare("ROLLBACK").run();
					else {
						nativeDb.prepare(`ROLLBACK TO SAVEPOINT ${spName}`).run();
						nativeDb.prepare(`RELEASE SAVEPOINT ${spName}`).run();
					}
				} catch {}
				throw err;
			}
		},
		async close() {
			closeDb();
		},
		async getMetaValue(key) {
			return getMetaValue(cwd, key);
		},
		async setMetaValue(key, value) {
			return setMetaValue(cwd, key, value);
		},
		async getSchemaVersion() {
			return (await getMetaValue(cwd, "file_meta"))?.schemaVersion ?? null;
		},
		async listAgentInstances(filters) {
			const { listAgentInstances: listAgents } = await import('./registry3-BKYEGGK8.js');
			return listAgents(filters, cwd);
		},
		async getAgentInstance(agentId) {
			const { getAgentInstance: getAgent } = await import('./registry3-BKYEGGK8.js');
			return getAgent(agentId, cwd);
		},
		async claimTask(taskId, agentId) {
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) throw new Error("Native database not initialized");
			if (!nativeDb.prepare("SELECT assignee FROM tasks_tasks WHERE id = ?").get(taskId)) throw new Error(`Task not found: ${taskId}`);
			if (nativeDb.prepare("UPDATE tasks_tasks SET assignee = ?, updated_at = ? WHERE id = ? AND (assignee IS NULL OR assignee = ?)").run(agentId, (/* @__PURE__ */ new Date()).toISOString(), taskId, agentId).changes === 0) {
				const currentRow = nativeDb.prepare("SELECT assignee FROM tasks_tasks WHERE id = ?").get(taskId);
				throw new Error(`Task ${taskId} is already claimed by agent: ${currentRow?.assignee ?? "unknown"}`);
			}
		},
		async unclaimTask(taskId) {
			const nativeDb = getNativeTasksDb();
			if (!nativeDb) throw new Error("Native database not initialized");
			if (!nativeDb.prepare("SELECT id FROM tasks_tasks WHERE id = ?").get(taskId)) throw new Error(`Task not found: ${taskId}`);
			nativeDb.prepare("UPDATE tasks_tasks SET assignee = NULL, updated_at = ? WHERE id = ?").run((/* @__PURE__ */ new Date()).toISOString(), taskId);
		}
	};
	return accessor;
}

export { createSqliteDataAccessor as c, setMetaValue as s };
//# sourceMappingURL=sqlite-data-accessor-C59wZ0zp.js.map
