import { h as __exportAll } from "./pi-embedded-utils-CPkknmhn.js";
import { c as resolveStateDir } from "./paths-BVqcPirq.js";
import { ct as createInternalHookEvent, lt as triggerInternalHook } from "./subsystem-CBhYrL33.js";
import { n as buildFtsQuery } from "./hybrid-0SH6eiv4.js";
import { t as applySqlitePragmas } from "./sqlite-pragmas-BgY_Vgdo.js";
import { t as requireNodeSqlite } from "./sqlite-DUluVPN0.js";
import path from "node:path";
import { mkdirSync } from "node:fs";
import os from "node:os";
import { randomUUID } from "node:crypto";
import { Type } from "@sinclair/typebox";

//#region src/memory/typed-schema.ts
/**
* Typed memory object schema — 8 object types + 5 relationship types.
*
* Extends (does not replace) the existing message ledger. Uses the same
* node:sqlite pattern (DatabaseSync singleton, WAL pragmas).
*
* Object types:  entity | fact | event | preference | task | belief | interaction | skill
* Relationship types: related_to | caused_by | part_of | precedes | contradicts
*
* @module
*/
const INIT_SQL = `
CREATE TABLE IF NOT EXISTS memory_objects (
  id         TEXT    PRIMARY KEY,
  type       TEXT    NOT NULL CHECK(type IN ('entity','fact','event','preference','task','belief','interaction','skill')),
  label      TEXT    NOT NULL,
  data       TEXT    NOT NULL DEFAULT '{}',
  created_at INTEGER NOT NULL,
  updated_at INTEGER NOT NULL,
  ttl        INTEGER
);

CREATE INDEX IF NOT EXISTS idx_memory_objects_type      ON memory_objects(type);
CREATE INDEX IF NOT EXISTS idx_memory_objects_label     ON memory_objects(label);
CREATE INDEX IF NOT EXISTS idx_memory_objects_ttl       ON memory_objects(ttl) WHERE ttl IS NOT NULL;

CREATE TABLE IF NOT EXISTS memory_relationships (
  from_id    TEXT    NOT NULL REFERENCES memory_objects(id) ON DELETE CASCADE,
  to_id      TEXT    NOT NULL REFERENCES memory_objects(id) ON DELETE CASCADE,
  rel_type   TEXT    NOT NULL CHECK(rel_type IN ('related_to','caused_by','part_of','precedes','contradicts')),
  weight     REAL    NOT NULL DEFAULT 1.0,
  created_at INTEGER NOT NULL,
  PRIMARY KEY (from_id, to_id, rel_type)
);

CREATE INDEX IF NOT EXISTS idx_memory_rels_from ON memory_relationships(from_id);
CREATE INDEX IF NOT EXISTS idx_memory_rels_to   ON memory_relationships(to_id);

CREATE VIRTUAL TABLE IF NOT EXISTS memory_objects_fts USING fts5(
  id UNINDEXED,
  label,
  content='memory_objects',
  content_rowid='rowid'
);
`;
let db = null;
function getTypedMemoryDb() {
	return db;
}
function requireDb() {
	if (!db) throw new Error("Typed memory DB not initialised — call openTypedMemoryDb() first");
	return db;
}
function nowMs() {
	return Date.now();
}
function encodeData(data) {
	try {
		return JSON.stringify(data);
	} catch {
		return "{}";
	}
}
function decodeData(raw) {
	if (typeof raw !== "string") return {};
	try {
		const parsed = JSON.parse(raw);
		return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
	} catch {
		return {};
	}
}
function rowToMemoryObject(row) {
	return {
		id: String(row["id"]),
		type: row["type"],
		label: String(row["label"]),
		data: decodeData(row["data"]),
		createdAt: Number(row["created_at"]),
		updatedAt: Number(row["updated_at"]),
		ttl: row["ttl"] == null ? null : Number(row["ttl"])
	};
}
function rowToRelationship(row) {
	return {
		fromId: String(row["from_id"]),
		toId: String(row["to_id"]),
		relType: row["rel_type"],
		weight: Number(row["weight"]),
		createdAt: Number(row["created_at"])
	};
}
/** Create a new memory object. Returns the id. */
function createObject(params) {
	const now = nowMs();
	requireDb().prepare(`INSERT INTO memory_objects (id, type, label, data, created_at, updated_at, ttl)
     VALUES (?, ?, ?, ?, ?, ?, ?)`).run(params.id, params.type, params.label, encodeData(params.data ?? {}), now, now, params.ttl ?? null);
	return params.id;
}
/** Get a memory object by id. Returns null if not found. */
function getObject(id) {
	const row = requireDb().prepare("SELECT * FROM memory_objects WHERE id = ?").get(id);
	return row ? rowToMemoryObject(row) : null;
}
/** Update label and/or data for an existing object. */
function updateObject(id, updates) {
	const obj = getObject(id);
	if (!obj) return;
	const newLabel = updates.label ?? obj.label;
	const newData = updates.data !== void 0 ? updates.data : obj.data;
	const newTtl = "ttl" in updates ? updates.ttl ?? null : obj.ttl;
	requireDb().prepare(`UPDATE memory_objects SET label = ?, data = ?, ttl = ?, updated_at = ? WHERE id = ?`).run(newLabel, encodeData(newData), newTtl, nowMs(), id);
}
/** Delete a memory object (and cascade-delete its relationships). */
function deleteObject(id) {
	requireDb().prepare("DELETE FROM memory_objects WHERE id = ?").run(id);
}
/** List all objects of a given type. */
function listObjectsByType(type) {
	return requireDb().prepare("SELECT * FROM memory_objects WHERE type = ? ORDER BY updated_at DESC").all(type).map(rowToMemoryObject);
}
/** Create or replace a relationship between two objects. */
function createRelationship(params) {
	const now = nowMs();
	requireDb().prepare(`INSERT OR REPLACE INTO memory_relationships (from_id, to_id, rel_type, weight, created_at)
     VALUES (?, ?, ?, ?, ?)`).run(params.fromId, params.toId, params.relType, params.weight ?? 1, now);
}
/** Get all objects related to the given id, optionally filtered by relationship type. */
function getRelated(id, relType) {
	const db = requireDb();
	return (relType ? db.prepare(`SELECT o.* FROM memory_objects o
           JOIN memory_relationships r ON o.id = r.to_id
           WHERE r.from_id = ? AND r.rel_type = ?
           ORDER BY r.weight DESC`).all(id, relType) : db.prepare(`SELECT o.* FROM memory_objects o
           JOIN memory_relationships r ON o.id = r.to_id
           WHERE r.from_id = ?
           ORDER BY r.weight DESC`).all(id)).map(rowToMemoryObject);
}
/**
* Full-text search over memory object labels.
* Searches across all types unless a type filter is provided.
*/
function searchObjects(query, type) {
	const db = requireDb();
	db.exec("INSERT INTO memory_objects_fts(memory_objects_fts) VALUES('rebuild')");
	const ftsQuery = buildFtsQuery(query);
	if (!ftsQuery) return [];
	return (type ? db.prepare(`SELECT * FROM memory_objects
           WHERE rowid IN (
             SELECT rowid FROM memory_objects_fts WHERE memory_objects_fts MATCH ?
           ) AND type = ?`).all(ftsQuery, type) : db.prepare(`SELECT * FROM memory_objects
           WHERE rowid IN (
             SELECT rowid FROM memory_objects_fts WHERE memory_objects_fts MATCH ?
           )`).all(ftsQuery)).map(rowToMemoryObject);
}
const _registry = /* @__PURE__ */ new Map();
/**
* Open (or return from cache) a DatabaseSync instance for the given path.
* The instance is initialised with WAL pragmas and the typed memory schema.
*/
function getOrOpenDb(dbPath) {
	let registeredDb = _registry.get(dbPath);
	if (!registeredDb) {
		const { DatabaseSync } = requireNodeSqlite();
		registeredDb = new DatabaseSync(dbPath);
		applySqlitePragmas(registeredDb);
		registeredDb.exec("PRAGMA foreign_keys = ON");
		registeredDb.exec(INIT_SQL);
		_registry.set(dbPath, registeredDb);
	}
	return registeredDb;
}
function createObjectInDb(db, params) {
	const now = nowMs();
	db.prepare(`INSERT INTO memory_objects (id, type, label, data, created_at, updated_at, ttl)
     VALUES (?, ?, ?, ?, ?, ?, ?)`).run(params.id, params.type, params.label, encodeData(params.data ?? {}), now, now, params.ttl ?? null);
	return params.id;
}
function getObjectInDb(db, id) {
	const row = db.prepare("SELECT * FROM memory_objects WHERE id = ?").get(id);
	return row ? rowToMemoryObject(row) : null;
}
function updateObjectInDb(db, id, updates) {
	const obj = getObjectInDb(db, id);
	if (!obj) return;
	const newLabel = updates.label ?? obj.label;
	const newData = updates.data !== void 0 ? updates.data : obj.data;
	const newTtl = "ttl" in updates ? updates.ttl ?? null : obj.ttl;
	db.prepare(`UPDATE memory_objects SET label = ?, data = ?, ttl = ?, updated_at = ? WHERE id = ?`).run(newLabel, encodeData(newData), newTtl, nowMs(), id);
}
function deleteObjectInDb(db, id) {
	db.prepare("DELETE FROM memory_objects WHERE id = ?").run(id);
}
function listObjectsByTypeInDb(db, type) {
	return db.prepare("SELECT * FROM memory_objects WHERE type = ? ORDER BY updated_at DESC").all(type).map(rowToMemoryObject);
}
/** List all memory objects, newest first. */
function listAllObjectsInDb(db) {
	return db.prepare("SELECT * FROM memory_objects ORDER BY updated_at DESC").all().map(rowToMemoryObject);
}
/** List all relationships. */
function listAllRelationshipsInDb(db) {
	return db.prepare("SELECT * FROM memory_relationships").all().map(rowToRelationship);
}
function getRelatedInDb(db, id, relType) {
	return (relType ? db.prepare(`SELECT o.* FROM memory_objects o
           JOIN memory_relationships r ON o.id = r.to_id
           WHERE r.from_id = ? AND r.rel_type = ?
           ORDER BY r.weight DESC`).all(id, relType) : db.prepare(`SELECT o.* FROM memory_objects o
           JOIN memory_relationships r ON o.id = r.to_id
           WHERE r.from_id = ?
           ORDER BY r.weight DESC`).all(id)).map(rowToMemoryObject);
}
function createRelationshipInDb(db, params) {
	const now = nowMs();
	db.prepare(`INSERT OR REPLACE INTO memory_relationships (from_id, to_id, rel_type, weight, created_at)
     VALUES (?, ?, ?, ?, ?)`).run(params.fromId, params.toId, params.relType, params.weight ?? 1, now);
}
function searchObjectsInDb(db, query, type) {
	db.exec("INSERT INTO memory_objects_fts(memory_objects_fts) VALUES('rebuild')");
	const ftsQuery = buildFtsQuery(query);
	if (!ftsQuery) return [];
	return (type ? db.prepare(`SELECT * FROM memory_objects
           WHERE rowid IN (
             SELECT rowid FROM memory_objects_fts WHERE memory_objects_fts MATCH ?
           ) AND type = ?`).all(ftsQuery, type) : db.prepare(`SELECT * FROM memory_objects
           WHERE rowid IN (
             SELECT rowid FROM memory_objects_fts WHERE memory_objects_fts MATCH ?
           )`).all(ftsQuery)).map(rowToMemoryObject);
}

//#endregion
//#region src/memory/knowledge-graph.ts
/**
* Knowledge graph query interface — high-level API over the typed memory schema.
*
* Exposes five operations as both TypeScript functions and agent tool definitions:
*   remember       — create/upsert a typed memory object
*   recall_entity  — find an entity by name (exact or fuzzy label match)
*   find_related   — one-hop relationship traversal
*   forget         — delete a memory object
*   search_facts   — FTS5 full-text search over labels
*
* Gracefully degrades when the DB is not initialised (returns null / empty arrays).
*
* @module
*/
var knowledge_graph_exports = /* @__PURE__ */ __exportAll({
	KnowledgeGraphSession: () => KnowledgeGraphSession,
	createKnowledgeGraphTools: () => createKnowledgeGraphTools,
	findRelated: () => findRelated,
	forget: () => forget,
	getMemoryObject: () => getMemoryObject,
	linkObjects: () => linkObjects,
	listByType: () => listByType,
	recallEntity: () => recallEntity,
	remember: () => remember,
	searchFacts: () => searchFacts,
	updateEntity: () => updateEntity
});
function isDbReady() {
	return getTypedMemoryDb() !== null;
}
function textResult(text) {
	return {
		content: [{
			type: "text",
			text
		}],
		details: void 0
	};
}
/** Fire a memory hook event (best-effort, never throws). */
function emitMemoryEvent(action, nodeId, label, objectType, data, previousData) {
	try {
		triggerInternalHook(createInternalHookEvent("memory", action, "", {
			nodeId,
			label,
			objectType,
			data,
			previousData
		}));
	} catch {}
}
/**
* Create (or replace) a typed memory object.
* Returns the generated ID.
*/
function remember(params) {
	if (!isDbReady()) return "";
	if (params.type === "entity") {
		const existing = recallEntity(params.label);
		if (existing) return existing.id;
	}
	const id = randomUUID();
	createObject({
		id,
		...params
	});
	return id;
}
/**
* Find an entity by label — exact match first, then fuzzy FTS5.
* Returns null if not found or DB not ready.
*/
function recallEntity(name) {
	if (!isDbReady()) return null;
	const hits = searchObjects(name, "entity");
	const exact = hits.find((h) => h.label.trim().toLowerCase() === name.trim().toLowerCase());
	if (exact) return exact;
	return hits[0] ?? null;
}
/**
* Find all objects related to the given entity ID.
* Optionally filter by relationship type.
* Returns empty array if DB not ready or entity not found.
*/
function findRelated(entityId, relType) {
	if (!isDbReady()) return [];
	return getRelated(entityId, relType);
}
/**
* Delete a memory object by ID.
* Cascades to its relationships.
*/
function forget(id) {
	if (!isDbReady()) return;
	deleteObject(id);
}
/**
* Full-text search over fact labels.
* Returns matching Fact objects sorted by relevance.
*/
function searchFacts(query) {
	if (!isDbReady()) return [];
	return searchObjects(query, "fact");
}
/**
* Update a memory object's label and/or data by ID.
* Data is shallow-merged with existing data (update individual fields without losing others).
* Returns the updated object, or null if not found.
*/
function updateEntity(id, updates) {
	if (!isDbReady()) return null;
	const existing = getObject(id);
	if (!existing) return null;
	const mergedData = updates.data ? {
		...existing.data,
		...updates.data
	} : void 0;
	updateObject(id, {
		label: updates.label,
		data: mergedData
	});
	return getObject(id);
}
/**
* Link two objects with a typed relationship.
* No-op if DB is not ready.
*/
function linkObjects(fromId, toId, relType, weight = 1) {
	if (!isDbReady()) return;
	createRelationship({
		fromId,
		toId,
		relType,
		weight
	});
}
/**
* Get all objects of a given type.
*/
function listByType(type) {
	if (!isDbReady()) return [];
	return listObjectsByType(type);
}
/**
* Get a single object by ID.
*/
function getMemoryObject(id) {
	if (!isDbReady()) return null;
	return getObject(id);
}
/**
* Per-agent knowledge graph session.
* Each agent gets its own isolated SQLite DB at {stateDir}/agents/{agentId}/KG/kg.sqlite.
*/
var KnowledgeGraphSession = class KnowledgeGraphSession {
	constructor(db) {
		this.db = db;
	}
	/** Open a session for the given DB file path (creates if absent). */
	static open(dbPath) {
		return new KnowledgeGraphSession(getOrOpenDb(dbPath));
	}
	/** Open a session for a named agent, using the configured state directory. */
	static forAgent(agentId) {
		const stateDir = resolveStateDir(process.env, os.homedir.bind(os));
		const kgDir = path.join(stateDir, "agents", agentId, "KG");
		mkdirSync(kgDir, { recursive: true });
		const dbPath = path.join(kgDir, "kg.sqlite");
		return KnowledgeGraphSession.open(dbPath);
	}
	remember(params) {
		if (params.type === "entity") {
			const existing = this.recallEntity(params.label);
			if (existing) return existing.id;
		}
		const id = randomUUID();
		createObjectInDb(this.db, {
			id,
			...params
		});
		return id;
	}
	recallEntity(name) {
		const hits = searchObjectsInDb(this.db, name, "entity");
		return hits.find((h) => h.label.trim().toLowerCase() === name.trim().toLowerCase()) ?? hits[0] ?? null;
	}
	findRelated(entityId, relType) {
		return getRelatedInDb(this.db, entityId, relType);
	}
	forget(id) {
		deleteObjectInDb(this.db, id);
	}
	updateEntity(id, updates) {
		const existing = getObjectInDb(this.db, id);
		if (!existing) return null;
		const mergedData = updates.data ? {
			...existing.data,
			...updates.data
		} : void 0;
		updateObjectInDb(this.db, id, {
			label: updates.label,
			data: mergedData
		});
		return getObjectInDb(this.db, id);
	}
	searchFacts(query) {
		return searchObjectsInDb(this.db, query, "fact");
	}
	linkObjects(fromId, toId, relType, weight = 1) {
		createRelationshipInDb(this.db, {
			fromId,
			toId,
			relType,
			weight
		});
	}
	listByType(type) {
		return listObjectsByTypeInDb(this.db, type);
	}
	listAll() {
		return listAllObjectsInDb(this.db);
	}
	listAllRelationships() {
		return listAllRelationshipsInDb(this.db);
	}
	getMemoryObject(id) {
		return getObjectInDb(this.db, id);
	}
};
const RememberSchema = Type.Object({
	label: Type.String({ description: "Short, descriptive name for this memory object." }),
	type: Type.Union([
		Type.Literal("entity"),
		Type.Literal("fact"),
		Type.Literal("event"),
		Type.Literal("preference"),
		Type.Literal("task"),
		Type.Literal("belief"),
		Type.Literal("interaction"),
		Type.Literal("skill")
	], { description: "Type of memory object to create." }),
	data: Type.Optional(Type.Record(Type.String(), Type.Unknown(), { description: "Optional structured data to attach (JSON object)." }))
});
const RecallEntitySchema = Type.Object({ name: Type.String({ description: "Entity name or label to search for." }) });
const FindRelatedSchema = Type.Object({
	entityId: Type.String({ description: "ID of the source entity." }),
	relType: Type.Optional(Type.Union([
		Type.Literal("related_to"),
		Type.Literal("caused_by"),
		Type.Literal("part_of"),
		Type.Literal("precedes"),
		Type.Literal("contradicts")
	], { description: "Filter by relationship type. Omit to return all." }))
});
const ForgetSchema = Type.Object({ id: Type.String({ description: "ID of the memory object to delete." }) });
const SearchFactsSchema = Type.Object({ query: Type.String({ description: "Search query to match against fact labels." }) });
const UpdateEntitySchema = Type.Object({
	id: Type.String({ description: "ID of the memory object to update." }),
	label: Type.Optional(Type.String({ description: "New label (leave empty to keep current)." })),
	data: Type.Optional(Type.Record(Type.String(), Type.Unknown(), { description: "Fields to merge into the existing data object. Shallow merge — existing keys not present here are preserved." }))
});
/**
* Create the set of knowledge graph agent tools.
* When a `session` is provided, all operations use the session's isolated DB.
* When omitted, falls back to the module-level singleton (CLI / legacy path).
*/
function createKnowledgeGraphTools(session) {
	return [
		{
			label: "Remember",
			name: "remember",
			description: "Store a typed fact permanently (person, preference, decision, event, etc.). Call this proactively whenever you learn something worth remembering across sessions — don't wait to be asked.",
			parameters: RememberSchema,
			execute: async (_id, args) => {
				const params = args;
				const label = params["label"] ?? "";
				const type = params["type"] ?? "fact";
				const data = params["data"] && typeof params["data"] === "object" && !Array.isArray(params["data"]) ? params["data"] : void 0;
				if (!label) return textResult("Error: label is required");
				const objectId = session ? session.remember({
					label,
					type,
					data
				}) : remember({
					label,
					type,
					data
				});
				if (objectId) emitMemoryEvent("node_created", objectId, label, type, data ?? {});
				return textResult(objectId ? `Stored ${type} with id: ${objectId}` : "DB not ready");
			}
		},
		{
			label: "Recall Entity",
			name: "recall_entity",
			description: "Look up a known entity by name before answering questions about a person, place, or thing. Use this first; fall back to search_facts for broad queries.",
			parameters: RecallEntitySchema,
			execute: async (_id, args) => {
				const name = args["name"] ?? "";
				const entity = session ? session.recallEntity(name) : recallEntity(name);
				if (!entity) return textResult(`No entity found for: ${name}`);
				return textResult(JSON.stringify({
					id: entity.id,
					label: entity.label,
					data: entity.data
				}, null, 2));
			}
		},
		{
			label: "Find Related",
			name: "find_related",
			description: "Find all facts, events, or entities connected to a known entity. Use when you need surrounding context (e.g. preferences linked to a person, decisions linked to a project).",
			parameters: FindRelatedSchema,
			execute: async (_id, args) => {
				const params = args;
				const entityId = params["entityId"] ?? "";
				const relType = params["relType"];
				const related = session ? session.findRelated(entityId, relType) : findRelated(entityId, relType);
				if (related.length === 0) return textResult("No related objects found.");
				return textResult(related.map((o) => `[${o.type}] ${o.label} (id: ${o.id})`).join("\n"));
			}
		},
		{
			label: "Forget",
			name: "forget",
			description: "Remove a fact that is outdated, wrong, or superseded. Prefer updating via remember (upsert) unless the fact should be fully erased.",
			parameters: ForgetSchema,
			execute: async (_id, args) => {
				const objectId = args["id"] ?? "";
				if (!objectId) return textResult("Error: id is required");
				const existing = session ? session.getMemoryObject(objectId) : getMemoryObject(objectId);
				if (session) session.forget(objectId);
				else forget(objectId);
				if (existing) emitMemoryEvent("node_deleted", objectId, existing.label, existing.type, existing.data);
				return textResult(`Deleted memory object: ${objectId}`);
			}
		},
		{
			label: "Search Facts",
			name: "search_facts",
			description: "Full-text search across all stored facts. Use when you don't know the entity name yet, or want to find anything related to a keyword.",
			parameters: SearchFactsSchema,
			execute: async (_id, args) => {
				const query = args["query"] ?? "";
				if (!query) return textResult("Error: query is required");
				const facts = session ? session.searchFacts(query) : searchFacts(query);
				if (facts.length === 0) return textResult(`No facts found for: ${query}`);
				return textResult(facts.slice(0, 10).map((f) => `• ${f.label}${Object.keys(f.data).length ? ` — ${JSON.stringify(f.data)}` : ""}`).join("\n"));
			}
		},
		{
			label: "Update Entity",
			name: "update_entity",
			description: "Update an existing memory object's label or data fields. Data is shallow-merged — only the keys you provide are overwritten; other existing keys are preserved. Use this to track evolving state (status, summary, timestamps) on knowledge graph nodes.",
			parameters: UpdateEntitySchema,
			execute: async (_id, args) => {
				const params = args;
				const objectId = params["id"] ?? "";
				if (!objectId) return textResult("Error: id is required");
				const label = params["label"];
				const data = params["data"] && typeof params["data"] === "object" && !Array.isArray(params["data"]) ? params["data"] : void 0;
				if (!label && !data) return textResult("Error: at least one of label or data is required");
				const previous = session ? session.getMemoryObject(objectId) : getMemoryObject(objectId);
				const updated = session ? session.updateEntity(objectId, {
					label,
					data
				}) : updateEntity(objectId, {
					label,
					data
				});
				if (!updated) return textResult(`No memory object found with id: ${objectId}`);
				emitMemoryEvent("node_updated", updated.id, updated.label, updated.type, updated.data, previous?.data);
				return textResult(JSON.stringify({
					id: updated.id,
					label: updated.label,
					data: updated.data
				}, null, 2));
			}
		}
	];
}

//#endregion
export { createKnowledgeGraphTools as n, knowledge_graph_exports as r, KnowledgeGraphSession as t };