lemma-mcp 0.19.0 → 0.21.0

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.
Files changed (111) hide show
  1. package/README.md +50 -1
  2. package/dist/db/backup.d.ts +63 -0
  3. package/dist/db/backup.d.ts.map +1 -0
  4. package/dist/db/backup.js +342 -0
  5. package/dist/db/backup.js.map +1 -0
  6. package/dist/db/clients.d.ts +26 -0
  7. package/dist/db/clients.d.ts.map +1 -0
  8. package/dist/db/clients.js +97 -0
  9. package/dist/db/clients.js.map +1 -0
  10. package/dist/db/database.d.ts +19 -0
  11. package/dist/db/database.d.ts.map +1 -1
  12. package/dist/db/database.js +44 -6
  13. package/dist/db/database.js.map +1 -1
  14. package/dist/db/legacy-vectors.d.ts +12 -0
  15. package/dist/db/legacy-vectors.d.ts.map +1 -0
  16. package/dist/db/legacy-vectors.js +45 -0
  17. package/dist/db/legacy-vectors.js.map +1 -0
  18. package/dist/db/library-store.d.ts.map +1 -1
  19. package/dist/db/library-store.js +4 -1
  20. package/dist/db/library-store.js.map +1 -1
  21. package/dist/db/memory-store.d.ts +34 -0
  22. package/dist/db/memory-store.d.ts.map +1 -1
  23. package/dist/db/memory-store.js +116 -8
  24. package/dist/db/memory-store.js.map +1 -1
  25. package/dist/db/schema.d.ts +5 -0
  26. package/dist/db/schema.d.ts.map +1 -1
  27. package/dist/db/schema.js +107 -1
  28. package/dist/db/schema.js.map +1 -1
  29. package/dist/guides/core.d.ts +9 -0
  30. package/dist/guides/core.d.ts.map +1 -1
  31. package/dist/guides/core.js +30 -0
  32. package/dist/guides/core.js.map +1 -1
  33. package/dist/guides/index.d.ts +1 -1
  34. package/dist/guides/index.d.ts.map +1 -1
  35. package/dist/guides/index.js +1 -1
  36. package/dist/guides/index.js.map +1 -1
  37. package/dist/intelligence/conflict.d.ts.map +1 -1
  38. package/dist/intelligence/conflict.js +26 -18
  39. package/dist/intelligence/conflict.js.map +1 -1
  40. package/dist/intelligence/consistency.d.ts +25 -0
  41. package/dist/intelligence/consistency.d.ts.map +1 -0
  42. package/dist/intelligence/consistency.js +112 -0
  43. package/dist/intelligence/consistency.js.map +1 -0
  44. package/dist/intelligence/hybrid.d.ts +48 -0
  45. package/dist/intelligence/hybrid.d.ts.map +1 -0
  46. package/dist/intelligence/hybrid.js +111 -0
  47. package/dist/intelligence/hybrid.js.map +1 -0
  48. package/dist/intelligence/index.d.ts +5 -0
  49. package/dist/intelligence/index.d.ts.map +1 -1
  50. package/dist/intelligence/index.js +3 -0
  51. package/dist/intelligence/index.js.map +1 -1
  52. package/dist/intelligence/semantic.d.ts +13 -0
  53. package/dist/intelligence/semantic.d.ts.map +1 -1
  54. package/dist/intelligence/semantic.js +29 -10
  55. package/dist/intelligence/semantic.js.map +1 -1
  56. package/dist/intelligence/vector-cache.d.ts +10 -0
  57. package/dist/intelligence/vector-cache.d.ts.map +1 -0
  58. package/dist/intelligence/vector-cache.js +0 -0
  59. package/dist/intelligence/vector-cache.js.map +1 -0
  60. package/dist/logger.d.ts.map +1 -1
  61. package/dist/logger.js +15 -3
  62. package/dist/logger.js.map +1 -1
  63. package/dist/memory/config.d.ts.map +1 -1
  64. package/dist/memory/config.js +11 -0
  65. package/dist/memory/config.js.map +1 -1
  66. package/dist/memory/core.d.ts +31 -1
  67. package/dist/memory/core.d.ts.map +1 -1
  68. package/dist/memory/core.js +115 -3
  69. package/dist/memory/core.js.map +1 -1
  70. package/dist/memory/evidence.d.ts +41 -0
  71. package/dist/memory/evidence.d.ts.map +1 -0
  72. package/dist/memory/evidence.js +57 -0
  73. package/dist/memory/evidence.js.map +1 -0
  74. package/dist/memory/index.d.ts +1 -1
  75. package/dist/memory/index.d.ts.map +1 -1
  76. package/dist/memory/index.js +1 -1
  77. package/dist/memory/index.js.map +1 -1
  78. package/dist/memory/privacy.d.ts.map +1 -1
  79. package/dist/memory/privacy.js +8 -1
  80. package/dist/memory/privacy.js.map +1 -1
  81. package/dist/memory/recall-explanation.d.ts +84 -0
  82. package/dist/memory/recall-explanation.d.ts.map +1 -0
  83. package/dist/memory/recall-explanation.js +75 -0
  84. package/dist/memory/recall-explanation.js.map +1 -0
  85. package/dist/server/backup-handlers.d.ts +58 -0
  86. package/dist/server/backup-handlers.d.ts.map +1 -0
  87. package/dist/server/backup-handlers.js +21 -0
  88. package/dist/server/backup-handlers.js.map +1 -0
  89. package/dist/server/handlers.d.ts +12 -0
  90. package/dist/server/handlers.d.ts.map +1 -1
  91. package/dist/server/handlers.js +189 -19
  92. package/dist/server/handlers.js.map +1 -1
  93. package/dist/server/index.d.ts.map +1 -1
  94. package/dist/server/index.js +28 -6
  95. package/dist/server/index.js.map +1 -1
  96. package/dist/server/tool-queue.d.ts +2 -0
  97. package/dist/server/tool-queue.d.ts.map +1 -0
  98. package/dist/server/tool-queue.js +9 -0
  99. package/dist/server/tool-queue.js.map +1 -0
  100. package/dist/server/tools.d.ts +4 -3
  101. package/dist/server/tools.d.ts.map +1 -1
  102. package/dist/server/tools.js +129 -2
  103. package/dist/server/tools.js.map +1 -1
  104. package/dist/sessions/virtual.d.ts +2 -0
  105. package/dist/sessions/virtual.d.ts.map +1 -1
  106. package/dist/sessions/virtual.js +13 -0
  107. package/dist/sessions/virtual.js.map +1 -1
  108. package/dist/types.d.ts +9 -0
  109. package/dist/types.d.ts.map +1 -1
  110. package/docs/README.tr.md +46 -1
  111. package/package.json +4 -1
package/README.md CHANGED
@@ -77,7 +77,7 @@ Lemma runs intelligence in the background — no manual triggering needed:
77
77
 
78
78
  Manual deep analysis is also available via dedicated tools.
79
79
 
80
- ## Tools (26)
80
+ ## Tools (29)
81
81
 
82
82
  Lemma exposes short MCP tool names such as `memory_read`, `memory_add`, and `session_start`. Most clients display tools with the server namespace prepended, so you may see names like `mcp_lemma_memory_add`; that is expected. Redundant doubled names like `mcp_lemma_lemma_memory_add` are not used.
83
83
 
@@ -127,6 +127,55 @@ Lemma exposes short MCP tool names such as `memory_read`, `memory_add`, and `ses
127
127
  | `project_analytics` | Cross-session project health, growth rate, skill coverage |
128
128
  | `semantic_search` | TF-IDF similarity search across memories |
129
129
 
130
+ ### Backup and restore (3)
131
+
132
+ | Tool | Purpose |
133
+ |------|---------|
134
+ | `backup_create` | Create and verify one portable backup of all database records |
135
+ | `backup_preview` | Validate a backup, compare record counts, and prepare a confirmation token |
136
+ | `backup_restore` | Replace memory after explicit confirmation, with a mandatory safety backup |
137
+
138
+ ## Backup and restore through MCP
139
+
140
+ No terminal command is needed. Ask your assistant **"Back up my Lemma memory to this folder"** or **"Restore my Lemma memory from this backup file"**. The assistant uses the three tools above.
141
+
142
+ 1. **Back up:** `backup_create` accepts an optional absolute `directory` (also supports `~/...`). It returns the path of a verified `.lemma-backup` file. By default files go to `~/.lemma/backups/`, with unique timestamped names; existing backups are never overwritten.
143
+ 2. **Move:** copy that file to your external drive or chosen storage. On a new computer, install a compatible Lemma MCP release and make the file available locally. The format is independent of Windows, macOS and Linux; it contains no destination directory paths.
144
+ 3. **Preview:** the assistant calls `backup_preview` with the file's absolute `path`. It shows backup/current record counts and explains that restore **replaces all projects and global memory**, rather than merging them. It also reports connection readiness: `ready` means no other cooperating connections were detected; `blocked` explains the connection blocker. A blocked preview returns no confirmation token, invalidates earlier tokens, and does not ask for approval. Close the other Lemma connections or visualizers as indicated, keep the assistant's current MCP connection open, and preview again. Once ready, explicitly approve that preview. Open conversation tabs alone do not determine readiness; registered database connections do. This check cannot detect older Lemma versions or external SQLite tools.
145
+ 4. **Restore:** the assistant passes the preview's `confirmation_token` and `confirm: true` to `backup_restore`. Lemma first creates and verifies a `before-restore-*.lemma-backup` of the current database, then restores and checks the records in one SQLite transaction. On failure before commit the old database remains intact. The returned `safety_backup_path` can be used in the same preview/restore flow to undo a restore.
146
+
147
+ The current MCP connection stays usable. Its old in-memory session is discarded so it cannot overwrite restored history. Sessions that were active when the backup was taken become abandoned history. Existing conversation text is not erased; start a new conversation when you need a fresh context.
148
+
149
+ The preview identifies the serving connection in `readiness.current_connection` and lists every detected live peer in `readiness.blocking_connections`, with process IDs and distinct connection IDs. `same_process` flags another connection inside the current process: do not terminate that process. Conversation names and IDs cannot be inferred from these registrations (`conversation_mapping: "unavailable"`). Unverifiable registrations and inspection failures remain blockers and are reported separately. These are diagnostic details, not instructions to terminate processes; close other connections through their application and preview again.
150
+
151
+ **Included:** all persisted database records, including global/project memories, invalidated fragments, archives, guides and learnings, relationships, evidence, feedback, fragment versions, session history, attempts, suggestions and search cache. Search indexes are rebuilt during restore. Paths quoted *inside* memory/evidence remain unchanged; project folder names and evidence paths may need attention after moving computers.
152
+
153
+ **Excluded:** `config.json`, raw session/traffic logs, diagnostic logs, installed skills/models, and the MCP client's own configuration. These are machine-specific settings or auxiliary files and are left unchanged on restore. This is a database recovery feature, not a full installation backup or cloud synchronization service. `lemma -lib` is a readable report, not a restorable backup.
154
+
155
+ **Limits and safeguards:**
156
+
157
+ Upgraded installations may retain the unused `memory_vectors` / `vec0` scaffold removed in Lemma 0.15.0. Backups accept only its known table definitions, version metadata and empty payload tables with no prior write counters. The original snapshot retains these legacy structures. Restore preserves them if already present on the target, but does not recreate the retired index on a fresh installation. Memories, guides and current search data are restored normally. Nonempty vectors, altered scaffolding and other unknown schema changes are rejected; the live database is never cleaned up automatically.
158
+
159
+ - Backups are unencrypted and contain private knowledge. Keep them in trusted storage. A backup on the disk being formatted will also be lost; keep a copy elsewhere.
160
+ - This initial format supports databases up to 128 MiB. Backups in this format with known database schemas 1–8 can be restored to the current schema (8). Older schemas are matched against their exact known definitions and complete migration history, then upgraded and verified on an in-memory copy using only bundled migrations. The original file is unchanged. Preview reports `schema_upgrade` with the version range and notes; show these before requesting confirmation. Upgrades from schema 1 or 2 normalize project paths/names and global scope, just as the corresponding application migration does. Unknown/newer formats or schemas, changed definitions, failed checksums, broken relationships and invalid SQLite/search indexes are rejected before replacement. This does not import arbitrary old SQLite or JSONL files and does not guarantee compatibility with future releases.
161
+ - Confirmation tokens expire after 10 minutes, are single-use, and are bound to both the exact file and current database state. If memory changes while you are deciding, or the MCP connection restarts, preview and confirm again.
162
+ - SQLite snapshots include committed WAL data while Lemma is running. Restore holds the SQLite writer lock; other cooperating Lemma connections block restore and newly opening clients wait for that lock. Older Lemma versions and external SQLite programs do not register as clients, so close them before confirming. SQLite still performs the replacement atomically; it never swaps a database file underneath an open connection.
163
+ - A checksum detects damaged data; it is not a signature proving who created the backup. Import files only from a trusted source.
164
+
165
+ ## Why was a memory recalled?
166
+
167
+ Ask your assistant to explain a recall, or pass `explain: true` to `memory_read` or `semantic_search`. It is off by default and adds no new tool. For example:
168
+
169
+ ```json
170
+ {"query":"retry policy","project":"my-project","explain":true,"response_format":"json"}
171
+ ```
172
+
173
+ The optional `recall_explanation` shows the actual selection method and score, recorded source/session, citations, and evidence-check status. Keyword search reports BM25; browsing reports confidence ordering; semantic search reports TF-IDF; hybrid search exposes its rank-fusion components and distinguishes the score from diversity ordering. Direct ID reads say that the ID was requested, and graph expansion reports its root/depth. Ranks refer to the current filtered search window, subject to existing candidate limits.
174
+
175
+ This explains **the current call**, not why an earlier conversation or automatic context injection selected a memory. Metadata is captured before this read boosts access/confidence. Scores and source labels do not establish correctness, and last access is not last verification. Citation checks run only when `verification.stale_check` is enabled, check at most five citations per record, and report truncation. A present snippet is not proof that the claim is true; without evidence or with verification disabled the result says so. Both readable text and JSON are supported.
176
+
177
+ No correction is applied by asking for an explanation. After reviewing it, use `memory_update` to correct content, `memory_forget` with `invalidate: true` to hide outdated knowledge while preserving history, or `memory_relate` to record a replacement/contradiction.
178
+
130
179
  ## Configuration
131
180
 
132
181
  Optional config at `~/.lemma/config.json`:
@@ -0,0 +1,63 @@
1
+ export declare const MAX_DATABASE_BYTES: number;
2
+ export declare const RESTORE_PREVIEW_TTL_MS: number;
3
+ export interface BackupSummary {
4
+ memories: number;
5
+ guides: number;
6
+ sessions: number;
7
+ relations: number;
8
+ archived_memories: number;
9
+ active_sessions: number;
10
+ }
11
+ export declare function createBackup(directory?: string): {
12
+ path: string;
13
+ verified: boolean;
14
+ summary: BackupSummary;
15
+ excluded: string[];
16
+ message: string;
17
+ };
18
+ export declare function previewRestore(file: string): {
19
+ path: string;
20
+ valid: boolean;
21
+ created_at: string;
22
+ lemma_version: string;
23
+ schema_upgrade: {
24
+ required: boolean;
25
+ from: number;
26
+ to: number;
27
+ applied_versions: number[];
28
+ notes: string[];
29
+ };
30
+ backup: BackupSummary;
31
+ current: BackupSummary;
32
+ confirmation_token: `${string}-${string}-${string}-${string}-${string}` | null;
33
+ readiness: {
34
+ conversation_mapping: "unavailable";
35
+ message: string;
36
+ current_connection: {
37
+ pid: number;
38
+ connection_id: string | null;
39
+ };
40
+ blocking_connections: {
41
+ pid: number;
42
+ connection_id: string;
43
+ same_process: boolean;
44
+ }[];
45
+ unverifiable_leases: number;
46
+ inspection_error: string | null;
47
+ status: "blocked" | "ready";
48
+ };
49
+ expires_at: string | null;
50
+ excluded: string[];
51
+ compatibility_note: string;
52
+ message: string;
53
+ };
54
+ export declare function restoreBackup(token: string | null, confirm: boolean): {
55
+ restored: boolean;
56
+ verified: boolean;
57
+ path: string;
58
+ safety_backup_path: string;
59
+ summary: BackupSummary;
60
+ closed_sessions: number;
61
+ message: string;
62
+ };
63
+ //# sourceMappingURL=backup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/db/backup.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,kBAAkB,QAAoB,CAAC;AAEpD,eAAO,MAAM,sBAAsB,QAAiB,CAAC;AAkBrD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CACzB;AAoMD,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM;;;;;;EAe9C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoC1C;AA4BD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO;;;;;;;;EAsCnE"}
@@ -0,0 +1,342 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { createHash, randomUUID } from "node:crypto";
5
+ import Database from "better-sqlite3";
6
+ import { getDb, LemmaDB } from "./database.js";
7
+ import { MIGRATIONS } from "./schema.js";
8
+ import { VERSION } from "../version.js";
9
+ import { assertEmptyLegacyVectors, isLegacyVectorTable, LEGACY_VECTOR_NOTE } from "./legacy-vectors.js";
10
+ // Snapshots and their base64 representation coexist in memory; bound both.
11
+ export const MAX_DATABASE_BYTES = 128 * 1024 * 1024;
12
+ const MAX_FILE_BYTES = Math.ceil(MAX_DATABASE_BYTES / 3) * 4 + 4096;
13
+ export const RESTORE_PREVIEW_TTL_MS = 10 * 60 * 1000;
14
+ const FORMAT = "lemma-backup";
15
+ const FORMAT_VERSION = 1;
16
+ const SCHEMA_VERSION = Math.max(...MIGRATIONS.map(([version]) => version));
17
+ // Extend this list only after testing the historical schema and its upgrade path.
18
+ const SUPPORTED_SOURCE_SCHEMAS = new Set([1, 2, 3, 4, 5, 6, 7, 8]);
19
+ const EXCLUDED = ["config.json", "diagnostic logs", "raw session/traffic logs", "installed skills and models", "MCP client configuration"];
20
+ const pendingRestores = new Map();
21
+ const expectedSchemas = new Map();
22
+ function sha256(bytes) {
23
+ return createHash("sha256").update(bytes).digest("hex");
24
+ }
25
+ function quoteIdentifier(name) {
26
+ return `"${name.replace(/"/g, '""')}"`;
27
+ }
28
+ function schemaSignature(db) {
29
+ const rows = db.prepare("SELECT type, name, tbl_name, sql FROM sqlite_master WHERE name NOT LIKE 'sqlite_%' ORDER BY type, name").all();
30
+ const legacy = assertEmptyLegacyVectors(db, rows);
31
+ const applicationRows = rows.filter(row => !(legacy && row.type === "table" && isLegacyVectorTable(row.name)));
32
+ return sha256(JSON.stringify(applicationRows.map(row => ({ ...row, sql: row.sql?.replace(/\s+/g, " ").trim() }))));
33
+ }
34
+ function assertSupportedSchema(db, version = SCHEMA_VERSION) {
35
+ if (!expectedSchemas.has(version)) {
36
+ const reference = new LemmaDB(":memory:");
37
+ try {
38
+ for (const [step, ddl] of MIGRATIONS) {
39
+ if (step <= version)
40
+ reference.db.exec(ddl);
41
+ }
42
+ expectedSchemas.set(version, schemaSignature(reference.db));
43
+ }
44
+ finally {
45
+ reference.close();
46
+ }
47
+ }
48
+ if (schemaSignature(db) !== expectedSchemas.get(version)) {
49
+ throw new Error(`Incompatible database schema. Expected the known Lemma schema ${version}; no data was replaced.`);
50
+ }
51
+ // memory-store uses -1 as the last decay timestamp, not a schema migration.
52
+ const history = db.prepare("SELECT version FROM schema_version WHERE version <> -1 ORDER BY version").all();
53
+ const expected = MIGRATIONS.filter(([step]) => step <= version).map(([step]) => step);
54
+ if (JSON.stringify(history.map(row => row.version)) !== JSON.stringify(expected)) {
55
+ throw new Error("Unsupported Lemma database version history; no data was replaced.");
56
+ }
57
+ }
58
+ function assertIntegrity(db) {
59
+ if (db.pragma("integrity_check", { simple: true }) !== "ok")
60
+ throw new Error("Backup database failed the SQLite integrity check.");
61
+ if (db.pragma("foreign_key_check").length !== 0)
62
+ throw new Error("Backup contains broken database relationships.");
63
+ // Fixed application SQL, never statements supplied by a backup.
64
+ for (const table of ["memory_fts", "guides_fts"]) {
65
+ db.prepare(`INSERT INTO ${table} (${table}, rank) VALUES ('integrity-check', 1)`).run();
66
+ }
67
+ }
68
+ function summarize(db) {
69
+ const count = (table) => db.prepare(`SELECT count(*) AS n FROM ${quoteIdentifier(table)}`).get().n;
70
+ return {
71
+ memories: count("memories"), guides: count("guides"), sessions: count("sessions"),
72
+ relations: count("relations"), archived_memories: count("fragments_archive"),
73
+ active_sessions: db.prepare("SELECT count(*) AS n FROM sessions WHERE status = 'active'").get().n,
74
+ };
75
+ }
76
+ /** Caller holds a transaction so other WAL writers cannot change this snapshot. */
77
+ function snapshot(db) {
78
+ const pageCount = db.pragma("page_count", { simple: true });
79
+ const pageSize = db.pragma("page_size", { simple: true });
80
+ if (pageCount * pageSize > MAX_DATABASE_BYTES)
81
+ throw new Error("Database exceeds the 128 MiB backup limit for this release.");
82
+ const bytes = db.serialize();
83
+ if (bytes.length > MAX_DATABASE_BYTES)
84
+ throw new Error("Database exceeds the 128 MiB backup limit for this release.");
85
+ // sqlite3_deserialize cannot open WAL images. SQLite documents this conversion
86
+ // of the PRIVATE serialized image; the live database remains in WAL mode.
87
+ // https://sqlite.org/c3ref/deserialize.html
88
+ bytes[18] = 1;
89
+ bytes[19] = 1;
90
+ return bytes;
91
+ }
92
+ function absolutePath(value) {
93
+ const expanded = value.startsWith("~/") || value.startsWith("~\\")
94
+ ? path.join(os.homedir(), value.slice(2)) : value;
95
+ if (!path.isAbsolute(expanded))
96
+ throw new Error("Please provide an absolute backup path (or a path starting with ~/).");
97
+ return path.resolve(expanded);
98
+ }
99
+ function backupDirectory(lemma) {
100
+ if (lemma.db.memory)
101
+ throw new Error("Choose a backup directory for an in-memory database.");
102
+ return path.join(path.dirname(path.resolve(lemma.db.name)), "backups");
103
+ }
104
+ function buildEnvelope(bytes) {
105
+ return {
106
+ format: FORMAT, format_version: FORMAT_VERSION, lemma_version: VERSION,
107
+ schema_version: SCHEMA_VERSION, created_at: new Date().toISOString(),
108
+ database_sha256: sha256(bytes), database: bytes.toString("base64"),
109
+ };
110
+ }
111
+ function openSnapshot(bytes, version = SCHEMA_VERSION) {
112
+ const db = new Database(bytes);
113
+ try {
114
+ // Validate BEFORE any migration: only application-owned DDL is accepted.
115
+ assertSupportedSchema(db, version);
116
+ assertIntegrity(db);
117
+ if (version !== SCHEMA_VERSION) {
118
+ db.transaction(() => {
119
+ for (const [step, ddl] of MIGRATIONS) {
120
+ if (step <= version)
121
+ continue;
122
+ db.exec(ddl);
123
+ db.prepare("INSERT INTO schema_version(version) VALUES (?)").run(step);
124
+ }
125
+ assertSupportedSchema(db);
126
+ assertIntegrity(db);
127
+ snapshot(db); // Enforce the size bound after expansion as well.
128
+ })();
129
+ }
130
+ return db;
131
+ }
132
+ catch (error) {
133
+ db.close();
134
+ throw error;
135
+ }
136
+ }
137
+ function loadBackup(file) {
138
+ const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NONBLOCK);
139
+ let bytes;
140
+ try {
141
+ const info = fs.fstatSync(fd);
142
+ if (!info.isFile() || info.size > MAX_FILE_BYTES)
143
+ throw new Error("Not a regular backup file, or backup exceeds the supported size limit.");
144
+ // Bounded even if another process grows the file after fstat.
145
+ bytes = Buffer.alloc(info.size);
146
+ let offset = 0;
147
+ while (offset < bytes.length) {
148
+ const read = fs.readSync(fd, bytes, offset, bytes.length - offset, offset);
149
+ if (!read)
150
+ throw new Error("Backup file changed or is truncated. Preview a complete backup file.");
151
+ offset += read;
152
+ }
153
+ if (fs.fstatSync(fd).size !== info.size)
154
+ throw new Error("Backup file changed while being read.");
155
+ }
156
+ finally {
157
+ fs.closeSync(fd);
158
+ }
159
+ const data = JSON.parse(bytes.toString("utf8"));
160
+ if (!data || typeof data !== "object")
161
+ throw new Error("Not a Lemma backup file.");
162
+ const envelope = data;
163
+ if (envelope.format !== FORMAT || envelope.format_version !== FORMAT_VERSION)
164
+ throw new Error("Unsupported Lemma backup format.");
165
+ if (!Number.isSafeInteger(envelope.schema_version) || !SUPPORTED_SOURCE_SCHEMAS.has(envelope.schema_version) || envelope.schema_version > SCHEMA_VERSION) {
166
+ throw new Error(`Unsupported Lemma database schema ${envelope.schema_version}. This release accepts known schemas 1–8 up to ${SCHEMA_VERSION}. Use a compatible Lemma release; no data was replaced.`);
167
+ }
168
+ if (typeof envelope.lemma_version !== "string" || envelope.lemma_version.length > 64 ||
169
+ typeof envelope.created_at !== "string" || !Number.isFinite(Date.parse(envelope.created_at)) ||
170
+ typeof envelope.database_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(envelope.database_sha256) ||
171
+ typeof envelope.database !== "string")
172
+ throw new Error("Invalid backup metadata.");
173
+ const image = Buffer.from(envelope.database, "base64");
174
+ if (image.length < 100 || image.length > MAX_DATABASE_BYTES || image.toString("base64") !== envelope.database ||
175
+ image.subarray(0, 16).toString() !== "SQLite format 3\0" || image[18] !== 1 || image[19] !== 1 ||
176
+ sha256(image) !== envelope.database_sha256)
177
+ throw new Error("Backup checksum or database payload is invalid.");
178
+ const db = openSnapshot(image, envelope.schema_version);
179
+ try {
180
+ return { envelope, db, fingerprint: sha256(bytes), summary: summarize(db) };
181
+ }
182
+ catch (error) {
183
+ db.close();
184
+ throw error;
185
+ }
186
+ }
187
+ function saveBackup(bytes, directory, prefix) {
188
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
189
+ const file = path.join(directory, `${prefix}-${new Date().toISOString().replace(/[:.]/g, "-")}-${randomUUID()}.lemma-backup`);
190
+ const fd = fs.openSync(file, "wx", 0o600);
191
+ try {
192
+ fs.writeFileSync(fd, JSON.stringify(buildEnvelope(bytes)));
193
+ fs.fsyncSync(fd);
194
+ }
195
+ catch (error) {
196
+ fs.closeSync(fd);
197
+ fs.unlinkSync(file);
198
+ throw error;
199
+ }
200
+ fs.closeSync(fd);
201
+ const verified = loadBackup(file);
202
+ verified.db.close();
203
+ return file;
204
+ }
205
+ export function createBackup(directory) {
206
+ const lemma = getDb();
207
+ const destination = directory === undefined ? backupDirectory(lemma) : absolutePath(directory);
208
+ const bytes = lemma.db.transaction(() => {
209
+ assertSupportedSchema(lemma.db);
210
+ return snapshot(lemma.db);
211
+ })();
212
+ const source = openSnapshot(bytes);
213
+ try {
214
+ const file = saveBackup(bytes, destination, "lemma");
215
+ return { path: file, verified: true, summary: summarize(source), excluded: EXCLUDED,
216
+ message: "Backup verified. Keep a copy outside this computer or the disk you plan to format. The file contains your private memory and is not encrypted. " + LEGACY_VECTOR_NOTE };
217
+ }
218
+ finally {
219
+ source.close();
220
+ }
221
+ }
222
+ export function previewRestore(file) {
223
+ const resolved = absolutePath(file);
224
+ const source = loadBackup(resolved);
225
+ try {
226
+ const lemma = getDb();
227
+ const target = lemma.db.transaction(() => {
228
+ assertSupportedSchema(lemma.db);
229
+ return { hash: sha256(snapshot(lemma.db)), summary: summarize(lemma.db) };
230
+ })();
231
+ const now = Date.now();
232
+ for (const [token, preview] of pendingRestores)
233
+ if (preview.expiresAt <= now)
234
+ pendingRestores.delete(token);
235
+ if (pendingRestores.size >= 16)
236
+ pendingRestores.delete(pendingRestores.keys().next().value);
237
+ const readiness = lemma.getRestoreReadiness();
238
+ const token = readiness.status === "ready" ? randomUUID() : null;
239
+ const expiresAt = now + RESTORE_PREVIEW_TTL_MS;
240
+ if (token)
241
+ pendingRestores.set(token, { path: resolved, sourceHash: source.fingerprint, targetHash: target.hash, target: lemma, expiresAt });
242
+ else
243
+ pendingRestores.clear(); // A blocked preview must not leave an older approval usable.
244
+ return {
245
+ path: resolved, valid: true, created_at: source.envelope.created_at, lemma_version: source.envelope.lemma_version,
246
+ schema_upgrade: {
247
+ required: source.envelope.schema_version !== SCHEMA_VERSION,
248
+ from: source.envelope.schema_version, to: SCHEMA_VERSION,
249
+ applied_versions: MIGRATIONS.filter(([step]) => step > source.envelope.schema_version).map(([step]) => step),
250
+ notes: source.envelope.schema_version < 3
251
+ ? ["Project keys are normalized: paths become project names, names become lowercase, and 'global' becomes global scope. The original backup file is unchanged."]
252
+ : ["The original backup file is unchanged. Known migrations are validated on an in-memory copy."],
253
+ },
254
+ backup: source.summary, current: target.summary, confirmation_token: token,
255
+ readiness, expires_at: token ? new Date(expiresAt).toISOString() : null, excluded: EXCLUDED,
256
+ compatibility_note: `Backup schema ${source.envelope.schema_version}; target schema ${SCHEMA_VERSION}. ` +
257
+ (source.envelope.schema_version !== SCHEMA_VERSION ? "Show schema_upgrade and its notes before asking for confirmation. The validated in-memory copy has been upgraded; the original backup file is unchanged. " : "No schema upgrade needed. ") + LEGACY_VECTOR_NOTE,
258
+ message: (token ? "Show this preview to the user and ask for explicit confirmation before backup_restore. " : "Show the connection blocker to the user. Do not ask for restore confirmation yet; resolve the blocker and call backup_preview again. ") + "Restore REPLACES all projects and global memory; it does not merge. Active sessions in the backup become abandoned history. Keep this MCP connection open. Older Lemma versions and external SQLite tools cannot be detected by this check and must be closed separately. Readiness is a point-in-time connection check, not a guarantee that restore will succeed. A verified safety backup is required before any replacement. Computer settings and diagnostic logs stay unchanged.",
259
+ };
260
+ }
261
+ finally {
262
+ source.db.close();
263
+ }
264
+ }
265
+ function replaceRows(target, source) {
266
+ // Enumerate the validated LOCAL schema. Never execute SQL supplied by a backup.
267
+ const tables = target.pragma("table_list")
268
+ .filter(row => row.schema === "main" && row.type === "table" && !row.name.startsWith("sqlite_") && !isLegacyVectorTable(row.name))
269
+ .map(row => row.name).sort();
270
+ const triggers = target.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'trigger' ORDER BY name").all();
271
+ target.pragma("defer_foreign_keys = ON");
272
+ for (const trigger of triggers)
273
+ target.exec(`DROP TRIGGER ${quoteIdentifier(trigger.name)}`);
274
+ for (const table of tables)
275
+ target.exec(`DELETE FROM ${quoteIdentifier(table)}`);
276
+ for (const table of tables) {
277
+ const columns = target.pragma(`table_info(${quoteIdentifier(table)})`).map(row => quoteIdentifier(row.name));
278
+ const insert = target.prepare(`INSERT INTO ${quoteIdentifier(table)} (${columns.join(",")}) VALUES (${columns.map(() => "?").join(",")})`);
279
+ for (const row of source.prepare(`SELECT ${columns.join(",")} FROM ${quoteIdentifier(table)}`).raw().safeIntegers().iterate()) {
280
+ insert.run(...row);
281
+ }
282
+ }
283
+ target.exec("DELETE FROM sqlite_sequence");
284
+ const insertSequence = target.prepare("INSERT INTO sqlite_sequence(name, seq) VALUES (?, ?)");
285
+ for (const row of source.prepare("SELECT name, seq FROM sqlite_sequence").raw().safeIntegers().iterate())
286
+ insertSequence.run(...row);
287
+ for (const trigger of triggers)
288
+ target.exec(trigger.sql);
289
+ for (const table of ["memory_fts", "guides_fts"])
290
+ target.exec(`INSERT INTO ${table} (${table}) VALUES ('rebuild')`);
291
+ // Sessions from another machine must not be resumed by the auto-session hook.
292
+ target.prepare("UPDATE sessions SET status = 'abandoned', outcome = COALESCE(outcome, 'abandoned'), ended_at = COALESCE(ended_at, ?) WHERE status = 'active'").run(new Date().toISOString());
293
+ assertIntegrity(target);
294
+ }
295
+ export function restoreBackup(token, confirm) {
296
+ if (confirm !== true)
297
+ throw new Error("Explicit user confirmation is required. Show backup_preview first, then call backup_restore with confirm=true only after the user approves.");
298
+ if (!token)
299
+ throw new Error("Restore preview is not ready. Resolve the connection blocker, call backup_preview and ask for confirmation again.");
300
+ const preview = pendingRestores.get(token);
301
+ if (!preview || preview.expiresAt <= Date.now()) {
302
+ pendingRestores.delete(token);
303
+ throw new Error("Restore preview is missing or expired. Call backup_preview and ask for confirmation again.");
304
+ }
305
+ pendingRestores.delete(token); // One attempt per confirmed preview, including failures.
306
+ const lemma = getDb();
307
+ if (preview.target !== lemma)
308
+ throw new Error("The target database connection changed. Preview the backup again.");
309
+ const source = loadBackup(preview.path);
310
+ let safetyBackup;
311
+ try {
312
+ if (source.fingerprint !== preview.sourceHash)
313
+ throw new Error("The backup changed after preview. Preview it again before confirming.");
314
+ const previousSync = lemma.db.pragma("synchronous", { simple: true });
315
+ try {
316
+ lemma.db.pragma("synchronous = FULL");
317
+ lemma.withRestoreLock(() => {
318
+ assertSupportedSchema(lemma.db);
319
+ const current = snapshot(lemma.db);
320
+ if (sha256(current) !== preview.targetHash)
321
+ throw new Error("Your memory changed after preview. Preview the backup again to review the current records.");
322
+ safetyBackup = saveBackup(current, backupDirectory(lemma), "before-restore");
323
+ replaceRows(lemma.db, source.db);
324
+ });
325
+ }
326
+ finally {
327
+ lemma.db.pragma(`synchronous = ${previousSync}`);
328
+ lemma.clearStatementCache();
329
+ }
330
+ pendingRestores.clear();
331
+ return { restored: true, verified: true, path: preview.path, safety_backup_path: safetyBackup,
332
+ summary: summarize(lemma.db), closed_sessions: source.summary.active_sessions,
333
+ message: "Memory restored and verified. Continue using this MCP connection; no terminal command or restart is needed. This replaces saved memory, not the text already in your current conversation. Start a new conversation if you need a fresh context." };
334
+ }
335
+ catch (error) {
336
+ throw new Error(`${error.message}${safetyBackup ? ` Safety backup: ${safetyBackup}.` : ""}`);
337
+ }
338
+ finally {
339
+ source.db.close();
340
+ }
341
+ }
342
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/db/backup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type Database from "better-sqlite3";
2
+ /** Cooperative leases cover MCP servers and visualizers using this version. */
3
+ export interface ConnectionInspection {
4
+ current_connection: {
5
+ pid: number;
6
+ connection_id: string | null;
7
+ };
8
+ blocking_connections: {
9
+ pid: number;
10
+ connection_id: string;
11
+ same_process: boolean;
12
+ }[];
13
+ unverifiable_leases: number;
14
+ inspection_error: string | null;
15
+ }
16
+ export declare function connectionBlocker(info: ConnectionInspection): string | null;
17
+ export declare class DatabaseClient {
18
+ private readonly directory;
19
+ private readonly lease;
20
+ private readonly prefix;
21
+ constructor(dbPath: string, db: Database.Database);
22
+ inspectConnections(): ConnectionInspection;
23
+ assertSoleClient(): void;
24
+ close(): void;
25
+ }
26
+ //# sourceMappingURL=clients.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../src/db/clients.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAgB3C,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,kBAAkB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAClE,oBAAoB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IACtF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,GAAG,IAAI,CAQ3E;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAmBjD,kBAAkB,IAAI,oBAAoB;IAsB1C,gBAAgB,IAAI,IAAI;IAKxB,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,97 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { randomUUID } from "node:crypto";
4
+ function isMissing(error) {
5
+ return error.code === "ENOENT";
6
+ }
7
+ function processIsAlive(pid) {
8
+ try {
9
+ process.kill(pid, 0);
10
+ return true;
11
+ }
12
+ catch (error) {
13
+ // Access denied is not evidence that a process has stopped.
14
+ return error.code !== "ESRCH";
15
+ }
16
+ }
17
+ export function connectionBlocker(info) {
18
+ if (info.inspection_error)
19
+ return `Cannot inspect Lemma connections: ${info.inspection_error}`;
20
+ if (info.unverifiable_leases)
21
+ return `Cannot verify ${info.unverifiable_leases} Lemma client lease(s).`;
22
+ if (info.blocking_connections.length) {
23
+ const pids = [...new Set(info.blocking_connections.map(connection => connection.pid))].join(", ");
24
+ return `Another Lemma client is using this memory. Blocking process IDs: ${pids}.`;
25
+ }
26
+ return null;
27
+ }
28
+ export class DatabaseClient {
29
+ directory;
30
+ lease;
31
+ prefix;
32
+ constructor(dbPath, db) {
33
+ const canonical = fs.realpathSync(dbPath);
34
+ this.directory = path.dirname(canonical);
35
+ this.prefix = `${path.basename(canonical)}.client-`;
36
+ this.lease = path.join(this.directory, `${this.prefix}${process.pid}-${randomUUID()}`);
37
+ // Registration shares SQLite's writer lock with restore. A new client cannot
38
+ // load data between the peer check and the destructive commit. SQLite also
39
+ // releases this lock after a crash; there is no stale maintenance lock file.
40
+ try {
41
+ db.transaction(() => {
42
+ // Ownership is in the filename: a crash cannot leave half-written JSON.
43
+ fs.writeFileSync(this.lease, "", { flag: "wx", mode: 0o600 });
44
+ }).immediate();
45
+ }
46
+ catch (error) {
47
+ this.close();
48
+ throw error;
49
+ }
50
+ }
51
+ inspectConnections() {
52
+ const result = {
53
+ current_connection: { pid: process.pid, connection_id: path.basename(this.lease) },
54
+ blocking_connections: [], unverifiable_leases: 0, inspection_error: null,
55
+ };
56
+ let entries;
57
+ try {
58
+ entries = fs.readdirSync(this.directory);
59
+ }
60
+ catch (error) {
61
+ result.inspection_error = error.message;
62
+ return result;
63
+ }
64
+ for (const name of entries.sort()) {
65
+ if (!name.startsWith(this.prefix))
66
+ continue;
67
+ const file = path.join(this.directory, name);
68
+ if (file === this.lease)
69
+ continue;
70
+ const match = /^([1-9]\d*)-[0-9a-f-]{36}$/.exec(name.slice(this.prefix.length));
71
+ const pid = match ? Number(match[1]) : NaN;
72
+ if (!Number.isSafeInteger(pid)) {
73
+ result.unverifiable_leases++;
74
+ continue;
75
+ }
76
+ if (processIsAlive(pid)) {
77
+ result.blocking_connections.push({ pid, connection_id: name, same_process: pid === process.pid });
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ assertSoleClient() {
83
+ const blocker = connectionBlocker(this.inspectConnections());
84
+ if (blocker)
85
+ throw new Error(`${blocker} Close other Lemma connections through their application, then preview again. Keep this MCP connection open; never terminate its process.`);
86
+ }
87
+ close() {
88
+ try {
89
+ fs.unlinkSync(this.lease);
90
+ }
91
+ catch (error) {
92
+ if (!isMissing(error))
93
+ throw error;
94
+ }
95
+ }
96
+ }
97
+ //# sourceMappingURL=clients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.js","sourceRoot":"","sources":["../../src/db/clients.ts"],"names":[],"mappings":"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"}