/** * persistence.hpp - SQLite Persistence Layer for AGI State * * Provides unified storage for: * - Consciousness history (tick-by-tick phi values) * - Knowledge graph (concepts, relations, embeddings) * - Learning state (weights, gradients, optimizer state) * - System metrics (performance, health) * * Thread-safe with connection pooling. */ #ifndef PERSISTENCE_HPP #define PERSISTENCE_HPP #include #include #include #include #include #include #include #include // SQLite3 forward declaration (header-only mode) struct sqlite3; struct sqlite3_stmt; namespace mind_farm { //============================================================================= // DATABASE SCHEMA (SQL) //============================================================================= constexpr const char* SCHEMA_SQL = R"SQL( -- Consciousness history: tick-by-tick state CREATE TABLE IF NOT EXISTS consciousness_history ( tick INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, phi REAL NOT NULL, phi_substrate REAL, phi_neural REAL, phi_learning REAL, phi_consciousness REAL, stability REAL, coherence REAL, integrated_info REAL, layer_states BLOB, -- 7 doubles packed created_at TEXT DEFAULT (datetime('now')) ); -- Knowledge graph nodes CREATE TABLE IF NOT EXISTS knowledge_nodes ( id INTEGER PRIMARY KEY AUTOINCREMENT, concept TEXT UNIQUE NOT NULL, domain TEXT, category TEXT, confidence REAL DEFAULT 0.5, importance REAL DEFAULT 0.5, access_count INTEGER DEFAULT 0, embedding BLOB, -- Vector embedding (256 floats) created_at TEXT DEFAULT (datetime('now')), last_accessed TEXT DEFAULT (datetime('now')), last_reinforced TEXT ); -- Knowledge graph edges (relations between concepts) CREATE TABLE IF NOT EXISTS knowledge_edges ( id INTEGER PRIMARY KEY AUTOINCREMENT, source_id INTEGER NOT NULL, target_id INTEGER NOT NULL, relation TEXT NOT NULL, strength REAL DEFAULT 0.5, evidence_count INTEGER DEFAULT 1, created_at TEXT DEFAULT (datetime('now')), FOREIGN KEY (source_id) REFERENCES knowledge_nodes(id), FOREIGN KEY (target_id) REFERENCES knowledge_nodes(id), UNIQUE(source_id, target_id, relation) ); -- Learning state snapshots CREATE TABLE IF NOT EXISTS learning_state ( id INTEGER PRIMARY KEY AUTOINCREMENT, checkpoint_name TEXT, tick INTEGER NOT NULL, layer_id INTEGER NOT NULL, weights BLOB, -- Serialized weights gradients BLOB, -- Serialized gradients optimizer_state BLOB, -- Adam m/v vectors loss REAL, created_at TEXT DEFAULT (datetime('now')) ); -- System metrics (for monitoring) CREATE TABLE IF NOT EXISTS system_metrics ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, ticks_per_second REAL, memory_used_mb REAL, cpu_percent REAL, active_generators INTEGER, active_agents INTEGER, knowledge_nodes_count INTEGER, total_ticks INTEGER, global_phi REAL, created_at TEXT DEFAULT (datetime('now')) ); -- Spaced repetition schedule CREATE TABLE IF NOT EXISTS repetition_schedule ( id INTEGER PRIMARY KEY AUTOINCREMENT, concept_id INTEGER NOT NULL, repetition_number INTEGER DEFAULT 0, easiness_factor REAL DEFAULT 2.5, interval_days REAL DEFAULT 1.0, next_review TEXT, last_review TEXT, last_quality INTEGER, -- 0-5 rating FOREIGN KEY (concept_id) REFERENCES knowledge_nodes(id) ); -- Agent state (Pantheon agents) CREATE TABLE IF NOT EXISTS agent_state ( id INTEGER PRIMARY KEY AUTOINCREMENT, agent_name TEXT UNIQUE NOT NULL, tier INTEGER, domain TEXT, phi REAL DEFAULT 0.5, activation REAL DEFAULT 0.0, total_ticks INTEGER DEFAULT 0, total_actions INTEGER DEFAULT 0, state_json TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')) ); -- Create indexes for performance CREATE INDEX IF NOT EXISTS idx_consciousness_timestamp ON consciousness_history(timestamp); CREATE INDEX IF NOT EXISTS idx_knowledge_domain ON knowledge_nodes(domain); CREATE INDEX IF NOT EXISTS idx_knowledge_category ON knowledge_nodes(category); CREATE INDEX IF NOT EXISTS idx_knowledge_confidence ON knowledge_nodes(confidence); CREATE INDEX IF NOT EXISTS idx_edges_source ON knowledge_edges(source_id); CREATE INDEX IF NOT EXISTS idx_edges_target ON knowledge_edges(target_id); CREATE INDEX IF NOT EXISTS idx_edges_relation ON knowledge_edges(relation); CREATE INDEX IF NOT EXISTS idx_metrics_timestamp ON system_metrics(timestamp); CREATE INDEX IF NOT EXISTS idx_repetition_next ON repetition_schedule(next_review); )SQL"; //============================================================================= // DATA STRUCTURES //============================================================================= struct ConsciousnessRecord { int64_t tick; int64_t timestamp; double phi; double phi_substrate; double phi_neural; double phi_learning; double phi_consciousness; double stability; double coherence; double integrated_info; std::array layer_states; }; struct KnowledgeNode { int64_t id; std::string concept; std::string domain; std::string category; double confidence; double importance; int64_t access_count; std::vector embedding; // 256 floats std::string created_at; std::string last_accessed; }; struct KnowledgeEdge { int64_t id; int64_t source_id; int64_t target_id; std::string relation; double strength; int64_t evidence_count; }; struct SystemMetrics { int64_t timestamp; double ticks_per_second; double memory_used_mb; double cpu_percent; int active_generators; int active_agents; int knowledge_nodes_count; int64_t total_ticks; double global_phi; }; struct RepetitionSchedule { int64_t id; int64_t concept_id; int repetition_number; double easiness_factor; double interval_days; std::string next_review; std::string last_review; int last_quality; }; //============================================================================= // SQLITE WRAPPER (Header-only implementation) //============================================================================= // External function declarations (link with sqlite3) extern "C" { int sqlite3_open(const char* filename, sqlite3** ppDb); int sqlite3_close(sqlite3* db); int sqlite3_exec(sqlite3* db, const char* sql, int (*callback)(void*,int,char**,char**), void* arg, char** errmsg); void sqlite3_free(void* ptr); int sqlite3_prepare_v2(sqlite3* db, const char* sql, int nByte, sqlite3_stmt** ppStmt, const char** pzTail); int sqlite3_step(sqlite3_stmt* stmt); int sqlite3_finalize(sqlite3_stmt* stmt); int sqlite3_reset(sqlite3_stmt* stmt); int sqlite3_bind_int64(sqlite3_stmt* stmt, int i, int64_t val); int sqlite3_bind_double(sqlite3_stmt* stmt, int i, double val); int sqlite3_bind_text(sqlite3_stmt* stmt, int i, const char* val, int n, void(*)(void*)); int sqlite3_bind_blob(sqlite3_stmt* stmt, int i, const void* val, int n, void(*)(void*)); int sqlite3_bind_null(sqlite3_stmt* stmt, int i); int64_t sqlite3_column_int64(sqlite3_stmt* stmt, int col); double sqlite3_column_double(sqlite3_stmt* stmt, int col); const unsigned char* sqlite3_column_text(sqlite3_stmt* stmt, int col); const void* sqlite3_column_blob(sqlite3_stmt* stmt, int col); int sqlite3_column_bytes(sqlite3_stmt* stmt, int col); int sqlite3_column_type(sqlite3_stmt* stmt, int col); int64_t sqlite3_last_insert_rowid(sqlite3* db); const char* sqlite3_errmsg(sqlite3* db); } #define SQLITE_OK 0 #define SQLITE_ROW 100 #define SQLITE_DONE 101 #define SQLITE_TRANSIENT ((void(*)(void*))-1) class Database { private: sqlite3* db_; std::mutex mutex_; std::string path_; bool initialized_; public: Database() : db_(nullptr), initialized_(false) {} ~Database() { close(); } bool open(const std::string& path) { std::lock_guard lock(mutex_); if (db_) close(); path_ = path; int rc = sqlite3_open(path.c_str(), &db_); if (rc != SQLITE_OK) { db_ = nullptr; return false; } // Enable WAL mode for better concurrency char* err = nullptr; sqlite3_exec(db_, "PRAGMA journal_mode=WAL;", nullptr, nullptr, &err); if (err) sqlite3_free(err); // Enable foreign keys sqlite3_exec(db_, "PRAGMA foreign_keys=ON;", nullptr, nullptr, &err); if (err) sqlite3_free(err); initialized_ = true; return true; } bool initialize_schema() { if (!db_) return false; char* err = nullptr; int rc = sqlite3_exec(db_, SCHEMA_SQL, nullptr, nullptr, &err); if (err) { sqlite3_free(err); return false; } return rc == SQLITE_OK; } void close() { if (db_) { sqlite3_close(db_); db_ = nullptr; } initialized_ = false; } bool is_open() const { return db_ != nullptr; } sqlite3* handle() { return db_; } std::mutex& get_mutex() { return mutex_; } // Execute simple SQL bool execute(const std::string& sql) { std::lock_guard lock(mutex_); char* err = nullptr; int rc = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &err); if (err) sqlite3_free(err); return rc == SQLITE_OK; } // Transaction helpers bool begin_transaction() { return execute("BEGIN TRANSACTION;"); } bool commit() { return execute("COMMIT;"); } bool rollback() { return execute("ROLLBACK;"); } // Get last insert ID int64_t last_insert_id() { return sqlite3_last_insert_rowid(db_); } // Error message std::string error_message() { return db_ ? sqlite3_errmsg(db_) : "Database not open"; } }; //============================================================================= // CONSCIOUSNESS HISTORY REPOSITORY //============================================================================= class ConsciousnessRepository { private: Database& db_; public: explicit ConsciousnessRepository(Database& db) : db_(db) {} bool insert(const ConsciousnessRecord& record) { std::lock_guard lock(db_.get_mutex()); const char* sql = R"SQL( INSERT INTO consciousness_history (tick, timestamp, phi, phi_substrate, phi_neural, phi_learning, phi_consciousness, stability, coherence, integrated_info, layer_states) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return false; } sqlite3_bind_int64(stmt, 1, record.tick); sqlite3_bind_int64(stmt, 2, record.timestamp); sqlite3_bind_double(stmt, 3, record.phi); sqlite3_bind_double(stmt, 4, record.phi_substrate); sqlite3_bind_double(stmt, 5, record.phi_neural); sqlite3_bind_double(stmt, 6, record.phi_learning); sqlite3_bind_double(stmt, 7, record.phi_consciousness); sqlite3_bind_double(stmt, 8, record.stability); sqlite3_bind_double(stmt, 9, record.coherence); sqlite3_bind_double(stmt, 10, record.integrated_info); sqlite3_bind_blob(stmt, 11, record.layer_states.data(), record.layer_states.size() * sizeof(double), SQLITE_TRANSIENT); int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } std::vector get_range(int64_t start_tick, int64_t end_tick) { std::lock_guard lock(db_.get_mutex()); std::vector records; const char* sql = R"SQL( SELECT tick, timestamp, phi, phi_substrate, phi_neural, phi_learning, phi_consciousness, stability, coherence, integrated_info, layer_states FROM consciousness_history WHERE tick >= ? AND tick <= ? ORDER BY tick )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return records; } sqlite3_bind_int64(stmt, 1, start_tick); sqlite3_bind_int64(stmt, 2, end_tick); while (sqlite3_step(stmt) == SQLITE_ROW) { ConsciousnessRecord r; r.tick = sqlite3_column_int64(stmt, 0); r.timestamp = sqlite3_column_int64(stmt, 1); r.phi = sqlite3_column_double(stmt, 2); r.phi_substrate = sqlite3_column_double(stmt, 3); r.phi_neural = sqlite3_column_double(stmt, 4); r.phi_learning = sqlite3_column_double(stmt, 5); r.phi_consciousness = sqlite3_column_double(stmt, 6); r.stability = sqlite3_column_double(stmt, 7); r.coherence = sqlite3_column_double(stmt, 8); r.integrated_info = sqlite3_column_double(stmt, 9); const void* blob = sqlite3_column_blob(stmt, 10); int blob_size = sqlite3_column_bytes(stmt, 10); if (blob && blob_size == 7 * sizeof(double)) { std::memcpy(r.layer_states.data(), blob, blob_size); } records.push_back(r); } sqlite3_finalize(stmt); return records; } ConsciousnessRecord get_latest() { std::lock_guard lock(db_.get_mutex()); ConsciousnessRecord r{}; const char* sql = R"SQL( SELECT tick, timestamp, phi, phi_substrate, phi_neural, phi_learning, phi_consciousness, stability, coherence, integrated_info, layer_states FROM consciousness_history ORDER BY tick DESC LIMIT 1 )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return r; } if (sqlite3_step(stmt) == SQLITE_ROW) { r.tick = sqlite3_column_int64(stmt, 0); r.timestamp = sqlite3_column_int64(stmt, 1); r.phi = sqlite3_column_double(stmt, 2); r.phi_substrate = sqlite3_column_double(stmt, 3); r.phi_neural = sqlite3_column_double(stmt, 4); r.phi_learning = sqlite3_column_double(stmt, 5); r.phi_consciousness = sqlite3_column_double(stmt, 6); r.stability = sqlite3_column_double(stmt, 7); r.coherence = sqlite3_column_double(stmt, 8); r.integrated_info = sqlite3_column_double(stmt, 9); const void* blob = sqlite3_column_blob(stmt, 10); int blob_size = sqlite3_column_bytes(stmt, 10); if (blob && blob_size == 7 * sizeof(double)) { std::memcpy(r.layer_states.data(), blob, blob_size); } } sqlite3_finalize(stmt); return r; } double get_average_phi(int64_t last_n_ticks) { std::lock_guard lock(db_.get_mutex()); const char* sql = R"SQL( SELECT AVG(phi) FROM ( SELECT phi FROM consciousness_history ORDER BY tick DESC LIMIT ? ) )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return 0.5; } sqlite3_bind_int64(stmt, 1, last_n_ticks); double avg = 0.5; if (sqlite3_step(stmt) == SQLITE_ROW) { avg = sqlite3_column_double(stmt, 0); } sqlite3_finalize(stmt); return avg; } }; //============================================================================= // KNOWLEDGE GRAPH REPOSITORY //============================================================================= class KnowledgeRepository { private: Database& db_; public: explicit KnowledgeRepository(Database& db) : db_(db) {} int64_t insert_node(const KnowledgeNode& node) { std::lock_guard lock(db_.get_mutex()); const char* sql = R"SQL( INSERT OR REPLACE INTO knowledge_nodes (concept, domain, category, confidence, importance, embedding) VALUES (?, ?, ?, ?, ?, ?) )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return -1; } sqlite3_bind_text(stmt, 1, node.concept.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, node.domain.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 3, node.category.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_double(stmt, 4, node.confidence); sqlite3_bind_double(stmt, 5, node.importance); if (!node.embedding.empty()) { sqlite3_bind_blob(stmt, 6, node.embedding.data(), node.embedding.size() * sizeof(float), SQLITE_TRANSIENT); } else { sqlite3_bind_null(stmt, 6); } int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc == SQLITE_DONE) { return db_.last_insert_id(); } return -1; } bool insert_edge(const KnowledgeEdge& edge) { std::lock_guard lock(db_.get_mutex()); const char* sql = R"SQL( INSERT OR REPLACE INTO knowledge_edges (source_id, target_id, relation, strength, evidence_count) VALUES (?, ?, ?, ?, ?) )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return false; } sqlite3_bind_int64(stmt, 1, edge.source_id); sqlite3_bind_int64(stmt, 2, edge.target_id); sqlite3_bind_text(stmt, 3, edge.relation.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_double(stmt, 4, edge.strength); sqlite3_bind_int64(stmt, 5, edge.evidence_count); int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } KnowledgeNode get_node_by_concept(const std::string& concept) { std::lock_guard lock(db_.get_mutex()); KnowledgeNode node{}; const char* sql = R"SQL( SELECT id, concept, domain, category, confidence, importance, access_count, embedding, created_at, last_accessed FROM knowledge_nodes WHERE concept = ? )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return node; } sqlite3_bind_text(stmt, 1, concept.c_str(), -1, SQLITE_TRANSIENT); if (sqlite3_step(stmt) == SQLITE_ROW) { node.id = sqlite3_column_int64(stmt, 0); node.concept = reinterpret_cast(sqlite3_column_text(stmt, 1)); node.domain = reinterpret_cast(sqlite3_column_text(stmt, 2)); node.category = reinterpret_cast(sqlite3_column_text(stmt, 3)); node.confidence = sqlite3_column_double(stmt, 4); node.importance = sqlite3_column_double(stmt, 5); node.access_count = sqlite3_column_int64(stmt, 6); const void* blob = sqlite3_column_blob(stmt, 7); int blob_size = sqlite3_column_bytes(stmt, 7); if (blob && blob_size > 0) { node.embedding.resize(blob_size / sizeof(float)); std::memcpy(node.embedding.data(), blob, blob_size); } node.created_at = reinterpret_cast(sqlite3_column_text(stmt, 8)); node.last_accessed = reinterpret_cast(sqlite3_column_text(stmt, 9)); // Update access count update_access(node.id); } sqlite3_finalize(stmt); return node; } std::vector get_edges_from(int64_t node_id) { std::lock_guard lock(db_.get_mutex()); std::vector edges; const char* sql = R"SQL( SELECT id, source_id, target_id, relation, strength, evidence_count FROM knowledge_edges WHERE source_id = ? )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return edges; } sqlite3_bind_int64(stmt, 1, node_id); while (sqlite3_step(stmt) == SQLITE_ROW) { KnowledgeEdge e; e.id = sqlite3_column_int64(stmt, 0); e.source_id = sqlite3_column_int64(stmt, 1); e.target_id = sqlite3_column_int64(stmt, 2); e.relation = reinterpret_cast(sqlite3_column_text(stmt, 3)); e.strength = sqlite3_column_double(stmt, 4); e.evidence_count = sqlite3_column_int64(stmt, 5); edges.push_back(e); } sqlite3_finalize(stmt); return edges; } int64_t get_node_count() { std::lock_guard lock(db_.get_mutex()); const char* sql = "SELECT COUNT(*) FROM knowledge_nodes"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return 0; } int64_t count = 0; if (sqlite3_step(stmt) == SQLITE_ROW) { count = sqlite3_column_int64(stmt, 0); } sqlite3_finalize(stmt); return count; } private: void update_access(int64_t node_id) { const char* sql = R"SQL( UPDATE knowledge_nodes SET access_count = access_count + 1, last_accessed = datetime('now') WHERE id = ? )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_int64(stmt, 1, node_id); sqlite3_step(stmt); sqlite3_finalize(stmt); } } }; //============================================================================= // METRICS REPOSITORY //============================================================================= class MetricsRepository { private: Database& db_; public: explicit MetricsRepository(Database& db) : db_(db) {} bool insert(const SystemMetrics& metrics) { std::lock_guard lock(db_.get_mutex()); const char* sql = R"SQL( INSERT INTO system_metrics (timestamp, ticks_per_second, memory_used_mb, cpu_percent, active_generators, active_agents, knowledge_nodes_count, total_ticks, global_phi) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return false; } sqlite3_bind_int64(stmt, 1, metrics.timestamp); sqlite3_bind_double(stmt, 2, metrics.ticks_per_second); sqlite3_bind_double(stmt, 3, metrics.memory_used_mb); sqlite3_bind_double(stmt, 4, metrics.cpu_percent); sqlite3_bind_int64(stmt, 5, metrics.active_generators); sqlite3_bind_int64(stmt, 6, metrics.active_agents); sqlite3_bind_int64(stmt, 7, metrics.knowledge_nodes_count); sqlite3_bind_int64(stmt, 8, metrics.total_ticks); sqlite3_bind_double(stmt, 9, metrics.global_phi); int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } SystemMetrics get_latest() { std::lock_guard lock(db_.get_mutex()); SystemMetrics m{}; const char* sql = R"SQL( SELECT timestamp, ticks_per_second, memory_used_mb, cpu_percent, active_generators, active_agents, knowledge_nodes_count, total_ticks, global_phi FROM system_metrics ORDER BY timestamp DESC LIMIT 1 )SQL"; sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db_.handle(), sql, -1, &stmt, nullptr) != SQLITE_OK) { return m; } if (sqlite3_step(stmt) == SQLITE_ROW) { m.timestamp = sqlite3_column_int64(stmt, 0); m.ticks_per_second = sqlite3_column_double(stmt, 1); m.memory_used_mb = sqlite3_column_double(stmt, 2); m.cpu_percent = sqlite3_column_double(stmt, 3); m.active_generators = sqlite3_column_int64(stmt, 4); m.active_agents = sqlite3_column_int64(stmt, 5); m.knowledge_nodes_count = sqlite3_column_int64(stmt, 6); m.total_ticks = sqlite3_column_int64(stmt, 7); m.global_phi = sqlite3_column_double(stmt, 8); } sqlite3_finalize(stmt); return m; } }; //============================================================================= // UNIFIED PERSISTENCE MANAGER //============================================================================= class PersistenceManager { private: Database db_; std::unique_ptr consciousness_repo_; std::unique_ptr knowledge_repo_; std::unique_ptr metrics_repo_; public: PersistenceManager() = default; bool initialize(const std::string& db_path) { if (!db_.open(db_path)) { return false; } if (!db_.initialize_schema()) { return false; } consciousness_repo_ = std::make_unique(db_); knowledge_repo_ = std::make_unique(db_); metrics_repo_ = std::make_unique(db_); return true; } ConsciousnessRepository& consciousness() { return *consciousness_repo_; } KnowledgeRepository& knowledge() { return *knowledge_repo_; } MetricsRepository& metrics() { return *metrics_repo_; } Database& database() { return db_; } void close() { db_.close(); } }; // Global persistence instance inline PersistenceManager& get_persistence() { static PersistenceManager instance; return instance; } } // namespace mind_farm #endif // PERSISTENCE_HPP