import { e as getBrainDb, d as getBrainNativeDb } from './sqlite-Dsbe0wec.js';
import { t as typedAll } from './typed-query-l1D_QElt.js';
import './chunk-DDP5xfzh.js';
import 'node:module';
import './paths-2M_UxD_H.js';
import 'node:fs';
import 'node:path';
import 'node:os';
import 'node:events';
import 'node:diagnostics_channel';
import 'fs';
import 'events';
import 'util';
import 'path';
import 'assert';
import 'worker_threads';
import 'module';
import 'node:url';
import 'url';
import 'buffer';
import 'node:child_process';
import 'node:crypto';
import 'node:process';
import 'node:async_hooks';
import './dual-scope-db-ATrwq0LH.js';
import './indexes-aYlULWG3.js';
import './utils-BvHPJOVk.js';
import './conduit-Ca_OFWR_.js';
import './unique-constraint-BM-Apx1H.js';
import './memory-schema-Bi7cChwo.js';
import './agent-registry-BtE5uhMO.js';
import './nexus-sqlite2-DgTuv9BQ.js';
import './nexus-schema2-BEd-ol6e.js';
import './logger-ChCwlV5k.js';
import 'node:sqlite';
import './lock-BoHF25mx.js';
import './index-IVoIz4D5.js';
import 'constants';
import 'stream';

//#region ../core/dist/memory/brain-consolidator.js
/**
* Brain Consolidator — contradiction detection for the CLEO BRAIN memory system.
*
* Implements heuristic contradiction detection based on keyword polarity flip.
* No LLM required — uses negation markers to identify entries that assert
* opposing claims about the same subject.
*
* When a contradiction is detected:
*   1. A 'contradicts' edge is created in brain_page_edges (both directions).
*   2. The lower-quality entry's qualityScore is reduced by 0.15.
*
* All operations are BEST-EFFORT — failures never crash the consolidator.
*
* @task T549 Wave 3-C
* @epic T549
*/
/** Minimum number of shared keywords to consider two entries related. */
var MIN_SHARED_KEYWORDS = 3;
/**
* Negation markers that, when present in one entry but not the other,
* indicate a potential contradiction.
*/
var NEGATION_MARKERS = [
	"not",
	"never",
	"deprecated",
	"removed",
	"replaced",
	"avoid",
	"no longer",
	"do not",
	"don't",
	"disabled",
	"deleted",
	"dropped",
	"broken",
	"invalid",
	"obsolete",
	"reverted",
	"undone",
	"superseded"
];
/** Stop words excluded from keyword extraction (matches brain-lifecycle stop words). */
var STOP_WORDS = new Set([
	"the",
	"a",
	"an",
	"is",
	"are",
	"was",
	"were",
	"be",
	"been",
	"being",
	"have",
	"has",
	"had",
	"do",
	"does",
	"did",
	"will",
	"would",
	"could",
	"should",
	"may",
	"might",
	"shall",
	"can",
	"to",
	"of",
	"in",
	"for",
	"on",
	"with",
	"at",
	"by",
	"from",
	"as",
	"into",
	"through",
	"and",
	"but",
	"or",
	"nor",
	"so",
	"yet",
	"this",
	"that",
	"these",
	"those",
	"it",
	"its",
	"not",
	"no"
]);
/**
* Extract significant keywords from text for overlap comparison.
* Filters stop words and tokens shorter than 4 characters.
*/
function extractKeywords(text) {
	const words = text.toLowerCase().replace(/[^a-z0-9\s\-_]/g, " ").split(/\s+/);
	const keywords = /* @__PURE__ */ new Set();
	for (const w of words) if (w.length >= 4 && !STOP_WORDS.has(w)) keywords.add(w);
	return keywords;
}
/**
* Find negation markers present in the text.
*/
function findNegationMarkers(text) {
	const lower = text.toLowerCase();
	return NEGATION_MARKERS.filter((marker) => lower.includes(marker));
}
/**
* Return the intersection of two keyword sets.
*/
function keywordIntersection(a, b) {
	const result = [];
	for (const w of a) if (b.has(w)) result.push(w);
	return result;
}
/**
* Detect contradiction pairs across all verified memory entries.
*
* For each verified entry:
*   1. Find entries with >= MIN_SHARED_KEYWORDS shared keywords.
*   2. Check for negation markers in one but not the other.
*   3. If contradiction detected: create 'contradicts' graph edges.
*   4. Lower quality score of the contradicted (lower quality) entry by 0.15.
*
* Only considers entries where invalid_at IS NULL (currently valid).
* Short-circuits after 50 contradiction pairs to limit consolidation cost.
*
* @param projectRoot - Project root directory for brain.db resolution
* @returns List of contradiction pairs found
*/
async function detectContradictions(projectRoot) {
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return [];
	const results = [];
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	for (const { table, textCol } of [
		{
			table: "brain_observations",
			textCol: "narrative"
		},
		{
			table: "brain_learnings",
			textCol: "insight"
		},
		{
			table: "brain_patterns",
			textCol: "pattern"
		},
		{
			table: "brain_decisions",
			textCol: "decision"
		}
	]) {
		if (results.length >= 50) break;
		let entries = [];
		try {
			entries = typedAll(nativeDb.prepare(`
          SELECT id, COALESCE(${textCol}, '') AS text, quality_score
          FROM ${table}
          WHERE invalid_at IS NULL
            AND verified = 1
          LIMIT 200
        `));
		} catch {
			continue;
		}
		if (entries.length < 2) continue;
		const keywordMap = /* @__PURE__ */ new Map();
		const negationMap = /* @__PURE__ */ new Map();
		for (const entry of entries) {
			keywordMap.set(entry.id, extractKeywords(entry.text));
			negationMap.set(entry.id, findNegationMarkers(entry.text));
		}
		for (let i = 0; i < entries.length; i++) {
			if (results.length >= 50) break;
			const entryA = entries[i];
			const keywordsA = keywordMap.get(entryA.id);
			const negationsA = negationMap.get(entryA.id);
			for (let j = i + 1; j < entries.length; j++) {
				if (results.length >= 50) break;
				const entryB = entries[j];
				const keywordsB = keywordMap.get(entryB.id);
				const negationsB = negationMap.get(entryB.id);
				const shared = keywordIntersection(keywordsA, keywordsB);
				if (shared.length < MIN_SHARED_KEYWORDS) continue;
				const negationsOnlyInA = negationsA.filter((m) => !negationsB.includes(m));
				const negationsOnlyInB = negationsB.filter((m) => !negationsA.includes(m));
				if (!(negationsOnlyInA.length > 0 || negationsOnlyInB.length > 0)) continue;
				const negMarkers = [...negationsOnlyInA, ...negationsOnlyInB];
				const contradictedId = (entryA.quality_score ?? .5) <= (entryB.quality_score ?? .5) ? entryA.id : entryB.id;
				contradictedId === entryA.id ? entryB.id : entryA.id;
				results.push({
					entryAId: entryA.id,
					entryBId: entryB.id,
					contradictedId,
					table,
					sharedKeywords: shared.slice(0, 10),
					negationMarkers: negMarkers.slice(0, 5)
				});
				const nodeA = buildNodeId(table, entryA.id);
				const nodeB = buildNodeId(table, entryB.id);
				try {
					nativeDb.prepare(`
              INSERT OR IGNORE INTO brain_page_edges
                (from_id, to_id, edge_type, weight, provenance, created_at)
              VALUES (?, ?, 'contradicts', 0.8, 'consolidation:contradiction', ?)
            `).run(nodeA, nodeB, now);
					nativeDb.prepare(`
              INSERT OR IGNORE INTO brain_page_edges
                (from_id, to_id, edge_type, weight, provenance, created_at)
              VALUES (?, ?, 'contradicts', 0.8, 'consolidation:contradiction', ?)
            `).run(nodeB, nodeA, now);
				} catch {}
				try {
					nativeDb.prepare(`
              UPDATE ${table}
              SET quality_score = MAX(0.0, COALESCE(quality_score, 0.5) - 0.15),
                  updated_at = ?
              WHERE id = ?
            `).run(now, contradictedId);
				} catch {}
			}
		}
	}
	return results;
}
/**
* Build the graph node ID for a given table + entry ID combination.
* Mirrors the convention used in graph-auto-populate.ts.
*/
function buildNodeId(table, entryId) {
	return `${{
		brain_observations: "observation",
		brain_learnings: "learning",
		brain_patterns: "pattern",
		brain_decisions: "decision"
	}[table] ?? "entry"}:${entryId}`;
}
/**
* Normalize a pattern title for dedup comparison.
* Lowercases, collapses whitespace, strips punctuation.
*/
function normalizePatternTitle(title) {
	return title.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
}
/**
* Collapse near-duplicate brain_patterns rows in a single consolidation pass.
*
* Two rows are considered near-duplicates when:
*   - `normalize(pattern)` is identical
*   - `peer_id` is the same
*   - `created_at` values fall within a configurable time window (default: 3600s / 1hr)
*
* The oldest row (earliest `created_at`) is kept. All others are deleted after
* their `occurrence_count` is merged into the survivor and `last_seen_at` is
* updated to the most-recent duplicate's timestamp.
*
* All operations run inside a single SQLite transaction — either everything
* succeeds or nothing changes.
*
* @param projectRoot - Project root directory for brain.db resolution
* @param windowSeconds - Max seconds between rows to consider them duplicates (default 3600)
* @returns Number of rows removed
*
* @task T1896
*/
async function dedupePatterns(projectRoot, windowSeconds = 3600) {
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return 0;
	const rows = typedAll(nativeDb.prepare("SELECT id, pattern, peer_id, extracted_at, COALESCE(occurrence_count, 1) AS occurrence_count FROM brain_patterns WHERE invalid_at IS NULL ORDER BY peer_id, pattern, extracted_at ASC"));
	const groups = /* @__PURE__ */ new Map();
	for (const row of rows) {
		const key = `${row.peer_id}::${normalizePatternTitle(row.pattern)}`;
		const group = groups.get(key) ?? [];
		group.push(row);
		groups.set(key, group);
	}
	const dedupGroups = [];
	for (const group of groups.values()) {
		if (group.length < 2) continue;
		const windows = [];
		let current = [group[0]];
		for (let i = 1; i < group.length; i++) {
			const prev = current[current.length - 1];
			const prevMs = new Date(prev.extracted_at).getTime();
			const curMs = new Date(group[i].extracted_at).getTime();
			if (Math.abs(curMs - prevMs) / 1e3 <= windowSeconds) current.push(group[i]);
			else {
				if (current.length >= 2) windows.push(current);
				current = [group[i]];
			}
		}
		if (current.length >= 2) windows.push(current);
		for (const window of windows) {
			const keeper = window[0];
			const duplicates = window.slice(1);
			dedupGroups.push({
				keptId: keeper.id,
				removedIds: duplicates.map((r) => r.id),
				normalizedTitle: normalizePatternTitle(keeper.pattern)
			});
		}
	}
	if (dedupGroups.length === 0) return 0;
	let totalRemoved = 0;
	nativeDb.prepare("BEGIN").run();
	try {
		for (const dg of dedupGroups) {
			const dupRows = typedAll(nativeDb.prepare(`SELECT id, pattern, peer_id, extracted_at, COALESCE(occurrence_count, 1) AS occurrence_count FROM brain_patterns WHERE id IN (${dg.removedIds.map(() => "?").join(",")})`), ...dg.removedIds);
			const summedCount = dupRows.reduce((acc, r) => acc + r.occurrence_count, 0);
			const latestSeenAt = dupRows.reduce((latest, r) => r.extracted_at > latest ? r.extracted_at : latest, dupRows[0]?.extracted_at ?? "");
			nativeDb.prepare("UPDATE brain_patterns SET occurrence_count = occurrence_count + ?, last_seen_at = ? WHERE id = ?").run(summedCount, latestSeenAt, dg.keptId);
			for (const removedId of dg.removedIds) {
				nativeDb.prepare("DELETE FROM brain_patterns WHERE id = ?").run(removedId);
				totalRemoved++;
			}
		}
		nativeDb.prepare("COMMIT").run();
	} catch (err) {
		nativeDb.prepare("ROLLBACK").run();
		throw err;
	}
	return totalRemoved;
}

export { dedupePatterns, detectContradictions };
//# sourceMappingURL=brain-consolidator-DzxPaTrt.js.map
