import { b as getLogger } from './paths-2M_UxD_H.js';
import { g as getBrainAccessor } from './memory-accessor2-BzlgmY-Z.js';
import { t as typedAll } from './typed-query-l1D_QElt.js';
import { E as EDGE_TYPES } from './edge-types-CgBvb0w1.js';
import { createHash } from 'node:crypto';
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:module';
import 'node:child_process';
import 'node:process';
import 'node:async_hooks';
import './memory-schema-Bi7cChwo.js';
import './indexes-aYlULWG3.js';
import './utils-BvHPJOVk.js';
import './chunk-DDP5xfzh.js';
import './unique-constraint-BM-Apx1H.js';
import './sqlite-Dsbe0wec.js';
import './dual-scope-db-ATrwq0LH.js';
import './conduit-Ca_OFWR_.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-lifecycle.js
/**
* BRAIN lifecycle operations — temporal decay, memory consolidation, and tier promotion.
*
* Reduces confidence of stale learnings based on age, following an
* exponential decay model: new_confidence = confidence * (decayRate ^ daysSinceUpdate).
*
* Consolidation groups old observations by keyword overlap and merges
* clusters into summary observations, archiving the originals.
*
* Wave 3 additions (T549):
* - runTierPromotion — promotes entries through short→medium→long based on citation count,
*   age, and verification; soft-evicts stale short-term entries.
* - runConsolidation — orchestrates all consolidation steps (dedup, quality recompute,
*   tier promotion, contradiction detection, soft eviction, graph strengthening, summaries).
*
* @task T5394 T5395 T549
* @epic T5149
*/
/** Stop words excluded from keyword extraction. */
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",
	"need",
	"dare",
	"ought",
	"used",
	"to",
	"of",
	"in",
	"for",
	"on",
	"with",
	"at",
	"by",
	"from",
	"as",
	"into",
	"through",
	"during",
	"before",
	"after",
	"above",
	"below",
	"between",
	"out",
	"off",
	"over",
	"under",
	"again",
	"further",
	"then",
	"once",
	"and",
	"but",
	"or",
	"nor",
	"not",
	"so",
	"yet",
	"both",
	"either",
	"neither",
	"each",
	"every",
	"all",
	"any",
	"few",
	"more",
	"most",
	"other",
	"some",
	"such",
	"no",
	"only",
	"own",
	"same",
	"than",
	"too",
	"very",
	"just",
	"because",
	"until",
	"while",
	"about",
	"against",
	"this",
	"that",
	"these",
	"those",
	"it",
	"its"
]);
/**
* Extract significant keywords from text for grouping.
* Filters stop words and short tokens, returns lowercase words.
*/
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;
}
/**
* Calculate keyword overlap ratio between two sets.
* Returns the Jaccard similarity coefficient.
*/
function keywordOverlap(a, b) {
	if (a.size === 0 || b.size === 0) return 0;
	let intersection = 0;
	for (const w of a) if (b.has(w)) intersection++;
	const union = a.size + b.size - intersection;
	return union > 0 ? intersection / union : 0;
}
/**
* Consolidate old observations by keyword similarity.
*
* Groups observations older than `olderThanDays` by FTS5 keyword overlap.
* For groups with at least `minClusterSize` entries, creates one summary
* observation and marks originals as archived (updated_at set, narrative
* prefixed with [ARCHIVED]).
*
* @param projectRoot - Project root directory for brain.db resolution
* @param options - Consolidation configuration
* @returns Counts of grouped, merged, and archived observations
*/
async function consolidateMemories(projectRoot, options) {
	const olderThanDays = options?.olderThanDays;
	const minClusterSize = options?.minClusterSize;
	const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return {
		grouped: 0,
		merged: 0,
		archived: 0
	};
	const cutoffDate = (/* @__PURE__ */ new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1e3)).toISOString().replace("T", " ").slice(0, 19);
	const oldObservations = typedAll(nativeDb.prepare(`
    SELECT id, type, title, narrative, project, created_at
    FROM brain_observations
    WHERE created_at < ?
      AND narrative NOT LIKE '[ARCHIVED]%'
    ORDER BY created_at DESC
  `), cutoffDate);
	if (oldObservations.length < minClusterSize) return {
		grouped: 0,
		merged: 0,
		archived: 0
	};
	const entries = oldObservations.map((obs) => ({
		...obs,
		keywords: extractKeywords(`${obs.title} ${obs.narrative ?? ""}`),
		clustered: false
	}));
	const clusters = [];
	const overlapThreshold = .3;
	for (let i = 0; i < entries.length; i++) {
		if (entries[i].clustered) continue;
		const cluster = [entries[i]];
		entries[i].clustered = true;
		for (let j = i + 1; j < entries.length; j++) {
			if (entries[j].clustered) continue;
			if (entries[i].type !== entries[j].type) continue;
			if (keywordOverlap(entries[i].keywords, entries[j].keywords) >= overlapThreshold) {
				cluster.push(entries[j]);
				entries[j].clustered = true;
			}
		}
		if (cluster.length >= minClusterSize) clusters.push(cluster);
	}
	let grouped = 0;
	let merged = 0;
	let archived = 0;
	const accessor = await getBrainAccessor(projectRoot);
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	for (const cluster of clusters) {
		grouped += cluster.length;
		const titles = cluster.map((c) => c.title).join("; ");
		const summaryTitle = `Consolidated: ${cluster[0].type} observations (${cluster.length} entries)`;
		const summaryNarrative = `Merged from ${cluster.length} observations: ${titles.slice(0, 500)}`;
		const contentHash = createHash("sha256").update(summaryTitle + summaryNarrative).digest("hex").slice(0, 16);
		const id = `O-${Date.now().toString(36)}`;
		await accessor.addObservation({
			id,
			type: cluster[0].type,
			title: summaryTitle,
			narrative: summaryNarrative,
			contentHash,
			project: cluster[0].project ?? null,
			sourceType: "agent",
			createdAt: now
		});
		merged++;
		for (const obs of cluster) {
			nativeDb.prepare(`
        UPDATE brain_observations
        SET narrative = '[ARCHIVED] ' || COALESCE(narrative, ''),
            updated_at = ?
        WHERE id = ?
      `).run(now, obs.id);
			archived++;
		}
	}
	return {
		grouped,
		merged,
		archived
	};
}
/**
* Run tier promotion for all memory tables.
*
* Promotion rules (per spec §1.1–§1.3, relaxed in T614):
* - short → medium:
*     A. (citationCount >= 3 AND age > 24h) — citation-based track
*     B. (qualityScore >= 0.7 AND age > 24h) — quality fast-track
*     C. (verified = true AND age > 24h) — owner-verified track
*   Note: `verified` is no longer a hard gate for routes A and B.
*   Requiring verified=true on all paths caused all 235 short-tier observations
*   to be permanently stuck (T614 bug).
* - medium → long:
*     (citationCount >= 5 AND age > 7 days) OR (verified = true AND age > 7 days)
*   Verified entries accelerate to long-tier without citation threshold.
*
* Eviction rules:
* - short-term entries older than 7 days with no promotion eligibility are
*   soft-evicted (invalidAt = now).
* - long-term entries are NEVER auto-evicted.
*
* @param projectRoot - Project root directory for brain.db resolution
* @returns Lists of promoted and evicted entries
*/
async function runTierPromotion(projectRoot) {
	const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return {
		promoted: [],
		evicted: []
	};
	const promoted = [];
	const evicted = [];
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	const age24h = (/* @__PURE__ */ new Date(Date.now() - 1440 * 60 * 1e3)).toISOString().replace("T", " ").slice(0, 19);
	const age7d = (/* @__PURE__ */ new Date(Date.now() - 10080 * 60 * 1e3)).toISOString().replace("T", " ").slice(0, 19);
	const evictionCutoff = (/* @__PURE__ */ new Date(Date.now() - 10080 * 60 * 1e3)).toISOString().replace("T", " ").slice(0, 19);
	for (const { table, dateCol } of [
		{
			table: "brain_observations",
			dateCol: "created_at"
		},
		{
			table: "brain_learnings",
			dateCol: "created_at"
		},
		{
			table: "brain_patterns",
			dateCol: "extracted_at"
		},
		{
			table: "brain_decisions",
			dateCol: "created_at"
		}
	]) {
		let shortToMedium = [];
		try {
			shortToMedium = typedAll(nativeDb.prepare(`
          SELECT id, citation_count, quality_score, verified
          FROM ${table}
          WHERE memory_tier = 'short'
            AND invalid_at IS NULL
            AND ${dateCol} < ?
            AND (
              citation_count >= 3
              OR quality_score >= 0.7
              OR verified = 1
            )
        `), age24h);
		} catch {
			continue;
		}
		for (const row of shortToMedium) try {
			let reason;
			if (row.citation_count >= 3) reason = `citationCount=${row.citation_count} >= 3, age > 24h`;
			else if ((row.quality_score ?? 0) >= .7) reason = `qualityScore=${row.quality_score?.toFixed(2)} >= 0.70, age > 24h`;
			else reason = `verified=true, age > 24h`;
			nativeDb.prepare(`UPDATE ${table} SET memory_tier = 'medium', updated_at = ?, tier_promoted_at = ?, tier_promotion_reason = ? WHERE id = ?`).run(now, now, reason, row.id);
			promoted.push({
				id: row.id,
				table,
				fromTier: "short",
				toTier: "medium",
				reason
			});
		} catch {}
		let mediumToLong = [];
		try {
			mediumToLong = typedAll(nativeDb.prepare(`
          SELECT id, citation_count, quality_score, verified
          FROM ${table}
          WHERE memory_tier = 'medium'
            AND invalid_at IS NULL
            AND ${dateCol} < ?
            AND (citation_count >= 5 OR verified = 1)
        `), age7d);
		} catch {
			continue;
		}
		for (const row of mediumToLong) try {
			const reason = row.citation_count >= 5 ? `citationCount=${row.citation_count} >= 5, age > 7d` : `verified=true, age > 7d`;
			nativeDb.prepare(`UPDATE ${table} SET memory_tier = 'long', updated_at = ?, tier_promoted_at = ?, tier_promotion_reason = ? WHERE id = ?`).run(now, now, reason, row.id);
			promoted.push({
				id: row.id,
				table,
				fromTier: "medium",
				toTier: "long",
				reason
			});
		} catch {}
		let toEvict = [];
		try {
			toEvict = typedAll(nativeDb.prepare(`
          SELECT id, quality_score
          FROM ${table}
          WHERE memory_tier = 'short'
            AND invalid_at IS NULL
            AND ${dateCol} < ?
            AND (verified = 0 OR verified IS NULL)
            AND (quality_score IS NULL OR quality_score < 0.5)
        `), evictionCutoff);
		} catch {
			continue;
		}
		for (const row of toEvict) try {
			nativeDb.prepare(`UPDATE ${table} SET invalid_at = ?, updated_at = ? WHERE id = ?`).run(now, now, row.id);
			evicted.push({
				id: row.id,
				table,
				tier: "short",
				reason: `age > 7d, unverified, qualityScore=${row.quality_score?.toFixed(2) ?? "null"} < 0.5`
			});
		} catch {}
	}
	return {
		promoted,
		evicted
	};
}
/**
* Run the full sleep-time consolidation pipeline.
*
* This is the "Letta OS sleep-time compute" pattern — consolidation runs
* during idle time (after session end), never during active work.
*
* Steps (in order):
*   1. Deduplication — merge entries with >0.85 content similarity
*   2. Quality recompute — recalculate scores with current age/citations
*   3. Tier promotion — runTierPromotion()
*   4. Contradiction detection — from brain-consolidator
*   5. Soft eviction — invalidate low-quality medium-term entries
*   6. Graph edge strengthening — increment weight on frequently-traversed edges
*   7. Summary generation — existing consolidateMemories() for large clusters
*   8. Code↔memory graph linking
*   9a. R-STDP reward backfill — assign reward_signal from task outcomes (T681)
*   9a.5. Outcome correlation — correlateOutcomes quality adjustments (T994)
*   9b. STDP timing-dependent plasticity — apply Δw using reward_signal (T679)
*   9c. Homeostatic decay — synaptic scaling + pruning (T690)
*   9d. History retention — prune brain_plasticity_events + brain_weight_history (T10348)
*   9e. Consolidation event log — INSERT into brain_consolidation_events (T694)
*   9f. Hard-sweeper — DELETE prune_candidate=1 + quality<0.2 + citations=0 + age>30d (T995)
*
* All steps are BEST-EFFORT — any step failure is caught and logged to console.warn.
*
* @param projectRoot - Project root directory for brain.db resolution
* @param sessionId - Active session ID, passed to backfillRewardSignals (Step 9a).
*   If null/undefined, Step 9a is a no-op (no task correlation available).
* @param trigger - What triggered this consolidation run (for observability).
*   Defaults to 'session_end'. Written to brain_consolidation_events.
* @returns Aggregated counts from each consolidation step
*/
async function runConsolidation(projectRoot, sessionId, trigger = "session_end") {
	const consolidationStartMs = Date.now();
	const result = {
		deduplicated: 0,
		qualityRecomputed: 0,
		tierPromotions: {
			promoted: [],
			evicted: []
		},
		contradictions: 0,
		softEvicted: 0,
		edgesStrengthened: 0,
		nexusEdgesStrengthened: 0,
		summariesGenerated: 0
	};
	try {
		result.deduplicated = await deduplicateByEmbedding(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 1 deduplication failed:", err);
	}
	try {
		result.qualityRecomputed = await recomputeQualityScores(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 2 quality recompute failed:", err);
	}
	try {
		result.tierPromotions = await runTierPromotion(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 3 tier promotion failed:", err);
	}
	try {
		result.autoExtractPromotion = await fulfillPromotionLog(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 3b promotion fulfillment failed:", err);
	}
	try {
		const { detectContradictions } = await import('./brain-consolidator-DzxPaTrt.js');
		result.contradictions = (await detectContradictions(projectRoot)).length;
	} catch (err) {
		console.warn("[consolidation] Step 4 contradiction detection failed:", err);
	}
	try {
		const { dedupePatterns } = await import('./brain-consolidator-DzxPaTrt.js');
		result.patternDeduped = await dedupePatterns(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 4b pattern dedup failed:", err);
	}
	try {
		result.softEvicted = await softEvictLowQualityMedium(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 5 soft eviction failed:", err);
	}
	try {
		result.edgesStrengthened = await strengthenCoRetrievedEdges(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 6 edge strengthening failed:", err);
	}
	try {
		const { extractNexusPairsFromRetrievalLog, strengthenNexusCoAccess } = await import('./nexus-plasticity-BrrQrtbL.js');
		const nexusPairs = await extractNexusPairsFromRetrievalLog(projectRoot);
		if (nexusPairs.length > 0) result.nexusEdgesStrengthened = (await strengthenNexusCoAccess(nexusPairs)).strengthened;
		else result.nexusEdgesStrengthened = 0;
	} catch (err) {
		console.warn("[consolidation] Step 6b nexus plasticity failed:", err);
		result.nexusEdgesStrengthened = 0;
	}
	try {
		const { applyPlasticityDecay } = await import('./nexus-plasticity-BrrQrtbL.js');
		result.nexusEdgesDecayed = (await applyPlasticityDecay()).updated;
	} catch (err) {
		console.warn("[consolidation] Step 6c plasticity decay failed:", err);
	}
	try {
		result.summariesGenerated = (await consolidateMemories(projectRoot, {
			olderThanDays: 30,
			minClusterSize: 5
		})).merged;
	} catch (err) {
		console.warn("[consolidation] Step 7 summary generation failed:", err);
	}
	try {
		const { autoLinkMemories } = await import('./graph-memory-bridge-CqXJdy0K.js');
		result.graphLinksCreated = (await autoLinkMemories(projectRoot)).linked;
	} catch (err) {
		console.warn("[consolidation] Step 8 graph memory bridge failed:", err);
	}
	try {
		const { backfillRewardSignals } = await import('./brain-stdp2-CIsmzhz3.js');
		result.rewardBackfilled = await backfillRewardSignals(projectRoot, sessionId ?? null);
	} catch (err) {
		console.warn("[consolidation] Step 9a reward backfill failed:", err);
	}
	try {
		const { correlateOutcomes } = await import('./quality-feedback-2etyCgV3.js');
		const correlateResult = await correlateOutcomes(projectRoot);
		result.outcomeCorrelation = {
			boosted: correlateResult.boosted,
			penalized: correlateResult.penalized,
			flaggedForPruning: correlateResult.flaggedForPruning
		};
	} catch (err) {
		console.warn("[consolidation] Step 9a.5 outcome correlation failed:", err);
	}
	try {
		const { applyStdpPlasticity, shouldRunPlasticity } = await import('./brain-stdp2-CIsmzhz3.js');
		if (await shouldRunPlasticity(projectRoot, sessionId ?? null, 2)) result.stdpPlasticity = await applyStdpPlasticity(projectRoot);
		else result.stdpPlasticity = {
			ltpEvents: 0,
			ltdEvents: 0,
			edgesCreated: 0,
			pairsExamined: 0
		};
	} catch (err) {
		console.warn("[consolidation] Step 9b STDP plasticity failed:", err);
	}
	try {
		const { applyHomeostaticDecay } = await import('./brain-stdp2-CIsmzhz3.js');
		result.homeostaticDecay = await applyHomeostaticDecay(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 9c homeostatic decay failed:", err);
	}
	try {
		const { pruneStaleHistory } = await import('./brain-stdp2-CIsmzhz3.js');
		result.historyRetention = await pruneStaleHistory(projectRoot);
	} catch (err) {
		console.warn("[consolidation] Step 9d history retention failed:", err);
	}
	try {
		const { runPruneSweep } = await import('./brain-maintenance2-QDa0k5B9.js');
		const sweepResult = await runPruneSweep(projectRoot);
		result.pruneSweep = {
			deleted: sweepResult.deleted,
			wouldDelete: sweepResult.wouldDelete,
			dryRun: sweepResult.dryRun
		};
	} catch (err) {
		console.warn("[consolidation] Step 9f prune sweep failed:", err);
	}
	try {
		const { consolidateAttention } = await import('./attention-consolidate-CHZm70iX.js');
		const attentionResult = await consolidateAttention(projectRoot);
		result.attentionConsolidation = {
			reviewed: attentionResult.reviewed,
			promoted: attentionResult.promoted,
			kept: attentionResult.kept,
			discarded: attentionResult.discarded
		};
	} catch (err) {
		getLogger("consolidation").warn({ err }, "Step 9g attention consolidation failed");
	}
	try {
		const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
		await getBrainDb(projectRoot);
		const nativeDb = getBrainNativeDb();
		if (nativeDb) {
			let consolidationEventsExist = false;
			try {
				nativeDb.prepare("SELECT 1 FROM brain_consolidation_events LIMIT 1").get();
				consolidationEventsExist = true;
			} catch {}
			if (consolidationEventsExist) {
				const durationMs = Date.now() - consolidationStartMs;
				const stepResultsJson = JSON.stringify(result);
				nativeDb.prepare(`INSERT INTO brain_consolidation_events
               (trigger, session_id, step_results_json, duration_ms, succeeded)
             VALUES (?, ?, ?, ?, 1)`).run(trigger, sessionId ?? null, stepResultsJson, durationMs);
			}
		}
	} catch (err) {
		console.warn("[consolidation] Step 9e consolidation event log failed:", err);
	}
	try {
		const { runSleepConsolidation } = await import('./sleep-consolidation-BBHx9FOA.js');
		const sleepResult = await runSleepConsolidation(projectRoot);
		result.sleepConsolidation = {
			ran: sleepResult.ran,
			merged: sleepResult.mergeDuplicates.merged,
			pruned: sleepResult.pruneStale.pruned,
			patternsGenerated: sleepResult.strengthenPatterns.patternsGenerated,
			insightsStored: sleepResult.generateInsights.insightsStored
		};
		if (sleepResult.ran) result.summariesGenerated += sleepResult.strengthenPatterns.patternsGenerated + sleepResult.generateInsights.insightsStored;
		else console.warn("[consolidation] Step 10 sleep consolidation skipped (disabled or no LLM)");
	} catch (err) {
		console.warn("[consolidation] Step 10 sleep consolidation failed:", err);
	}
	return result;
}
/**
* Fulfill pending entries in brain_promotion_log by inserting them into
* brain_learnings or brain_patterns.
*
* `promoteObservationsToTyped` (Step 3 of runConsolidation) only writes audit
* rows to brain_promotion_log — it does NOT insert into the typed tables.
* This step reads those pending log rows and performs the actual inserts.
*
* Idempotent: rows that have already been fulfilled (fulfilled_at IS NOT NULL)
* are skipped.
*
* @param projectRoot - Project root for brain.db resolution
* @returns AutoExtractMetrics describing what happened
*
* @task T1903
*/
async function fulfillPromotionLog(projectRoot) {
	const metrics = {
		invocations: 0,
		candidates: 0,
		promoted: 0,
		rejected: {
			already_fulfilled: 0,
			store_error: 0,
			no_narrative: 0,
			dedup_hash: 0,
			other: 0
		}
	};
	const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return metrics;
	try {
		nativeDb.prepare("SELECT 1 FROM brain_promotion_log LIMIT 1").get();
	} catch {
		return metrics;
	}
	let pending = [];
	try {
		pending = typedAll(nativeDb.prepare(`
        SELECT id, observation_id, to_tier, score, fulfilled_at
        FROM brain_promotion_log
        WHERE fulfilled_at IS NULL
          AND decided_by = 'composite-scorer'
        ORDER BY score DESC
        LIMIT 200
      `));
	} catch {
		return metrics;
	}
	metrics.invocations = pending.length;
	if (pending.length === 0) return metrics;
	const obsIds = pending.map((r) => r.observation_id);
	const placeholders = obsIds.map(() => "?").join(", ");
	const obsMap = /* @__PURE__ */ new Map();
	try {
		const rows = typedAll(nativeDb.prepare(`SELECT id, type, title, narrative, created_at, citation_count
         FROM brain_observations
         WHERE id IN (${placeholders}) AND invalid_at IS NULL`), ...obsIds);
		for (const row of rows) obsMap.set(row.id, row);
	} catch {
		return metrics;
	}
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	const { checkHashDedup } = await import('./extraction-gate-Ctn6QtD_.js');
	for (const logRow of pending) {
		const obs = obsMap.get(logRow.observation_id);
		if (!obs) {
			metrics.rejected.other++;
			continue;
		}
		const content = obs.narrative ?? obs.title;
		if (!content?.trim()) {
			metrics.rejected.no_narrative++;
			try {
				nativeDb.prepare(`UPDATE brain_promotion_log SET fulfilled_at = ?, fulfillment_note = 'no-narrative' WHERE id = ?`).run(now, logRow.id);
			} catch {}
			continue;
		}
		metrics.candidates++;
		if ((await checkHashDedup(projectRoot, content, logRow.to_tier === "pattern" ? "brain_patterns" : "brain_learnings")).matched) {
			metrics.rejected.dedup_hash++;
			try {
				nativeDb.prepare(`UPDATE brain_promotion_log SET fulfilled_at = ?, fulfillment_note = 'dedup-hash' WHERE id = ?`).run(now, logRow.id);
			} catch {}
			continue;
		}
		try {
			if (logRow.to_tier === "pattern") {
				const { storePattern } = await import('./patterns-C696SQfJ.js');
				await storePattern(projectRoot, {
					type: "workflow",
					pattern: content,
					context: obs.title,
					_skipGate: true
				});
			} else {
				const { storeLearning } = await import('./learnings-WTVKqfNE.js');
				await storeLearning(projectRoot, {
					insight: content,
					source: `promotion-log:${logRow.id}`,
					confidence: Math.min(logRow.score, 1),
					actionable: obs.type === "bugfix" || obs.type === "discovery",
					_skipGate: true
				});
			}
			metrics.promoted++;
			try {
				nativeDb.prepare(`UPDATE brain_promotion_log SET fulfilled_at = ?, fulfillment_note = 'stored' WHERE id = ?`).run(now, logRow.id);
			} catch {}
		} catch {
			metrics.rejected.store_error++;
		}
	}
	return metrics;
}
/**
* Merge entries with high embedding similarity (> 0.85 cosine similarity).
*
* For each pair above the threshold: keep the higher-quality entry, transfer
* citationCount, and soft-evict the clone (set invalidAt). When embeddings
* are unavailable, falls back to exact content-hash deduplication.
*
* @param projectRoot - Project root for brain.db resolution
* @returns Count of merged/evicted duplicate entries
*/
async function deduplicateByEmbedding(projectRoot) {
	const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return 0;
	let merged = 0;
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	const dupHashes = typedAll(nativeDb.prepare(`
      SELECT content_hash, COUNT(*) as cnt
      FROM brain_observations
      WHERE content_hash IS NOT NULL
        AND invalid_at IS NULL
      GROUP BY content_hash
      HAVING cnt > 1
    `));
	for (const dup of dupHashes) {
		const dupes = typedAll(nativeDb.prepare(`
        SELECT id, quality_score, citation_count
        FROM brain_observations
        WHERE content_hash = ?
          AND invalid_at IS NULL
        ORDER BY quality_score DESC, citation_count DESC
      `), dup.content_hash);
		if (dupes.length < 2) continue;
		const [keeper, ...clones] = dupes;
		if (!keeper) continue;
		let totalCitations = keeper.citation_count;
		for (const clone of clones) {
			totalCitations += clone.citation_count;
			nativeDb.prepare(`UPDATE brain_observations SET invalid_at = ?, updated_at = ? WHERE id = ?`).run(now, now, clone.id);
			merged++;
		}
		if (totalCitations !== keeper.citation_count) nativeDb.prepare(`UPDATE brain_observations SET citation_count = ?, updated_at = ? WHERE id = ?`).run(totalCitations, now, keeper.id);
	}
	return merged;
}
/**
* Recompute quality scores factoring in current citation count and age.
*
* Applies a citation boost: +0.01 per citation, capped at +0.15.
* Applies age decay only on episodic entries (observations, episodic learnings).
*
* @param projectRoot - Project root for brain.db resolution
* @returns Count of entries updated
*/
async function recomputeQualityScores(projectRoot) {
	const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return 0;
	let updated = 0;
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	try {
		const r = nativeDb.prepare(`
      UPDATE brain_observations
      SET quality_score = MIN(1.0, COALESCE(quality_score, 0.5) + MIN(0.15, citation_count * 0.01)),
          updated_at = ?
      WHERE citation_count > 0
        AND invalid_at IS NULL
    `).run(now);
		updated += typeof r.changes === "number" ? r.changes : 0;
	} catch {}
	try {
		const r = nativeDb.prepare(`
      UPDATE brain_learnings
      SET quality_score = MIN(1.0, COALESCE(quality_score, 0.5) + MIN(0.15, citation_count * 0.01)),
          updated_at = ?
      WHERE citation_count > 0
        AND invalid_at IS NULL
    `).run(now);
		updated += typeof r.changes === "number" ? r.changes : 0;
	} catch {}
	return updated;
}
/**
* Soft-evict medium-term entries that have decayed below quality threshold.
*
* Entries are invalidated (invalidAt = now) when:
* - memoryTier = 'medium'
* - qualityScore < 0.30 AND age > 30 days
* - NOT long-term (protected from auto-eviction)
*
* @param projectRoot - Project root for brain.db resolution
* @returns Count of entries soft-evicted
*/
async function softEvictLowQualityMedium(projectRoot) {
	const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return 0;
	let evicted = 0;
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	const age30d = (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString().replace("T", " ").slice(0, 19);
	for (const { table, dateCol } of [
		{
			table: "brain_observations",
			dateCol: "created_at"
		},
		{
			table: "brain_learnings",
			dateCol: "created_at"
		},
		{
			table: "brain_patterns",
			dateCol: "extracted_at"
		},
		{
			table: "brain_decisions",
			dateCol: "created_at"
		}
	]) try {
		const r = nativeDb.prepare(`
        UPDATE ${table}
        SET invalid_at = ?, updated_at = ?
        WHERE memory_tier = 'medium'
          AND invalid_at IS NULL
          AND quality_score IS NOT NULL
          AND quality_score < 0.30
          AND ${dateCol} < ?
      `).run(now, now, age30d);
		evicted += typeof r.changes === "number" ? r.changes : 0;
	} catch {}
	return evicted;
}
/**
* Strengthen edges between frequently co-retrieved brain graph nodes.
*
* Queries the brain_retrieval_log table (if it exists) to find pairs of
* entries that were returned together frequently (>= 3 times in the last
* 30 days). For each qualifying pair, increments the edge weight by 0.1
* (capped at 1.0) in brain_page_edges.
*
* Gracefully no-ops when brain_retrieval_log does not yet exist.
*
* @param projectRoot - Project root for brain.db resolution
* @returns Count of edges strengthened
*/
/**
* Parse entry_ids which may be either JSON array or comma-separated string (BUG-2 fix).
*
* @param raw - Raw entry_ids value from brain_retrieval_log
* @returns Parsed array of entry IDs, or empty array if parse fails
*/
function parseEntryIds(raw) {
	const trimmed = raw.trim();
	if (trimmed.startsWith("[")) try {
		const parsed = JSON.parse(trimmed);
		return Array.isArray(parsed) ? parsed : [];
	} catch {
		return [];
	}
	return trimmed.split(",").map((s) => s.trim()).filter(Boolean);
}
async function strengthenCoRetrievedEdges(projectRoot) {
	const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return 0;
	try {
		nativeDb.prepare("SELECT 1 FROM brain_retrieval_log LIMIT 1").get();
	} catch {
		return 0;
	}
	const age30d = (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString().replace("T", " ").slice(0, 19);
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	let logRows = [];
	try {
		logRows = typedAll(nativeDb.prepare(`SELECT entry_ids, query FROM brain_retrieval_log WHERE created_at >= ? LIMIT 1000`), age30d);
	} catch {
		return 0;
	}
	const coOccurrence = /* @__PURE__ */ new Map();
	for (const row of logRows) {
		const ids = parseEntryIds(row.entry_ids);
		if (ids.length < 2) continue;
		for (let i = 0; i < ids.length; i++) for (let j = i + 1; j < ids.length; j++) {
			const a = ids[i];
			const b = ids[j];
			const key = a < b ? `${a}|${b}` : `${b}|${a}`;
			const querySet = coOccurrence.get(key) ?? /* @__PURE__ */ new Set();
			querySet.add(row.query);
			coOccurrence.set(key, querySet);
		}
	}
	let strengthened = 0;
	for (const [pair, querySet] of coOccurrence) {
		if (querySet.size < 3) continue;
		const [fromId, toId] = pair.split("|");
		if (!fromId || !toId) continue;
		const nodeFrom = `observation:${fromId}`;
		const nodeTo = `observation:${toId}`;
		try {
			const updateResult = nativeDb.prepare(`
        UPDATE brain_page_edges
        SET weight = MIN(1.0, weight + 0.1),
            plasticity_class = 'hebbian'
        WHERE from_id = ? AND to_id = ? AND edge_type = ?
      `).run(nodeFrom, nodeTo, EDGE_TYPES.CO_RETRIEVED);
			if ((typeof updateResult.changes === "number" ? updateResult.changes : 0) === 0) nativeDb.prepare(`
            INSERT OR IGNORE INTO brain_page_edges
              (from_id, to_id, edge_type, weight, provenance, plasticity_class, created_at)
            VALUES (?, ?, ?, 0.3, 'consolidation:co-retrieval', 'hebbian', ?)
          `).run(nodeFrom, nodeTo, EDGE_TYPES.CO_RETRIEVED, now);
			strengthened++;
		} catch {}
	}
	return strengthened;
}

export { runConsolidation };
//# sourceMappingURL=brain-lifecycle-CWp_g1sm.js.map
