@membank/core 0.5.0 → 0.6.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.cjs +173 -23
- package/dist/index.d.cts +32 -8
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +32 -8
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +172 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -66,6 +66,39 @@ CREATE TABLE IF NOT EXISTS memories (
|
|
|
66
66
|
CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(
|
|
67
67
|
embedding FLOAT[384]
|
|
68
68
|
);
|
|
69
|
+
`], [2, `
|
|
70
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
71
|
+
id TEXT PRIMARY KEY,
|
|
72
|
+
name TEXT NOT NULL,
|
|
73
|
+
scope_hash TEXT NOT NULL UNIQUE,
|
|
74
|
+
created_at TEXT NOT NULL,
|
|
75
|
+
updated_at TEXT NOT NULL
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
CREATE TABLE IF NOT EXISTS memory_projects (
|
|
79
|
+
memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
80
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
81
|
+
PRIMARY KEY (memory_id, project_id)
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at)
|
|
85
|
+
SELECT
|
|
86
|
+
lower(hex(randomblob(16))),
|
|
87
|
+
'project-' || substr(scope, 1, 8),
|
|
88
|
+
scope,
|
|
89
|
+
datetime('now'),
|
|
90
|
+
datetime('now')
|
|
91
|
+
FROM memories
|
|
92
|
+
WHERE scope != 'global'
|
|
93
|
+
GROUP BY scope;
|
|
94
|
+
|
|
95
|
+
INSERT OR IGNORE INTO memory_projects (memory_id, project_id)
|
|
96
|
+
SELECT m.id, p.id
|
|
97
|
+
FROM memories m
|
|
98
|
+
JOIN projects p ON p.scope_hash = m.scope
|
|
99
|
+
WHERE m.scope != 'global';
|
|
100
|
+
|
|
101
|
+
ALTER TABLE memories DROP COLUMN scope;
|
|
69
102
|
`]];
|
|
70
103
|
var DatabaseManager = class DatabaseManager {
|
|
71
104
|
#db;
|
|
@@ -96,6 +129,7 @@ var DatabaseManager = class DatabaseManager {
|
|
|
96
129
|
throw new DatabaseError("Failed to load sqlite-vec extension", { cause: err });
|
|
97
130
|
}
|
|
98
131
|
db.pragma("journal_mode = WAL");
|
|
132
|
+
db.pragma("foreign_keys = ON");
|
|
99
133
|
const manager = new DatabaseManager(db);
|
|
100
134
|
manager.#runMigrations();
|
|
101
135
|
return manager;
|
|
@@ -123,13 +157,13 @@ var DatabaseManager = class DatabaseManager {
|
|
|
123
157
|
};
|
|
124
158
|
//#endregion
|
|
125
159
|
//#region src/db/row-types.ts
|
|
126
|
-
function rowToMemory(row) {
|
|
160
|
+
function rowToMemory(row, projects) {
|
|
127
161
|
return {
|
|
128
162
|
id: row.id,
|
|
129
163
|
content: row.content,
|
|
130
164
|
type: row.type,
|
|
131
165
|
tags: JSON.parse(row.tags),
|
|
132
|
-
|
|
166
|
+
projects,
|
|
133
167
|
sourceHarness: row.source,
|
|
134
168
|
accessCount: row.access_count,
|
|
135
169
|
pinned: row.pinned !== 0,
|
|
@@ -138,6 +172,15 @@ function rowToMemory(row) {
|
|
|
138
172
|
updatedAt: row.updated_at
|
|
139
173
|
};
|
|
140
174
|
}
|
|
175
|
+
function rowToProject(row) {
|
|
176
|
+
return {
|
|
177
|
+
id: row.id,
|
|
178
|
+
name: row.name,
|
|
179
|
+
scopeHash: row.scope_hash,
|
|
180
|
+
createdAt: row.created_at,
|
|
181
|
+
updatedAt: row.updated_at
|
|
182
|
+
};
|
|
183
|
+
}
|
|
141
184
|
//#endregion
|
|
142
185
|
//#region src/embedding/service.ts
|
|
143
186
|
var EmbeddingService = class {
|
|
@@ -177,30 +220,44 @@ const MEMORY_TYPE_VALUES = [
|
|
|
177
220
|
var MemoryRepository = class {
|
|
178
221
|
#db;
|
|
179
222
|
#embedding;
|
|
180
|
-
|
|
223
|
+
#projects;
|
|
224
|
+
constructor(db, embeddingService, projects) {
|
|
181
225
|
this.#db = db;
|
|
182
226
|
this.#embedding = embeddingService;
|
|
227
|
+
this.#projects = projects;
|
|
183
228
|
}
|
|
184
229
|
async save(options) {
|
|
185
|
-
const { content, type, tags = [],
|
|
230
|
+
const { content, type, tags = [], projectHash, sourceHarness } = options;
|
|
186
231
|
const embedding = await this.#embedding.embed(content);
|
|
187
232
|
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
233
|
+
let top;
|
|
234
|
+
if (projectHash !== void 0) top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
|
|
235
|
+
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
236
|
+
JOIN memory_projects mp ON mp.memory_id = m.id
|
|
237
|
+
JOIN projects p ON p.id = mp.project_id
|
|
238
|
+
WHERE m.type = ? AND p.scope_hash = ?
|
|
239
|
+
ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, projectHash);
|
|
240
|
+
else top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
|
|
241
|
+
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
242
|
+
WHERE m.type = ?
|
|
243
|
+
AND m.id NOT IN (SELECT memory_id FROM memory_projects)
|
|
244
|
+
ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type);
|
|
192
245
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
193
246
|
if (top !== void 0 && top.similarity > .92) {
|
|
194
247
|
this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
|
|
195
248
|
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
|
|
196
|
-
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id));
|
|
249
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id), this.#projects.getProjectsForMemories([top.id]).get(top.id) ?? []);
|
|
197
250
|
}
|
|
198
251
|
if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
|
|
199
252
|
const id = (0, node_crypto.randomUUID)();
|
|
200
|
-
this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags,
|
|
201
|
-
VALUES (?, ?, ?, ?, ?,
|
|
253
|
+
this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, source, access_count, pinned, needs_review, created_at, updated_at)
|
|
254
|
+
VALUES (?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), sourceHarness ?? null, now, now);
|
|
202
255
|
this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
|
|
203
|
-
|
|
256
|
+
if (projectHash !== void 0) {
|
|
257
|
+
const project = this.#projects.upsertByHash(projectHash, `project-${projectHash.slice(0, 8)}`);
|
|
258
|
+
this.#projects.addAssociation(id, project.id);
|
|
259
|
+
}
|
|
260
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
|
|
204
261
|
}
|
|
205
262
|
async update(id, patch) {
|
|
206
263
|
const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
|
|
@@ -223,11 +280,12 @@ var MemoryRepository = class {
|
|
|
223
280
|
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
224
281
|
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
|
|
225
282
|
}
|
|
226
|
-
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id));
|
|
283
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
|
|
227
284
|
}
|
|
228
285
|
delete(id) {
|
|
229
286
|
const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
|
|
230
287
|
if (row !== void 0) this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);
|
|
288
|
+
this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ?`).run(id);
|
|
231
289
|
this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);
|
|
232
290
|
return Promise.resolve();
|
|
233
291
|
}
|
|
@@ -240,7 +298,11 @@ var MemoryRepository = class {
|
|
|
240
298
|
}
|
|
241
299
|
if (opts?.pinned === true) conditions.push("pinned = 1");
|
|
242
300
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
243
|
-
|
|
301
|
+
const rows = this.#db.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC`).all(...params);
|
|
302
|
+
if (rows.length === 0) return [];
|
|
303
|
+
const ids = rows.map((r) => r.id);
|
|
304
|
+
const projectMap = this.#projects.getProjectsForMemories(ids);
|
|
305
|
+
return rows.map((row) => rowToMemory(row, projectMap.get(row.id) ?? []));
|
|
244
306
|
}
|
|
245
307
|
stats() {
|
|
246
308
|
const byType = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
|
|
@@ -256,11 +318,65 @@ var MemoryRepository = class {
|
|
|
256
318
|
needsReview: totals.needsReview ?? 0
|
|
257
319
|
};
|
|
258
320
|
}
|
|
321
|
+
setPin(id, pinned) {
|
|
322
|
+
if (this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id) === void 0) throw new Error(`Memory not found: ${id}`);
|
|
323
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
324
|
+
this.#db.db.prepare(`UPDATE memories SET pinned = ?, updated_at = ? WHERE id = ?`).run(pinned ? 1 : 0, now, id);
|
|
325
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
|
|
326
|
+
}
|
|
259
327
|
incrementAccessCount(id) {
|
|
260
328
|
this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
|
|
261
329
|
}
|
|
262
330
|
};
|
|
263
331
|
//#endregion
|
|
332
|
+
//#region src/project/repository.ts
|
|
333
|
+
var ProjectRepository = class {
|
|
334
|
+
#db;
|
|
335
|
+
constructor(db) {
|
|
336
|
+
this.#db = db;
|
|
337
|
+
}
|
|
338
|
+
upsertByHash(hash, name) {
|
|
339
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
340
|
+
const id = (0, node_crypto.randomUUID)();
|
|
341
|
+
this.#db.db.prepare(`INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`).run(id, name, hash, now, now);
|
|
342
|
+
return rowToProject(this.#db.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(hash));
|
|
343
|
+
}
|
|
344
|
+
rename(id, name) {
|
|
345
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
346
|
+
this.#db.db.prepare(`UPDATE projects SET name = ?, updated_at = ? WHERE id = ?`).run(name, now, id);
|
|
347
|
+
const row = this.#db.db.prepare(`SELECT * FROM projects WHERE id = ?`).get(id);
|
|
348
|
+
if (row === void 0) throw new Error(`Project not found: ${id}`);
|
|
349
|
+
return rowToProject(row);
|
|
350
|
+
}
|
|
351
|
+
list() {
|
|
352
|
+
return this.#db.db.prepare(`SELECT * FROM projects ORDER BY name ASC`).all().map(rowToProject);
|
|
353
|
+
}
|
|
354
|
+
getByHash(hash) {
|
|
355
|
+
const row = this.#db.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(hash);
|
|
356
|
+
return row !== void 0 ? rowToProject(row) : void 0;
|
|
357
|
+
}
|
|
358
|
+
addAssociation(memoryId, projectId) {
|
|
359
|
+
this.#db.db.prepare(`INSERT OR IGNORE INTO memory_projects (memory_id, project_id) VALUES (?, ?)`).run(memoryId, projectId);
|
|
360
|
+
}
|
|
361
|
+
removeAssociation(memoryId, projectId) {
|
|
362
|
+
this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`).run(memoryId, projectId);
|
|
363
|
+
}
|
|
364
|
+
getProjectsForMemories(memoryIds) {
|
|
365
|
+
if (memoryIds.length === 0) return /* @__PURE__ */ new Map();
|
|
366
|
+
const placeholders = memoryIds.map(() => "?").join(",");
|
|
367
|
+
const rows = this.#db.db.prepare(`SELECT p.*, mp.memory_id FROM projects p
|
|
368
|
+
JOIN memory_projects mp ON mp.project_id = p.id
|
|
369
|
+
WHERE mp.memory_id IN (${placeholders})`).all(...memoryIds);
|
|
370
|
+
const result = /* @__PURE__ */ new Map();
|
|
371
|
+
for (const row of rows) {
|
|
372
|
+
const list = result.get(row.memory_id) ?? [];
|
|
373
|
+
list.push(rowToProject(row));
|
|
374
|
+
result.set(row.memory_id, list);
|
|
375
|
+
}
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
//#endregion
|
|
264
380
|
//#region src/query/engine.ts
|
|
265
381
|
const TYPE_WEIGHTS = {
|
|
266
382
|
correction: 1,
|
|
@@ -279,28 +395,32 @@ var QueryEngine = class {
|
|
|
279
395
|
this.#repo = repo;
|
|
280
396
|
}
|
|
281
397
|
async query(options) {
|
|
282
|
-
const { query, type,
|
|
398
|
+
const { query, type, projectHash, limit = 10 } = options;
|
|
283
399
|
const queryEmbedding = await this.#embedding.embed(query);
|
|
284
400
|
const queryBlob = Buffer.from(queryEmbedding.buffer);
|
|
285
401
|
const whereClauses = [];
|
|
286
402
|
const params = [queryBlob];
|
|
403
|
+
let joinClause = "";
|
|
287
404
|
if (type !== void 0) {
|
|
288
405
|
whereClauses.push("m.type = ?");
|
|
289
406
|
params.push(type);
|
|
290
407
|
}
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
408
|
+
if (projectHash !== void 0) {
|
|
409
|
+
joinClause = "LEFT JOIN memory_projects mp ON mp.memory_id = m.id LEFT JOIN projects p ON p.id = mp.project_id";
|
|
410
|
+
whereClauses.push("p.scope_hash = ?");
|
|
411
|
+
params.push(projectHash);
|
|
294
412
|
}
|
|
413
|
+
const whereSQL = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
295
414
|
const sql = `
|
|
296
415
|
SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim
|
|
297
416
|
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
298
|
-
${
|
|
417
|
+
${joinClause}
|
|
418
|
+
${whereSQL}
|
|
299
419
|
`;
|
|
300
420
|
const rows = this.#db.db.prepare(sql).all(...params);
|
|
301
421
|
const now = Date.now();
|
|
302
422
|
const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
|
|
303
|
-
const memory = rowToMemory(row);
|
|
423
|
+
const memory = rowToMemory(row, []);
|
|
304
424
|
const score = this.#computeScore(memory, now);
|
|
305
425
|
return {
|
|
306
426
|
...memory,
|
|
@@ -326,6 +446,29 @@ const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
|
326
446
|
function sha256Truncated(input) {
|
|
327
447
|
return (0, node_crypto.createHash)("sha256").update(input).digest("hex").slice(0, 16);
|
|
328
448
|
}
|
|
449
|
+
async function resolveProject() {
|
|
450
|
+
try {
|
|
451
|
+
const { stdout } = await execFileAsync("git", [
|
|
452
|
+
"remote",
|
|
453
|
+
"get-url",
|
|
454
|
+
"origin"
|
|
455
|
+
]);
|
|
456
|
+
const url = stdout.trim();
|
|
457
|
+
if (url) {
|
|
458
|
+
const hash = sha256Truncated(url);
|
|
459
|
+
return {
|
|
460
|
+
hash,
|
|
461
|
+
name: url.split("/").pop()?.replace(/\.git$/, "") ?? hash.slice(0, 8)
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
} catch {}
|
|
465
|
+
const cwd = process.cwd();
|
|
466
|
+
const hash = sha256Truncated(cwd);
|
|
467
|
+
return {
|
|
468
|
+
hash,
|
|
469
|
+
name: cwd.split(/[/\\]/).filter(Boolean).pop() ?? hash.slice(0, 8)
|
|
470
|
+
};
|
|
471
|
+
}
|
|
329
472
|
async function resolveScope() {
|
|
330
473
|
try {
|
|
331
474
|
const { stdout } = await execFileAsync("git", [
|
|
@@ -348,9 +491,14 @@ var SessionContextBuilder = class {
|
|
|
348
491
|
constructor(db) {
|
|
349
492
|
this.#db = db;
|
|
350
493
|
}
|
|
351
|
-
getSessionContext(
|
|
352
|
-
const pinnedGlobal = this.#db.db.prepare(
|
|
353
|
-
|
|
494
|
+
getSessionContext(projectHash) {
|
|
495
|
+
const pinnedGlobal = this.#db.db.prepare(`SELECT * FROM memories
|
|
496
|
+
WHERE id NOT IN (SELECT memory_id FROM memory_projects)
|
|
497
|
+
AND pinned = 1`).all().map((row) => rowToMemory(row, []));
|
|
498
|
+
const pinnedProject = this.#db.db.prepare(`SELECT m.* FROM memories m
|
|
499
|
+
JOIN memory_projects mp ON mp.memory_id = m.id
|
|
500
|
+
JOIN projects p ON p.id = mp.project_id
|
|
501
|
+
WHERE p.scope_hash = ? AND m.pinned = 1`).all(projectHash).map((row) => rowToMemory(row, []));
|
|
354
502
|
const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
|
|
355
503
|
const stats = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
|
|
356
504
|
for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
|
|
@@ -368,8 +516,10 @@ exports.EmbeddingService = EmbeddingService;
|
|
|
368
516
|
exports.MEMORY_TYPE_VALUES = MEMORY_TYPE_VALUES;
|
|
369
517
|
exports.MembankError = MembankError;
|
|
370
518
|
exports.MemoryRepository = MemoryRepository;
|
|
519
|
+
exports.ProjectRepository = ProjectRepository;
|
|
371
520
|
exports.QueryEngine = QueryEngine;
|
|
372
521
|
exports.SessionContextBuilder = SessionContextBuilder;
|
|
373
522
|
exports.listMemoryTypes = listMemoryTypes;
|
|
523
|
+
exports.resolveProject = resolveProject;
|
|
374
524
|
exports.resolveScope = resolveScope;
|
|
375
525
|
exports.rowToMemory = rowToMemory;
|
package/dist/index.d.cts
CHANGED
|
@@ -24,12 +24,19 @@ declare class DatabaseManager {
|
|
|
24
24
|
//#region src/types.d.ts
|
|
25
25
|
type MemoryType = "correction" | "preference" | "decision" | "learning" | "fact";
|
|
26
26
|
declare const MEMORY_TYPE_VALUES: readonly ["correction", "preference", "decision", "learning", "fact"];
|
|
27
|
+
interface Project {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
scopeHash: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
}
|
|
27
34
|
interface Memory {
|
|
28
35
|
id: string;
|
|
29
36
|
content: string;
|
|
30
37
|
type: MemoryType;
|
|
31
38
|
tags: string[];
|
|
32
|
-
|
|
39
|
+
projects: Project[];
|
|
33
40
|
sourceHarness: string | null;
|
|
34
41
|
accessCount: number;
|
|
35
42
|
pinned: boolean;
|
|
@@ -40,14 +47,14 @@ interface Memory {
|
|
|
40
47
|
interface QueryOptions {
|
|
41
48
|
query: string;
|
|
42
49
|
type?: MemoryType;
|
|
43
|
-
|
|
50
|
+
projectHash?: string;
|
|
44
51
|
limit?: number;
|
|
45
52
|
}
|
|
46
53
|
interface SaveOptions {
|
|
47
54
|
content: string;
|
|
48
55
|
type: MemoryType;
|
|
49
56
|
tags?: string[];
|
|
50
|
-
|
|
57
|
+
projectHash?: string;
|
|
51
58
|
sourceHarness?: string;
|
|
52
59
|
}
|
|
53
60
|
interface SessionContext {
|
|
@@ -62,7 +69,6 @@ interface MemoryRow {
|
|
|
62
69
|
content: string;
|
|
63
70
|
type: string;
|
|
64
71
|
tags: string;
|
|
65
|
-
scope: string;
|
|
66
72
|
source: string | null;
|
|
67
73
|
access_count: number;
|
|
68
74
|
pinned: number;
|
|
@@ -70,7 +76,7 @@ interface MemoryRow {
|
|
|
70
76
|
created_at: string;
|
|
71
77
|
updated_at: string;
|
|
72
78
|
}
|
|
73
|
-
declare function rowToMemory(row: MemoryRow): Memory;
|
|
79
|
+
declare function rowToMemory(row: MemoryRow, projects: Project[]): Memory;
|
|
74
80
|
//#endregion
|
|
75
81
|
//#region src/embedding/service.d.ts
|
|
76
82
|
type ProgressCallback = (progress: {
|
|
@@ -86,10 +92,23 @@ declare class EmbeddingService {
|
|
|
86
92
|
embed(text: string): Promise<Float32Array>;
|
|
87
93
|
}
|
|
88
94
|
//#endregion
|
|
95
|
+
//#region src/project/repository.d.ts
|
|
96
|
+
declare class ProjectRepository {
|
|
97
|
+
#private;
|
|
98
|
+
constructor(db: DatabaseManager);
|
|
99
|
+
upsertByHash(hash: string, name: string): Project;
|
|
100
|
+
rename(id: string, name: string): Project;
|
|
101
|
+
list(): Project[];
|
|
102
|
+
getByHash(hash: string): Project | undefined;
|
|
103
|
+
addAssociation(memoryId: string, projectId: string): void;
|
|
104
|
+
removeAssociation(memoryId: string, projectId: string): void;
|
|
105
|
+
getProjectsForMemories(memoryIds: string[]): Map<string, Project[]>;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
89
108
|
//#region src/memory/repository.d.ts
|
|
90
109
|
declare class MemoryRepository {
|
|
91
110
|
#private;
|
|
92
|
-
constructor(db: DatabaseManager, embeddingService: EmbeddingService);
|
|
111
|
+
constructor(db: DatabaseManager, embeddingService: EmbeddingService, projects: ProjectRepository);
|
|
93
112
|
save(options: SaveOptions): Promise<Memory>;
|
|
94
113
|
update(id: string, patch: {
|
|
95
114
|
content?: string;
|
|
@@ -105,6 +124,7 @@ declare class MemoryRepository {
|
|
|
105
124
|
total: number;
|
|
106
125
|
needsReview: number;
|
|
107
126
|
};
|
|
127
|
+
setPin(id: string, pinned: boolean): Memory;
|
|
108
128
|
incrementAccessCount(id: string): void;
|
|
109
129
|
}
|
|
110
130
|
//#endregion
|
|
@@ -118,6 +138,10 @@ declare class QueryEngine {
|
|
|
118
138
|
}
|
|
119
139
|
//#endregion
|
|
120
140
|
//#region src/scope/resolver.d.ts
|
|
141
|
+
declare function resolveProject(): Promise<{
|
|
142
|
+
hash: string;
|
|
143
|
+
name: string;
|
|
144
|
+
}>;
|
|
121
145
|
declare function resolveScope(): Promise<string>;
|
|
122
146
|
//#endregion
|
|
123
147
|
//#region src/session/builder.d.ts
|
|
@@ -125,8 +149,8 @@ declare function listMemoryTypes(): MemoryType[];
|
|
|
125
149
|
declare class SessionContextBuilder {
|
|
126
150
|
#private;
|
|
127
151
|
constructor(db: DatabaseManager);
|
|
128
|
-
getSessionContext(
|
|
152
|
+
getSessionContext(projectHash: string): SessionContext;
|
|
129
153
|
}
|
|
130
154
|
//#endregion
|
|
131
|
-
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, Memory, MemoryRepository, type MemoryRow, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope, rowToMemory };
|
|
155
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, Memory, MemoryRepository, type MemoryRow, MemoryType, ProgressCallback, Project, ProjectRepository, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveProject, resolveScope, rowToMemory };
|
|
132
156
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/types.ts","../src/db/row-types.ts","../src/embedding/service.ts","../src/memory/repository.ts","../src/query/engine.ts","../src/scope/resolver.ts","../src/session/builder.ts"],"mappings":";;;cAAa,YAAA,SAAqB,KAAA;cACpB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;AAAA,cAM5B,aAAA,SAAsB,YAAA;cACrB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;KCCpC,SAAA,IAAa,EAAA,EAAI,aAAA,CAAc,QAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/types.ts","../src/db/row-types.ts","../src/embedding/service.ts","../src/project/repository.ts","../src/memory/repository.ts","../src/query/engine.ts","../src/scope/resolver.ts","../src/session/builder.ts"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -24,12 +24,19 @@ declare class DatabaseManager {
|
|
|
24
24
|
//#region src/types.d.ts
|
|
25
25
|
type MemoryType = "correction" | "preference" | "decision" | "learning" | "fact";
|
|
26
26
|
declare const MEMORY_TYPE_VALUES: readonly ["correction", "preference", "decision", "learning", "fact"];
|
|
27
|
+
interface Project {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
scopeHash: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
}
|
|
27
34
|
interface Memory {
|
|
28
35
|
id: string;
|
|
29
36
|
content: string;
|
|
30
37
|
type: MemoryType;
|
|
31
38
|
tags: string[];
|
|
32
|
-
|
|
39
|
+
projects: Project[];
|
|
33
40
|
sourceHarness: string | null;
|
|
34
41
|
accessCount: number;
|
|
35
42
|
pinned: boolean;
|
|
@@ -40,14 +47,14 @@ interface Memory {
|
|
|
40
47
|
interface QueryOptions {
|
|
41
48
|
query: string;
|
|
42
49
|
type?: MemoryType;
|
|
43
|
-
|
|
50
|
+
projectHash?: string;
|
|
44
51
|
limit?: number;
|
|
45
52
|
}
|
|
46
53
|
interface SaveOptions {
|
|
47
54
|
content: string;
|
|
48
55
|
type: MemoryType;
|
|
49
56
|
tags?: string[];
|
|
50
|
-
|
|
57
|
+
projectHash?: string;
|
|
51
58
|
sourceHarness?: string;
|
|
52
59
|
}
|
|
53
60
|
interface SessionContext {
|
|
@@ -62,7 +69,6 @@ interface MemoryRow {
|
|
|
62
69
|
content: string;
|
|
63
70
|
type: string;
|
|
64
71
|
tags: string;
|
|
65
|
-
scope: string;
|
|
66
72
|
source: string | null;
|
|
67
73
|
access_count: number;
|
|
68
74
|
pinned: number;
|
|
@@ -70,7 +76,7 @@ interface MemoryRow {
|
|
|
70
76
|
created_at: string;
|
|
71
77
|
updated_at: string;
|
|
72
78
|
}
|
|
73
|
-
declare function rowToMemory(row: MemoryRow): Memory;
|
|
79
|
+
declare function rowToMemory(row: MemoryRow, projects: Project[]): Memory;
|
|
74
80
|
//#endregion
|
|
75
81
|
//#region src/embedding/service.d.ts
|
|
76
82
|
type ProgressCallback = (progress: {
|
|
@@ -86,10 +92,23 @@ declare class EmbeddingService {
|
|
|
86
92
|
embed(text: string): Promise<Float32Array>;
|
|
87
93
|
}
|
|
88
94
|
//#endregion
|
|
95
|
+
//#region src/project/repository.d.ts
|
|
96
|
+
declare class ProjectRepository {
|
|
97
|
+
#private;
|
|
98
|
+
constructor(db: DatabaseManager);
|
|
99
|
+
upsertByHash(hash: string, name: string): Project;
|
|
100
|
+
rename(id: string, name: string): Project;
|
|
101
|
+
list(): Project[];
|
|
102
|
+
getByHash(hash: string): Project | undefined;
|
|
103
|
+
addAssociation(memoryId: string, projectId: string): void;
|
|
104
|
+
removeAssociation(memoryId: string, projectId: string): void;
|
|
105
|
+
getProjectsForMemories(memoryIds: string[]): Map<string, Project[]>;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
89
108
|
//#region src/memory/repository.d.ts
|
|
90
109
|
declare class MemoryRepository {
|
|
91
110
|
#private;
|
|
92
|
-
constructor(db: DatabaseManager, embeddingService: EmbeddingService);
|
|
111
|
+
constructor(db: DatabaseManager, embeddingService: EmbeddingService, projects: ProjectRepository);
|
|
93
112
|
save(options: SaveOptions): Promise<Memory>;
|
|
94
113
|
update(id: string, patch: {
|
|
95
114
|
content?: string;
|
|
@@ -105,6 +124,7 @@ declare class MemoryRepository {
|
|
|
105
124
|
total: number;
|
|
106
125
|
needsReview: number;
|
|
107
126
|
};
|
|
127
|
+
setPin(id: string, pinned: boolean): Memory;
|
|
108
128
|
incrementAccessCount(id: string): void;
|
|
109
129
|
}
|
|
110
130
|
//#endregion
|
|
@@ -118,6 +138,10 @@ declare class QueryEngine {
|
|
|
118
138
|
}
|
|
119
139
|
//#endregion
|
|
120
140
|
//#region src/scope/resolver.d.ts
|
|
141
|
+
declare function resolveProject(): Promise<{
|
|
142
|
+
hash: string;
|
|
143
|
+
name: string;
|
|
144
|
+
}>;
|
|
121
145
|
declare function resolveScope(): Promise<string>;
|
|
122
146
|
//#endregion
|
|
123
147
|
//#region src/session/builder.d.ts
|
|
@@ -125,8 +149,8 @@ declare function listMemoryTypes(): MemoryType[];
|
|
|
125
149
|
declare class SessionContextBuilder {
|
|
126
150
|
#private;
|
|
127
151
|
constructor(db: DatabaseManager);
|
|
128
|
-
getSessionContext(
|
|
152
|
+
getSessionContext(projectHash: string): SessionContext;
|
|
129
153
|
}
|
|
130
154
|
//#endregion
|
|
131
|
-
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, Memory, MemoryRepository, type MemoryRow, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope, rowToMemory };
|
|
155
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, Memory, MemoryRepository, type MemoryRow, MemoryType, ProgressCallback, Project, ProjectRepository, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveProject, resolveScope, rowToMemory };
|
|
132
156
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/types.ts","../src/db/row-types.ts","../src/embedding/service.ts","../src/memory/repository.ts","../src/query/engine.ts","../src/scope/resolver.ts","../src/session/builder.ts"],"mappings":";;;cAAa,YAAA,SAAqB,KAAA;cACpB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;AAAA,cAM5B,aAAA,SAAsB,YAAA;cACrB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;KCCpC,SAAA,IAAa,EAAA,EAAI,aAAA,CAAc,QAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/types.ts","../src/db/row-types.ts","../src/embedding/service.ts","../src/project/repository.ts","../src/memory/repository.ts","../src/query/engine.ts","../src/scope/resolver.ts","../src/session/builder.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -41,6 +41,39 @@ CREATE TABLE IF NOT EXISTS memories (
|
|
|
41
41
|
CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(
|
|
42
42
|
embedding FLOAT[384]
|
|
43
43
|
);
|
|
44
|
+
`], [2, `
|
|
45
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
46
|
+
id TEXT PRIMARY KEY,
|
|
47
|
+
name TEXT NOT NULL,
|
|
48
|
+
scope_hash TEXT NOT NULL UNIQUE,
|
|
49
|
+
created_at TEXT NOT NULL,
|
|
50
|
+
updated_at TEXT NOT NULL
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
CREATE TABLE IF NOT EXISTS memory_projects (
|
|
54
|
+
memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
55
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
56
|
+
PRIMARY KEY (memory_id, project_id)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at)
|
|
60
|
+
SELECT
|
|
61
|
+
lower(hex(randomblob(16))),
|
|
62
|
+
'project-' || substr(scope, 1, 8),
|
|
63
|
+
scope,
|
|
64
|
+
datetime('now'),
|
|
65
|
+
datetime('now')
|
|
66
|
+
FROM memories
|
|
67
|
+
WHERE scope != 'global'
|
|
68
|
+
GROUP BY scope;
|
|
69
|
+
|
|
70
|
+
INSERT OR IGNORE INTO memory_projects (memory_id, project_id)
|
|
71
|
+
SELECT m.id, p.id
|
|
72
|
+
FROM memories m
|
|
73
|
+
JOIN projects p ON p.scope_hash = m.scope
|
|
74
|
+
WHERE m.scope != 'global';
|
|
75
|
+
|
|
76
|
+
ALTER TABLE memories DROP COLUMN scope;
|
|
44
77
|
`]];
|
|
45
78
|
var DatabaseManager = class DatabaseManager {
|
|
46
79
|
#db;
|
|
@@ -71,6 +104,7 @@ var DatabaseManager = class DatabaseManager {
|
|
|
71
104
|
throw new DatabaseError("Failed to load sqlite-vec extension", { cause: err });
|
|
72
105
|
}
|
|
73
106
|
db.pragma("journal_mode = WAL");
|
|
107
|
+
db.pragma("foreign_keys = ON");
|
|
74
108
|
const manager = new DatabaseManager(db);
|
|
75
109
|
manager.#runMigrations();
|
|
76
110
|
return manager;
|
|
@@ -98,13 +132,13 @@ var DatabaseManager = class DatabaseManager {
|
|
|
98
132
|
};
|
|
99
133
|
//#endregion
|
|
100
134
|
//#region src/db/row-types.ts
|
|
101
|
-
function rowToMemory(row) {
|
|
135
|
+
function rowToMemory(row, projects) {
|
|
102
136
|
return {
|
|
103
137
|
id: row.id,
|
|
104
138
|
content: row.content,
|
|
105
139
|
type: row.type,
|
|
106
140
|
tags: JSON.parse(row.tags),
|
|
107
|
-
|
|
141
|
+
projects,
|
|
108
142
|
sourceHarness: row.source,
|
|
109
143
|
accessCount: row.access_count,
|
|
110
144
|
pinned: row.pinned !== 0,
|
|
@@ -113,6 +147,15 @@ function rowToMemory(row) {
|
|
|
113
147
|
updatedAt: row.updated_at
|
|
114
148
|
};
|
|
115
149
|
}
|
|
150
|
+
function rowToProject(row) {
|
|
151
|
+
return {
|
|
152
|
+
id: row.id,
|
|
153
|
+
name: row.name,
|
|
154
|
+
scopeHash: row.scope_hash,
|
|
155
|
+
createdAt: row.created_at,
|
|
156
|
+
updatedAt: row.updated_at
|
|
157
|
+
};
|
|
158
|
+
}
|
|
116
159
|
//#endregion
|
|
117
160
|
//#region src/embedding/service.ts
|
|
118
161
|
var EmbeddingService = class {
|
|
@@ -152,30 +195,44 @@ const MEMORY_TYPE_VALUES = [
|
|
|
152
195
|
var MemoryRepository = class {
|
|
153
196
|
#db;
|
|
154
197
|
#embedding;
|
|
155
|
-
|
|
198
|
+
#projects;
|
|
199
|
+
constructor(db, embeddingService, projects) {
|
|
156
200
|
this.#db = db;
|
|
157
201
|
this.#embedding = embeddingService;
|
|
202
|
+
this.#projects = projects;
|
|
158
203
|
}
|
|
159
204
|
async save(options) {
|
|
160
|
-
const { content, type, tags = [],
|
|
205
|
+
const { content, type, tags = [], projectHash, sourceHarness } = options;
|
|
161
206
|
const embedding = await this.#embedding.embed(content);
|
|
162
207
|
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
208
|
+
let top;
|
|
209
|
+
if (projectHash !== void 0) top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
|
|
210
|
+
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
211
|
+
JOIN memory_projects mp ON mp.memory_id = m.id
|
|
212
|
+
JOIN projects p ON p.id = mp.project_id
|
|
213
|
+
WHERE m.type = ? AND p.scope_hash = ?
|
|
214
|
+
ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, projectHash);
|
|
215
|
+
else top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
|
|
216
|
+
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
217
|
+
WHERE m.type = ?
|
|
218
|
+
AND m.id NOT IN (SELECT memory_id FROM memory_projects)
|
|
219
|
+
ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type);
|
|
167
220
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
168
221
|
if (top !== void 0 && top.similarity > .92) {
|
|
169
222
|
this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
|
|
170
223
|
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
|
|
171
|
-
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id));
|
|
224
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id), this.#projects.getProjectsForMemories([top.id]).get(top.id) ?? []);
|
|
172
225
|
}
|
|
173
226
|
if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
|
|
174
227
|
const id = randomUUID();
|
|
175
|
-
this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags,
|
|
176
|
-
VALUES (?, ?, ?, ?, ?,
|
|
228
|
+
this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, source, access_count, pinned, needs_review, created_at, updated_at)
|
|
229
|
+
VALUES (?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), sourceHarness ?? null, now, now);
|
|
177
230
|
this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
|
|
178
|
-
|
|
231
|
+
if (projectHash !== void 0) {
|
|
232
|
+
const project = this.#projects.upsertByHash(projectHash, `project-${projectHash.slice(0, 8)}`);
|
|
233
|
+
this.#projects.addAssociation(id, project.id);
|
|
234
|
+
}
|
|
235
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
|
|
179
236
|
}
|
|
180
237
|
async update(id, patch) {
|
|
181
238
|
const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
|
|
@@ -198,11 +255,12 @@ var MemoryRepository = class {
|
|
|
198
255
|
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
199
256
|
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
|
|
200
257
|
}
|
|
201
|
-
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id));
|
|
258
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id), this.#projects.getProjectsForMemories([id]).get(id) ?? []);
|
|
202
259
|
}
|
|
203
260
|
delete(id) {
|
|
204
261
|
const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
|
|
205
262
|
if (row !== void 0) this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);
|
|
263
|
+
this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ?`).run(id);
|
|
206
264
|
this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);
|
|
207
265
|
return Promise.resolve();
|
|
208
266
|
}
|
|
@@ -215,7 +273,11 @@ var MemoryRepository = class {
|
|
|
215
273
|
}
|
|
216
274
|
if (opts?.pinned === true) conditions.push("pinned = 1");
|
|
217
275
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
218
|
-
|
|
276
|
+
const rows = this.#db.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC`).all(...params);
|
|
277
|
+
if (rows.length === 0) return [];
|
|
278
|
+
const ids = rows.map((r) => r.id);
|
|
279
|
+
const projectMap = this.#projects.getProjectsForMemories(ids);
|
|
280
|
+
return rows.map((row) => rowToMemory(row, projectMap.get(row.id) ?? []));
|
|
219
281
|
}
|
|
220
282
|
stats() {
|
|
221
283
|
const byType = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
|
|
@@ -231,11 +293,65 @@ var MemoryRepository = class {
|
|
|
231
293
|
needsReview: totals.needsReview ?? 0
|
|
232
294
|
};
|
|
233
295
|
}
|
|
296
|
+
setPin(id, pinned) {
|
|
297
|
+
if (this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id) === void 0) throw new Error(`Memory not found: ${id}`);
|
|
298
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
299
|
+
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) ?? []);
|
|
301
|
+
}
|
|
234
302
|
incrementAccessCount(id) {
|
|
235
303
|
this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
|
|
236
304
|
}
|
|
237
305
|
};
|
|
238
306
|
//#endregion
|
|
307
|
+
//#region src/project/repository.ts
|
|
308
|
+
var ProjectRepository = class {
|
|
309
|
+
#db;
|
|
310
|
+
constructor(db) {
|
|
311
|
+
this.#db = db;
|
|
312
|
+
}
|
|
313
|
+
upsertByHash(hash, name) {
|
|
314
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
315
|
+
const id = randomUUID();
|
|
316
|
+
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));
|
|
318
|
+
}
|
|
319
|
+
rename(id, name) {
|
|
320
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
321
|
+
this.#db.db.prepare(`UPDATE projects SET name = ?, updated_at = ? WHERE id = ?`).run(name, now, id);
|
|
322
|
+
const row = this.#db.db.prepare(`SELECT * FROM projects WHERE id = ?`).get(id);
|
|
323
|
+
if (row === void 0) throw new Error(`Project not found: ${id}`);
|
|
324
|
+
return rowToProject(row);
|
|
325
|
+
}
|
|
326
|
+
list() {
|
|
327
|
+
return this.#db.db.prepare(`SELECT * FROM projects ORDER BY name ASC`).all().map(rowToProject);
|
|
328
|
+
}
|
|
329
|
+
getByHash(hash) {
|
|
330
|
+
const row = this.#db.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(hash);
|
|
331
|
+
return row !== void 0 ? rowToProject(row) : void 0;
|
|
332
|
+
}
|
|
333
|
+
addAssociation(memoryId, projectId) {
|
|
334
|
+
this.#db.db.prepare(`INSERT OR IGNORE INTO memory_projects (memory_id, project_id) VALUES (?, ?)`).run(memoryId, projectId);
|
|
335
|
+
}
|
|
336
|
+
removeAssociation(memoryId, projectId) {
|
|
337
|
+
this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`).run(memoryId, projectId);
|
|
338
|
+
}
|
|
339
|
+
getProjectsForMemories(memoryIds) {
|
|
340
|
+
if (memoryIds.length === 0) return /* @__PURE__ */ new Map();
|
|
341
|
+
const placeholders = memoryIds.map(() => "?").join(",");
|
|
342
|
+
const rows = this.#db.db.prepare(`SELECT p.*, mp.memory_id FROM projects p
|
|
343
|
+
JOIN memory_projects mp ON mp.project_id = p.id
|
|
344
|
+
WHERE mp.memory_id IN (${placeholders})`).all(...memoryIds);
|
|
345
|
+
const result = /* @__PURE__ */ new Map();
|
|
346
|
+
for (const row of rows) {
|
|
347
|
+
const list = result.get(row.memory_id) ?? [];
|
|
348
|
+
list.push(rowToProject(row));
|
|
349
|
+
result.set(row.memory_id, list);
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
//#endregion
|
|
239
355
|
//#region src/query/engine.ts
|
|
240
356
|
const TYPE_WEIGHTS = {
|
|
241
357
|
correction: 1,
|
|
@@ -254,28 +370,32 @@ var QueryEngine = class {
|
|
|
254
370
|
this.#repo = repo;
|
|
255
371
|
}
|
|
256
372
|
async query(options) {
|
|
257
|
-
const { query, type,
|
|
373
|
+
const { query, type, projectHash, limit = 10 } = options;
|
|
258
374
|
const queryEmbedding = await this.#embedding.embed(query);
|
|
259
375
|
const queryBlob = Buffer.from(queryEmbedding.buffer);
|
|
260
376
|
const whereClauses = [];
|
|
261
377
|
const params = [queryBlob];
|
|
378
|
+
let joinClause = "";
|
|
262
379
|
if (type !== void 0) {
|
|
263
380
|
whereClauses.push("m.type = ?");
|
|
264
381
|
params.push(type);
|
|
265
382
|
}
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
|
|
383
|
+
if (projectHash !== void 0) {
|
|
384
|
+
joinClause = "LEFT JOIN memory_projects mp ON mp.memory_id = m.id LEFT JOIN projects p ON p.id = mp.project_id";
|
|
385
|
+
whereClauses.push("p.scope_hash = ?");
|
|
386
|
+
params.push(projectHash);
|
|
269
387
|
}
|
|
388
|
+
const whereSQL = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
270
389
|
const sql = `
|
|
271
390
|
SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim
|
|
272
391
|
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
273
|
-
${
|
|
392
|
+
${joinClause}
|
|
393
|
+
${whereSQL}
|
|
274
394
|
`;
|
|
275
395
|
const rows = this.#db.db.prepare(sql).all(...params);
|
|
276
396
|
const now = Date.now();
|
|
277
397
|
const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
|
|
278
|
-
const memory = rowToMemory(row);
|
|
398
|
+
const memory = rowToMemory(row, []);
|
|
279
399
|
const score = this.#computeScore(memory, now);
|
|
280
400
|
return {
|
|
281
401
|
...memory,
|
|
@@ -301,6 +421,29 @@ const execFileAsync = promisify(execFile);
|
|
|
301
421
|
function sha256Truncated(input) {
|
|
302
422
|
return createHash("sha256").update(input).digest("hex").slice(0, 16);
|
|
303
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
|
+
}
|
|
304
447
|
async function resolveScope() {
|
|
305
448
|
try {
|
|
306
449
|
const { stdout } = await execFileAsync("git", [
|
|
@@ -323,9 +466,14 @@ var SessionContextBuilder = class {
|
|
|
323
466
|
constructor(db) {
|
|
324
467
|
this.#db = db;
|
|
325
468
|
}
|
|
326
|
-
getSessionContext(
|
|
327
|
-
const pinnedGlobal = this.#db.db.prepare(
|
|
328
|
-
|
|
469
|
+
getSessionContext(projectHash) {
|
|
470
|
+
const pinnedGlobal = this.#db.db.prepare(`SELECT * FROM memories
|
|
471
|
+
WHERE id NOT IN (SELECT memory_id FROM memory_projects)
|
|
472
|
+
AND pinned = 1`).all().map((row) => rowToMemory(row, []));
|
|
473
|
+
const pinnedProject = this.#db.db.prepare(`SELECT m.* FROM memories m
|
|
474
|
+
JOIN memory_projects mp ON mp.memory_id = m.id
|
|
475
|
+
JOIN projects p ON p.id = mp.project_id
|
|
476
|
+
WHERE p.scope_hash = ? AND m.pinned = 1`).all(projectHash).map((row) => rowToMemory(row, []));
|
|
329
477
|
const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
|
|
330
478
|
const stats = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
|
|
331
479
|
for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
|
|
@@ -337,6 +485,6 @@ var SessionContextBuilder = class {
|
|
|
337
485
|
}
|
|
338
486
|
};
|
|
339
487
|
//#endregion
|
|
340
|
-
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, MemoryRepository, QueryEngine, SessionContextBuilder, listMemoryTypes, resolveScope, rowToMemory };
|
|
488
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, MemoryRepository, ProjectRepository, QueryEngine, SessionContextBuilder, listMemoryTypes, resolveProject, resolveScope, rowToMemory };
|
|
341
489
|
|
|
342
490
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["#db","#init","#initInMemory","#runMigrations","#db","#embedding","#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/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\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\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 } from \"../types.js\";\n\nexport interface MemoryRow {\n id: string;\n content: string;\n type: string;\n tags: string;\n scope: 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 function rowToMemory(row: MemoryRow): 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 scope: row.scope,\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","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 Memory {\n id: string;\n content: string;\n type: MemoryType;\n tags: string[];\n scope: string;\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 scope?: string;\n limit?: number;\n}\n\nexport interface SaveOptions {\n content: string;\n type: MemoryType;\n tags?: string[];\n scope?: 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 { 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\n constructor(db: DatabaseManager, embeddingService: EmbeddingService) {\n this.#db = db;\n this.#embedding = embeddingService;\n }\n\n async save(options: SaveOptions): Promise<Memory> {\n const { content, type, tags = [], scope = \"global\", sourceHarness } = options;\n\n const embedding = await this.#embedding.embed(content);\n const embeddingBlob = Buffer.from(embedding.buffer);\n\n const 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 WHERE m.type = ? AND m.scope = ?\n ORDER BY similarity DESC LIMIT 1`\n )\n .get(embeddingBlob, type, scope);\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\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);\n\n // updated is guaranteed to exist since we just updated it\n return rowToMemory(updated as MemoryRow);\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, scope, source, access_count, pinned, needs_review, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`\n )\n .run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);\n\n // sqlite-vec v0.1.9 does not accept parameterized rowid on INSERT into vec0 tables.\n // Use a SELECT subquery to copy the rowid from the memories row we just inserted.\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 const row = this.#db.db\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\n .get(id) as MemoryRow;\n\n return rowToMemory(row);\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 return rowToMemory(updated);\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 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 return rows.map(rowToMemory);\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 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 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, scope, 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\n if (type !== undefined) {\n whereClauses.push(\"m.type = ?\");\n params.push(type);\n }\n\n if (scope !== undefined) {\n whereClauses.push(\"m.scope = ?\");\n params.push(scope);\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 ${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 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(projectScope: string): SessionContext {\n const pinnedGlobal = this.#db.db\n .prepare<[], MemoryRow>(\"SELECT * FROM memories WHERE scope = 'global' AND pinned = 1\")\n .all()\n .map(rowToMemory);\n\n const pinnedProject = this.#db.db\n .prepare<[string], MemoryRow>(\"SELECT * FROM memories WHERE scope = ? AND pinned = 1\")\n .all(projectScope)\n .map(rowToMemory);\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":["#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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"}
|