@membank/core 0.0.2 → 0.0.4
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 +36 -69
- package/dist/index.d.cts +32 -15
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +32 -15
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +35 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -122,6 +122,23 @@ var DatabaseManager = class DatabaseManager {
|
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
//#endregion
|
|
125
|
+
//#region src/db/row-types.ts
|
|
126
|
+
function rowToMemory(row) {
|
|
127
|
+
return {
|
|
128
|
+
id: row.id,
|
|
129
|
+
content: row.content,
|
|
130
|
+
type: row.type,
|
|
131
|
+
tags: JSON.parse(row.tags),
|
|
132
|
+
scope: row.scope,
|
|
133
|
+
sourceHarness: row.source,
|
|
134
|
+
accessCount: row.access_count,
|
|
135
|
+
pinned: row.pinned !== 0,
|
|
136
|
+
needsReview: row.needs_review !== 0,
|
|
137
|
+
createdAt: row.created_at,
|
|
138
|
+
updatedAt: row.updated_at
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
125
142
|
//#region src/embedding/service.ts
|
|
126
143
|
var EmbeddingService = class {
|
|
127
144
|
modelCachePath;
|
|
@@ -147,6 +164,15 @@ var EmbeddingService = class {
|
|
|
147
164
|
}
|
|
148
165
|
};
|
|
149
166
|
//#endregion
|
|
167
|
+
//#region src/types.ts
|
|
168
|
+
const MEMORY_TYPE_VALUES = [
|
|
169
|
+
"correction",
|
|
170
|
+
"preference",
|
|
171
|
+
"decision",
|
|
172
|
+
"learning",
|
|
173
|
+
"fact"
|
|
174
|
+
];
|
|
175
|
+
//#endregion
|
|
150
176
|
//#region src/memory/repository.ts
|
|
151
177
|
var MemoryRepository = class {
|
|
152
178
|
#db;
|
|
@@ -167,16 +193,14 @@ var MemoryRepository = class {
|
|
|
167
193
|
if (top !== void 0 && top.similarity > .92) {
|
|
168
194
|
this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
|
|
169
195
|
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
|
|
170
|
-
|
|
171
|
-
return this.#rowToMemory(updated);
|
|
196
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id));
|
|
172
197
|
}
|
|
173
198
|
if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
|
|
174
199
|
const id = (0, node_crypto.randomUUID)();
|
|
175
200
|
this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, scope, source, access_count, pinned, needs_review, created_at, updated_at)
|
|
176
201
|
VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);
|
|
177
202
|
this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
|
|
178
|
-
|
|
179
|
-
return this.#rowToMemory(row);
|
|
203
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id));
|
|
180
204
|
}
|
|
181
205
|
async update(id, patch) {
|
|
182
206
|
const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
|
|
@@ -199,8 +223,7 @@ var MemoryRepository = class {
|
|
|
199
223
|
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
200
224
|
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
|
|
201
225
|
}
|
|
202
|
-
|
|
203
|
-
return this.#rowToMemory(updated);
|
|
226
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id));
|
|
204
227
|
}
|
|
205
228
|
delete(id) {
|
|
206
229
|
const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
|
|
@@ -217,16 +240,10 @@ var MemoryRepository = class {
|
|
|
217
240
|
}
|
|
218
241
|
if (opts?.pinned === true) conditions.push("pinned = 1");
|
|
219
242
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
220
|
-
return this.#db.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC`).all(...params).map(
|
|
243
|
+
return this.#db.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC`).all(...params).map(rowToMemory);
|
|
221
244
|
}
|
|
222
245
|
stats() {
|
|
223
|
-
const byType = Object.fromEntries([
|
|
224
|
-
"correction",
|
|
225
|
-
"preference",
|
|
226
|
-
"decision",
|
|
227
|
-
"learning",
|
|
228
|
-
"fact"
|
|
229
|
-
].map((t) => [t, 0]));
|
|
246
|
+
const byType = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
|
|
230
247
|
const typeRows = this.#db.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();
|
|
231
248
|
for (const row of typeRows) if (row.type in byType) byType[row.type] = row.count;
|
|
232
249
|
const totals = this.#db.db.prepare(`SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`).get() ?? {
|
|
@@ -242,21 +259,6 @@ var MemoryRepository = class {
|
|
|
242
259
|
incrementAccessCount(id) {
|
|
243
260
|
this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
|
|
244
261
|
}
|
|
245
|
-
#rowToMemory(row) {
|
|
246
|
-
return {
|
|
247
|
-
id: row.id,
|
|
248
|
-
content: row.content,
|
|
249
|
-
type: row.type,
|
|
250
|
-
tags: JSON.parse(row.tags),
|
|
251
|
-
scope: row.scope,
|
|
252
|
-
sourceHarness: row.source,
|
|
253
|
-
accessCount: row.access_count,
|
|
254
|
-
pinned: row.pinned !== 0,
|
|
255
|
-
needsReview: row.needs_review !== 0,
|
|
256
|
-
createdAt: row.created_at,
|
|
257
|
-
updatedAt: row.updated_at
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
262
|
};
|
|
261
263
|
//#endregion
|
|
262
264
|
//#region src/query/engine.ts
|
|
@@ -298,7 +300,7 @@ var QueryEngine = class {
|
|
|
298
300
|
const rows = this.#db.db.prepare(sql).all(...params);
|
|
299
301
|
const now = Date.now();
|
|
300
302
|
const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
|
|
301
|
-
const memory =
|
|
303
|
+
const memory = rowToMemory(row);
|
|
302
304
|
const score = this.#computeScore(memory, now);
|
|
303
305
|
return {
|
|
304
306
|
...memory,
|
|
@@ -317,21 +319,6 @@ var QueryEngine = class {
|
|
|
317
319
|
const pinned = memory.pinned ? 1 : 0;
|
|
318
320
|
return typeWeight * .4 + accessCountNorm * .3 + recencyNorm * .2 + pinned * .1;
|
|
319
321
|
}
|
|
320
|
-
#rowToMemory(row) {
|
|
321
|
-
return {
|
|
322
|
-
id: row.id,
|
|
323
|
-
content: row.content,
|
|
324
|
-
type: row.type,
|
|
325
|
-
tags: JSON.parse(row.tags),
|
|
326
|
-
scope: row.scope,
|
|
327
|
-
sourceHarness: row.source,
|
|
328
|
-
accessCount: row.access_count,
|
|
329
|
-
pinned: row.pinned !== 0,
|
|
330
|
-
needsReview: row.needs_review !== 0,
|
|
331
|
-
createdAt: row.created_at,
|
|
332
|
-
updatedAt: row.updated_at
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
322
|
};
|
|
336
323
|
//#endregion
|
|
337
324
|
//#region src/scope/resolver.ts
|
|
@@ -353,30 +340,8 @@ async function resolveScope() {
|
|
|
353
340
|
}
|
|
354
341
|
//#endregion
|
|
355
342
|
//#region src/session/builder.ts
|
|
356
|
-
const MEMORY_TYPES = [
|
|
357
|
-
"correction",
|
|
358
|
-
"preference",
|
|
359
|
-
"decision",
|
|
360
|
-
"learning",
|
|
361
|
-
"fact"
|
|
362
|
-
];
|
|
363
|
-
function rowToMemory(row) {
|
|
364
|
-
return {
|
|
365
|
-
id: row.id,
|
|
366
|
-
content: row.content,
|
|
367
|
-
type: row.type,
|
|
368
|
-
tags: JSON.parse(row.tags),
|
|
369
|
-
scope: row.scope,
|
|
370
|
-
sourceHarness: row.source,
|
|
371
|
-
accessCount: row.access_count,
|
|
372
|
-
pinned: row.pinned !== 0,
|
|
373
|
-
needsReview: row.needs_review !== 0,
|
|
374
|
-
createdAt: row.created_at,
|
|
375
|
-
updatedAt: row.updated_at
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
343
|
function listMemoryTypes() {
|
|
379
|
-
return [...
|
|
344
|
+
return [...MEMORY_TYPE_VALUES];
|
|
380
345
|
}
|
|
381
346
|
var SessionContextBuilder = class {
|
|
382
347
|
#db;
|
|
@@ -387,7 +352,7 @@ var SessionContextBuilder = class {
|
|
|
387
352
|
const pinnedGlobal = this.#db.db.prepare("SELECT * FROM memories WHERE scope = 'global' AND pinned = 1").all().map(rowToMemory);
|
|
388
353
|
const pinnedProject = this.#db.db.prepare("SELECT * FROM memories WHERE scope = ? AND pinned = 1").all(projectScope).map(rowToMemory);
|
|
389
354
|
const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
|
|
390
|
-
const stats = Object.fromEntries(
|
|
355
|
+
const stats = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
|
|
391
356
|
for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
|
|
392
357
|
return {
|
|
393
358
|
stats,
|
|
@@ -400,9 +365,11 @@ var SessionContextBuilder = class {
|
|
|
400
365
|
exports.DatabaseError = DatabaseError;
|
|
401
366
|
exports.DatabaseManager = DatabaseManager;
|
|
402
367
|
exports.EmbeddingService = EmbeddingService;
|
|
368
|
+
exports.MEMORY_TYPE_VALUES = MEMORY_TYPE_VALUES;
|
|
403
369
|
exports.MembankError = MembankError;
|
|
404
370
|
exports.MemoryRepository = MemoryRepository;
|
|
405
371
|
exports.QueryEngine = QueryEngine;
|
|
406
372
|
exports.SessionContextBuilder = SessionContextBuilder;
|
|
407
373
|
exports.listMemoryTypes = listMemoryTypes;
|
|
408
374
|
exports.resolveScope = resolveScope;
|
|
375
|
+
exports.rowToMemory = rowToMemory;
|
package/dist/index.d.cts
CHANGED
|
@@ -21,22 +21,9 @@ declare class DatabaseManager {
|
|
|
21
21
|
close(): void;
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
24
|
-
//#region src/embedding/service.d.ts
|
|
25
|
-
type ProgressCallback = (progress: {
|
|
26
|
-
status: string;
|
|
27
|
-
progress?: number;
|
|
28
|
-
}) => void;
|
|
29
|
-
declare class EmbeddingService {
|
|
30
|
-
private readonly modelCachePath;
|
|
31
|
-
private readonly onProgress;
|
|
32
|
-
private pipelineInstance;
|
|
33
|
-
constructor(modelCachePath?: string, onProgress?: ProgressCallback);
|
|
34
|
-
private getPipeline;
|
|
35
|
-
embed(text: string): Promise<Float32Array>;
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
24
|
//#region src/types.d.ts
|
|
39
25
|
type MemoryType = "correction" | "preference" | "decision" | "learning" | "fact";
|
|
26
|
+
declare const MEMORY_TYPE_VALUES: readonly ["correction", "preference", "decision", "learning", "fact"];
|
|
40
27
|
interface Memory {
|
|
41
28
|
id: string;
|
|
42
29
|
content: string;
|
|
@@ -69,6 +56,36 @@ interface SessionContext {
|
|
|
69
56
|
pinnedProject: Memory[];
|
|
70
57
|
}
|
|
71
58
|
//#endregion
|
|
59
|
+
//#region src/db/row-types.d.ts
|
|
60
|
+
interface MemoryRow {
|
|
61
|
+
id: string;
|
|
62
|
+
content: string;
|
|
63
|
+
type: string;
|
|
64
|
+
tags: string;
|
|
65
|
+
scope: string;
|
|
66
|
+
source: string | null;
|
|
67
|
+
access_count: number;
|
|
68
|
+
pinned: number;
|
|
69
|
+
needs_review: number;
|
|
70
|
+
created_at: string;
|
|
71
|
+
updated_at: string;
|
|
72
|
+
}
|
|
73
|
+
declare function rowToMemory(row: MemoryRow): Memory;
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/embedding/service.d.ts
|
|
76
|
+
type ProgressCallback = (progress: {
|
|
77
|
+
status: string;
|
|
78
|
+
progress?: number;
|
|
79
|
+
}) => void;
|
|
80
|
+
declare class EmbeddingService {
|
|
81
|
+
private readonly modelCachePath;
|
|
82
|
+
private readonly onProgress;
|
|
83
|
+
private pipelineInstance;
|
|
84
|
+
constructor(modelCachePath?: string, onProgress?: ProgressCallback);
|
|
85
|
+
private getPipeline;
|
|
86
|
+
embed(text: string): Promise<Float32Array>;
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
72
89
|
//#region src/memory/repository.d.ts
|
|
73
90
|
declare class MemoryRepository {
|
|
74
91
|
#private;
|
|
@@ -111,5 +128,5 @@ declare class SessionContextBuilder {
|
|
|
111
128
|
getSessionContext(projectScope: string): SessionContext;
|
|
112
129
|
}
|
|
113
130
|
//#endregion
|
|
114
|
-
export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, Memory, MemoryRepository, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope };
|
|
131
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, Memory, MemoryRepository, type MemoryRow, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope, rowToMemory };
|
|
115
132
|
//# 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/
|
|
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,cA2BvB,eAAA;EAAA;UAGJ,WAAA,CAAA;EAAA,OAIA,IAAA,CAAK,MAAA,YAAkB,eAAA;EAAA,OAOvB,YAAA,CAAA,GAAgB,eAAA;EDlDS;EAAA,OCuDzB,uBAAA,CAAwB,MAAA,EAAQ,SAAA,GAAY,eAAA;EAAA,IAkD/C,EAAA,CAAA,GAAM,aAAA,CAAc,QAAA;EAIxB,KAAA,CAAA;AAAA;;;KC7GU,UAAA;AAAA,cAEC,kBAAA;AAAA,UAQI,MAAA;EACf,EAAA;EACA,OAAA;EACA,IAAA,EAAM,UAAA;EACN,IAAA;EACA,KAAA;EACA,aAAA;EACA,WAAA;EACA,MAAA;EACA,WAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,YAAA;EACf,KAAA;EACA,IAAA,GAAO,UAAA;EACP,KAAA;EACA,KAAA;AAAA;AAAA,UAGe,WAAA;EACf,OAAA;EACA,IAAA,EAAM,UAAA;EACN,IAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,UAGe,cAAA;EACf,KAAA,EAAO,MAAA,CAAO,UAAA;EACd,YAAA,EAAc,MAAA;EACd,aAAA,EAAe,MAAA;AAAA;;;UCxCA,SAAA;EACf,EAAA;EACA,OAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,YAAA;EACA,MAAA;EACA,YAAA;EACA,UAAA;EACA,UAAA;AAAA;AAAA,iBAGc,WAAA,CAAY,GAAA,EAAK,SAAA,GAAY,MAAA;;;KCZjC,gBAAA,IAAoB,QAAA;EAAY,MAAA;EAAgB,QAAA;AAAA;AAAA,cAE/C,gBAAA;EAAA,iBACM,cAAA;EAAA,iBACA,UAAA;EAAA,QACT,gBAAA;cAEI,cAAA,WAAyB,UAAA,GAAa,gBAAA;EAAA,QAKpC,WAAA;EAUR,KAAA,CAAM,IAAA,WAAe,OAAA,CAAQ,YAAA;AAAA;;;cCbxB,gBAAA;EAAA;cAIC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA;EAK7C,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,MAAA;EA6DpC,MAAA,CAAO,EAAA,UAAY,KAAA;IAAS,OAAA;IAAkB,IAAA;EAAA,IAAoB,OAAA,CAAQ,MAAA;EA2ChF,MAAA,CAAO,EAAA,WAAa,OAAA;EAcpB,IAAA,CAAK,IAAA;IAAS,IAAA,GAAO,UAAA;IAAY,MAAA;EAAA,IAAqB,MAAA;EAuBtD,KAAA,CAAA;IAAW,MAAA,EAAQ,MAAA,CAAO,UAAA;IAAqB,KAAA;IAAe,WAAA;EAAA;EA+B9D,oBAAA,CAAqB,EAAA;AAAA;;;cC/KV,WAAA;EAAA;cAKC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA,EAAkB,IAAA,EAAM,gBAAA;EAMrE,KAAA,CAAM,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,MAAA;IAAW,KAAA;EAAA;AAAA;;;iBCpBzC,YAAA,CAAA,GAAgB,OAAA;;;iBCCtB,eAAA,CAAA,GAAmB,UAAA;AAAA,cAItB,qBAAA;EAAA;cAGC,EAAA,EAAI,eAAA;EAIhB,iBAAA,CAAkB,YAAA,WAAuB,cAAA;AAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -21,22 +21,9 @@ declare class DatabaseManager {
|
|
|
21
21
|
close(): void;
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
24
|
-
//#region src/embedding/service.d.ts
|
|
25
|
-
type ProgressCallback = (progress: {
|
|
26
|
-
status: string;
|
|
27
|
-
progress?: number;
|
|
28
|
-
}) => void;
|
|
29
|
-
declare class EmbeddingService {
|
|
30
|
-
private readonly modelCachePath;
|
|
31
|
-
private readonly onProgress;
|
|
32
|
-
private pipelineInstance;
|
|
33
|
-
constructor(modelCachePath?: string, onProgress?: ProgressCallback);
|
|
34
|
-
private getPipeline;
|
|
35
|
-
embed(text: string): Promise<Float32Array>;
|
|
36
|
-
}
|
|
37
|
-
//#endregion
|
|
38
24
|
//#region src/types.d.ts
|
|
39
25
|
type MemoryType = "correction" | "preference" | "decision" | "learning" | "fact";
|
|
26
|
+
declare const MEMORY_TYPE_VALUES: readonly ["correction", "preference", "decision", "learning", "fact"];
|
|
40
27
|
interface Memory {
|
|
41
28
|
id: string;
|
|
42
29
|
content: string;
|
|
@@ -69,6 +56,36 @@ interface SessionContext {
|
|
|
69
56
|
pinnedProject: Memory[];
|
|
70
57
|
}
|
|
71
58
|
//#endregion
|
|
59
|
+
//#region src/db/row-types.d.ts
|
|
60
|
+
interface MemoryRow {
|
|
61
|
+
id: string;
|
|
62
|
+
content: string;
|
|
63
|
+
type: string;
|
|
64
|
+
tags: string;
|
|
65
|
+
scope: string;
|
|
66
|
+
source: string | null;
|
|
67
|
+
access_count: number;
|
|
68
|
+
pinned: number;
|
|
69
|
+
needs_review: number;
|
|
70
|
+
created_at: string;
|
|
71
|
+
updated_at: string;
|
|
72
|
+
}
|
|
73
|
+
declare function rowToMemory(row: MemoryRow): Memory;
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/embedding/service.d.ts
|
|
76
|
+
type ProgressCallback = (progress: {
|
|
77
|
+
status: string;
|
|
78
|
+
progress?: number;
|
|
79
|
+
}) => void;
|
|
80
|
+
declare class EmbeddingService {
|
|
81
|
+
private readonly modelCachePath;
|
|
82
|
+
private readonly onProgress;
|
|
83
|
+
private pipelineInstance;
|
|
84
|
+
constructor(modelCachePath?: string, onProgress?: ProgressCallback);
|
|
85
|
+
private getPipeline;
|
|
86
|
+
embed(text: string): Promise<Float32Array>;
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
72
89
|
//#region src/memory/repository.d.ts
|
|
73
90
|
declare class MemoryRepository {
|
|
74
91
|
#private;
|
|
@@ -111,5 +128,5 @@ declare class SessionContextBuilder {
|
|
|
111
128
|
getSessionContext(projectScope: string): SessionContext;
|
|
112
129
|
}
|
|
113
130
|
//#endregion
|
|
114
|
-
export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, Memory, MemoryRepository, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope };
|
|
131
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, Memory, MemoryRepository, type MemoryRow, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope, rowToMemory };
|
|
115
132
|
//# 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/
|
|
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,cA2BvB,eAAA;EAAA;UAGJ,WAAA,CAAA;EAAA,OAIA,IAAA,CAAK,MAAA,YAAkB,eAAA;EAAA,OAOvB,YAAA,CAAA,GAAgB,eAAA;EDlDS;EAAA,OCuDzB,uBAAA,CAAwB,MAAA,EAAQ,SAAA,GAAY,eAAA;EAAA,IAkD/C,EAAA,CAAA,GAAM,aAAA,CAAc,QAAA;EAIxB,KAAA,CAAA;AAAA;;;KC7GU,UAAA;AAAA,cAEC,kBAAA;AAAA,UAQI,MAAA;EACf,EAAA;EACA,OAAA;EACA,IAAA,EAAM,UAAA;EACN,IAAA;EACA,KAAA;EACA,aAAA;EACA,WAAA;EACA,MAAA;EACA,WAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,YAAA;EACf,KAAA;EACA,IAAA,GAAO,UAAA;EACP,KAAA;EACA,KAAA;AAAA;AAAA,UAGe,WAAA;EACf,OAAA;EACA,IAAA,EAAM,UAAA;EACN,IAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,UAGe,cAAA;EACf,KAAA,EAAO,MAAA,CAAO,UAAA;EACd,YAAA,EAAc,MAAA;EACd,aAAA,EAAe,MAAA;AAAA;;;UCxCA,SAAA;EACf,EAAA;EACA,OAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,YAAA;EACA,MAAA;EACA,YAAA;EACA,UAAA;EACA,UAAA;AAAA;AAAA,iBAGc,WAAA,CAAY,GAAA,EAAK,SAAA,GAAY,MAAA;;;KCZjC,gBAAA,IAAoB,QAAA;EAAY,MAAA;EAAgB,QAAA;AAAA;AAAA,cAE/C,gBAAA;EAAA,iBACM,cAAA;EAAA,iBACA,UAAA;EAAA,QACT,gBAAA;cAEI,cAAA,WAAyB,UAAA,GAAa,gBAAA;EAAA,QAKpC,WAAA;EAUR,KAAA,CAAM,IAAA,WAAe,OAAA,CAAQ,YAAA;AAAA;;;cCbxB,gBAAA;EAAA;cAIC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA;EAK7C,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,MAAA;EA6DpC,MAAA,CAAO,EAAA,UAAY,KAAA;IAAS,OAAA;IAAkB,IAAA;EAAA,IAAoB,OAAA,CAAQ,MAAA;EA2ChF,MAAA,CAAO,EAAA,WAAa,OAAA;EAcpB,IAAA,CAAK,IAAA;IAAS,IAAA,GAAO,UAAA;IAAY,MAAA;EAAA,IAAqB,MAAA;EAuBtD,KAAA,CAAA;IAAW,MAAA,EAAQ,MAAA,CAAO,UAAA;IAAqB,KAAA;IAAe,WAAA;EAAA;EA+B9D,oBAAA,CAAqB,EAAA;AAAA;;;cC/KV,WAAA;EAAA;cAKC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA,EAAkB,IAAA,EAAM,gBAAA;EAMrE,KAAA,CAAM,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,MAAA;IAAW,KAAA;EAAA;AAAA;;;iBCpBzC,YAAA,CAAA,GAAgB,OAAA;;;iBCCtB,eAAA,CAAA,GAAmB,UAAA;AAAA,cAItB,qBAAA;EAAA;cAGC,EAAA,EAAI,eAAA;EAIhB,iBAAA,CAAkB,YAAA,WAAuB,cAAA;AAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -97,6 +97,23 @@ var DatabaseManager = class DatabaseManager {
|
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
99
|
//#endregion
|
|
100
|
+
//#region src/db/row-types.ts
|
|
101
|
+
function rowToMemory(row) {
|
|
102
|
+
return {
|
|
103
|
+
id: row.id,
|
|
104
|
+
content: row.content,
|
|
105
|
+
type: row.type,
|
|
106
|
+
tags: JSON.parse(row.tags),
|
|
107
|
+
scope: row.scope,
|
|
108
|
+
sourceHarness: row.source,
|
|
109
|
+
accessCount: row.access_count,
|
|
110
|
+
pinned: row.pinned !== 0,
|
|
111
|
+
needsReview: row.needs_review !== 0,
|
|
112
|
+
createdAt: row.created_at,
|
|
113
|
+
updatedAt: row.updated_at
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
100
117
|
//#region src/embedding/service.ts
|
|
101
118
|
var EmbeddingService = class {
|
|
102
119
|
modelCachePath;
|
|
@@ -122,6 +139,15 @@ var EmbeddingService = class {
|
|
|
122
139
|
}
|
|
123
140
|
};
|
|
124
141
|
//#endregion
|
|
142
|
+
//#region src/types.ts
|
|
143
|
+
const MEMORY_TYPE_VALUES = [
|
|
144
|
+
"correction",
|
|
145
|
+
"preference",
|
|
146
|
+
"decision",
|
|
147
|
+
"learning",
|
|
148
|
+
"fact"
|
|
149
|
+
];
|
|
150
|
+
//#endregion
|
|
125
151
|
//#region src/memory/repository.ts
|
|
126
152
|
var MemoryRepository = class {
|
|
127
153
|
#db;
|
|
@@ -142,16 +168,14 @@ var MemoryRepository = class {
|
|
|
142
168
|
if (top !== void 0 && top.similarity > .92) {
|
|
143
169
|
this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
|
|
144
170
|
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
|
|
145
|
-
|
|
146
|
-
return this.#rowToMemory(updated);
|
|
171
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id));
|
|
147
172
|
}
|
|
148
173
|
if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
|
|
149
174
|
const id = randomUUID();
|
|
150
175
|
this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, scope, source, access_count, pinned, needs_review, created_at, updated_at)
|
|
151
176
|
VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);
|
|
152
177
|
this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
|
|
153
|
-
|
|
154
|
-
return this.#rowToMemory(row);
|
|
178
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id));
|
|
155
179
|
}
|
|
156
180
|
async update(id, patch) {
|
|
157
181
|
const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
|
|
@@ -174,8 +198,7 @@ var MemoryRepository = class {
|
|
|
174
198
|
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
175
199
|
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
|
|
176
200
|
}
|
|
177
|
-
|
|
178
|
-
return this.#rowToMemory(updated);
|
|
201
|
+
return rowToMemory(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id));
|
|
179
202
|
}
|
|
180
203
|
delete(id) {
|
|
181
204
|
const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
|
|
@@ -192,16 +215,10 @@ var MemoryRepository = class {
|
|
|
192
215
|
}
|
|
193
216
|
if (opts?.pinned === true) conditions.push("pinned = 1");
|
|
194
217
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
195
|
-
return this.#db.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC`).all(...params).map(
|
|
218
|
+
return this.#db.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC`).all(...params).map(rowToMemory);
|
|
196
219
|
}
|
|
197
220
|
stats() {
|
|
198
|
-
const byType = Object.fromEntries([
|
|
199
|
-
"correction",
|
|
200
|
-
"preference",
|
|
201
|
-
"decision",
|
|
202
|
-
"learning",
|
|
203
|
-
"fact"
|
|
204
|
-
].map((t) => [t, 0]));
|
|
221
|
+
const byType = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
|
|
205
222
|
const typeRows = this.#db.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();
|
|
206
223
|
for (const row of typeRows) if (row.type in byType) byType[row.type] = row.count;
|
|
207
224
|
const totals = this.#db.db.prepare(`SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`).get() ?? {
|
|
@@ -217,21 +234,6 @@ var MemoryRepository = class {
|
|
|
217
234
|
incrementAccessCount(id) {
|
|
218
235
|
this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
|
|
219
236
|
}
|
|
220
|
-
#rowToMemory(row) {
|
|
221
|
-
return {
|
|
222
|
-
id: row.id,
|
|
223
|
-
content: row.content,
|
|
224
|
-
type: row.type,
|
|
225
|
-
tags: JSON.parse(row.tags),
|
|
226
|
-
scope: row.scope,
|
|
227
|
-
sourceHarness: row.source,
|
|
228
|
-
accessCount: row.access_count,
|
|
229
|
-
pinned: row.pinned !== 0,
|
|
230
|
-
needsReview: row.needs_review !== 0,
|
|
231
|
-
createdAt: row.created_at,
|
|
232
|
-
updatedAt: row.updated_at
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
237
|
};
|
|
236
238
|
//#endregion
|
|
237
239
|
//#region src/query/engine.ts
|
|
@@ -273,7 +275,7 @@ var QueryEngine = class {
|
|
|
273
275
|
const rows = this.#db.db.prepare(sql).all(...params);
|
|
274
276
|
const now = Date.now();
|
|
275
277
|
const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
|
|
276
|
-
const memory =
|
|
278
|
+
const memory = rowToMemory(row);
|
|
277
279
|
const score = this.#computeScore(memory, now);
|
|
278
280
|
return {
|
|
279
281
|
...memory,
|
|
@@ -292,21 +294,6 @@ var QueryEngine = class {
|
|
|
292
294
|
const pinned = memory.pinned ? 1 : 0;
|
|
293
295
|
return typeWeight * .4 + accessCountNorm * .3 + recencyNorm * .2 + pinned * .1;
|
|
294
296
|
}
|
|
295
|
-
#rowToMemory(row) {
|
|
296
|
-
return {
|
|
297
|
-
id: row.id,
|
|
298
|
-
content: row.content,
|
|
299
|
-
type: row.type,
|
|
300
|
-
tags: JSON.parse(row.tags),
|
|
301
|
-
scope: row.scope,
|
|
302
|
-
sourceHarness: row.source,
|
|
303
|
-
accessCount: row.access_count,
|
|
304
|
-
pinned: row.pinned !== 0,
|
|
305
|
-
needsReview: row.needs_review !== 0,
|
|
306
|
-
createdAt: row.created_at,
|
|
307
|
-
updatedAt: row.updated_at
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
297
|
};
|
|
311
298
|
//#endregion
|
|
312
299
|
//#region src/scope/resolver.ts
|
|
@@ -328,30 +315,8 @@ async function resolveScope() {
|
|
|
328
315
|
}
|
|
329
316
|
//#endregion
|
|
330
317
|
//#region src/session/builder.ts
|
|
331
|
-
const MEMORY_TYPES = [
|
|
332
|
-
"correction",
|
|
333
|
-
"preference",
|
|
334
|
-
"decision",
|
|
335
|
-
"learning",
|
|
336
|
-
"fact"
|
|
337
|
-
];
|
|
338
|
-
function rowToMemory(row) {
|
|
339
|
-
return {
|
|
340
|
-
id: row.id,
|
|
341
|
-
content: row.content,
|
|
342
|
-
type: row.type,
|
|
343
|
-
tags: JSON.parse(row.tags),
|
|
344
|
-
scope: row.scope,
|
|
345
|
-
sourceHarness: row.source,
|
|
346
|
-
accessCount: row.access_count,
|
|
347
|
-
pinned: row.pinned !== 0,
|
|
348
|
-
needsReview: row.needs_review !== 0,
|
|
349
|
-
createdAt: row.created_at,
|
|
350
|
-
updatedAt: row.updated_at
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
318
|
function listMemoryTypes() {
|
|
354
|
-
return [...
|
|
319
|
+
return [...MEMORY_TYPE_VALUES];
|
|
355
320
|
}
|
|
356
321
|
var SessionContextBuilder = class {
|
|
357
322
|
#db;
|
|
@@ -362,7 +327,7 @@ var SessionContextBuilder = class {
|
|
|
362
327
|
const pinnedGlobal = this.#db.db.prepare("SELECT * FROM memories WHERE scope = 'global' AND pinned = 1").all().map(rowToMemory);
|
|
363
328
|
const pinnedProject = this.#db.db.prepare("SELECT * FROM memories WHERE scope = ? AND pinned = 1").all(projectScope).map(rowToMemory);
|
|
364
329
|
const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
|
|
365
|
-
const stats = Object.fromEntries(
|
|
330
|
+
const stats = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
|
|
366
331
|
for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
|
|
367
332
|
return {
|
|
368
333
|
stats,
|
|
@@ -372,6 +337,6 @@ var SessionContextBuilder = class {
|
|
|
372
337
|
}
|
|
373
338
|
};
|
|
374
339
|
//#endregion
|
|
375
|
-
export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, MemoryRepository, QueryEngine, SessionContextBuilder, listMemoryTypes, resolveScope };
|
|
340
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MembankError, MemoryRepository, QueryEngine, SessionContextBuilder, listMemoryTypes, resolveScope, rowToMemory };
|
|
376
341
|
|
|
377
342
|
//# 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","#rowToMemory","#db","#embedding","#repo","#rowToMemory","#computeScore","#db"],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/embedding/service.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\n// Each entry is [targetVersion, sql]. Migrations are applied in ascending order.\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 { 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 type { EmbeddingService } from \"../embedding/service.js\";\nimport type { Memory, MemoryType, SaveOptions } from \"../types.js\";\n\ninterface 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\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 // Overwrite existing record\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 this.#rowToMemory(updated as MemoryRow);\n }\n\n if (top !== undefined && top.similarity >= 0.75) {\n // Flag existing as needs_review\n this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);\n }\n\n // Insert new record\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 this.#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 this.#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((r) => this.#rowToMemory(r));\n }\n\n stats(): { byType: Record<MemoryType, number>; total: number; needsReview: number } {\n const allTypes: MemoryType[] = [\"correction\", \"preference\", \"decision\", \"learning\", \"fact\"];\n const byType = Object.fromEntries(allTypes.map((t) => [t, 0])) as Record<MemoryType, number>;\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 #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}\n","import type { DatabaseManager } from \"../db/manager.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 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 cosine_sim: number;\n}\n\nconst TYPE_WEIGHTS: Record<MemoryType, number> = {\n correction: 1.0,\n preference: 0.8,\n decision: 0.6,\n learning: 0.4,\n fact: 0.2,\n};\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[], MemoryRow>(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 = this.#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 #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}\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 { Memory, MemoryType, SessionContext } from \"../types.js\";\n\ninterface 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\ninterface TypeCountRow {\n type: string;\n count: number;\n}\n\nconst MEMORY_TYPES: MemoryType[] = [\"correction\", \"preference\", \"decision\", \"learning\", \"fact\"];\n\nfunction 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\nexport function listMemoryTypes(): MemoryType[] {\n return [...MEMORY_TYPES];\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_TYPES.map((t) => [t, 0])) as Record<MemoryType, number>;\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","#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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"}
|