import { a as applyPerfPragmas } from './dual-scope-db-ATrwq0LH.js';
import { h as getCleoHome, r as resolveProjectByCwd, i as resolveCanonicalCleoDir } from './paths-2M_UxD_H.js';
import { createRequire } from 'node:module';
import { existsSync } from 'node:fs';
import { join, dirname } from 'node:path';

//#region ../brain/dist/src/cleo-home.js
/**
* CLEO home + project DB path resolution for `@cleocode/brain`.
*
* Path resolution delegates to the `@cleocode/paths` SSoT
* ({@link getCleoHome}). Project data (tasks.db, brain.db, conduit.db) is
* resolved via projectId → nexus.db registry lookup
* ({@link resolveProjectByCwd} + {@link resolveCanonicalCleoDir}), with a
* `CLEO_ROOT` / `process.cwd()` fallback for non-project contexts.
*
* Previously this module imported `env-paths` directly which caused a Windows
* path mismatch with the rest of the CLEO ecosystem (the CLI used env-paths
* returning `%LOCALAPPDATA%\cleo\Data` while brain used a bare
* `%LOCALAPPDATA%\cleo`, looking up `nexus.db` one directory shallower than
* where it was written). Fixed in T1874 (Closes #102, supersedes #103).
*
* @task T1874 (original Windows fix)
* @task T1886 (migrated to @cleocode/paths SSoT)
* @task T11040 (verified 2026-05-27: projectId-based resolution confirmed; 72/72 tests pass)
*/
/**
* Returns the project's `.cleo/` directory.
*
* Resolution order:
* 1. {@link resolveProjectByCwd} — reads `.cleo/project-info.json` for a stable
*    `projectId`, then resolves the canonical `.cleo/` path via
*    {@link resolveCanonicalCleoDir} (nexus.db registry lookup).
* 2. Fallback: `CLEO_ROOT` env var or `process.cwd()` + `'.cleo'` for
*    non-project contexts (e.g., before `cleo init`).
*
* @task T11040 — migrate from CWD-walk-up to projectId-based resolution
*/
function getCleoProjectDir() {
	const project = resolveProjectByCwd();
	if (project !== null) {
		const canonical = resolveCanonicalCleoDir(project.projectId);
		if (canonical !== null) return canonical;
		return join(project.projectRoot, ".cleo");
	}
	return join(process.env["CLEO_ROOT"] ?? process.cwd(), ".cleo");
}
/** Returns the absolute path to the global nexus.db file. */
function getNexusDbPath() {
	return join(getCleoHome(), "nexus.db");
}
/** Returns the absolute path to the global signaldock.db file. */
function getAgentRegistryDbPath() {
	return join(getCleoHome(), "signaldock.db");
}
/** Returns true when the given DB file exists on disk. */
function dbExists(dbPath) {
	return existsSync(dbPath);
}
//#endregion
//#region ../brain/dist/src/db-connections.js
/**
* Read-only SQLite connection helpers for the Living Brain substrate adapters.
*
* Uses node:sqlite (Node.js built-in).
*
* Global databases (nexus.db, signaldock.db) are shared across all projects
* and use module-level caches — their path is fixed per machine and never
* changes between requests.
*
* Per-project databases (brain.db, tasks.db, conduit.db) are resolved from
* the active {@link ProjectContext} passed by the caller. No cross-request
* caching is performed: opening SQLite with `node:sqlite` is sub-millisecond,
* and caching across different ProjectContexts is the precise bug this
* module's Studio counterpart was rewritten to avoid.
*
* This file is a trimmed mirror of
* `packages/studio/src/lib/server/db/connections.ts`, containing only the
* getters required by the substrate adapters.
*
* ## E4-T5 transition (T11516 · SG-DB-SUBSTRATE-V2)
*
* This module now depends on `@cleocode/core` and delegates to
* {@link openDualScopeDb} for new consolidated `cleo.db` access.  The legacy
* per-domain DB getters ({@link getNexusDb}, {@link getBrainDb}, etc.) still
* open the old per-domain files via per-line-allowed raw opens because the
* substrate adapters in this package still target those legacy table schemas.
* The full exodus to the consolidated `cleo.db` schema happens in E6 (T11249),
* at which point the per-line `// db-open-allowed` markers and the legacy
* getters are removed.
*
* @task T969
* @task T11516 (E4-T5)
*/
var { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
/**
* Executes a prepared statement and returns rows typed as `T[]`.
*
* This helper consolidates the SQL-to-TypeScript boundary into a single,
* auditable location. Each adapter specifies the expected row shape via the
* type parameter; the unknown→T conversion happens exactly once per call
* site rather than being scattered across every query site.
*
* Callers remain responsible for validating that the SQL projection matches
* the declared type `T`.
*
* @param stmt - A prepared `node:sqlite` StatementSync.
* @param params - Anonymous parameter bindings for the statement.
* @returns Rows materialised as `T[]`.
*/
function allTyped(stmt, ...params) {
	return stmt.all(...params);
}
/** Cached nexus.db connection — global, path is machine-scoped. */
var nexusDb = null;
/** Cached signaldock.db connection — global, path is machine-scoped. */
var signaldockDb = null;
/**
* Returns a read-only connection to the global nexus.db.
* Returns null when the file does not exist on disk.
*
* @remarks
* Uses `applyPerfPragmas` from `@cleocode/core` (SSoT, T9045).
* The raw open is per-line-allowed during the E4 → E6 transition; substrate
* adapters still target legacy nexus.db table names. Full exodus in E6 (T11249).
*/
function getNexusDb() {
	if (nexusDb) return nexusDb;
	const path = getNexusDbPath();
	if (!dbExists(path)) return null;
	nexusDb = new DatabaseSync(path, { open: true });
	applyPerfPragmas(nexusDb);
	return nexusDb;
}
/**
* Returns a read-only connection to the global signaldock.db.
* Returns null when the file does not exist on disk.
*
* @remarks
* Uses `applyPerfPragmas` from `@cleocode/core` (SSoT, T9045).
* The raw open is per-line-allowed during the E4 → E6 transition; substrate
* adapters still target legacy signaldock.db table names. Full exodus in E6 (T11249).
*/
function getAgentRegistryDb() {
	if (signaldockDb) return signaldockDb;
	const path = getAgentRegistryDbPath();
	if (!dbExists(path)) return null;
	signaldockDb = new DatabaseSync(path, { open: true });
	applyPerfPragmas(signaldockDb);
	return signaldockDb;
}
/**
* Detect the brain.db malformation signature (T10303) without taking a
* dependency on `@cleocode/core`. The reader side does NOT auto-recover —
* recovery is owned by the writer side in `@cleocode/core/store/memory-sqlite.ts`.
* Reader-side detection returns `null` so substrate adapters degrade
* gracefully (matches the existing "DB doesn't exist" semantics).
*
* @internal — brain-package-local.
* @task T10303
*/
function isMalformationError(err) {
	if (!(err instanceof Error)) return false;
	const code = err.code;
	const errcode = err.errcode;
	if (code === "ERR_SQLITE_ERROR" && errcode === 11) return true;
	return /malformed/i.test(err.message ?? "");
}
/**
* Opens a connection to brain.db for the given project context.
*
* Each call opens a fresh DatabaseSync against the path stored in `ctx`.
* Returns null when brain.db does not exist for the project OR when the
* file is detected as malformed (T10303). Reader-side malformation is
* handled by returning null — recovery is owned by the writer side in
* `@cleocode/core/store/memory-sqlite.ts:getBrainDb`, which runs the
* `recoverMalformedBrainDb()` pipeline before the next process re-opens.
*
* @param ctx - The active project context.
*
* @remarks
* Uses `applyPerfPragmas` from `@cleocode/core` (SSoT, T9045).
* The raw open is per-line-allowed during the E4 → E6 transition; substrate
* adapters still target legacy brain.db table names. Full exodus in E6 (T11249).
*/
function getBrainDb(ctx) {
	const path = ctx.brainDbPath;
	if (!existsSync(path)) return null;
	try {
		const __db = new DatabaseSync(path, { open: true });
		applyPerfPragmas(__db);
		return __db;
	} catch (err) {
		if (isMalformationError(err)) return null;
		throw err;
	}
}
/**
* Opens a connection to tasks.db for the given project context.
*
* Each call opens a fresh DatabaseSync against the path stored in `ctx`.
* Returns null when tasks.db does not exist for the project.
*
* @param ctx - The active project context.
*
* @remarks
* Uses `applyPerfPragmas` from `@cleocode/core` (SSoT, T9045).
* The raw open is per-line-allowed during the E4 → E6 transition; substrate
* adapters still target legacy tasks.db table names. Full exodus in E6 (T11249).
*/
function getTasksDb(ctx) {
	const path = ctx.tasksDbPath;
	if (!existsSync(path)) return null;
	const __db = new DatabaseSync(path, { open: true });
	applyPerfPragmas(__db);
	return __db;
}
/**
* Opens a connection to conduit.db for the given project context.
*
* conduit.db lives alongside brain.db in the project's `.cleo/` directory.
* Its path is derived from the brain.db path since `ProjectContext` does not
* carry a dedicated `conduitDbPath` field.
*
* Returns null when conduit.db does not exist for the project.
*
* @param ctx - The active project context.
*
* @remarks
* Uses `applyPerfPragmas` from `@cleocode/core` (SSoT, T9045).
* The raw open is per-line-allowed during the E4 → E6 transition; substrate
* adapters still target legacy conduit.db table names. Full exodus in E6 (T11249).
*/
function getConduitDb(ctx) {
	const path = join(dirname(ctx.brainDbPath), "conduit.db");
	if (!existsSync(path)) return null;
	const __db = new DatabaseSync(path, { open: true });
	applyPerfPragmas(__db);
	return __db;
}
//#endregion
//#region ../brain/dist/src/adapters/agent-registry.js
/**
* AGENT-REGISTRY substrate adapter for the Living Brain API.
*
* Read-only visualization path. Queries the LEGACY standalone `signaldock.db`
* (resolved by `getAgentRegistryDbPath`) and returns BrainNodes/BrainEdges for
* agents and agent-to-agent social connections. The legacy standalone file still
* carries the BARE table names (`agents`, `agent_connections`); the brain-package
* read path is migrated to the consolidated `cleo.db` separately in E6 (T11249),
* so the SQL below intentionally still targets the bare names.
*
* The substrate identifier + node-id prefix were renamed `signaldock` →
* `agent-registry` under T11622 (display identifiers, decoupled from the physical
* table names).
*
* Node IDs are prefixed with "agent-registry:" to prevent collisions.
* Agents are the cross-substrate identity bridge — they appear in TASKS
* (assignee), CONDUIT (from/to), and BRAIN (source agent).
*
* @task T11622 (Signaldock → Agent Registry rename; folds T11578 AC2)
*/
/**
* Converts a UNIX epoch seconds value to an ISO-8601 string.
* Returns null when the value is not a finite positive number.
*
* @param epoch - UNIX timestamp in seconds, or null.
* @returns ISO-8601 string or null.
*/
function epochToIso$1(epoch) {
	if (epoch === null || !Number.isFinite(epoch) || epoch <= 0) return null;
	return (/* @__PURE__ */ new Date(epoch * 1e3)).toISOString();
}
/**
* Returns all BrainNodes and BrainEdges sourced from the Agent Registry.
*
* Fetches all active agents plus their declared connections.
* Agent nodes serve as the cross-substrate identity anchors.
*
* @param options - Query options (limit).
* @returns Nodes and edges from the AGENT-REGISTRY substrate.
*/
function getAgentRegistrySubstrate(options = {}) {
	const db = getAgentRegistryDb();
	if (!db) return {
		nodes: [],
		edges: []
	};
	const perSubstrateLimit = Math.ceil((options.limit ?? 500) / 5);
	const nodes = [];
	const edges = [];
	try {
		const agentRows = allTyped(db.prepare(`SELECT agent_id, name, status, created_at
         FROM agents
         WHERE status != 'deleted'
         ORDER BY created_at DESC
         LIMIT ?`), perSubstrateLimit);
		const agentIds = /* @__PURE__ */ new Set();
		for (const row of agentRows) {
			agentIds.add(row.agent_id);
			nodes.push({
				id: `agent-registry:${row.agent_id}`,
				kind: "agent",
				substrate: "agent-registry",
				label: row.name,
				weight: row.status === "active" ? 1 : .5,
				createdAt: epochToIso$1(row.created_at),
				meta: {
					status: row.status,
					created_at: row.created_at
				}
			});
		}
		if (agentIds.size > 0) {
			const placeholders = [...agentIds].map(() => "?").join(",");
			const connRows = allTyped(db.prepare(`SELECT from_agent_id, to_agent_id, connection_type, strength
           FROM agent_connections
           WHERE from_agent_id IN (${placeholders})
             AND to_agent_id IN (${placeholders})`), ...agentIds, ...agentIds);
			for (const row of connRows) edges.push({
				source: `agent-registry:${row.from_agent_id}`,
				target: `agent-registry:${row.to_agent_id}`,
				type: row.connection_type,
				weight: row.strength ?? .5,
				substrate: "agent-registry"
			});
		}
	} catch {}
	return {
		nodes,
		edges
	};
}
//#endregion
//#region ../brain/dist/src/project-context.js
/**
* Resolve the default project context using projectId-based resolution.
*
* Derives `projectPath` from {@link resolveProjectByCwd} (canonical project
* root), falling back to the `.cleo`-relative path for non-project contexts.
* DB paths flow through {@link getCleoProjectDir}, which now uses
* projectId→nexus.db resolution instead of CWD-walk-up (T11040).
*
* @returns A context whose paths derive from projectId resolution where possible.
* @task T11040 — migrate from CWD-walk-up to projectId-based resolution
*/
function resolveDefaultProjectContext() {
	const project = resolveProjectByCwd();
	const projectDir = getCleoProjectDir();
	return {
		projectPath: project !== null ? project.projectRoot : projectDir.replace(/\/.cleo$/, ""),
		brainDbPath: join(projectDir, "brain.db"),
		tasksDbPath: join(projectDir, "tasks.db")
	};
}
//#endregion
//#region ../brain/dist/src/adapters/brain.js
/**
* BRAIN substrate adapter for the Living Brain API.
*
* Queries brain.db and returns BrainNodes/BrainEdges for all typed memory tables:
* observations, decisions, patterns, learnings, plus the graph layer
* (brain_page_nodes / brain_page_edges).
*
* Cross-substrate bridges are synthesized for:
* - brain_page_edges whose to_id references a task (task:T-xxx → tasks:T-xxx)
* - brain_page_edges whose to_id is a nexus-style path (foo.ts::Symbol → nexus:...)
* - brain_memory_links rows (memory_type + memory_id → task_id link)
* - brain_observations.files_modified_json (observation → nexus file path)
*
* Node IDs are prefixed with "brain:" to prevent collisions.
*/
/**
* Converts a brain_page_edges type-prefixed ID (e.g. "observation:O-abc")
* into the BrainNode ID prefix (e.g. "brain:O-abc").
*
* Returns null when the prefix is not a recognised brain type.
*
* @param typeId - Type-prefixed ID from brain_page_edges.from_id or to_id.
* @returns BrainNode-prefixed ID or null.
*/
function brainTypeIdToBrainNodeId(typeId) {
	const sep = typeId.indexOf(":");
	if (sep === -1) return null;
	const prefix = typeId.slice(0, sep);
	const rawId = typeId.slice(sep + 1);
	if (prefix === "observation" || prefix === "decision" || prefix === "pattern" || prefix === "learning") return `brain:${rawId}`;
	return null;
}
/**
* Returns true when a brain_page_edges to_id looks like a nexus node path
* (contains "::" separator used by nexus for file::Symbol paths, or is a
* relative file path with a known extension).
*
* @param toId - to_id value from brain_page_edges.
* @returns True when the ID appears to reference a nexus node.
*/
function isNexusStyleId(toId) {
	if (toId.includes("::")) return true;
	if (!toId.includes(":") && toId.includes("/")) return true;
	return false;
}
/**
* Returns true when a brain_page_edges ID looks like a task reference
* (e.g. "task:T532").
*
* @param id - ID from brain_page_edges.
* @returns True when the ID references a task node.
*/
function isTaskId(id) {
	return id.startsWith("task:");
}
/**
* Converts a brain_page_edges task-reference to a tasks-substrate BrainNode ID.
* e.g. "task:T532" → "tasks:T532"
*
* @param taskRef - Task reference from brain_page_edges.
* @returns tasks-substrate BrainNode ID.
*/
function taskRefToBrainNodeId(taskRef) {
	return `tasks:${taskRef.slice(5)}`;
}
/**
* Returns all BrainNodes and BrainEdges sourced from brain.db.
*
* Pulls from all four typed memory tables plus brain_page_edges.
* Emits intra-brain edges between loaded nodes, cross-substrate
* brain→tasks bridges, cross-substrate brain→nexus bridges, and
* brain_memory_links as cross-substrate edges.
*
* Applies `minWeight` filter where quality_score is available.
* Node count is bounded by `limit / 5` to share budget with other substrates.
*
* @param options - Query options (limit, minWeight).
* @returns Nodes and edges from the BRAIN substrate.
*/
function getBrainSubstrate(options = {}) {
	const db = getBrainDb(options.projectCtx ?? resolveDefaultProjectContext());
	if (!db) return {
		nodes: [],
		edges: []
	};
	const minWeight = options.minWeight ?? 0;
	const perSubstrateLimit = Math.ceil((options.limit ?? 500) / 5);
	const nodes = [];
	const edges = [];
	try {
		const obsRows = allTyped(db.prepare(`SELECT id, title, quality_score, memory_tier,
                strftime('%Y-%m-%dT%H:%M:%S', created_at) AS created_at,
                source_session_id, files_modified_json
         FROM brain_observations
         WHERE (quality_score IS NULL OR quality_score >= ?)
         ORDER BY quality_score DESC, created_at DESC
         LIMIT ?`), minWeight, Math.ceil(perSubstrateLimit * .4));
		for (const row of obsRows) nodes.push({
			id: `brain:${row.id}`,
			kind: "observation",
			substrate: "brain",
			label: row.title,
			weight: row.quality_score ?? void 0,
			createdAt: row.created_at,
			meta: {
				memory_tier: row.memory_tier,
				created_at: row.created_at,
				source_session_id: row.source_session_id
			}
		});
		const decRows = allTyped(db.prepare(`SELECT id, decision, quality_score, context_task_id,
                strftime('%Y-%m-%dT%H:%M:%S', created_at) AS created_at
         FROM brain_decisions
         WHERE (quality_score IS NULL OR quality_score >= ?)
         ORDER BY quality_score DESC, created_at DESC
         LIMIT ?`), minWeight, Math.ceil(perSubstrateLimit * .25));
		for (const row of decRows) nodes.push({
			id: `brain:${row.id}`,
			kind: "decision",
			substrate: "brain",
			label: row.decision.slice(0, 100),
			weight: row.quality_score ?? void 0,
			createdAt: row.created_at,
			meta: {
				context_task_id: row.context_task_id,
				created_at: row.created_at
			}
		});
		const patRows = allTyped(db.prepare(`SELECT id, pattern, type, quality_score,
                strftime('%Y-%m-%dT%H:%M:%S', extracted_at) AS extracted_at
         FROM brain_patterns
         WHERE (quality_score IS NULL OR quality_score >= ?)
         ORDER BY quality_score DESC, extracted_at DESC
         LIMIT ?`), minWeight, Math.ceil(perSubstrateLimit * .2));
		for (const row of patRows) nodes.push({
			id: `brain:${row.id}`,
			kind: "pattern",
			substrate: "brain",
			label: row.pattern.slice(0, 100),
			weight: row.quality_score ?? void 0,
			createdAt: row.extracted_at,
			meta: {
				pattern_type: row.type,
				created_at: row.extracted_at
			}
		});
		const learnRows = allTyped(db.prepare(`SELECT id, insight, quality_score,
                strftime('%Y-%m-%dT%H:%M:%S', created_at) AS created_at
         FROM brain_learnings
         WHERE (quality_score IS NULL OR quality_score >= ?)
         ORDER BY quality_score DESC, created_at DESC
         LIMIT ?`), minWeight, Math.ceil(perSubstrateLimit * .15));
		for (const row of learnRows) nodes.push({
			id: `brain:${row.id}`,
			kind: "learning",
			substrate: "brain",
			label: row.insight.slice(0, 100),
			weight: row.quality_score ?? void 0,
			createdAt: row.created_at,
			meta: { created_at: row.created_at }
		});
		const typeIdToBrainNodeId = /* @__PURE__ */ new Map();
		for (const n of nodes) {
			const rawId = n.id.slice(6);
			let typePrefix;
			if (n.kind === "observation") typePrefix = "observation";
			else if (n.kind === "decision") typePrefix = "decision";
			else if (n.kind === "pattern") typePrefix = "pattern";
			else typePrefix = "learning";
			typeIdToBrainNodeId.set(`${typePrefix}:${rawId}`, n.id);
		}
		const pageEdgeRows = allTyped(db.prepare(`SELECT from_id, to_id, edge_type, weight FROM brain_page_edges`));
		for (const row of pageEdgeRows) {
			const sourceBrainNodeId = typeIdToBrainNodeId.get(row.from_id) ?? brainTypeIdToBrainNodeId(row.from_id);
			if (!sourceBrainNodeId) continue;
			if (isTaskId(row.to_id)) edges.push({
				source: sourceBrainNodeId,
				target: taskRefToBrainNodeId(row.to_id),
				type: row.edge_type,
				weight: row.weight ?? .5,
				substrate: "cross"
			});
			else if (isNexusStyleId(row.to_id)) edges.push({
				source: sourceBrainNodeId,
				target: `nexus:${row.to_id}`,
				type: row.edge_type,
				weight: row.weight ?? .5,
				substrate: "cross"
			});
			else {
				const targetBrainNodeId = typeIdToBrainNodeId.get(row.to_id) ?? brainTypeIdToBrainNodeId(row.to_id);
				if (targetBrainNodeId) edges.push({
					source: sourceBrainNodeId,
					target: targetBrainNodeId,
					type: row.edge_type,
					weight: row.weight ?? .5,
					substrate: "brain"
				});
			}
		}
		const memLinkRows = allTyped(db.prepare(`SELECT memory_type, memory_id, task_id, link_type
         FROM brain_memory_links`));
		for (const row of memLinkRows) {
			const sourceTypeId = `${row.memory_type}:${row.memory_id}`;
			const sourceBrainNodeId = typeIdToBrainNodeId.get(sourceTypeId) ?? brainTypeIdToBrainNodeId(sourceTypeId);
			if (!sourceBrainNodeId) continue;
			edges.push({
				source: sourceBrainNodeId,
				target: `tasks:${row.task_id}`,
				type: row.link_type,
				weight: .7,
				substrate: "cross"
			});
		}
		for (const row of obsRows) {
			if (!row.files_modified_json) continue;
			let filePaths;
			try {
				filePaths = JSON.parse(row.files_modified_json);
			} catch {
				continue;
			}
			if (!Array.isArray(filePaths)) continue;
			const sourceBrainNodeId = `brain:${row.id}`;
			for (const rawPath of filePaths) {
				if (typeof rawPath !== "string" || rawPath.length === 0) continue;
				edges.push({
					source: sourceBrainNodeId,
					target: `nexus:${rawPath}`,
					type: "modified_by",
					weight: .6,
					substrate: "cross"
				});
			}
		}
		for (const dec of decRows) if (dec.context_task_id) edges.push({
			source: `brain:${dec.id}`,
			target: `tasks:${dec.context_task_id}`,
			type: "applies_to",
			weight: .8,
			substrate: "cross"
		});
	} catch {}
	return {
		nodes,
		edges
	};
}
//#endregion
//#region ../brain/dist/src/adapters/conduit.js
/**
* CONDUIT substrate adapter for the Living Brain API.
*
* Queries conduit.db and returns BrainNodes/BrainEdges for agent-to-agent messages.
* Each message becomes a node; `from_agent_id → to_agent_id` becomes an edge.
* Co-authoring agent pairs produce cross-substrate edges to SIGNALDOCK.
*
* Node IDs are prefixed with "conduit:" to prevent collisions.
*/
/**
* Converts a UNIX epoch seconds value to an ISO-8601 string.
* Returns null when the value is not a finite positive number.
*
* @param epoch - UNIX timestamp in seconds.
* @returns ISO-8601 string or null.
*/
function epochToIso(epoch) {
	if (!Number.isFinite(epoch) || epoch <= 0) return null;
	return (/* @__PURE__ */ new Date(epoch * 1e3)).toISOString();
}
/**
* Returns all BrainNodes and BrainEdges sourced from conduit.db.
*
* Fetches the most recent messages (capped at perSubstrateLimit).
* Synthesizes agent→agent edges and cross-substrate agent references
* pointing to signaldock.
*
* @param options - Query options (limit).
* @returns Nodes and edges from the CONDUIT substrate.
*/
function getConduitSubstrate(options = {}) {
	const db = getConduitDb(options.projectCtx ?? resolveDefaultProjectContext());
	if (!db) return {
		nodes: [],
		edges: []
	};
	const perSubstrateLimit = Math.ceil((options.limit ?? 500) / 5);
	const nodes = [];
	const edges = [];
	try {
		const msgRows = allTyped(db.prepare(`SELECT id, content, from_agent_id, to_agent_id, created_at, conversation_id
         FROM messages
         ORDER BY created_at DESC
         LIMIT ?`), perSubstrateLimit);
		const agentPairs = /* @__PURE__ */ new Map();
		for (const row of msgRows) {
			const label = row.content.length > 80 ? `${row.content.slice(0, 80)}…` : row.content;
			nodes.push({
				id: `conduit:${row.id}`,
				kind: "message",
				substrate: "conduit",
				label,
				createdAt: epochToIso(row.created_at),
				meta: {
					from_agent_id: row.from_agent_id,
					to_agent_id: row.to_agent_id,
					conversation_id: row.conversation_id,
					created_at: row.created_at
				}
			});
			if (row.from_agent_id && row.to_agent_id) {
				const key = `${row.from_agent_id}→${row.to_agent_id}`;
				const existing = agentPairs.get(key);
				if (existing) existing.count++;
				else agentPairs.set(key, {
					count: 1,
					from: row.from_agent_id,
					to: row.to_agent_id
				});
			}
		}
		for (const [, pair] of agentPairs) {
			const weight = Math.min(1, pair.count / 10);
			edges.push({
				source: `agent-registry:${pair.from}`,
				target: `agent-registry:${pair.to}`,
				type: "messages",
				weight,
				substrate: "cross"
			});
		}
	} catch {}
	return {
		nodes,
		edges
	};
}
//#endregion
//#region ../brain/dist/src/adapters/nexus.js
/**
* NEXUS substrate adapter for the Living Brain API.
*
* Queries nexus.db (global) and returns BrainNodes/BrainEdges for code symbols and files.
* Prioritises high-in-degree nodes (most-called functions, most-imported files)
* since the full nexus graph can exceed 10k nodes.
*
* Node IDs are prefixed with "nexus:" to prevent collisions.
*/
/** Maps nexus node kinds to BrainNodeKind. */
function mapKind(nexusKind) {
	if (nexusKind === "file" || nexusKind === "folder" || nexusKind === "module") return "file";
	return "symbol";
}
/**
* Returns all BrainNodes and BrainEdges sourced from nexus.db.
*
* Fetches the highest in-degree nodes (capped at perSubstrateLimit) and
* all relations between those nodes.
*
* @param options - Query options (limit, minWeight).
* @returns Nodes and edges from the NEXUS substrate.
*/
function getNexusSubstrate(options = {}) {
	const db = getNexusDb();
	if (!db) return {
		nodes: [],
		edges: []
	};
	const perSubstrateLimit = Math.ceil((options.limit ?? 500) / 5);
	const nodes = [];
	const edges = [];
	try {
		const nodeRows = allTyped(db.prepare(`SELECT n.id, n.kind, n.name, n.indexed_at,
                COUNT(r.target_id) AS in_degree
         FROM nexus_nodes n
         LEFT JOIN nexus_relations r ON r.target_id = n.id
         WHERE n.kind IN (
           'file', 'function', 'method', 'class', 'interface',
           'type_alias', 'constant', 'module', 'enum'
         )
         GROUP BY n.id
         ORDER BY in_degree DESC
         LIMIT ?`), perSubstrateLimit);
		for (const row of nodeRows) nodes.push({
			id: `nexus:${row.id}`,
			kind: mapKind(row.kind),
			substrate: "nexus",
			label: row.name,
			weight: row.in_degree > 0 ? Math.min(1, row.in_degree / 50) : void 0,
			createdAt: row.indexed_at ?? null,
			meta: {
				nexus_kind: row.kind,
				in_degree: row.in_degree
			}
		});
		const nodeIds = new Set(nodes.map((n) => n.id));
		const rawIds = new Set([...nodeIds].map((id) => id.replace(/^nexus:/, "")));
		const placeholders = [...rawIds].map(() => "?").join(",");
		if (rawIds.size === 0) return {
			nodes,
			edges
		};
		const relRows = allTyped(db.prepare(`SELECT source_id, target_id, type, confidence
         FROM nexus_relations
         WHERE source_id IN (${placeholders})
           AND target_id IN (${placeholders})`), ...rawIds, ...rawIds);
		for (const row of relRows) edges.push({
			source: `nexus:${row.source_id}`,
			target: `nexus:${row.target_id}`,
			type: row.type,
			weight: row.confidence ?? .5,
			substrate: "nexus"
		});
	} catch {}
	return {
		nodes,
		edges
	};
}
//#endregion
//#region ../brain/dist/src/adapters/tasks.js
/**
* TASKS substrate adapter for the Living Brain API.
*
* Queries tasks.db and returns BrainNodes/BrainEdges for tasks and sessions.
* Prioritises critical/high priority tasks and active sessions.
*
* Node IDs are prefixed with "tasks:" to prevent collisions.
*/
/** Maps priority string to numeric weight for BrainNode.weight. */
function priorityWeight(priority) {
	return {
		critical: 1,
		high: .75,
		medium: .5,
		low: .25
	}[priority] ?? .25;
}
/**
* Returns all BrainNodes and BrainEdges sourced from tasks.db.
*
* Fetches tasks ordered by priority, plus recent sessions.
* Synthesizes parent→child, dependency, and relation edges.
*
* @param options - Query options (limit, minWeight).
* @returns Nodes and edges from the TASKS substrate.
*/
function getTasksSubstrate(options = {}) {
	const db = getTasksDb(options.projectCtx ?? resolveDefaultProjectContext());
	if (!db) return {
		nodes: [],
		edges: []
	};
	const perSubstrateLimit = Math.ceil((options.limit ?? 500) / 5);
	const minWeight = options.minWeight ?? 0;
	const nodes = [];
	const edges = [];
	try {
		const taskRows = allTyped(db.prepare(`SELECT id, title, status, priority, type, parent_id, created_at
         FROM tasks
         WHERE status NOT IN ('archived', 'cancelled')
         ORDER BY
           CASE priority
             WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3
           END,
           created_at DESC
         LIMIT ?`), Math.ceil(perSubstrateLimit * .8));
		const taskIds = /* @__PURE__ */ new Set();
		for (const row of taskRows) {
			const weight = priorityWeight(row.priority);
			if (weight < minWeight) continue;
			taskIds.add(row.id);
			nodes.push({
				id: `tasks:${row.id}`,
				kind: "task",
				substrate: "tasks",
				label: row.title,
				weight,
				createdAt: row.created_at,
				meta: {
					status: row.status,
					priority: row.priority,
					type: row.type,
					parent_id: row.parent_id,
					created_at: row.created_at
				}
			});
		}
		const sessionRows = allTyped(db.prepare(`SELECT id, status, started_at, ended_at
         FROM sessions
         ORDER BY started_at DESC
         LIMIT ?`), Math.ceil(perSubstrateLimit * .2));
		const sessionIds = /* @__PURE__ */ new Set();
		for (const row of sessionRows) {
			sessionIds.add(row.id);
			nodes.push({
				id: `tasks:${row.id}`,
				kind: "session",
				substrate: "tasks",
				label: `Session ${row.id.slice(-8)}`,
				weight: row.status === "active" ? .9 : .4,
				createdAt: row.started_at,
				meta: {
					status: row.status,
					started_at: row.started_at,
					ended_at: row.ended_at
				}
			});
		}
		for (const row of taskRows) if (row.parent_id && taskIds.has(row.parent_id)) edges.push({
			source: `tasks:${row.parent_id}`,
			target: `tasks:${row.id}`,
			type: "parent_of",
			weight: .9,
			substrate: "tasks"
		});
		if (taskIds.size > 0) {
			const placeholders = [...taskIds].map(() => "?").join(",");
			const relRows = allTyped(db.prepare(`SELECT task_id, related_task_id, relation_type
           FROM task_relations
           WHERE task_id IN (${placeholders})
             AND related_task_id IN (${placeholders})`), ...taskIds, ...taskIds);
			for (const row of relRows) edges.push({
				source: `tasks:${row.task_id}`,
				target: `tasks:${row.related_task_id}`,
				type: row.relation_type,
				weight: .7,
				substrate: "tasks"
			});
			const depRows = allTyped(db.prepare(`SELECT task_id, depends_on_task_id
           FROM task_dependencies
           WHERE task_id IN (${placeholders})
             AND depends_on_task_id IN (${placeholders})`), ...taskIds, ...taskIds);
			for (const row of depRows) edges.push({
				source: `tasks:${row.task_id}`,
				target: `tasks:${row.depends_on_task_id}`,
				type: "depends_on",
				weight: .85,
				substrate: "tasks"
			});
		}
	} catch {}
	return {
		nodes,
		edges
	};
}
//#endregion
//#region ../brain/dist/src/adapters/index.js
/** Substrate names ordered for iteration. */
var ALL_SUBSTRATES = [
	"brain",
	"nexus",
	"tasks",
	"conduit",
	"agent-registry"
];
/** Maps substrate name to its adapter function. */
var ADAPTER_MAP = {
	brain: getBrainSubstrate,
	nexus: getNexusSubstrate,
	tasks: getTasksSubstrate,
	conduit: getConduitSubstrate,
	"agent-registry": getAgentRegistrySubstrate
};
/**
* Loads minimal stub nodes for any edge target IDs not already loaded.
*
* This second-pass loader ensures that cross-substrate edges
* (particularly brain→nexus bridges) don't reference unloaded target nodes.
*
* Stub nodes carry only minimal metadata: {id, substrate, kind, label}.
* Full details defer to side-panel click or future full-load request.
*
* @param loadedNodeIds - Set of already-loaded node IDs (substrate-prefixed).
* @param edges - All edges collected from substrates (may reference unloaded targets).
* @returns Array of stub BrainNode objects for missing targets.
*/
function loadStubNodesForEdgeTargets(loadedNodeIds, edges) {
	const missingTargetIds = /* @__PURE__ */ new Set();
	for (const edge of edges) if (!loadedNodeIds.has(edge.target)) missingTargetIds.add(edge.target);
	if (missingTargetIds.size === 0) return [];
	const stubsBySubstrate = /* @__PURE__ */ new Map();
	for (const nodeId of missingTargetIds) {
		const sep = nodeId.indexOf(":");
		if (sep === -1) continue;
		const substrate = nodeId.slice(0, sep);
		if (![
			"brain",
			"nexus",
			"tasks",
			"conduit",
			"agent-registry"
		].includes(substrate)) continue;
		if (!stubsBySubstrate.has(substrate)) stubsBySubstrate.set(substrate, []);
		stubsBySubstrate.get(substrate).push(nodeId);
	}
	const stubs = [];
	const nexusStubs = stubsBySubstrate.get("nexus");
	if (nexusStubs && nexusStubs.length > 0) {
		const db = getNexusDb();
		if (db) try {
			const rawNexusIds = nexusStubs.map((id) => id.replace(/^nexus:/, ""));
			const placeholders = rawNexusIds.map(() => "?").join(",");
			const stubRows = allTyped(db.prepare(`SELECT id, kind, name
             FROM nexus_nodes
             WHERE id IN (${placeholders})`), ...rawNexusIds);
			for (const row of stubRows) {
				const kind = row.kind === "file" || row.kind === "folder" || row.kind === "module" ? "file" : "symbol";
				stubs.push({
					id: `nexus:${row.id}`,
					kind,
					substrate: "nexus",
					label: row.name,
					weight: void 0,
					createdAt: null,
					meta: {
						nexus_kind: row.kind,
						isStub: true
					}
				});
			}
		} catch {}
	}
	for (const [substrate, nodeIds] of stubsBySubstrate) {
		if (substrate === "nexus") continue;
		for (const nodeId of nodeIds) {
			const rawId = nodeId.replace(new RegExp(`^${substrate}:`), "");
			stubs.push({
				id: nodeId,
				kind: "observation",
				substrate,
				label: rawId,
				weight: void 0,
				createdAt: null,
				meta: { isStub: true }
			});
		}
	}
	return stubs;
}
/**
* Queries all five substrates and merges the results into a unified BrainGraph.
*
* When `options.substrates` is provided, only those substrates are queried.
* Node IDs are substrate-prefixed, so deduplication is safe to perform
* by ID equality alone.
*
* After all substrates are loaded, performs a second-pass stub-node load:
* any edge target ID not yet in the loaded node set is fetched as a minimal
* stub (id, substrate, kind, label). This recovers cross-substrate edges
* that would otherwise be silently dropped.
*
* Stub nodes carry `meta.isStub: true` for optional UI differentiation.
*
* @param options - Query options forwarded to each substrate adapter.
* @returns Merged BrainGraph across all requested substrates, with stub nodes for unresolved edge targets.
*/
function getAllSubstrates(options = {}) {
	const substrates = options.substrates ?? ALL_SUBSTRATES;
	const limit = options.limit ?? 500;
	const allNodes = [];
	const allEdges = [];
	const nodeCounts = Object.fromEntries(ALL_SUBSTRATES.map((s) => [s, 0]));
	const edgeCounts = Object.fromEntries([...ALL_SUBSTRATES, "cross"].map((s) => [s, 0]));
	for (const substrate of substrates) {
		const adapter = ADAPTER_MAP[substrate];
		if (!adapter) continue;
		const { nodes, edges } = adapter({
			...options,
			limit
		});
		allNodes.push(...nodes);
		allEdges.push(...edges);
		nodeCounts[substrate] = nodes.length;
		for (const edge of edges) edgeCounts[edge.substrate] = (edgeCounts[edge.substrate] ?? 0) + 1;
	}
	const seenIds = /* @__PURE__ */ new Set();
	const uniqueNodes = [];
	for (const node of allNodes) if (!seenIds.has(node.id)) {
		seenIds.add(node.id);
		uniqueNodes.push(node);
	}
	const requestedSubstrateSet = options.substrates ? new Set(options.substrates) : null;
	const stubNodes = loadStubNodesForEdgeTargets(seenIds, allEdges);
	for (const stubNode of stubNodes) if (!seenIds.has(stubNode.id)) {
		if (requestedSubstrateSet && !requestedSubstrateSet.has(stubNode.substrate)) continue;
		seenIds.add(stubNode.id);
		uniqueNodes.push(stubNode);
	}
	const truncated = uniqueNodes.length >= limit;
	return {
		nodes: truncated ? uniqueNodes.slice(0, limit) : uniqueNodes,
		edges: allEdges,
		counts: {
			nodes: nodeCounts,
			edges: edgeCounts
		},
		truncated
	};
}

export { getAgentRegistryDb as a, getNexusDb as b, getConduitDb as c, getTasksDb as d, getBrainDb as e, getAllSubstrates as g };
//# sourceMappingURL=src-DG3l2-J-.js.map
