import { t as typedAll } from './typed-query-l1D_QElt.js';

//#region ../core/dist/memory/nexus-plasticity.js
/**
* NEXUS plasticity — Hebbian co-access strengthening for nexus_relations edges.
*
* Implements "fire together, wire together": each time two nodes are
* co-accessed during BRAIN retrieval, the directed edge(s) between them in
* nexus_relations gain plasticity weight.  Weight is capped at 1.0 to prevent
* runaway strengthening.
*
* T11545 (ADR-090 §5.3) partitioned the plasticity columns (`weight`,
* `last_accessed_at`, `co_accessed_count`) out of `nexus_relations` into the
* sibling 1:1 `nexus_relation_weights` table (keyed by `relation_id`). Writes
* here UPSERT that sibling table; rows are created lazily on first strengthen.
*
* Designed as Step 6b in runConsolidation (brain-lifecycle.ts) so every
* nexus-backed read passively strengthens the code-graph edges that
* participated in that retrieval.
*
* @task T998
* @task T11545
* @epic T991
*/
/** Increment applied to `weight` per co-access event. */
var WEIGHT_INCREMENT = .05;
/** Default plasticity half-life in days (configurable via CLEO_PLASTICITY_HALFLIFE_DAYS env var). */
var DEFAULT_PLASTICITY_HALFLIFE_DAYS = 14;
/**
* Strengthen nexus_relations edges for co-accessed node pairs.
*
* For each `{sourceId, targetId}` pair, UPSERTs into the partitioned
* `nexus_relation_weights` table (T11545 · ADR-090 §5.3) for every matching
* `nexus_relations.id`:
* ```sql
* INSERT INTO nexus_relation_weights (relation_id, weight, last_accessed_at, co_accessed_count)
* SELECT id, MIN(1.0, 0.05), datetime('now'), 1
*   FROM nexus_relations
*  WHERE source_id = ? AND target_id = ?
* ON CONFLICT(relation_id) DO UPDATE SET
*   weight            = MIN(1.0, weight + 0.05),
*   co_accessed_count = co_accessed_count + 1,
*   last_accessed_at  = excluded.last_accessed_at
* ```
*
* A weights row is created lazily on first strengthen and never for a pair that
* has no matching code-graph edge — when the SELECT is empty, zero rows change
* and the pair is silently skipped (recorded in `result.skipped`). This keeps
* the plasticity pass additive and non-destructive.
*
* This function is safe to call from any consolidation context.  It
* gracefully no-ops when nexus.db has not been initialised (returns zeros).
*
* @param pairs - Array of source/target ID pairs to strengthen.
* @returns Counts of strengthened and skipped pairs.
*
* @example
* ```typescript
* const result = await strengthenNexusCoAccess([
*   { sourceId: 'src/foo.ts::bar', targetId: 'src/baz.ts::qux' },
* ]);
* // result.strengthened → 1 (if edge existed)
* // result.skipped      → 0
* ```
*/
async function strengthenNexusCoAccess(pairs) {
	if (pairs.length === 0) return {
		strengthened: 0,
		skipped: 0
	};
	const { getNexusNativeDb } = await import('./nexus-sqlite-CBdrYCMo.js');
	const nativeDb = getNexusNativeDb();
	if (!nativeDb) return {
		strengthened: 0,
		skipped: 0
	};
	try {
		nativeDb.prepare("SELECT relation_id FROM nexus_relation_weights LIMIT 1").get();
	} catch {
		return {
			strengthened: 0,
			skipped: 0
		};
	}
	const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
	let strengthened = 0;
	let skipped = 0;
	const stmt = nativeDb.prepare(`
    INSERT INTO nexus_relation_weights (relation_id, weight, last_accessed_at, co_accessed_count)
    SELECT id, MIN(1.0, ${WEIGHT_INCREMENT}), ?, 1
      FROM nexus_relations
     WHERE source_id = ? AND target_id = ?
    ON CONFLICT(relation_id) DO UPDATE SET
      weight            = MIN(1.0, nexus_relation_weights.weight + ${WEIGHT_INCREMENT}),
      co_accessed_count = nexus_relation_weights.co_accessed_count + 1,
      last_accessed_at  = excluded.last_accessed_at
  `);
	for (const pair of pairs) try {
		const result = stmt.run(now, pair.sourceId, pair.targetId);
		if ((typeof result.changes === "number" ? result.changes : 0) > 0) strengthened++;
		else skipped++;
	} catch {
		skipped++;
	}
	return {
		strengthened,
		skipped
	};
}
/**
* Apply temporal decay to nexus_relations plasticity weights.
*
* Weights decay exponentially based on days since last access using a
* configurable half-life. Formula:
* ```
* new_weight = weight * (0.5 ^ (daysSinceLastAccess / halfLifeDays))
* ```
*
* The half-life can be configured via the `CLEO_PLASTICITY_HALFLIFE_DAYS`
* environment variable (default 14 days). This matches the development
* cadence of typical projects: edges unused for 14 days halve their weight,
* unused for 28 days drop to 0.25, and so on.
*
* Only updates rows where `last_accessed_at` is not NULL — new edges
* with no access record are left unchanged.
*
* Operates on the GLOBAL nexus.db (resolved via `getCleoHome()` through the
* singleton `getNexusNativeDb()`), not a per-project database. The function
* takes no arguments intentionally — nexus.db spans all projects in a
* CLEO installation and decay is applied globally.
*
* @returns Decay result including row count and parameters used
*
* @example
* ```typescript
* const result = await applyPlasticityDecay();
* console.log(`Decayed ${result.updated} edges with ${result.halfLifeDays}-day half-life`);
* ```
*/
async function applyPlasticityDecay() {
	const { getNexusNativeDb } = await import('./nexus-sqlite-CBdrYCMo.js');
	const nativeDb = getNexusNativeDb();
	if (!nativeDb) return {
		updated: 0,
		halfLifeDays: DEFAULT_PLASTICITY_HALFLIFE_DAYS,
		decayPerDay: 1 - .5 ** (1 / DEFAULT_PLASTICITY_HALFLIFE_DAYS)
	};
	const halfLifeDays = Number.parseFloat(process.env.CLEO_PLASTICITY_HALFLIFE_DAYS ?? String(DEFAULT_PLASTICITY_HALFLIFE_DAYS));
	if (!Number.isFinite(halfLifeDays) || halfLifeDays <= 0) return {
		updated: 0,
		halfLifeDays: DEFAULT_PLASTICITY_HALFLIFE_DAYS,
		decayPerDay: 1 - .5 ** (1 / DEFAULT_PLASTICITY_HALFLIFE_DAYS)
	};
	const decayPerDay = 1 - .5 ** (1 / halfLifeDays);
	try {
		nativeDb.prepare("SELECT weight, last_accessed_at FROM nexus_relation_weights LIMIT 1").get();
	} catch {
		return {
			updated: 0,
			halfLifeDays,
			decayPerDay
		};
	}
	const stmt = nativeDb.prepare(`
    UPDATE nexus_relation_weights
    SET weight = MAX(0.0, weight * EXP(LN(0.5) * (julianday('now') - julianday(last_accessed_at)) / ?))
    WHERE last_accessed_at IS NOT NULL AND weight > 0.001
  `);
	try {
		const result = stmt.run(halfLifeDays);
		return {
			updated: typeof result.changes === "number" ? result.changes : 0,
			halfLifeDays,
			decayPerDay
		};
	} catch {
		return {
			updated: 0,
			halfLifeDays,
			decayPerDay
		};
	}
}
/**
* 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);
}
/**
* Extract nexus node ID pairs from the brain_retrieval_log that were
* co-retrieved in the same search session.
*
* Used by Step 6b in runConsolidation to derive which nexus edges to
* strengthen from recent BRAIN retrieval activity.
*
* The entry_ids column stores a JSON array of brain entry IDs (e.g.,
* `["D-abc", "L-def", "O-ghi"]`).  These are mapped to nexus node IDs
* via the brain-nexus bridge (brain_page_edges of type `documents` /
* `applies_to`).  When no bridge data is available, the raw brain IDs
* are returned as-is — callers should filter non-node-format IDs before
* passing to nexus_relations.
*
* BUG-1 fix: The lookback window is separate from insertion timestamp.
* Consolidation scans retrieval events within `lookbackDays` of now.
*
* @param projectRoot - Project root for brain.db resolution.
* @param lookbackDays - How far back in time to scan retrieval events (default 30 days).
* @returns Deduplicated source/target pairs ready for strengthenNexusCoAccess.
*/
async function extractNexusPairsFromRetrievalLog(projectRoot, lookbackDays = 30) {
	const { getBrainDb, getBrainNativeDb } = await import('./memory-sqlite-Dg5uGJCo.js');
	await getBrainDb(projectRoot);
	const nativeDb = getBrainNativeDb();
	if (!nativeDb) return [];
	try {
		nativeDb.prepare("SELECT 1 FROM brain_retrieval_log LIMIT 1").get();
	} catch {
		return [];
	}
	const cutoff = (/* @__PURE__ */ new Date(Date.now() - lookbackDays * 24 * 60 * 60 * 1e3)).toISOString().replace("T", " ").slice(0, 19);
	let rows = [];
	try {
		rows = typedAll(nativeDb.prepare(`SELECT entry_ids FROM brain_retrieval_log WHERE created_at >= ? LIMIT 1000`), cutoff);
	} catch {
		return [];
	}
	const seen = /* @__PURE__ */ new Set();
	const pairs = [];
	for (const row of rows) {
		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];
			if (!a || !b) continue;
			const fwd = `${a}|${b}`;
			const rev = `${b}|${a}`;
			if (!seen.has(fwd)) {
				seen.add(fwd);
				pairs.push({
					sourceId: a,
					targetId: b
				});
			}
			if (!seen.has(rev)) {
				seen.add(rev);
				pairs.push({
					sourceId: b,
					targetId: a
				});
			}
		}
	}
	return pairs;
}

export { applyPlasticityDecay, extractNexusPairsFromRetrievalLog, strengthenNexusCoAccess };
//# sourceMappingURL=nexus-plasticity-BrrQrtbL.js.map
