@membank/core 0.6.0 → 0.7.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.
package/dist/index.mjs CHANGED
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
3
3
  import { dirname, join } from "node:path";
4
4
  import BetterSqlite3 from "better-sqlite3";
5
5
  import * as sqliteVec from "sqlite-vec";
6
+ import { z } from "zod";
6
7
  import { pipeline } from "@huggingface/transformers";
7
8
  import { createHash, randomUUID } from "node:crypto";
8
9
  import { execFile } from "node:child_process";
@@ -23,7 +24,7 @@ var DatabaseError = class extends MembankError {
23
24
  //#endregion
24
25
  //#region src/db/manager.ts
25
26
  const DEFAULT_DB_PATH = join(homedir(), ".membank", "memory.db");
26
- const MIGRATIONS = [[1, `
27
+ const MIGRATIONS$1 = [[1, `
27
28
  CREATE TABLE IF NOT EXISTS memories (
28
29
  id TEXT PRIMARY KEY,
29
30
  content TEXT NOT NULL,
@@ -118,7 +119,7 @@ var DatabaseManager = class DatabaseManager {
118
119
  `);
119
120
  const row = this.#db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get();
120
121
  const currentVersion = row ? Number.parseInt(row.value, 10) : 0;
121
- for (const [targetVersion, sql] of MIGRATIONS) if (currentVersion < targetVersion) {
122
+ for (const [targetVersion, sql] of MIGRATIONS$1) if (currentVersion < targetVersion) {
122
123
  this.#db.exec(sql);
123
124
  this.#db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)").run(String(targetVersion));
124
125
  }
@@ -131,13 +132,89 @@ var DatabaseManager = class DatabaseManager {
131
132
  }
132
133
  };
133
134
  //#endregion
135
+ //#region src/schemas.ts
136
+ const MEMORY_TYPE_VALUES = [
137
+ "correction",
138
+ "preference",
139
+ "decision",
140
+ "learning",
141
+ "fact"
142
+ ];
143
+ const MemoryTypeSchema = z.enum(MEMORY_TYPE_VALUES);
144
+ const TagsJsonSchema = z.array(z.string());
145
+ const ProjectSchema = z.object({
146
+ id: z.string(),
147
+ name: z.string(),
148
+ scopeHash: z.string(),
149
+ createdAt: z.string(),
150
+ updatedAt: z.string()
151
+ });
152
+ const MemorySchema = z.object({
153
+ id: z.string(),
154
+ content: z.string(),
155
+ type: MemoryTypeSchema,
156
+ tags: z.array(z.string()),
157
+ projects: z.array(ProjectSchema),
158
+ sourceHarness: z.string().nullable(),
159
+ accessCount: z.number().int().nonnegative(),
160
+ pinned: z.boolean(),
161
+ needsReview: z.boolean(),
162
+ createdAt: z.string(),
163
+ updatedAt: z.string()
164
+ });
165
+ const QueryOptionsSchema = z.object({
166
+ query: z.string().min(1),
167
+ type: MemoryTypeSchema.optional(),
168
+ projectHash: z.string().optional(),
169
+ limit: z.number().int().positive().optional(),
170
+ includePinned: z.boolean().optional()
171
+ });
172
+ const SaveOptionsSchema = z.object({
173
+ content: z.string().min(1),
174
+ type: MemoryTypeSchema,
175
+ tags: z.array(z.string()).optional(),
176
+ projectScope: z.object({
177
+ hash: z.string(),
178
+ name: z.string()
179
+ }).optional(),
180
+ sourceHarness: z.string().optional()
181
+ });
182
+ const MemoryPatchSchema = z.object({
183
+ content: z.string().min(1).optional(),
184
+ tags: z.array(z.string()).optional()
185
+ });
186
+ const SessionContextSchema = z.object({
187
+ stats: z.record(MemoryTypeSchema, z.number()),
188
+ pinnedGlobal: z.array(MemorySchema),
189
+ pinnedProject: z.array(MemorySchema)
190
+ });
191
+ const MemoryRowSchema = z.object({
192
+ id: z.string(),
193
+ content: z.string(),
194
+ type: z.string(),
195
+ tags: z.string(),
196
+ source: z.string().nullable(),
197
+ access_count: z.number(),
198
+ pinned: z.number(),
199
+ needs_review: z.number(),
200
+ created_at: z.string(),
201
+ updated_at: z.string()
202
+ });
203
+ const ProjectRowSchema = z.object({
204
+ id: z.string(),
205
+ name: z.string(),
206
+ scope_hash: z.string(),
207
+ created_at: z.string(),
208
+ updated_at: z.string()
209
+ });
210
+ //#endregion
134
211
  //#region src/db/row-types.ts
135
212
  function rowToMemory(row, projects) {
136
213
  return {
137
214
  id: row.id,
138
215
  content: row.content,
139
- type: row.type,
140
- tags: JSON.parse(row.tags),
216
+ type: MemoryTypeSchema.parse(row.type),
217
+ tags: TagsJsonSchema.parse(JSON.parse(row.tags)),
141
218
  projects,
142
219
  sourceHarness: row.source,
143
220
  accessCount: row.access_count,
@@ -182,15 +259,6 @@ var EmbeddingService = class {
182
259
  }
183
260
  };
184
261
  //#endregion
185
- //#region src/types.ts
186
- const MEMORY_TYPE_VALUES = [
187
- "correction",
188
- "preference",
189
- "decision",
190
- "learning",
191
- "fact"
192
- ];
193
- //#endregion
194
262
  //#region src/memory/repository.ts
195
263
  var MemoryRepository = class {
196
264
  #db;
@@ -202,16 +270,16 @@ var MemoryRepository = class {
202
270
  this.#projects = projects;
203
271
  }
204
272
  async save(options) {
205
- const { content, type, tags = [], projectHash, sourceHarness } = options;
273
+ const { content, type, tags = [], projectScope, sourceHarness } = SaveOptionsSchema.parse(options);
206
274
  const embedding = await this.#embedding.embed(content);
207
275
  const embeddingBlob = Buffer.from(embedding.buffer);
208
276
  let top;
209
- if (projectHash !== void 0) top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
277
+ if (projectScope !== void 0) top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
210
278
  FROM memories m JOIN embeddings e ON e.rowid = m.rowid
211
279
  JOIN memory_projects mp ON mp.memory_id = m.id
212
280
  JOIN projects p ON p.id = mp.project_id
213
281
  WHERE m.type = ? AND p.scope_hash = ?
214
- ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, projectHash);
282
+ ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, projectScope.hash);
215
283
  else top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
216
284
  FROM memories m JOIN embeddings e ON e.rowid = m.rowid
217
285
  WHERE m.type = ?
@@ -221,41 +289,42 @@ var MemoryRepository = class {
221
289
  if (top !== void 0 && top.similarity > .92) {
222
290
  this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
223
291
  this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
224
- return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id), this.#projects.getProjectsForMemories([top.id]).get(top.id) ?? []);
292
+ return rowToMemory(MemoryRowSchema.parse(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id)), this.#projects.getProjectsForMemories([top.id]).get(top.id) ?? []);
225
293
  }
226
294
  if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
227
295
  const id = randomUUID();
228
296
  this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, source, access_count, pinned, needs_review, created_at, updated_at)
229
297
  VALUES (?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), sourceHarness ?? null, now, now);
230
298
  this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
231
- if (projectHash !== void 0) {
232
- const project = this.#projects.upsertByHash(projectHash, `project-${projectHash.slice(0, 8)}`);
299
+ if (projectScope !== void 0) {
300
+ const project = this.#projects.upsertByHash(projectScope.hash, projectScope.name);
233
301
  this.#projects.addAssociation(id, project.id);
234
302
  }
235
- return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
303
+ return rowToMemory(MemoryRowSchema.parse(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id)), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
236
304
  }
237
305
  async update(id, patch) {
306
+ const { content, tags } = MemoryPatchSchema.parse(patch);
238
307
  const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
239
308
  if (existing === void 0) throw new Error(`Memory not found: ${id}`);
240
309
  const now = (/* @__PURE__ */ new Date()).toISOString();
241
310
  const sets = ["updated_at = ?"];
242
311
  const values = [now];
243
- if (patch.content !== void 0) {
312
+ if (content !== void 0) {
244
313
  sets.push("content = ?");
245
- values.push(patch.content);
314
+ values.push(content);
246
315
  }
247
- if (patch.tags !== void 0) {
316
+ if (tags !== void 0) {
248
317
  sets.push("tags = ?");
249
- values.push(JSON.stringify(patch.tags));
318
+ values.push(JSON.stringify(tags));
250
319
  }
251
320
  values.push(id);
252
321
  this.#db.db.prepare(`UPDATE memories SET ${sets.join(", ")} WHERE id = ?`).run(...values);
253
- if (patch.content !== void 0) {
254
- const embedding = await this.#embedding.embed(patch.content);
322
+ if (content !== void 0) {
323
+ const embedding = await this.#embedding.embed(content);
255
324
  const embeddingBlob = Buffer.from(embedding.buffer);
256
325
  this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
257
326
  }
258
- return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
327
+ return rowToMemory(MemoryRowSchema.parse(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id)), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
259
328
  }
260
329
  delete(id) {
261
330
  const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
@@ -282,7 +351,10 @@ var MemoryRepository = class {
282
351
  stats() {
283
352
  const byType = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
284
353
  const typeRows = this.#db.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();
285
- for (const row of typeRows) if (row.type in byType) byType[row.type] = row.count;
354
+ for (const row of typeRows) {
355
+ const parsed = MemoryTypeSchema.safeParse(row.type);
356
+ if (parsed.success) byType[parsed.data] = row.count;
357
+ }
286
358
  const totals = this.#db.db.prepare(`SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`).get() ?? {
287
359
  total: 0,
288
360
  needsReview: 0
@@ -297,13 +369,74 @@ var MemoryRepository = class {
297
369
  if (this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id) === void 0) throw new Error(`Memory not found: ${id}`);
298
370
  const now = (/* @__PURE__ */ new Date()).toISOString();
299
371
  this.#db.db.prepare(`UPDATE memories SET pinned = ?, updated_at = ? WHERE id = ?`).run(pinned ? 1 : 0, now, id);
300
- return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
372
+ return rowToMemory(MemoryRowSchema.parse(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id)), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
301
373
  }
302
374
  incrementAccessCount(id) {
303
375
  this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
304
376
  }
305
377
  };
306
378
  //#endregion
379
+ //#region src/scope/resolver.ts
380
+ const execFileAsync = promisify(execFile);
381
+ function sha256Truncated(input) {
382
+ return createHash("sha256").update(input).digest("hex").slice(0, 16);
383
+ }
384
+ async function resolveProject() {
385
+ try {
386
+ const { stdout } = await execFileAsync("git", [
387
+ "remote",
388
+ "get-url",
389
+ "origin"
390
+ ]);
391
+ const url = stdout.trim();
392
+ if (url) {
393
+ const hash = sha256Truncated(url);
394
+ return {
395
+ hash,
396
+ name: url.split("/").pop()?.replace(/\.git$/, "") ?? hash.slice(0, 8)
397
+ };
398
+ }
399
+ } catch {}
400
+ const cwd = process.cwd();
401
+ const hash = sha256Truncated(cwd);
402
+ return {
403
+ hash,
404
+ name: cwd.split(/[/\\]/).filter(Boolean).pop() ?? hash.slice(0, 8)
405
+ };
406
+ }
407
+ async function resolveScope() {
408
+ try {
409
+ const { stdout } = await execFileAsync("git", [
410
+ "remote",
411
+ "get-url",
412
+ "origin"
413
+ ]);
414
+ const url = stdout.trim();
415
+ if (url) return sha256Truncated(url);
416
+ } catch {}
417
+ return sha256Truncated(process.cwd());
418
+ }
419
+ //#endregion
420
+ //#region src/migrations/index.ts
421
+ const MIGRATIONS = [{
422
+ name: "scope-to-projects",
423
+ description: "Rename the auto-migrated project for the current directory from its generic hash-derived name to the resolved repo/directory name."
424
+ }];
425
+ async function runScopeToProjectsMigration(projects) {
426
+ const resolved = await resolveProject();
427
+ const project = projects.getByHash(resolved.hash);
428
+ if (project === void 0) return null;
429
+ const oldName = project.name;
430
+ const memoryCount = projects.countMemories(project.id);
431
+ projects.rename(project.id, resolved.name);
432
+ return {
433
+ migration: "scope-to-projects",
434
+ oldName,
435
+ newName: resolved.name,
436
+ memoryCount
437
+ };
438
+ }
439
+ //#endregion
307
440
  //#region src/project/repository.ts
308
441
  var ProjectRepository = class {
309
442
  #db;
@@ -314,7 +447,7 @@ var ProjectRepository = class {
314
447
  const now = (/* @__PURE__ */ new Date()).toISOString();
315
448
  const id = randomUUID();
316
449
  this.#db.db.prepare(`INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`).run(id, name, hash, now, now);
317
- return rowToProject(this.#db.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(hash));
450
+ return rowToProject(ProjectRowSchema.parse(this.#db.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(hash)));
318
451
  }
319
452
  rename(id, name) {
320
453
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -336,6 +469,9 @@ var ProjectRepository = class {
336
469
  removeAssociation(memoryId, projectId) {
337
470
  this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`).run(memoryId, projectId);
338
471
  }
472
+ countMemories(projectId) {
473
+ return this.#db.db.prepare(`SELECT COUNT(*) AS count FROM memory_projects WHERE project_id = ?`).get(projectId)?.count ?? 0;
474
+ }
339
475
  getProjectsForMemories(memoryIds) {
340
476
  if (memoryIds.length === 0) return /* @__PURE__ */ new Map();
341
477
  const placeholders = memoryIds.map(() => "?").join(",");
@@ -370,12 +506,13 @@ var QueryEngine = class {
370
506
  this.#repo = repo;
371
507
  }
372
508
  async query(options) {
373
- const { query, type, projectHash, limit = 10 } = options;
509
+ const { query, type, projectHash, limit = 10, includePinned } = QueryOptionsSchema.parse(options);
374
510
  const queryEmbedding = await this.#embedding.embed(query);
375
511
  const queryBlob = Buffer.from(queryEmbedding.buffer);
376
512
  const whereClauses = [];
377
513
  const params = [queryBlob];
378
514
  let joinClause = "";
515
+ if (!includePinned) whereClauses.push("m.pinned = 0");
379
516
  if (type !== void 0) {
380
517
  whereClauses.push("m.type = ?");
381
518
  params.push(type);
@@ -416,47 +553,6 @@ var QueryEngine = class {
416
553
  }
417
554
  };
418
555
  //#endregion
419
- //#region src/scope/resolver.ts
420
- const execFileAsync = promisify(execFile);
421
- function sha256Truncated(input) {
422
- return createHash("sha256").update(input).digest("hex").slice(0, 16);
423
- }
424
- async function resolveProject() {
425
- try {
426
- const { stdout } = await execFileAsync("git", [
427
- "remote",
428
- "get-url",
429
- "origin"
430
- ]);
431
- const url = stdout.trim();
432
- if (url) {
433
- const hash = sha256Truncated(url);
434
- return {
435
- hash,
436
- name: url.split("/").pop()?.replace(/\.git$/, "") ?? hash.slice(0, 8)
437
- };
438
- }
439
- } catch {}
440
- const cwd = process.cwd();
441
- const hash = sha256Truncated(cwd);
442
- return {
443
- hash,
444
- name: cwd.split(/[/\\]/).filter(Boolean).pop() ?? hash.slice(0, 8)
445
- };
446
- }
447
- async function resolveScope() {
448
- try {
449
- const { stdout } = await execFileAsync("git", [
450
- "remote",
451
- "get-url",
452
- "origin"
453
- ]);
454
- const url = stdout.trim();
455
- if (url) return sha256Truncated(url);
456
- } catch {}
457
- return sha256Truncated(process.cwd());
458
- }
459
- //#endregion
460
556
  //#region src/session/builder.ts
461
557
  function listMemoryTypes() {
462
558
  return [...MEMORY_TYPE_VALUES];
@@ -476,7 +572,10 @@ var SessionContextBuilder = class {
476
572
  WHERE p.scope_hash = ? AND m.pinned = 1`).all(projectHash).map((row) => rowToMemory(row, []));
477
573
  const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
478
574
  const stats = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
479
- for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
575
+ for (const row of typeCounts) {
576
+ const parsed = MemoryTypeSchema.safeParse(row.type);
577
+ if (parsed.success) stats[parsed.data] = row.count;
578
+ }
480
579
  return {
481
580
  stats,
482
581
  pinnedGlobal,
@@ -485,6 +584,6 @@ var SessionContextBuilder = class {
485
584
  }
486
585
  };
487
586
  //#endregion
488
- export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, MemoryRepository, ProjectRepository, QueryEngine, SessionContextBuilder, listMemoryTypes, resolveProject, resolveScope, rowToMemory };
587
+ export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MIGRATIONS, MembankError, MemoryPatchSchema, MemoryRepository, MemoryRowSchema, MemorySchema, MemoryTypeSchema, ProjectRepository, ProjectRowSchema, ProjectSchema, QueryEngine, QueryOptionsSchema, SaveOptionsSchema, SessionContextBuilder, SessionContextSchema, TagsJsonSchema, listMemoryTypes, resolveProject, resolveScope, rowToMemory, rowToProject, runScopeToProjectsMigration };
489
588
 
490
589
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["#db","#init","#initInMemory","#runMigrations","#db","#embedding","#projects","#db","#db","#embedding","#repo","#computeScore","#db"],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/db/row-types.ts","../src/embedding/service.ts","../src/types.ts","../src/memory/repository.ts","../src/project/repository.ts","../src/query/engine.ts","../src/scope/resolver.ts","../src/session/builder.ts"],"sourcesContent":["export class MembankError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"MembankError\";\n }\n}\n\nexport class DatabaseError extends MembankError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"DatabaseError\";\n }\n}\n","import { mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport BetterSqlite3 from \"better-sqlite3\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport { DatabaseError } from \"./errors.js\";\n\nconst DEFAULT_DB_PATH = join(homedir(), \".membank\", \"memory.db\");\n\ntype VecLoader = (db: BetterSqlite3.Database) => void;\n\nconst MIGRATIONS: [number, string][] = [\n [\n 1,\n `\nCREATE TABLE IF NOT EXISTS memories (\n id TEXT PRIMARY KEY,\n content TEXT NOT NULL,\n type TEXT NOT NULL,\n tags TEXT NOT NULL DEFAULT '[]',\n scope TEXT NOT NULL,\n source TEXT,\n access_count INTEGER NOT NULL DEFAULT 0,\n pinned INTEGER NOT NULL DEFAULT 0,\n needs_review INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n\nCREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(\n embedding FLOAT[384]\n);\n`,\n ],\n [\n 2,\n `\nCREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n scope_hash TEXT NOT NULL UNIQUE,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS memory_projects (\n memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n PRIMARY KEY (memory_id, project_id)\n);\n\nINSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at)\nSELECT\n lower(hex(randomblob(16))),\n 'project-' || substr(scope, 1, 8),\n scope,\n datetime('now'),\n datetime('now')\nFROM memories\nWHERE scope != 'global'\nGROUP BY scope;\n\nINSERT OR IGNORE INTO memory_projects (memory_id, project_id)\nSELECT m.id, p.id\nFROM memories m\nJOIN projects p ON p.scope_hash = m.scope\nWHERE m.scope != 'global';\n\nALTER TABLE memories DROP COLUMN scope;\n`,\n ],\n];\n\nexport class DatabaseManager {\n readonly #db: BetterSqlite3.Database;\n\n private constructor(db: BetterSqlite3.Database) {\n this.#db = db;\n }\n\n static open(dbPath?: string): DatabaseManager {\n const resolvedPath = dbPath ?? DEFAULT_DB_PATH;\n mkdirSync(dirname(resolvedPath), { recursive: true });\n const db = new BetterSqlite3(resolvedPath);\n return DatabaseManager.#init(db, sqliteVec.load);\n }\n\n static openInMemory(): DatabaseManager {\n return DatabaseManager.#initInMemory(sqliteVec.load);\n }\n\n /** For testing: inject a custom vec loader (e.g. a throwing stub). */\n static _openInMemoryWithLoader(loader: VecLoader): DatabaseManager {\n return DatabaseManager.#initInMemory(loader);\n }\n\n static #initInMemory(loader: VecLoader): DatabaseManager {\n const db = new BetterSqlite3(\":memory:\");\n return DatabaseManager.#init(db, loader);\n }\n\n static #init(db: BetterSqlite3.Database, loader: VecLoader): DatabaseManager {\n try {\n loader(db);\n } catch (err) {\n throw new DatabaseError(\"Failed to load sqlite-vec extension\", {\n cause: err,\n });\n }\n\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"foreign_keys = ON\");\n\n const manager = new DatabaseManager(db);\n manager.#runMigrations();\n return manager;\n }\n\n #runMigrations(): void {\n // Bootstrap the meta table before reading schema_version from it\n this.#db.exec(`\n CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\n\n const row = this.#db\n .prepare<[], { value: string }>(\"SELECT value FROM meta WHERE key = 'schema_version'\")\n .get();\n\n const currentVersion = row ? Number.parseInt(row.value, 10) : 0;\n\n for (const [targetVersion, sql] of MIGRATIONS) {\n if (currentVersion < targetVersion) {\n this.#db.exec(sql);\n this.#db\n .prepare(\"INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)\")\n .run(String(targetVersion));\n }\n }\n }\n\n get db(): BetterSqlite3.Database {\n return this.#db;\n }\n\n close(): void {\n this.#db.close();\n }\n}\n","import type { Memory, MemoryType, Project } from \"../types.js\";\n\nexport interface MemoryRow {\n id: string;\n content: string;\n type: string;\n tags: string;\n source: string | null;\n access_count: number;\n pinned: number;\n needs_review: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface ProjectRow {\n id: string;\n name: string;\n scope_hash: string;\n created_at: string;\n updated_at: string;\n}\n\nexport function rowToMemory(row: MemoryRow, projects: Project[]): Memory {\n return {\n id: row.id,\n content: row.content,\n type: row.type as MemoryType,\n tags: JSON.parse(row.tags) as string[],\n projects,\n sourceHarness: row.source,\n accessCount: row.access_count,\n pinned: row.pinned !== 0,\n needsReview: row.needs_review !== 0,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport function rowToProject(row: ProjectRow): Project {\n return {\n id: row.id,\n name: row.name,\n scopeHash: row.scope_hash,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"@huggingface/transformers\";\n\nexport type ProgressCallback = (progress: { status: string; progress?: number }) => void;\n\nexport class EmbeddingService {\n private readonly modelCachePath: string;\n private readonly onProgress: ProgressCallback | undefined;\n private pipelineInstance: Awaited<ReturnType<typeof pipeline>> | null = null;\n\n constructor(modelCachePath?: string, onProgress?: ProgressCallback) {\n this.modelCachePath = modelCachePath ?? join(homedir(), \".membank\", \"models\");\n this.onProgress = onProgress;\n }\n\n private async getPipeline(): Promise<Awaited<ReturnType<typeof pipeline>>> {\n if (this.pipelineInstance === null) {\n this.pipelineInstance = await pipeline(\"feature-extraction\", \"Xenova/bge-small-en-v1.5\", {\n cache_dir: this.modelCachePath,\n progress_callback: this.onProgress,\n });\n }\n return this.pipelineInstance;\n }\n\n async embed(text: string): Promise<Float32Array> {\n const pipe = await this.getPipeline();\n // Shape: [1, seq_len, 384]. Cast to any to bypass the non-unified pipeline union signature.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const output = await (\n pipe as (input: string, opts: Record<string, unknown>) => Promise<unknown>\n )(text, { pooling: \"mean\", normalize: true });\n\n // @huggingface/transformers Tensor has a .data property with the flat array\n const tensor = output as { data: Float32Array | number[] };\n const flat = tensor.data;\n\n return flat instanceof Float32Array ? flat : new Float32Array(flat);\n }\n}\n","export type MemoryType = \"correction\" | \"preference\" | \"decision\" | \"learning\" | \"fact\";\n\nexport const MEMORY_TYPE_VALUES = [\n \"correction\",\n \"preference\",\n \"decision\",\n \"learning\",\n \"fact\",\n] as const satisfies readonly MemoryType[];\n\nexport interface Project {\n id: string;\n name: string;\n scopeHash: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface Memory {\n id: string;\n content: string;\n type: MemoryType;\n tags: string[];\n projects: Project[];\n sourceHarness: string | null;\n accessCount: number;\n pinned: boolean;\n needsReview: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface QueryOptions {\n query: string;\n type?: MemoryType;\n projectHash?: string;\n limit?: number;\n}\n\nexport interface SaveOptions {\n content: string;\n type: MemoryType;\n tags?: string[];\n projectHash?: string;\n sourceHarness?: string;\n}\n\nexport interface SessionContext {\n stats: Record<MemoryType, number>;\n pinnedGlobal: Memory[];\n pinnedProject: Memory[];\n}\n","import { randomUUID } from \"node:crypto\";\nimport type { DatabaseManager } from \"../db/manager.js\";\nimport type { MemoryRow } from \"../db/row-types.js\";\nimport { rowToMemory } from \"../db/row-types.js\";\nimport type { EmbeddingService } from \"../embedding/service.js\";\nimport type { ProjectRepository } from \"../project/repository.js\";\nimport type { Memory, MemoryType, SaveOptions } from \"../types.js\";\nimport { MEMORY_TYPE_VALUES } from \"../types.js\";\n\ninterface SimilarityRow extends MemoryRow {\n rowid: number;\n similarity: number;\n}\n\nexport class MemoryRepository {\n readonly #db: DatabaseManager;\n readonly #embedding: EmbeddingService;\n readonly #projects: ProjectRepository;\n\n constructor(\n db: DatabaseManager,\n embeddingService: EmbeddingService,\n projects: ProjectRepository\n ) {\n this.#db = db;\n this.#embedding = embeddingService;\n this.#projects = projects;\n }\n\n async save(options: SaveOptions): Promise<Memory> {\n const { content, type, tags = [], projectHash, sourceHarness } = options;\n\n const embedding = await this.#embedding.embed(content);\n const embeddingBlob = Buffer.from(embedding.buffer);\n\n // Dedup: find similar memory in same context\n let top: SimilarityRow | undefined;\n if (projectHash !== undefined) {\n top = this.#db.db\n .prepare<[Buffer, string, string], SimilarityRow>(\n `SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\n JOIN memory_projects mp ON mp.memory_id = m.id\n JOIN projects p ON p.id = mp.project_id\n WHERE m.type = ? AND p.scope_hash = ?\n ORDER BY similarity DESC LIMIT 1`\n )\n .get(embeddingBlob, type, projectHash);\n } else {\n top = this.#db.db\n .prepare<[Buffer, string], SimilarityRow>(\n `SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\n WHERE m.type = ?\n AND m.id NOT IN (SELECT memory_id FROM memory_projects)\n ORDER BY similarity DESC LIMIT 1`\n )\n .get(embeddingBlob, type);\n }\n\n const now = new Date().toISOString();\n\n if (top !== undefined && top.similarity > 0.92) {\n this.#db.db\n .prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`)\n .run(content, now, top.id);\n this.#db.db\n .prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`)\n .run(embeddingBlob, top.rowid);\n\n const updated = this.#db.db\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\n .get(top.id) as MemoryRow;\n\n const projectMap = this.#projects.getProjectsForMemories([top.id]);\n return rowToMemory(updated, projectMap.get(top.id) ?? []);\n }\n\n if (top !== undefined && top.similarity >= 0.75) {\n this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);\n }\n\n const id = randomUUID();\n this.#db.db\n .prepare(\n `INSERT INTO memories (id, content, type, tags, source, access_count, pinned, needs_review, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`\n )\n .run(id, content, type, JSON.stringify(tags), sourceHarness ?? null, now, now);\n\n this.#db.db\n .prepare(\n `INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`\n )\n .run(embeddingBlob, id);\n\n if (projectHash !== undefined) {\n // resolveProject name not available here; caller should have upserted already\n // if project not yet upserted (e.g. CLI path), upsert with hash as placeholder name\n const project = this.#projects.upsertByHash(\n projectHash,\n `project-${projectHash.slice(0, 8)}`\n );\n this.#projects.addAssociation(id, project.id);\n }\n\n const row = this.#db.db\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\n .get(id) as MemoryRow;\n\n const projectMap = this.#projects.getProjectsForMemories([id]);\n return rowToMemory(row, projectMap.get(id) ?? []);\n }\n\n async update(id: string, patch: { content?: string; tags?: string[] }): Promise<Memory> {\n const existing = this.#db.db\n .prepare<[string], MemoryRow & { rowid: number }>(\n `SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`\n )\n .get(id);\n\n if (existing === undefined) {\n throw new Error(`Memory not found: ${id}`);\n }\n\n const now = new Date().toISOString();\n const sets: string[] = [\"updated_at = ?\"];\n const values: string[] = [now];\n\n if (patch.content !== undefined) {\n sets.push(\"content = ?\");\n values.push(patch.content);\n }\n\n if (patch.tags !== undefined) {\n sets.push(\"tags = ?\");\n values.push(JSON.stringify(patch.tags));\n }\n\n values.push(id);\n this.#db.db.prepare(`UPDATE memories SET ${sets.join(\", \")} WHERE id = ?`).run(...values);\n\n if (patch.content !== undefined) {\n const embedding = await this.#embedding.embed(patch.content);\n const embeddingBlob = Buffer.from(embedding.buffer);\n this.#db.db\n .prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`)\n .run(embeddingBlob, existing.rowid);\n }\n\n const updated = this.#db.db\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\n .get(id) as MemoryRow;\n\n const projectMap = this.#projects.getProjectsForMemories([id]);\n return rowToMemory(updated, projectMap.get(id) ?? []);\n }\n\n delete(id: string): Promise<void> {\n const row = this.#db.db\n .prepare<[string], { rowid: number }>(`SELECT rowid FROM memories WHERE id = ?`)\n .get(id);\n\n if (row !== undefined) {\n this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);\n }\n\n this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ?`).run(id);\n this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);\n\n return Promise.resolve();\n }\n\n list(opts?: { type?: MemoryType; pinned?: boolean }): Memory[] {\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (opts?.type !== undefined) {\n conditions.push(\"type = ?\");\n params.push(opts.type);\n }\n\n if (opts?.pinned === true) {\n conditions.push(\"pinned = 1\");\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n const rows = this.#db.db\n .prepare<(string | number)[], MemoryRow>(\n `SELECT * FROM memories ${where} ORDER BY created_at DESC`\n )\n .all(...params);\n\n if (rows.length === 0) return [];\n\n const ids = rows.map((r) => r.id);\n const projectMap = this.#projects.getProjectsForMemories(ids);\n return rows.map((row) => rowToMemory(row, projectMap.get(row.id) ?? []));\n }\n\n stats(): { byType: Record<MemoryType, number>; total: number; needsReview: number } {\n const byType = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0])) as Record<\n MemoryType,\n number\n >;\n\n const typeRows = this.#db.db\n .prepare<[], { type: string; count: number }>(\n `SELECT type, COUNT(*) as count FROM memories GROUP BY type`\n )\n .all();\n\n for (const row of typeRows) {\n if (row.type in byType) {\n byType[row.type as MemoryType] = row.count;\n }\n }\n\n const totals = this.#db.db\n .prepare<[], { total: number; needsReview: number }>(\n `SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`\n )\n .get() ?? { total: 0, needsReview: 0 };\n\n return {\n byType,\n total: totals.total,\n needsReview: totals.needsReview ?? 0,\n };\n }\n\n setPin(id: string, pinned: boolean): Memory {\n const existing = this.#db.db\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\n .get(id);\n\n if (existing === undefined) {\n throw new Error(`Memory not found: ${id}`);\n }\n\n const now = new Date().toISOString();\n this.#db.db\n .prepare(`UPDATE memories SET pinned = ?, updated_at = ? WHERE id = ?`)\n .run(pinned ? 1 : 0, now, id);\n\n const updated = this.#db.db\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\n .get(id) as MemoryRow;\n\n const projectMap = this.#projects.getProjectsForMemories([id]);\n return rowToMemory(updated, projectMap.get(id) ?? []);\n }\n\n incrementAccessCount(id: string): void {\n this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport type { DatabaseManager } from \"../db/manager.js\";\nimport type { ProjectRow } from \"../db/row-types.js\";\nimport { rowToProject } from \"../db/row-types.js\";\nimport type { Project } from \"../types.js\";\n\ninterface ProjectMemoryRow extends ProjectRow {\n memory_id: string;\n}\n\nexport class ProjectRepository {\n readonly #db: DatabaseManager;\n\n constructor(db: DatabaseManager) {\n this.#db = db;\n }\n\n upsertByHash(hash: string, name: string): Project {\n const now = new Date().toISOString();\n const id = randomUUID();\n this.#db.db\n .prepare(\n `INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`\n )\n .run(id, name, hash, now, now);\n\n const row = this.#db.db\n .prepare<[string], ProjectRow>(`SELECT * FROM projects WHERE scope_hash = ?`)\n .get(hash) as ProjectRow;\n\n return rowToProject(row);\n }\n\n rename(id: string, name: string): Project {\n const now = new Date().toISOString();\n this.#db.db\n .prepare(`UPDATE projects SET name = ?, updated_at = ? WHERE id = ?`)\n .run(name, now, id);\n\n const row = this.#db.db\n .prepare<[string], ProjectRow>(`SELECT * FROM projects WHERE id = ?`)\n .get(id);\n\n if (row === undefined) throw new Error(`Project not found: ${id}`);\n return rowToProject(row);\n }\n\n list(): Project[] {\n return this.#db.db\n .prepare<[], ProjectRow>(`SELECT * FROM projects ORDER BY name ASC`)\n .all()\n .map(rowToProject);\n }\n\n getByHash(hash: string): Project | undefined {\n const row = this.#db.db\n .prepare<[string], ProjectRow>(`SELECT * FROM projects WHERE scope_hash = ?`)\n .get(hash);\n return row !== undefined ? rowToProject(row) : undefined;\n }\n\n addAssociation(memoryId: string, projectId: string): void {\n this.#db.db\n .prepare(`INSERT OR IGNORE INTO memory_projects (memory_id, project_id) VALUES (?, ?)`)\n .run(memoryId, projectId);\n }\n\n removeAssociation(memoryId: string, projectId: string): void {\n this.#db.db\n .prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`)\n .run(memoryId, projectId);\n }\n\n getProjectsForMemories(memoryIds: string[]): Map<string, Project[]> {\n if (memoryIds.length === 0) return new Map();\n const placeholders = memoryIds.map(() => \"?\").join(\",\");\n const rows = this.#db.db\n .prepare<string[], ProjectMemoryRow>(\n `SELECT p.*, mp.memory_id FROM projects p\n JOIN memory_projects mp ON mp.project_id = p.id\n WHERE mp.memory_id IN (${placeholders})`\n )\n .all(...memoryIds);\n\n const result = new Map<string, Project[]>();\n for (const row of rows) {\n const list = result.get(row.memory_id) ?? [];\n list.push(rowToProject(row));\n result.set(row.memory_id, list);\n }\n return result;\n }\n}\n","import type { DatabaseManager } from \"../db/manager.js\";\nimport type { MemoryRow } from \"../db/row-types.js\";\nimport { rowToMemory } from \"../db/row-types.js\";\nimport type { EmbeddingService } from \"../embedding/service.js\";\nimport type { MemoryRepository } from \"../memory/repository.js\";\nimport type { Memory, MemoryType, QueryOptions } from \"../types.js\";\n\ninterface QueryMemoryRow extends MemoryRow {\n cosine_sim: number;\n}\n\nconst TYPE_WEIGHTS = {\n correction: 1.0,\n preference: 0.8,\n decision: 0.6,\n learning: 0.4,\n fact: 0.2,\n} satisfies Record<MemoryType, number>;\n\nexport class QueryEngine {\n readonly #db: DatabaseManager;\n readonly #embedding: EmbeddingService;\n readonly #repo: MemoryRepository;\n\n constructor(db: DatabaseManager, embeddingService: EmbeddingService, repo: MemoryRepository) {\n this.#db = db;\n this.#embedding = embeddingService;\n this.#repo = repo;\n }\n\n async query(options: QueryOptions): Promise<Array<Memory & { score: number }>> {\n const { query, type, projectHash, limit = 10 } = options;\n\n const queryEmbedding = await this.#embedding.embed(query);\n const queryBlob = Buffer.from(queryEmbedding.buffer);\n\n const whereClauses: string[] = [];\n const params: unknown[] = [queryBlob];\n let joinClause = \"\";\n\n if (type !== undefined) {\n whereClauses.push(\"m.type = ?\");\n params.push(type);\n }\n\n if (projectHash !== undefined) {\n joinClause =\n \"LEFT JOIN memory_projects mp ON mp.memory_id = m.id LEFT JOIN projects p ON p.id = mp.project_id\";\n whereClauses.push(\"p.scope_hash = ?\");\n params.push(projectHash);\n }\n\n const whereSQL = whereClauses.length > 0 ? `WHERE ${whereClauses.join(\" AND \")}` : \"\";\n\n const sql = `\n SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\n ${joinClause}\n ${whereSQL}\n `;\n\n const rows = this.#db.db.prepare<unknown[], QueryMemoryRow>(sql).all(...params);\n\n const now = Date.now();\n\n const scored = rows\n .filter((row) => row.cosine_sim > 0)\n .map((row) => {\n const memory = rowToMemory(row, []);\n const score = this.#computeScore(memory, now);\n return { ...memory, score };\n });\n\n scored.sort((a, b) => b.score - a.score);\n\n const results = scored.slice(0, limit);\n\n for (const result of results) {\n this.#repo.incrementAccessCount(result.id);\n }\n\n return results;\n }\n\n #computeScore(memory: Memory, now: number): number {\n const typeWeight = TYPE_WEIGHTS[memory.type];\n const accessCountNorm = memory.accessCount / (memory.accessCount + 10);\n const daysSinceUpdate = (now - new Date(memory.updatedAt).getTime()) / 86400000;\n const recencyNorm = 1 / (1 + daysSinceUpdate);\n const pinned = memory.pinned ? 1.0 : 0.0;\n\n return typeWeight * 0.4 + accessCountNorm * 0.3 + recencyNorm * 0.2 + pinned * 0.1;\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nfunction sha256Truncated(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\").slice(0, 16);\n}\n\nexport async function resolveProject(): Promise<{ hash: string; name: string }> {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"remote\", \"get-url\", \"origin\"]);\n const url = stdout.trim();\n if (url) {\n const hash = sha256Truncated(url);\n // parse last path segment, strip .git suffix\n const name =\n url\n .split(\"/\")\n .pop()\n ?.replace(/\\.git$/, \"\") ?? hash.slice(0, 8);\n return { hash, name };\n }\n } catch {\n // fall through\n }\n\n const cwd = process.cwd();\n const hash = sha256Truncated(cwd);\n const name = cwd.split(/[/\\\\]/).filter(Boolean).pop() ?? hash.slice(0, 8);\n return { hash, name };\n}\n\nexport async function resolveScope(): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"remote\", \"get-url\", \"origin\"]);\n const url = stdout.trim();\n if (url) {\n return sha256Truncated(url);\n }\n } catch {\n // git not available, not a repo, or no remote — fall through to cwd fallback\n }\n\n return sha256Truncated(process.cwd());\n}\n","import type { DatabaseManager } from \"../db/manager.js\";\nimport type { MemoryRow } from \"../db/row-types.js\";\nimport { rowToMemory } from \"../db/row-types.js\";\nimport type { MemoryType, SessionContext } from \"../types.js\";\nimport { MEMORY_TYPE_VALUES } from \"../types.js\";\n\ninterface TypeCountRow {\n type: string;\n count: number;\n}\n\nexport function listMemoryTypes(): MemoryType[] {\n return [...MEMORY_TYPE_VALUES];\n}\n\nexport class SessionContextBuilder {\n readonly #db: DatabaseManager;\n\n constructor(db: DatabaseManager) {\n this.#db = db;\n }\n\n getSessionContext(projectHash: string): SessionContext {\n const pinnedGlobal = this.#db.db\n .prepare<[], MemoryRow>(\n `SELECT * FROM memories\n WHERE id NOT IN (SELECT memory_id FROM memory_projects)\n AND pinned = 1`\n )\n .all()\n .map((row) => rowToMemory(row, []));\n\n const pinnedProject = this.#db.db\n .prepare<[string], MemoryRow>(\n `SELECT m.* FROM memories m\n JOIN memory_projects mp ON mp.memory_id = m.id\n JOIN projects p ON p.id = mp.project_id\n WHERE p.scope_hash = ? AND m.pinned = 1`\n )\n .all(projectHash)\n .map((row) => rowToMemory(row, []));\n\n const typeCounts = this.#db.db\n .prepare<[], TypeCountRow>(\"SELECT type, COUNT(*) as count FROM memories GROUP BY type\")\n .all();\n\n const stats = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0])) as Record<\n MemoryType,\n number\n >;\n for (const row of typeCounts) {\n if (stats[row.type as MemoryType] !== undefined) {\n stats[row.type as MemoryType] = row.count;\n }\n }\n\n return { stats, pinnedGlobal, pinnedProject };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["MIGRATIONS","#db","#init","#initInMemory","#runMigrations","#db","#embedding","#projects","#db","#db","#embedding","#repo","#computeScore","#db"],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/schemas.ts","../src/db/row-types.ts","../src/embedding/service.ts","../src/memory/repository.ts","../src/scope/resolver.ts","../src/migrations/index.ts","../src/project/repository.ts","../src/query/engine.ts","../src/session/builder.ts"],"sourcesContent":["export class MembankError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"MembankError\";\n }\n}\n\nexport class DatabaseError extends MembankError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"DatabaseError\";\n }\n}\n","import { mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport BetterSqlite3 from \"better-sqlite3\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport { DatabaseError } from \"./errors.js\";\n\nconst DEFAULT_DB_PATH = join(homedir(), \".membank\", \"memory.db\");\n\ntype VecLoader = (db: BetterSqlite3.Database) => void;\n\nconst MIGRATIONS: [number, string][] = [\n [\n 1,\n `\nCREATE TABLE IF NOT EXISTS memories (\n id TEXT PRIMARY KEY,\n content TEXT NOT NULL,\n type TEXT NOT NULL,\n tags TEXT NOT NULL DEFAULT '[]',\n scope TEXT NOT NULL,\n source TEXT,\n access_count INTEGER NOT NULL DEFAULT 0,\n pinned INTEGER NOT NULL DEFAULT 0,\n needs_review INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n\nCREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(\n embedding FLOAT[384]\n);\n`,\n ],\n [\n 2,\n `\nCREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n scope_hash TEXT NOT NULL UNIQUE,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS memory_projects (\n memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n PRIMARY KEY (memory_id, project_id)\n);\n\nINSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at)\nSELECT\n lower(hex(randomblob(16))),\n 'project-' || substr(scope, 1, 8),\n scope,\n datetime('now'),\n datetime('now')\nFROM memories\nWHERE scope != 'global'\nGROUP BY scope;\n\nINSERT OR IGNORE INTO memory_projects (memory_id, project_id)\nSELECT m.id, p.id\nFROM memories m\nJOIN projects p ON p.scope_hash = m.scope\nWHERE m.scope != 'global';\n\nALTER TABLE memories DROP COLUMN scope;\n`,\n ],\n];\n\nexport class DatabaseManager {\n readonly #db: BetterSqlite3.Database;\n\n private constructor(db: BetterSqlite3.Database) {\n this.#db = db;\n }\n\n static open(dbPath?: string): DatabaseManager {\n const resolvedPath = dbPath ?? DEFAULT_DB_PATH;\n mkdirSync(dirname(resolvedPath), { recursive: true });\n const db = new BetterSqlite3(resolvedPath);\n return DatabaseManager.#init(db, sqliteVec.load);\n }\n\n static openInMemory(): DatabaseManager {\n return DatabaseManager.#initInMemory(sqliteVec.load);\n }\n\n /** For testing: inject a custom vec loader (e.g. a throwing stub). */\n static _openInMemoryWithLoader(loader: VecLoader): DatabaseManager {\n return DatabaseManager.#initInMemory(loader);\n }\n\n static #initInMemory(loader: VecLoader): DatabaseManager {\n const db = new BetterSqlite3(\":memory:\");\n return DatabaseManager.#init(db, loader);\n }\n\n static #init(db: BetterSqlite3.Database, loader: VecLoader): DatabaseManager {\n try {\n loader(db);\n } catch (err) {\n throw new DatabaseError(\"Failed to load sqlite-vec extension\", {\n cause: err,\n });\n }\n\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"foreign_keys = ON\");\n\n const manager = new DatabaseManager(db);\n manager.#runMigrations();\n return manager;\n }\n\n #runMigrations(): void {\n // Bootstrap the meta table before reading schema_version from it\n this.#db.exec(`\n CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\n\n const row = this.#db\n .prepare<[], { value: string }>(\"SELECT value FROM meta WHERE key = 'schema_version'\")\n .get();\n\n const currentVersion = row ? Number.parseInt(row.value, 10) : 0;\n\n for (const [targetVersion, sql] of MIGRATIONS) {\n if (currentVersion < targetVersion) {\n this.#db.exec(sql);\n this.#db\n .prepare(\"INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)\")\n .run(String(targetVersion));\n }\n }\n }\n\n get db(): BetterSqlite3.Database {\n return this.#db;\n }\n\n close(): void {\n this.#db.close();\n }\n}\n","import { z } from \"zod\";\n\nexport const MEMORY_TYPE_VALUES = [\n \"correction\",\n \"preference\",\n \"decision\",\n \"learning\",\n \"fact\",\n] as const;\n\nexport const MemoryTypeSchema = z.enum(MEMORY_TYPE_VALUES);\nexport type MemoryType = z.infer<typeof MemoryTypeSchema>;\n\nexport const TagsJsonSchema = z.array(z.string());\n\nexport const ProjectSchema = z.object({\n id: z.string(),\n name: z.string(),\n scopeHash: z.string(),\n createdAt: z.string(),\n updatedAt: z.string(),\n});\nexport type Project = z.infer<typeof ProjectSchema>;\n\nexport const MemorySchema = z.object({\n id: z.string(),\n content: z.string(),\n type: MemoryTypeSchema,\n tags: z.array(z.string()),\n projects: z.array(ProjectSchema),\n sourceHarness: z.string().nullable(),\n accessCount: z.number().int().nonnegative(),\n pinned: z.boolean(),\n needsReview: z.boolean(),\n createdAt: z.string(),\n updatedAt: z.string(),\n});\nexport type Memory = z.infer<typeof MemorySchema>;\n\nexport const QueryOptionsSchema = z.object({\n query: z.string().min(1),\n type: MemoryTypeSchema.optional(),\n projectHash: z.string().optional(),\n limit: z.number().int().positive().optional(),\n includePinned: z.boolean().optional(),\n});\nexport type QueryOptions = z.infer<typeof QueryOptionsSchema>;\n\nexport const SaveOptionsSchema = z.object({\n content: z.string().min(1),\n type: MemoryTypeSchema,\n tags: z.array(z.string()).optional(),\n projectScope: z.object({ hash: z.string(), name: z.string() }).optional(),\n sourceHarness: z.string().optional(),\n});\nexport type SaveOptions = z.infer<typeof SaveOptionsSchema>;\n\nexport const MemoryPatchSchema = z.object({\n content: z.string().min(1).optional(),\n tags: z.array(z.string()).optional(),\n});\nexport type MemoryPatch = z.infer<typeof MemoryPatchSchema>;\n\nexport const SessionContextSchema = z.object({\n stats: z.record(MemoryTypeSchema, z.number()),\n pinnedGlobal: z.array(MemorySchema),\n pinnedProject: z.array(MemorySchema),\n});\nexport type SessionContext = z.infer<typeof SessionContextSchema>;\n\nexport const MemoryRowSchema = z.object({\n id: z.string(),\n content: z.string(),\n type: z.string(),\n tags: z.string(),\n source: z.string().nullable(),\n access_count: z.number(),\n pinned: z.number(),\n needs_review: z.number(),\n created_at: z.string(),\n updated_at: z.string(),\n});\nexport type MemoryRow = z.infer<typeof MemoryRowSchema>;\n\nexport const ProjectRowSchema = z.object({\n id: z.string(),\n name: z.string(),\n scope_hash: z.string(),\n created_at: z.string(),\n updated_at: z.string(),\n});\nexport type ProjectRow = z.infer<typeof ProjectRowSchema>;\n","import { MemoryTypeSchema, TagsJsonSchema } from \"../schemas.js\";\nimport type { Memory, MemoryRow, Project, ProjectRow } from \"../types.js\";\n\nexport function rowToMemory(row: MemoryRow, projects: Project[]): Memory {\n return {\n id: row.id,\n content: row.content,\n type: MemoryTypeSchema.parse(row.type),\n tags: TagsJsonSchema.parse(JSON.parse(row.tags)),\n projects,\n sourceHarness: row.source,\n accessCount: row.access_count,\n pinned: row.pinned !== 0,\n needsReview: row.needs_review !== 0,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport function rowToProject(row: ProjectRow): Project {\n return {\n id: row.id,\n name: row.name,\n scopeHash: row.scope_hash,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"@huggingface/transformers\";\n\nexport type ProgressCallback = (progress: { status: string; progress?: number }) => void;\n\nexport class EmbeddingService {\n private readonly modelCachePath: string;\n private readonly onProgress: ProgressCallback | undefined;\n private pipelineInstance: Awaited<ReturnType<typeof pipeline>> | null = null;\n\n constructor(modelCachePath?: string, onProgress?: ProgressCallback) {\n this.modelCachePath = modelCachePath ?? join(homedir(), \".membank\", \"models\");\n this.onProgress = onProgress;\n }\n\n private async getPipeline(): Promise<Awaited<ReturnType<typeof pipeline>>> {\n if (this.pipelineInstance === null) {\n this.pipelineInstance = await pipeline(\"feature-extraction\", \"Xenova/bge-small-en-v1.5\", {\n cache_dir: this.modelCachePath,\n progress_callback: this.onProgress,\n });\n }\n return this.pipelineInstance;\n }\n\n async embed(text: string): Promise<Float32Array> {\n const pipe = await this.getPipeline();\n // Shape: [1, seq_len, 384]. Cast to any to bypass the non-unified pipeline union signature.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const output = await (\n pipe as (input: string, opts: Record<string, unknown>) => Promise<unknown>\n )(text, { pooling: \"mean\", normalize: true });\n\n // @huggingface/transformers Tensor has a .data property with the flat array\n const tensor = output as { data: Float32Array | number[] };\n const flat = tensor.data;\n\n return flat instanceof Float32Array ? flat : new Float32Array(flat);\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport type { DatabaseManager } from \"../db/manager.js\";\nimport { rowToMemory } from \"../db/row-types.js\";\nimport type { EmbeddingService } from \"../embedding/service.js\";\nimport type { ProjectRepository } from \"../project/repository.js\";\nimport {\n MEMORY_TYPE_VALUES,\n MemoryPatchSchema,\n MemoryRowSchema,\n MemoryTypeSchema,\n SaveOptionsSchema,\n} from \"../schemas.js\";\nimport type { Memory, MemoryPatch, MemoryRow, MemoryType, SaveOptions } from \"../types.js\";\n\ninterface SimilarityRow extends MemoryRow {\n rowid: number;\n similarity: number;\n}\n\nexport class MemoryRepository {\n readonly #db: DatabaseManager;\n readonly #embedding: EmbeddingService;\n readonly #projects: ProjectRepository;\n\n constructor(\n db: DatabaseManager,\n embeddingService: EmbeddingService,\n projects: ProjectRepository\n ) {\n this.#db = db;\n this.#embedding = embeddingService;\n this.#projects = projects;\n }\n\n async save(options: SaveOptions): Promise<Memory> {\n const {\n content,\n type,\n tags = [],\n projectScope,\n sourceHarness,\n } = SaveOptionsSchema.parse(options);\n\n const embedding = await this.#embedding.embed(content);\n const embeddingBlob = Buffer.from(embedding.buffer);\n\n // Dedup: find similar memory in same context\n let top: SimilarityRow | undefined;\n if (projectScope !== undefined) {\n top = this.#db.db\n .prepare<[Buffer, string, string], SimilarityRow>(\n `SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\n JOIN memory_projects mp ON mp.memory_id = m.id\n JOIN projects p ON p.id = mp.project_id\n WHERE m.type = ? AND p.scope_hash = ?\n ORDER BY similarity DESC LIMIT 1`\n )\n .get(embeddingBlob, type, projectScope.hash);\n } else {\n top = this.#db.db\n .prepare<[Buffer, string], SimilarityRow>(\n `SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\n WHERE m.type = ?\n AND m.id NOT IN (SELECT memory_id FROM memory_projects)\n ORDER BY similarity DESC LIMIT 1`\n )\n .get(embeddingBlob, type);\n }\n\n const now = new Date().toISOString();\n\n if (top !== undefined && top.similarity > 0.92) {\n this.#db.db\n .prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`)\n .run(content, now, top.id);\n this.#db.db\n .prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`)\n .run(embeddingBlob, top.rowid);\n\n const updated = MemoryRowSchema.parse(\n this.#db.db.prepare<[string], unknown>(`SELECT * FROM memories WHERE id = ?`).get(top.id)\n );\n\n const projectMap = this.#projects.getProjectsForMemories([top.id]);\n return rowToMemory(updated, projectMap.get(top.id) ?? []);\n }\n\n if (top !== undefined && top.similarity >= 0.75) {\n this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);\n }\n\n const id = randomUUID();\n this.#db.db\n .prepare(\n `INSERT INTO memories (id, content, type, tags, source, access_count, pinned, needs_review, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`\n )\n .run(id, content, type, JSON.stringify(tags), sourceHarness ?? null, now, now);\n\n this.#db.db\n .prepare(\n `INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`\n )\n .run(embeddingBlob, id);\n\n if (projectScope !== undefined) {\n const project = this.#projects.upsertByHash(projectScope.hash, projectScope.name);\n this.#projects.addAssociation(id, project.id);\n }\n\n const row = MemoryRowSchema.parse(\n this.#db.db.prepare<[string], unknown>(`SELECT * FROM memories WHERE id = ?`).get(id)\n );\n\n const projectMap = this.#projects.getProjectsForMemories([id]);\n return rowToMemory(row, projectMap.get(id) ?? []);\n }\n\n async update(id: string, patch: MemoryPatch): Promise<Memory> {\n const { content, tags } = MemoryPatchSchema.parse(patch);\n\n const existing = this.#db.db\n .prepare<[string], MemoryRow & { rowid: number }>(\n `SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`\n )\n .get(id);\n\n if (existing === undefined) {\n throw new Error(`Memory not found: ${id}`);\n }\n\n const now = new Date().toISOString();\n const sets: string[] = [\"updated_at = ?\"];\n const values: string[] = [now];\n\n if (content !== undefined) {\n sets.push(\"content = ?\");\n values.push(content);\n }\n\n if (tags !== undefined) {\n sets.push(\"tags = ?\");\n values.push(JSON.stringify(tags));\n }\n\n values.push(id);\n this.#db.db.prepare(`UPDATE memories SET ${sets.join(\", \")} WHERE id = ?`).run(...values);\n\n if (content !== undefined) {\n const embedding = await this.#embedding.embed(content);\n const embeddingBlob = Buffer.from(embedding.buffer);\n this.#db.db\n .prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`)\n .run(embeddingBlob, existing.rowid);\n }\n\n const updated = MemoryRowSchema.parse(\n this.#db.db.prepare<[string], unknown>(`SELECT * FROM memories WHERE id = ?`).get(id)\n );\n\n const projectMap = this.#projects.getProjectsForMemories([id]);\n return rowToMemory(updated, projectMap.get(id) ?? []);\n }\n\n delete(id: string): Promise<void> {\n const row = this.#db.db\n .prepare<[string], { rowid: number }>(`SELECT rowid FROM memories WHERE id = ?`)\n .get(id);\n\n if (row !== undefined) {\n this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);\n }\n\n this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ?`).run(id);\n this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);\n\n return Promise.resolve();\n }\n\n list(opts?: { type?: MemoryType; pinned?: boolean }): Memory[] {\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (opts?.type !== undefined) {\n conditions.push(\"type = ?\");\n params.push(opts.type);\n }\n\n if (opts?.pinned === true) {\n conditions.push(\"pinned = 1\");\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n const rows = this.#db.db\n .prepare<(string | number)[], MemoryRow>(\n `SELECT * FROM memories ${where} ORDER BY created_at DESC`\n )\n .all(...params);\n\n if (rows.length === 0) return [];\n\n const ids = rows.map((r) => r.id);\n const projectMap = this.#projects.getProjectsForMemories(ids);\n return rows.map((row) => rowToMemory(row, projectMap.get(row.id) ?? []));\n }\n\n stats(): { byType: Record<MemoryType, number>; total: number; needsReview: number } {\n const byType = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0])) as Record<\n MemoryType,\n number\n >;\n\n const typeRows = this.#db.db\n .prepare<[], { type: string; count: number }>(\n `SELECT type, COUNT(*) as count FROM memories GROUP BY type`\n )\n .all();\n\n for (const row of typeRows) {\n const parsed = MemoryTypeSchema.safeParse(row.type);\n if (parsed.success) {\n byType[parsed.data] = row.count;\n }\n }\n\n const totals = this.#db.db\n .prepare<[], { total: number; needsReview: number }>(\n `SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`\n )\n .get() ?? { total: 0, needsReview: 0 };\n\n return {\n byType,\n total: totals.total,\n needsReview: totals.needsReview ?? 0,\n };\n }\n\n setPin(id: string, pinned: boolean): Memory {\n const existing = this.#db.db\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\n .get(id);\n\n if (existing === undefined) {\n throw new Error(`Memory not found: ${id}`);\n }\n\n const now = new Date().toISOString();\n this.#db.db\n .prepare(`UPDATE memories SET pinned = ?, updated_at = ? WHERE id = ?`)\n .run(pinned ? 1 : 0, now, id);\n\n const updated = MemoryRowSchema.parse(\n this.#db.db.prepare<[string], unknown>(`SELECT * FROM memories WHERE id = ?`).get(id)\n );\n\n const projectMap = this.#projects.getProjectsForMemories([id]);\n return rowToMemory(updated, projectMap.get(id) ?? []);\n }\n\n incrementAccessCount(id: string): void {\n this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nfunction sha256Truncated(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\").slice(0, 16);\n}\n\nexport async function resolveProject(): Promise<{ hash: string; name: string }> {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"remote\", \"get-url\", \"origin\"]);\n const url = stdout.trim();\n if (url) {\n const hash = sha256Truncated(url);\n // parse last path segment, strip .git suffix\n const name =\n url\n .split(\"/\")\n .pop()\n ?.replace(/\\.git$/, \"\") ?? hash.slice(0, 8);\n return { hash, name };\n }\n } catch {\n // fall through\n }\n\n const cwd = process.cwd();\n const hash = sha256Truncated(cwd);\n const name = cwd.split(/[/\\\\]/).filter(Boolean).pop() ?? hash.slice(0, 8);\n return { hash, name };\n}\n\nexport async function resolveScope(): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"remote\", \"get-url\", \"origin\"]);\n const url = stdout.trim();\n if (url) {\n return sha256Truncated(url);\n }\n } catch {\n // git not available, not a repo, or no remote — fall through to cwd fallback\n }\n\n return sha256Truncated(process.cwd());\n}\n","import type { ProjectRepository } from \"../project/index.js\";\nimport { resolveProject } from \"../scope/index.js\";\n\nexport interface MigrationMeta {\n name: string;\n description: string;\n}\n\nexport interface ScopeToProjectsResult {\n migration: \"scope-to-projects\";\n oldName: string;\n newName: string;\n memoryCount: number;\n}\n\nexport const MIGRATIONS: MigrationMeta[] = [\n {\n name: \"scope-to-projects\",\n description:\n \"Rename the auto-migrated project for the current directory from its generic hash-derived name to the resolved repo/directory name.\",\n },\n];\n\nexport async function runScopeToProjectsMigration(\n projects: ProjectRepository\n): Promise<ScopeToProjectsResult | null> {\n const resolved = await resolveProject();\n const project = projects.getByHash(resolved.hash);\n\n if (project === undefined) {\n return null;\n }\n\n const oldName = project.name;\n const memoryCount = projects.countMemories(project.id);\n projects.rename(project.id, resolved.name);\n\n return {\n migration: \"scope-to-projects\",\n oldName,\n newName: resolved.name,\n memoryCount,\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport type { DatabaseManager } from \"../db/manager.js\";\nimport { rowToProject } from \"../db/row-types.js\";\nimport { ProjectRowSchema } from \"../schemas.js\";\nimport type { Project, ProjectRow } from \"../types.js\";\n\ninterface ProjectMemoryRow extends ProjectRow {\n memory_id: string;\n}\n\nexport class ProjectRepository {\n readonly #db: DatabaseManager;\n\n constructor(db: DatabaseManager) {\n this.#db = db;\n }\n\n upsertByHash(hash: string, name: string): Project {\n const now = new Date().toISOString();\n const id = randomUUID();\n this.#db.db\n .prepare(\n `INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`\n )\n .run(id, name, hash, now, now);\n\n const row = ProjectRowSchema.parse(\n this.#db.db\n .prepare<[string], unknown>(`SELECT * FROM projects WHERE scope_hash = ?`)\n .get(hash)\n );\n\n return rowToProject(row);\n }\n\n rename(id: string, name: string): Project {\n const now = new Date().toISOString();\n this.#db.db\n .prepare(`UPDATE projects SET name = ?, updated_at = ? WHERE id = ?`)\n .run(name, now, id);\n\n const row = this.#db.db\n .prepare<[string], ProjectRow>(`SELECT * FROM projects WHERE id = ?`)\n .get(id);\n\n if (row === undefined) throw new Error(`Project not found: ${id}`);\n return rowToProject(row);\n }\n\n list(): Project[] {\n return this.#db.db\n .prepare<[], ProjectRow>(`SELECT * FROM projects ORDER BY name ASC`)\n .all()\n .map(rowToProject);\n }\n\n getByHash(hash: string): Project | undefined {\n const row = this.#db.db\n .prepare<[string], ProjectRow>(`SELECT * FROM projects WHERE scope_hash = ?`)\n .get(hash);\n return row !== undefined ? rowToProject(row) : undefined;\n }\n\n addAssociation(memoryId: string, projectId: string): void {\n this.#db.db\n .prepare(`INSERT OR IGNORE INTO memory_projects (memory_id, project_id) VALUES (?, ?)`)\n .run(memoryId, projectId);\n }\n\n removeAssociation(memoryId: string, projectId: string): void {\n this.#db.db\n .prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`)\n .run(memoryId, projectId);\n }\n\n countMemories(projectId: string): number {\n const row = this.#db.db\n .prepare<[string], { count: number }>(\n `SELECT COUNT(*) AS count FROM memory_projects WHERE project_id = ?`\n )\n .get(projectId);\n return row?.count ?? 0;\n }\n\n getProjectsForMemories(memoryIds: string[]): Map<string, Project[]> {\n if (memoryIds.length === 0) return new Map();\n const placeholders = memoryIds.map(() => \"?\").join(\",\");\n const rows = this.#db.db\n .prepare<string[], ProjectMemoryRow>(\n `SELECT p.*, mp.memory_id FROM projects p\n JOIN memory_projects mp ON mp.project_id = p.id\n WHERE mp.memory_id IN (${placeholders})`\n )\n .all(...memoryIds);\n\n const result = new Map<string, Project[]>();\n for (const row of rows) {\n const list = result.get(row.memory_id) ?? [];\n list.push(rowToProject(row));\n result.set(row.memory_id, list);\n }\n return result;\n }\n}\n","import type { DatabaseManager } from \"../db/manager.js\";\nimport { rowToMemory } from \"../db/row-types.js\";\nimport type { EmbeddingService } from \"../embedding/service.js\";\nimport type { MemoryRepository } from \"../memory/repository.js\";\nimport { QueryOptionsSchema } from \"../schemas.js\";\nimport type { Memory, MemoryRow, MemoryType, QueryOptions } from \"../types.js\";\n\ninterface QueryMemoryRow extends MemoryRow {\n cosine_sim: number;\n}\n\nconst TYPE_WEIGHTS = {\n correction: 1.0,\n preference: 0.8,\n decision: 0.6,\n learning: 0.4,\n fact: 0.2,\n} satisfies Record<MemoryType, number>;\n\nexport class QueryEngine {\n readonly #db: DatabaseManager;\n readonly #embedding: EmbeddingService;\n readonly #repo: MemoryRepository;\n\n constructor(db: DatabaseManager, embeddingService: EmbeddingService, repo: MemoryRepository) {\n this.#db = db;\n this.#embedding = embeddingService;\n this.#repo = repo;\n }\n\n async query(options: QueryOptions): Promise<Array<Memory & { score: number }>> {\n const {\n query,\n type,\n projectHash,\n limit = 10,\n includePinned,\n } = QueryOptionsSchema.parse(options);\n\n const queryEmbedding = await this.#embedding.embed(query);\n const queryBlob = Buffer.from(queryEmbedding.buffer);\n\n const whereClauses: string[] = [];\n const params: unknown[] = [queryBlob];\n let joinClause = \"\";\n\n if (!includePinned) {\n whereClauses.push(\"m.pinned = 0\");\n }\n\n if (type !== undefined) {\n whereClauses.push(\"m.type = ?\");\n params.push(type);\n }\n\n if (projectHash !== undefined) {\n joinClause =\n \"LEFT JOIN memory_projects mp ON mp.memory_id = m.id LEFT JOIN projects p ON p.id = mp.project_id\";\n whereClauses.push(\"p.scope_hash = ?\");\n params.push(projectHash);\n }\n\n const whereSQL = whereClauses.length > 0 ? `WHERE ${whereClauses.join(\" AND \")}` : \"\";\n\n const sql = `\n SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\n ${joinClause}\n ${whereSQL}\n `;\n\n const rows = this.#db.db.prepare<unknown[], QueryMemoryRow>(sql).all(...params);\n\n const now = Date.now();\n\n const scored = rows\n .filter((row) => row.cosine_sim > 0)\n .map((row) => {\n const memory = rowToMemory(row, []);\n const score = this.#computeScore(memory, now);\n return { ...memory, score };\n });\n\n scored.sort((a, b) => b.score - a.score);\n\n const results = scored.slice(0, limit);\n\n for (const result of results) {\n this.#repo.incrementAccessCount(result.id);\n }\n\n return results;\n }\n\n #computeScore(memory: Memory, now: number): number {\n const typeWeight = TYPE_WEIGHTS[memory.type];\n const accessCountNorm = memory.accessCount / (memory.accessCount + 10);\n const daysSinceUpdate = (now - new Date(memory.updatedAt).getTime()) / 86400000;\n const recencyNorm = 1 / (1 + daysSinceUpdate);\n const pinned = memory.pinned ? 1.0 : 0.0;\n\n return typeWeight * 0.4 + accessCountNorm * 0.3 + recencyNorm * 0.2 + pinned * 0.1;\n }\n}\n","import type { DatabaseManager } from \"../db/manager.js\";\nimport { rowToMemory } from \"../db/row-types.js\";\nimport { MEMORY_TYPE_VALUES, MemoryTypeSchema } from \"../schemas.js\";\nimport type { MemoryRow, MemoryType, SessionContext } from \"../types.js\";\n\ninterface TypeCountRow {\n type: string;\n count: number;\n}\n\nexport function listMemoryTypes(): MemoryType[] {\n return [...MEMORY_TYPE_VALUES];\n}\n\nexport class SessionContextBuilder {\n readonly #db: DatabaseManager;\n\n constructor(db: DatabaseManager) {\n this.#db = db;\n }\n\n getSessionContext(projectHash: string): SessionContext {\n const pinnedGlobal = this.#db.db\n .prepare<[], MemoryRow>(\n `SELECT * FROM memories\n WHERE id NOT IN (SELECT memory_id FROM memory_projects)\n AND pinned = 1`\n )\n .all()\n .map((row) => rowToMemory(row, []));\n\n const pinnedProject = this.#db.db\n .prepare<[string], MemoryRow>(\n `SELECT m.* FROM memories m\n JOIN memory_projects mp ON mp.memory_id = m.id\n JOIN projects p ON p.id = mp.project_id\n WHERE p.scope_hash = ? AND m.pinned = 1`\n )\n .all(projectHash)\n .map((row) => rowToMemory(row, []));\n\n const typeCounts = this.#db.db\n .prepare<[], TypeCountRow>(\"SELECT type, COUNT(*) as count FROM memories GROUP BY type\")\n .all();\n\n const stats = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0])) as Record<\n MemoryType,\n number\n >;\n for (const row of typeCounts) {\n const parsed = MemoryTypeSchema.safeParse(row.type);\n if (parsed.success) {\n stats[parsed.data] = row.count;\n }\n }\n\n return { stats, pinnedGlobal, pinnedProject };\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@membank/core",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,8 @@
20
20
  "dependencies": {
21
21
  "@huggingface/transformers": "^4.2.0",
22
22
  "better-sqlite3": "^12.9.0",
23
- "sqlite-vec": "^0.1.9"
23
+ "sqlite-vec": "^0.1.9",
24
+ "zod": "^4.4.3"
24
25
  },
25
26
  "devDependencies": {
26
27
  "@types/better-sqlite3": "^7.6.13",