import './dual-scope-db-ATrwq0LH.js';
import { e as getBrainDb, d as getBrainNativeDb } from './sqlite-Dsbe0wec.js';
import './memory-schema-Bi7cChwo.js';
import { g as getNexusDb, a as getNexusNativeDb } from './nexus-sqlite2-DgTuv9BQ.js';
import { t as typedAll } from './typed-query-l1D_QElt.js';
import { createRequire } from 'node:module';
import 'node:fs';

//#region ../core/dist/memory/graph-memory-bridge.js
var { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
/**
* Regex patterns to extract entity references from text.
*
* Matches:
* - File paths: relative paths ending in known source extensions
* - Function/symbol names: camelCase, PascalCase, snake_case identifiers (≥4 chars)
*/
var FILE_PATH_PATTERN = /(?:^|\s|['"`(])([a-zA-Z0-9_\-./]+\.(?:ts|tsx|js|jsx|rs|go|py|mjs|cjs))(?:$|\s|['"`)])/g;
var SYMBOL_PATTERN = /\b([a-zA-Z_][a-zA-Z0-9_]*(?:[A-Z][a-zA-Z0-9_]*)+|[a-zA-Z_]{4,}[a-zA-Z0-9_]*)\b/g;
/** Common stop-words to skip in symbol extraction (short-circuits false positives). */
var SYMBOL_STOP_WORDS = new Set([
	"true",
	"false",
	"null",
	"undefined",
	"const",
	"async",
	"await",
	"return",
	"export",
	"import",
	"from",
	"type",
	"interface",
	"function",
	"class",
	"this",
	"super",
	"extends",
	"implements",
	"with",
	"that",
	"then",
	"when",
	"have",
	"been",
	"will",
	"should",
	"could",
	"would",
	"error",
	"result",
	"value",
	"data",
	"info",
	"note",
	"todo",
	"done",
	"fail",
	"pass"
]);
/**
* Extract file paths from text.
*/
function extractFilePaths(text) {
	const paths = /* @__PURE__ */ new Set();
	for (const m of text.matchAll(FILE_PATH_PATTERN)) {
		const p = m[1];
		if (p) paths.add(p);
	}
	return Array.from(paths);
}
/**
* Extract potential symbol names from text (camelCase / PascalCase / snake_case ≥ 4 chars).
*/
function extractSymbolCandidates(text) {
	const syms = /* @__PURE__ */ new Set();
	for (const m of text.matchAll(SYMBOL_PATTERN)) {
		const s = m[1];
		if (s && s.length >= 4 && !SYMBOL_STOP_WORDS.has(s.toLowerCase())) syms.add(s);
	}
	return Array.from(syms);
}
/**
* Build a plain-text corpus from a brain_page_nodes metadata_json blob.
* Returns empty string if metadata is absent or malformed.
*/
function metadataText(metaJson) {
	if (!metaJson) return "";
	try {
		const obj = JSON.parse(metaJson);
		return Object.values(obj).filter((v) => typeof v === "string").join(" ");
	} catch {
		return "";
	}
}
/**
* Write `modified_by` edges from file nodes to observation nodes.
*
* For each file path in the observation's files_modified_json, finds the
* corresponding file node in nexus_nodes and writes a `modified_by` edge.
*
* This function is idempotent — calling it multiple times is safe.
*
* @param obsId - Brain observation node ID (format: 'observation:<source-id>')
* @param filesModifiedJson - JSON array of file paths (may be null)
* @param projectRoot - Absolute path to project root
* @param nexusNative - Optional pre-loaded nexus database handle
* @returns Count of edges written
*/
async function linkObservationToModifiedFiles(obsId, filesModifiedJson, projectRoot, nexusNative) {
	let edgeCount = 0;
	if (!filesModifiedJson) return 0;
	try {
		await getBrainDb(projectRoot);
		const brainNative = getBrainNativeDb();
		if (!brainNative) return 0;
		const nexusDb = nexusNative ?? getNexusNativeDb();
		if (!nexusDb) return 0;
		const filesArray = JSON.parse(filesModifiedJson);
		if (!Array.isArray(filesArray)) return 0;
		const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
		for (const filePath of filesArray) {
			if (!filePath || typeof filePath !== "string") continue;
			const nexusNode = nexusDb.prepare("SELECT id FROM nexus_nodes WHERE file_path = ? LIMIT 1").get(filePath);
			if (!nexusNode) continue;
			try {
				brainNative.prepare(`
            INSERT OR IGNORE INTO brain_page_edges
              (from_id, to_id, edge_type, weight, provenance, created_at)
            VALUES (?, ?, 'modified_by', 1.0, 'auto:file-modify', ?)
          `).run(nexusNode.id, obsId, now);
				edgeCount++;
			} catch (err) {
				console.warn("[graph-memory-bridge] modified_by edge insert failed:", err);
			}
		}
	} catch (err) {
		console.warn("[graph-memory-bridge] linkObservationToModifiedFiles failed:", err);
	}
	return edgeCount;
}
/**
* Write `mentions` edges from observation nodes to symbol nodes found in text.
*
* Scans the observation text for symbol names present in nexus_nodes using
* case-sensitive word-boundary matching. Caps results at 20 matches per
* observation to prevent runaway on large-text observations.
*
* This function is idempotent — calling it multiple times is safe.
*
* @param obsId - Brain observation node ID
* @param text - Text content to scan for symbol names
* @param projectRoot - Absolute path to project root
* @param nexusNative - Optional pre-loaded nexus database handle
* @returns Count of edges written
*/
async function linkObservationToMentionedSymbols(obsId, text, projectRoot, nexusNative) {
	let edgeCount = 0;
	if (!text || text.length === 0) return 0;
	try {
		await getBrainDb(projectRoot);
		const brainNative = getBrainNativeDb();
		if (!brainNative) return 0;
		const nexusDb = nexusNative ?? getNexusNativeDb();
		if (!nexusDb) return 0;
		const nexusNames = typedAll(nexusDb.prepare("SELECT id, name FROM nexus_nodes WHERE name IS NOT NULL LIMIT 10000"));
		if (nexusNames.length === 0) return 0;
		const symbolNameSet = new Set(nexusNames.map((n) => n.name));
		const symbolNames = Array.from(symbolNameSet);
		const candidates = [];
		for (const name of symbolNames) if (new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(text)) candidates.push(name);
		const matches = candidates.slice(0, 20);
		const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
		for (const name of matches) {
			const nexusNode = nexusNames.find((n) => n.name === name);
			if (!nexusNode) continue;
			try {
				brainNative.prepare(`
            INSERT OR IGNORE INTO brain_page_edges
              (from_id, to_id, edge_type, weight, provenance, created_at)
            VALUES (?, ?, 'mentions', 1.0, 'auto:symbol-ner', ?)
          `).run(obsId, nexusNode.id, now);
				edgeCount++;
			} catch (err) {
				console.warn("[graph-memory-bridge] mentions edge insert failed:", err);
			}
		}
	} catch (err) {
		console.warn("[graph-memory-bridge] linkObservationToMentionedSymbols failed:", err);
	}
	return edgeCount;
}
/**
* Write `documents` edges from decision nodes to symbol nodes found in context.
*
* Scans the decision's context text for symbol names present in nexus_nodes.
* Same NER pattern as linkObservationToMentionedSymbols but for decisions.
* Caps results at 20 matches per decision to prevent runaway.
*
* This function is idempotent — calling it multiple times is safe.
*
* @param decisionId - Brain decision node ID (format: 'decision:D-<id>')
* @param contextText - Context/rationale text to scan for symbol names
* @param projectRoot - Absolute path to project root
* @param nexusNative - Optional pre-loaded nexus database handle
* @returns Count of edges written
*/
async function linkDecisionToSymbols(decisionId, contextText, projectRoot, nexusNative) {
	let edgeCount = 0;
	if (!contextText || contextText.length === 0) return 0;
	try {
		await getBrainDb(projectRoot);
		const brainNative = getBrainNativeDb();
		if (!brainNative) return 0;
		const nexusDb = nexusNative ?? getNexusNativeDb();
		if (!nexusDb) return 0;
		const nexusNames = typedAll(nexusDb.prepare("SELECT id, name FROM nexus_nodes WHERE name IS NOT NULL LIMIT 10000"));
		if (nexusNames.length === 0) return 0;
		const symbolNameSet = new Set(nexusNames.map((n) => n.name));
		const symbolNames = Array.from(symbolNameSet);
		const candidates = [];
		for (const name of symbolNames) if (new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(contextText)) candidates.push(name);
		const matches = candidates.slice(0, 20);
		const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
		for (const name of matches) {
			const nexusNode = nexusNames.find((n) => n.name === name);
			if (!nexusNode) continue;
			try {
				brainNative.prepare(`
            INSERT OR IGNORE INTO brain_page_edges
              (from_id, to_id, edge_type, weight, provenance, created_at)
            VALUES (?, ?, 'documents', 1.0, 'auto:decision-ner', ?)
          `).run(decisionId, nexusNode.id, now);
				edgeCount++;
			} catch (err) {
				console.warn("[graph-memory-bridge] documents edge insert failed:", err);
			}
		}
	} catch (err) {
		console.warn("[graph-memory-bridge] linkDecisionToSymbols failed:", err);
	}
	return edgeCount;
}
/**
* Scan brain memory nodes for entity references and match them against nexus.
*
* For each brain node, extracts:
* - File path references → matched against nexus_nodes.file_path (exact)
* - Symbol name references → matched against nexus_nodes.name (exact, then fuzzy)
* - Files modified (observations) → writes modified_by edges via linkObservationToModifiedFiles
* - Symbol mentions (observations/decisions) → writes mentions/documents edges via symbol NER
*
* Matching edges are written to brain_page_edges with edge types: code_reference, modified_by,
* mentions, documents. This function is idempotent — existing edges are skipped.
*
* Should be called from runConsolidation() as a best-effort step. All errors
* are caught and logged; never throws.
*
* @param projectRoot - Absolute path to project root (locates brain.db)
* @returns Summary of scanned entries and created links
*/
async function autoLinkMemories(projectRoot) {
	const result = {
		scanned: 0,
		linked: 0,
		alreadyLinked: 0,
		links: []
	};
	try {
		await getBrainDb(projectRoot);
		const brainNative = getBrainNativeDb();
		await getNexusDb();
		const nexusNative = getNexusNativeDb();
		if (!brainNative || !nexusNative) return result;
		const brainNodes = typedAll(brainNative.prepare(`
        SELECT id, node_type, label, quality_score, metadata_json
        FROM brain_page_nodes
        WHERE node_type IN ('observation', 'decision', 'pattern', 'learning')
          AND quality_score >= 0.3
        ORDER BY quality_score DESC
        LIMIT 500
      `));
		result.scanned = brainNodes.length;
		if (brainNodes.length === 0) return result;
		const nexusNodes = typedAll(nexusNative.prepare(`
        SELECT id, label, name, file_path, kind
        FROM nexus_nodes
        WHERE kind NOT IN ('community', 'process', 'folder')
        LIMIT 20000
      `));
		if (nexusNodes.length === 0) return result;
		const byFilePath = /* @__PURE__ */ new Map();
		const byNameExact = /* @__PURE__ */ new Map();
		const byNameLower = /* @__PURE__ */ new Map();
		for (const node of nexusNodes) {
			if (node.file_path) {
				const fp = node.file_path.toLowerCase();
				const existing = byFilePath.get(fp) ?? [];
				existing.push(node);
				byFilePath.set(fp, existing);
			}
			if (node.name) {
				const exact = byNameExact.get(node.name) ?? [];
				exact.push(node);
				byNameExact.set(node.name, exact);
				const lower = node.name.toLowerCase();
				const fuzzy = byNameLower.get(lower) ?? [];
				fuzzy.push(node);
				byNameLower.set(lower, fuzzy);
			}
		}
		const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
		const existingEdges = /* @__PURE__ */ new Set();
		const rawEdges = typedAll(brainNative.prepare(`
        SELECT from_id, to_id FROM brain_page_edges WHERE edge_type = 'code_reference'
      `));
		for (const e of rawEdges) existingEdges.add(`${e.from_id}|${e.to_id}`);
		for (const brainNode of brainNodes) {
			const corpus = `${brainNode.label} ${metadataText(brainNode.metadata_json)}`;
			const filePaths = extractFilePaths(corpus);
			const symbolCandidates = extractSymbolCandidates(corpus);
			const candidates = [];
			for (const fp of filePaths) {
				const matches = byFilePath.get(fp.toLowerCase());
				if (matches) for (const n of matches) candidates.push({
					nexusNode: n,
					strategy: "exact-file",
					weight: 1
				});
			}
			for (const sym of symbolCandidates) {
				const exactMatches = byNameExact.get(sym);
				if (exactMatches) for (const n of exactMatches) candidates.push({
					nexusNode: n,
					strategy: "exact-symbol",
					weight: 1
				});
			}
			const exactSymSet = new Set(symbolCandidates.flatMap((s) => byNameExact.get(s) ?? []).map((n) => n.id));
			for (const sym of symbolCandidates) {
				if (sym.length < 5) continue;
				const lower = sym.toLowerCase();
				const fuzzyMatches = byNameLower.get(lower);
				if (fuzzyMatches) {
					for (const n of fuzzyMatches) if (!exactSymSet.has(n.id)) candidates.push({
						nexusNode: n,
						strategy: "fuzzy-symbol",
						weight: .6
					});
				}
			}
			const bestByNexusId = /* @__PURE__ */ new Map();
			for (const c of candidates) {
				const existing = bestByNexusId.get(c.nexusNode.id);
				if (!existing || c.weight > existing.weight) bestByNexusId.set(c.nexusNode.id, c);
			}
			const sortedCandidates = Array.from(bestByNexusId.values()).sort((a, b) => b.weight - a.weight).slice(0, 10);
			for (const { nexusNode, strategy, weight } of sortedCandidates) {
				const edgeKey = `${brainNode.id}|${nexusNode.id}`;
				if (existingEdges.has(edgeKey)) {
					result.alreadyLinked++;
					continue;
				}
				brainNative.prepare(`
            INSERT OR IGNORE INTO brain_page_nodes
              (id, node_type, label, quality_score, content_hash, metadata_json, last_activity_at, created_at, updated_at)
            VALUES (?, ?, ?, ?, NULL, NULL, ?, ?, ?)
          `).run(brainNode.id, brainNode.node_type, brainNode.label, brainNode.quality_score, now, now, now);
				try {
					brainNative.prepare(`
              INSERT OR IGNORE INTO brain_page_edges
                (from_id, to_id, edge_type, weight, provenance, created_at)
              VALUES (?, ?, 'code_reference', ?, ?, ?)
            `).run(brainNode.id, nexusNode.id, weight, `auto:${strategy}`, now);
					existingEdges.add(edgeKey);
					result.linked++;
					result.links.push({
						brainNodeId: brainNode.id,
						nexusNodeId: nexusNode.id,
						nexusLabel: nexusNode.label,
						matchStrategy: strategy,
						weight
					});
				} catch (edgeErr) {
					console.warn("[graph-memory-bridge] edge insert failed:", edgeErr);
				}
			}
		}
		try {
			const observations = typedAll(brainNative.prepare(`
          SELECT id, files_modified_json, narrative
          FROM brain_observations
          WHERE quality_score >= 0.3
          LIMIT 200
        `));
			for (const obs of observations) {
				if (obs.files_modified_json) {
					const edgesWritten = await linkObservationToModifiedFiles(`observation:${obs.id}`, obs.files_modified_json, projectRoot, nexusNative);
					result.linked += edgesWritten;
				}
				if (obs.narrative) {
					const edgesWritten = await linkObservationToMentionedSymbols(`observation:${obs.id}`, obs.narrative, projectRoot, nexusNative);
					result.linked += edgesWritten;
				}
			}
			const decisions = typedAll(brainNative.prepare(`
          SELECT id, decision, rationale
          FROM brain_decisions
          WHERE quality_score >= 0.3
          LIMIT 200
        `));
			for (const dec of decisions) {
				const contextText = `${dec.decision ?? ""} ${dec.rationale ?? ""}`;
				if (contextText.trim().length > 0) {
					const edgesWritten = await linkDecisionToSymbols(`decision:${dec.id}`, contextText, projectRoot, nexusNative);
					result.linked += edgesWritten;
				}
			}
		} catch (err) {
			console.warn("[graph-memory-bridge] specialized edge writers failed:", err);
		}
	} catch (err) {
		console.warn("[graph-memory-bridge] autoLinkMemories failed:", err);
	}
	return result;
}

export { autoLinkMemories as a };
//# sourceMappingURL=graph-memory-bridge2-C7Y32Mk4.js.map
