mnemosyne-core 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3736,9 +3736,22 @@ function loadConfig() {
3736
3736
  return defaultConfig();
3737
3737
  }
3738
3738
  }
3739
+ function getVersion() {
3740
+ try {
3741
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
3742
+ return pkg.version;
3743
+ } catch {
3744
+ try {
3745
+ const pkg = JSON.parse(readFileSync(resolve(process.cwd(), "package.json"), "utf-8"));
3746
+ return pkg.version;
3747
+ } catch {
3748
+ return "2.0.1";
3749
+ }
3750
+ }
3751
+ }
3739
3752
  function defaultConfig() {
3740
3753
  return {
3741
- server: { port: 7321, host: "localhost", version: "2.0.0" },
3754
+ server: { port: 7321, host: "localhost", version: getVersion() },
3742
3755
  database: { path: "data/nexus.db", wal_mode: true, vec_extension_path: "data/vec0" },
3743
3756
  storage: { files_dir: "data/files", max_file_size_mb: 50, backups_dir: "data/backups", backup_interval_hours: 24, max_backups: 7 },
3744
3757
  limits: { max_atoms_per_project: 1e4, rate_limit_requests: 100, rate_limit_window_ms: 6e4 },
@@ -4537,7 +4550,7 @@ var require_filesystem = __commonJS({
4537
4550
  var LDD_PATH = "/usr/bin/ldd";
4538
4551
  var SELF_PATH = "/proc/self/exe";
4539
4552
  var MAX_LENGTH = 2048;
4540
- var readFileSync6 = (path) => {
4553
+ var readFileSync5 = (path) => {
4541
4554
  const fd = fs.openSync(path, "r");
4542
4555
  const buffer = Buffer.alloc(MAX_LENGTH);
4543
4556
  const bytesRead = fs.readSync(fd, buffer, 0, MAX_LENGTH, 0);
@@ -4562,7 +4575,7 @@ var require_filesystem = __commonJS({
4562
4575
  module.exports = {
4563
4576
  LDD_PATH,
4564
4577
  SELF_PATH,
4565
- readFileSync: readFileSync6,
4578
+ readFileSync: readFileSync5,
4566
4579
  readFile
4567
4580
  };
4568
4581
  }
@@ -4611,7 +4624,7 @@ var require_detect_libc = __commonJS({
4611
4624
  "use strict";
4612
4625
  var childProcess = __require("child_process");
4613
4626
  var { isLinux, getReport } = require_process();
4614
- var { LDD_PATH, SELF_PATH, readFile, readFileSync: readFileSync6 } = require_filesystem();
4627
+ var { LDD_PATH, SELF_PATH, readFile, readFileSync: readFileSync5 } = require_filesystem();
4615
4628
  var { interpreterPath } = require_elf();
4616
4629
  var cachedFamilyInterpreter;
4617
4630
  var cachedFamilyFilesystem;
@@ -4703,7 +4716,7 @@ var require_detect_libc = __commonJS({
4703
4716
  }
4704
4717
  cachedFamilyFilesystem = null;
4705
4718
  try {
4706
- const lddContent = readFileSync6(LDD_PATH);
4719
+ const lddContent = readFileSync5(LDD_PATH);
4707
4720
  cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
4708
4721
  } catch (e) {
4709
4722
  }
@@ -4728,7 +4741,7 @@ var require_detect_libc = __commonJS({
4728
4741
  }
4729
4742
  cachedFamilyInterpreter = null;
4730
4743
  try {
4731
- const selfContent = readFileSync6(SELF_PATH);
4744
+ const selfContent = readFileSync5(SELF_PATH);
4732
4745
  const path = interpreterPath(selfContent);
4733
4746
  cachedFamilyInterpreter = familyFromInterpreterPath(path);
4734
4747
  } catch (e) {
@@ -4792,7 +4805,7 @@ var require_detect_libc = __commonJS({
4792
4805
  }
4793
4806
  cachedVersionFilesystem = null;
4794
4807
  try {
4795
- const lddContent = readFileSync6(LDD_PATH);
4808
+ const lddContent = readFileSync5(LDD_PATH);
4796
4809
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
4797
4810
  if (versionMatch) {
4798
4811
  cachedVersionFilesystem = versionMatch[1];
@@ -9602,7 +9615,7 @@ var init_file_processor = __esm({
9602
9615
 
9603
9616
  // src/server/MnemosyneServer.ts
9604
9617
  import { createServer } from "http";
9605
- import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
9618
+ import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
9606
9619
  import { resolve as resolve6 } from "path";
9607
9620
 
9608
9621
  // node_modules/ws/wrapper.mjs
@@ -10907,12 +10920,20 @@ function handleEvents(store, pathname, method, res, searchParams) {
10907
10920
  }
10908
10921
 
10909
10922
  // src/api/routes/health.ts
10923
+ var PKG_VERSION = (() => {
10924
+ try {
10925
+ const pkg = JSON.parse(__require("fs").readFileSync(__require("path").resolve(__dirname, "../../../package.json"), "utf-8"));
10926
+ return pkg.version;
10927
+ } catch {
10928
+ return "2.0.1";
10929
+ }
10930
+ })();
10910
10931
  function handleHealth(store, pathname, method, res) {
10911
- if (pathname === "/health" && method === "GET") {
10932
+ if ((pathname === "/health" || pathname === "/api/v1/health") && method === "GET") {
10912
10933
  const stats = store.getStats();
10913
10934
  json(res, 200, {
10914
10935
  status: "ok",
10915
- version: "2.0.0",
10936
+ version: PKG_VERSION,
10916
10937
  storage: "sqlite",
10917
10938
  database: store.config.database.path,
10918
10939
  uptime: process.uptime(),
@@ -12298,8 +12319,218 @@ ${JSON.stringify(analysis.metadata, null, 2)}
12298
12319
  // src/db/connection.ts
12299
12320
  init_config();
12300
12321
  import Database from "better-sqlite3";
12301
- import { readFileSync as readFileSync4 } from "fs";
12302
12322
  import { resolve as resolve5 } from "path";
12323
+
12324
+ // src/db/schema.ts
12325
+ var SCHEMA_SQL = `-- Mnemosyne v1.1 Database Schema
12326
+ -- SQLite with sqlite-vec extension for semantic search
12327
+
12328
+ -- PROJECTS (Spaces)
12329
+ CREATE TABLE IF NOT EXISTS projects (
12330
+ id TEXT PRIMARY KEY,
12331
+ name TEXT NOT NULL UNIQUE,
12332
+ description TEXT,
12333
+ icon TEXT DEFAULT '\u25C9',
12334
+ color TEXT DEFAULT '#6366f1',
12335
+ created_at INTEGER DEFAULT (unixepoch()),
12336
+ updated_at INTEGER DEFAULT (unixepoch()),
12337
+ owner TEXT DEFAULT 'human',
12338
+ metadata TEXT DEFAULT '{}'
12339
+ );
12340
+
12341
+ -- ATOMS (Ideas/Nodes)
12342
+ CREATE TABLE IF NOT EXISTS atoms (
12343
+ id TEXT PRIMARY KEY,
12344
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
12345
+ parent_id TEXT REFERENCES atoms(id) ON DELETE CASCADE,
12346
+ title TEXT NOT NULL,
12347
+ summary TEXT,
12348
+ icon TEXT DEFAULT '\u25CF',
12349
+ color TEXT,
12350
+ x REAL,
12351
+ y REAL,
12352
+ auto_path TEXT,
12353
+ path_overridden INTEGER DEFAULT 0,
12354
+ status TEXT DEFAULT 'draft',
12355
+ status_updated_at INTEGER DEFAULT (unixepoch()),
12356
+ embedding_status TEXT DEFAULT 'pending',
12357
+ embedding_error TEXT,
12358
+ block_count INTEGER DEFAULT 0,
12359
+ bond_count INTEGER DEFAULT 0,
12360
+ template_id TEXT,
12361
+ created_at INTEGER DEFAULT (unixepoch()),
12362
+ updated_at INTEGER DEFAULT (unixepoch()),
12363
+ version INTEGER DEFAULT 1,
12364
+ locked_by TEXT,
12365
+ locked_at INTEGER,
12366
+ locked_reason TEXT,
12367
+ owner TEXT DEFAULT 'human',
12368
+ metadata TEXT DEFAULT '{}'
12369
+ );
12370
+
12371
+ -- BLOCKS (Content pieces inside atoms)
12372
+ CREATE TABLE IF NOT EXISTS blocks (
12373
+ id TEXT PRIMARY KEY,
12374
+ atom_id TEXT NOT NULL REFERENCES atoms(id) ON DELETE CASCADE,
12375
+ type TEXT NOT NULL DEFAULT 'text',
12376
+ content TEXT,
12377
+ order_index INTEGER DEFAULT 0,
12378
+ created_at INTEGER DEFAULT (unixepoch()),
12379
+ updated_at INTEGER DEFAULT (unixepoch()),
12380
+ metadata TEXT DEFAULT '{}'
12381
+ );
12382
+
12383
+ -- BONDS (Graph connections between atoms)
12384
+ CREATE TABLE IF NOT EXISTS bonds (
12385
+ id TEXT PRIMARY KEY,
12386
+ source_id TEXT NOT NULL REFERENCES atoms(id) ON DELETE CASCADE,
12387
+ target_id TEXT NOT NULL REFERENCES atoms(id) ON DELETE CASCADE,
12388
+ label TEXT DEFAULT 'connects',
12389
+ color TEXT,
12390
+ created_at INTEGER DEFAULT (unixepoch()),
12391
+ UNIQUE(source_id, target_id)
12392
+ );
12393
+
12394
+ -- ATTACHMENTS (Files in CAS)
12395
+ CREATE TABLE IF NOT EXISTS attachments (
12396
+ id TEXT PRIMARY KEY,
12397
+ block_id TEXT REFERENCES blocks(id) ON DELETE CASCADE,
12398
+ file_name TEXT NOT NULL,
12399
+ file_hash TEXT NOT NULL,
12400
+ file_size INTEGER,
12401
+ mime_type TEXT,
12402
+ created_at INTEGER DEFAULT (unixepoch()),
12403
+ UNIQUE(file_hash)
12404
+ );
12405
+
12406
+ -- EVENT LOG (Immutable audit trail)
12407
+ CREATE TABLE IF NOT EXISTS event_log (
12408
+ id TEXT PRIMARY KEY,
12409
+ timestamp INTEGER DEFAULT (unixepoch()),
12410
+ event_type TEXT NOT NULL,
12411
+ project_id TEXT,
12412
+ atom_id TEXT,
12413
+ block_id TEXT,
12414
+ bond_id TEXT,
12415
+ actor TEXT NOT NULL DEFAULT 'system',
12416
+ actor_type TEXT DEFAULT 'system',
12417
+ diff TEXT,
12418
+ trigger TEXT,
12419
+ metadata TEXT DEFAULT '{}'
12420
+ );
12421
+
12422
+ -- ASSISTANTS (AI Agents)
12423
+ CREATE TABLE IF NOT EXISTS assistants (
12424
+ id TEXT PRIMARY KEY,
12425
+ name TEXT NOT NULL,
12426
+ role TEXT DEFAULT 'worker',
12427
+ permissions TEXT DEFAULT '{"read":["*"],"write":[],"delete":false}',
12428
+ status TEXT DEFAULT 'active',
12429
+ provider TEXT,
12430
+ connected_at INTEGER DEFAULT (unixepoch()),
12431
+ last_seen INTEGER DEFAULT (unixepoch()),
12432
+ metadata TEXT DEFAULT '{}'
12433
+ );
12434
+
12435
+ -- QUEUE (Agent waitlist for locked atoms)
12436
+ CREATE TABLE IF NOT EXISTS queue (
12437
+ id TEXT PRIMARY KEY,
12438
+ atom_id TEXT NOT NULL REFERENCES atoms(id) ON DELETE CASCADE,
12439
+ assistant_id TEXT NOT NULL REFERENCES assistants(id) ON DELETE CASCADE,
12440
+ requested_at INTEGER DEFAULT (unixepoch()),
12441
+ priority INTEGER DEFAULT 5,
12442
+ status TEXT DEFAULT 'waiting',
12443
+ reason TEXT
12444
+ );
12445
+
12446
+ -- INDICES
12447
+ CREATE INDEX IF NOT EXISTS idx_atoms_project ON atoms(project_id);
12448
+ CREATE INDEX IF NOT EXISTS idx_atoms_parent ON atoms(parent_id);
12449
+ CREATE INDEX IF NOT EXISTS idx_atoms_locked ON atoms(locked_by) WHERE locked_by IS NOT NULL;
12450
+ -- idx_atoms_path_project is created by migration after auto_path column is added
12451
+ CREATE INDEX IF NOT EXISTS idx_blocks_atom ON blocks(atom_id);
12452
+ CREATE INDEX IF NOT EXISTS idx_bonds_source ON bonds(source_id);
12453
+ CREATE INDEX IF NOT EXISTS idx_bonds_target ON bonds(target_id);
12454
+ CREATE INDEX IF NOT EXISTS idx_event_log_time ON event_log(timestamp);
12455
+ CREATE INDEX IF NOT EXISTS idx_event_log_atom ON event_log(atom_id);
12456
+ CREATE INDEX IF NOT EXISTS idx_queue_atom ON queue(atom_id);
12457
+ CREATE INDEX IF NOT EXISTS idx_attachments_hash ON attachments(file_hash);
12458
+
12459
+ -- Auto-update block_count and bond_count triggers
12460
+ CREATE TRIGGER IF NOT EXISTS update_block_count_insert AFTER INSERT ON blocks BEGIN
12461
+ UPDATE atoms SET block_count = block_count + 1 WHERE id = NEW.atom_id;
12462
+ END;
12463
+
12464
+ CREATE TRIGGER IF NOT EXISTS update_block_count_delete AFTER DELETE ON blocks BEGIN
12465
+ UPDATE atoms SET block_count = block_count - 1 WHERE id = OLD.atom_id;
12466
+ END;
12467
+
12468
+ CREATE TRIGGER IF NOT EXISTS update_bond_count_insert AFTER INSERT ON bonds BEGIN
12469
+ UPDATE atoms SET bond_count = bond_count + 1 WHERE id = NEW.source_id OR id = NEW.target_id;
12470
+ END;
12471
+
12472
+ CREATE TRIGGER IF NOT EXISTS update_bond_count_delete AFTER DELETE ON bonds BEGIN
12473
+ UPDATE atoms SET bond_count = bond_count - 1 WHERE id = OLD.source_id OR id = OLD.target_id;
12474
+ END;
12475
+
12476
+ -- FTS5 FULL-TEXT SEARCH
12477
+ -- Separate tables to avoid rowid collisions between atoms and blocks
12478
+
12479
+ CREATE VIRTUAL TABLE IF NOT EXISTS search_atoms USING fts5(
12480
+ title, summary,
12481
+ content='atoms', content_rowid='rowid'
12482
+ );
12483
+
12484
+ CREATE VIRTUAL TABLE IF NOT EXISTS search_blocks USING fts5(
12485
+ content,
12486
+ content='blocks', content_rowid='rowid'
12487
+ );
12488
+
12489
+ -- Triggers to keep search_atoms in sync
12490
+ CREATE TRIGGER IF NOT EXISTS trig_search_atoms_insert AFTER INSERT ON atoms BEGIN
12491
+ INSERT INTO search_atoms(rowid, title, summary) VALUES (new.rowid, new.title, COALESCE(new.summary, ''));
12492
+ END;
12493
+
12494
+ CREATE TRIGGER IF NOT EXISTS trig_search_atoms_update AFTER UPDATE ON atoms BEGIN
12495
+ UPDATE search_atoms SET title = new.title, summary = COALESCE(new.summary, '') WHERE rowid = new.rowid;
12496
+ END;
12497
+
12498
+ CREATE TRIGGER IF NOT EXISTS trig_search_atoms_delete AFTER DELETE ON atoms BEGIN
12499
+ DELETE FROM search_atoms WHERE rowid = old.rowid;
12500
+ END;
12501
+
12502
+ -- Triggers to keep search_blocks in sync
12503
+ CREATE TRIGGER IF NOT EXISTS trig_search_blocks_insert AFTER INSERT ON blocks BEGIN
12504
+ INSERT INTO search_blocks(rowid, content) VALUES (new.rowid, COALESCE(new.content, ''));
12505
+ END;
12506
+
12507
+ CREATE TRIGGER IF NOT EXISTS trig_search_blocks_update AFTER UPDATE ON blocks BEGIN
12508
+ UPDATE search_blocks SET content = COALESCE(new.content, '') WHERE rowid = new.rowid;
12509
+ END;
12510
+
12511
+ CREATE TRIGGER IF NOT EXISTS trig_search_blocks_delete AFTER DELETE ON blocks BEGIN
12512
+ DELETE FROM search_blocks WHERE rowid = old.rowid;
12513
+ END;
12514
+
12515
+
12516
+ -- PER-ATOM PERMISSIONS (Granular access control)
12517
+ CREATE TABLE IF NOT EXISTS atom_permissions (
12518
+ id TEXT PRIMARY KEY,
12519
+ atom_id TEXT NOT NULL REFERENCES atoms(id) ON DELETE CASCADE,
12520
+ assistant_id TEXT NOT NULL REFERENCES assistants(id) ON DELETE CASCADE,
12521
+ level TEXT NOT NULL DEFAULT 'view',
12522
+ granted_by TEXT,
12523
+ granted_at INTEGER DEFAULT (unixepoch()),
12524
+ metadata TEXT DEFAULT '{}',
12525
+ UNIQUE(atom_id, assistant_id)
12526
+ );
12527
+
12528
+ -- sqlite-vec virtual table for semantic embeddings
12529
+ -- Created dynamically by connection.ts after loading the extension
12530
+ -- CREATE VIRTUAL TABLE IF NOT EXISTS atom_vectors USING vec0(atom_id TEXT PRIMARY KEY, embedding float[384]);
12531
+ `;
12532
+
12533
+ // src/db/connection.ts
12303
12534
  var DB_PATH2 = process.env.MNEMOSYNE_DB_PATH || resolve5(process.cwd(), CONFIG.database.path);
12304
12535
  var db = null;
12305
12536
  var vecEnabled = false;
@@ -12318,8 +12549,7 @@ function getDb() {
12318
12549
  console.warn("[DB] sqlite-vec not available:", err.message);
12319
12550
  vecEnabled = false;
12320
12551
  }
12321
- const schema = readFileSync4(resolve5(process.cwd(), "src", "db", "schema.sql"), "utf-8");
12322
- db.exec(schema);
12552
+ db.exec(SCHEMA_SQL);
12323
12553
  if (vecEnabled) {
12324
12554
  try {
12325
12555
  db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS atom_vectors USING vec0(atom_id TEXT PRIMARY KEY, embedding float[${CONFIG.embeddings.dimension}])`);
@@ -12351,7 +12581,6 @@ var Store2 = class extends Store {
12351
12581
  };
12352
12582
 
12353
12583
  // src/server/MnemosyneServer.ts
12354
- init_config();
12355
12584
  var MnemosyneServer = class {
12356
12585
  store;
12357
12586
  api;
@@ -12359,8 +12588,9 @@ var MnemosyneServer = class {
12359
12588
  httpServer;
12360
12589
  brain;
12361
12590
  constructor(options = {}, brain) {
12362
- const port = options.port ?? CONFIG.server.port;
12363
- const host = options.host ?? CONFIG.server.host;
12591
+ const cfg = brain?.config?.config;
12592
+ const port = options.port ?? cfg?.server?.port ?? 7321;
12593
+ const host = options.host ?? cfg?.server?.host ?? "localhost";
12364
12594
  const enableDashboard = options.dashboard !== false;
12365
12595
  const enableWebsocket = options.websocket !== false;
12366
12596
  process.on("uncaughtException", (err) => {
@@ -12388,8 +12618,9 @@ var MnemosyneServer = class {
12388
12618
  const wss = new import_websocket_server.default({ server: this.httpServer });
12389
12619
  this.wsHandler = new WebSocketHandler(wss, this.store);
12390
12620
  }
12621
+ const version = cfg?.server?.version || "2.0.1";
12391
12622
  this.httpServer.listen(port, () => {
12392
- console.log(`Mnemosyne v${CONFIG.server.version} \u2014 port ${port}`);
12623
+ console.log(`Mnemosyne v${version} \u2014 port ${port}`);
12393
12624
  console.log(`Dashboard: http://${host}:${port}/dashboard`);
12394
12625
  console.log(`API: http://${host}:${port}/api/v1`);
12395
12626
  console.log(`MCP: http://${host}:${port}/mcp/manifest`);
@@ -12414,12 +12645,13 @@ var MnemosyneServer = class {
12414
12645
  };
12415
12646
  const dirs = [
12416
12647
  resolve6(process.cwd(), "src/dashboard"),
12417
- resolve6(process.cwd(), "dashboard")
12648
+ resolve6(process.cwd(), "dashboard"),
12649
+ resolve6(__dirname, "../dashboard")
12418
12650
  ];
12419
12651
  for (const dir of dirs) {
12420
12652
  const filePath = resolve6(dir, urlPath);
12421
12653
  if (existsSync5(filePath)) {
12422
- const data = readFileSync5(filePath);
12654
+ const data = readFileSync4(filePath);
12423
12655
  res.writeHead(200, { "Content-Type": mime[ext] || "application/octet-stream" });
12424
12656
  res.end(data);
12425
12657
  return;