@membank/core 0.0.0-dev-20260427133418
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 +377 -0
- package/dist/index.d.cts +115 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +115 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +377 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +36 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let node_fs = require("node:fs");
|
|
25
|
+
let node_os = require("node:os");
|
|
26
|
+
let node_path = require("node:path");
|
|
27
|
+
let better_sqlite3 = require("better-sqlite3");
|
|
28
|
+
better_sqlite3 = __toESM(better_sqlite3, 1);
|
|
29
|
+
let sqlite_vec = require("sqlite-vec");
|
|
30
|
+
sqlite_vec = __toESM(sqlite_vec, 1);
|
|
31
|
+
let _huggingface_transformers = require("@huggingface/transformers");
|
|
32
|
+
let node_crypto = require("node:crypto");
|
|
33
|
+
let node_child_process = require("node:child_process");
|
|
34
|
+
let node_util = require("node:util");
|
|
35
|
+
//#region src/db/errors.ts
|
|
36
|
+
var MembankError = class extends Error {
|
|
37
|
+
constructor(message, options) {
|
|
38
|
+
super(message, options);
|
|
39
|
+
this.name = "MembankError";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var DatabaseError = class extends MembankError {
|
|
43
|
+
constructor(message, options) {
|
|
44
|
+
super(message, options);
|
|
45
|
+
this.name = "DatabaseError";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/db/manager.ts
|
|
50
|
+
const DEFAULT_DB_PATH = (0, node_path.join)((0, node_os.homedir)(), ".membank", "memory.db");
|
|
51
|
+
const MIGRATIONS = [[1, `
|
|
52
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
53
|
+
id TEXT PRIMARY KEY,
|
|
54
|
+
content TEXT NOT NULL,
|
|
55
|
+
type TEXT NOT NULL,
|
|
56
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
57
|
+
scope TEXT NOT NULL,
|
|
58
|
+
source TEXT,
|
|
59
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
60
|
+
pinned INTEGER NOT NULL DEFAULT 0,
|
|
61
|
+
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
62
|
+
created_at TEXT NOT NULL,
|
|
63
|
+
updated_at TEXT NOT NULL
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(
|
|
67
|
+
embedding FLOAT[384]
|
|
68
|
+
);
|
|
69
|
+
`]];
|
|
70
|
+
var DatabaseManager = class DatabaseManager {
|
|
71
|
+
#db;
|
|
72
|
+
constructor(db) {
|
|
73
|
+
this.#db = db;
|
|
74
|
+
}
|
|
75
|
+
static open(dbPath) {
|
|
76
|
+
const resolvedPath = dbPath ?? DEFAULT_DB_PATH;
|
|
77
|
+
(0, node_fs.mkdirSync)((0, node_path.dirname)(resolvedPath), { recursive: true });
|
|
78
|
+
const db = new better_sqlite3.default(resolvedPath);
|
|
79
|
+
return DatabaseManager.#init(db, sqlite_vec.load);
|
|
80
|
+
}
|
|
81
|
+
static openInMemory() {
|
|
82
|
+
return DatabaseManager.#initInMemory(sqlite_vec.load);
|
|
83
|
+
}
|
|
84
|
+
/** For testing: inject a custom vec loader (e.g. a throwing stub). */
|
|
85
|
+
static _openInMemoryWithLoader(loader) {
|
|
86
|
+
return DatabaseManager.#initInMemory(loader);
|
|
87
|
+
}
|
|
88
|
+
static #initInMemory(loader) {
|
|
89
|
+
const db = new better_sqlite3.default(":memory:");
|
|
90
|
+
return DatabaseManager.#init(db, loader);
|
|
91
|
+
}
|
|
92
|
+
static #init(db, loader) {
|
|
93
|
+
try {
|
|
94
|
+
loader(db);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
throw new DatabaseError("Failed to load sqlite-vec extension", { cause: err });
|
|
97
|
+
}
|
|
98
|
+
db.pragma("journal_mode = WAL");
|
|
99
|
+
const manager = new DatabaseManager(db);
|
|
100
|
+
manager.#runMigrations();
|
|
101
|
+
return manager;
|
|
102
|
+
}
|
|
103
|
+
#runMigrations() {
|
|
104
|
+
this.#db.exec(`
|
|
105
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
106
|
+
key TEXT PRIMARY KEY,
|
|
107
|
+
value TEXT NOT NULL
|
|
108
|
+
);
|
|
109
|
+
`);
|
|
110
|
+
const row = this.#db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get();
|
|
111
|
+
const currentVersion = row ? Number.parseInt(row.value, 10) : 0;
|
|
112
|
+
for (const [targetVersion, sql] of MIGRATIONS) if (currentVersion < targetVersion) {
|
|
113
|
+
this.#db.exec(sql);
|
|
114
|
+
this.#db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)").run(String(targetVersion));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
get db() {
|
|
118
|
+
return this.#db;
|
|
119
|
+
}
|
|
120
|
+
close() {
|
|
121
|
+
this.#db.close();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/embedding/service.ts
|
|
126
|
+
var EmbeddingService = class {
|
|
127
|
+
modelCachePath;
|
|
128
|
+
onProgress;
|
|
129
|
+
pipelineInstance = null;
|
|
130
|
+
constructor(modelCachePath, onProgress) {
|
|
131
|
+
this.modelCachePath = modelCachePath ?? (0, node_path.join)((0, node_os.homedir)(), ".membank", "models");
|
|
132
|
+
this.onProgress = onProgress;
|
|
133
|
+
}
|
|
134
|
+
async getPipeline() {
|
|
135
|
+
if (this.pipelineInstance === null) this.pipelineInstance = await (0, _huggingface_transformers.pipeline)("feature-extraction", "Xenova/bge-small-en-v1.5", {
|
|
136
|
+
cache_dir: this.modelCachePath,
|
|
137
|
+
progress_callback: this.onProgress
|
|
138
|
+
});
|
|
139
|
+
return this.pipelineInstance;
|
|
140
|
+
}
|
|
141
|
+
async embed(text) {
|
|
142
|
+
const flat = (await (await this.getPipeline())(text, {
|
|
143
|
+
pooling: "mean",
|
|
144
|
+
normalize: true
|
|
145
|
+
})).data;
|
|
146
|
+
return flat instanceof Float32Array ? flat : new Float32Array(flat);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/memory/repository.ts
|
|
151
|
+
var MemoryRepository = class {
|
|
152
|
+
#db;
|
|
153
|
+
#embedding;
|
|
154
|
+
constructor(db, embeddingService) {
|
|
155
|
+
this.#db = db;
|
|
156
|
+
this.#embedding = embeddingService;
|
|
157
|
+
}
|
|
158
|
+
async save(options) {
|
|
159
|
+
const { content, type, tags = [], scope = "global", sourceHarness } = options;
|
|
160
|
+
const embedding = await this.#embedding.embed(content);
|
|
161
|
+
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
162
|
+
const top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
|
|
163
|
+
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
164
|
+
WHERE m.type = ? AND m.scope = ?
|
|
165
|
+
ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, scope);
|
|
166
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
167
|
+
if (top !== void 0 && top.similarity > .92) {
|
|
168
|
+
this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
|
|
169
|
+
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
|
|
170
|
+
const updated = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id);
|
|
171
|
+
return this.#rowToMemory(updated);
|
|
172
|
+
}
|
|
173
|
+
if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
|
|
174
|
+
const id = (0, node_crypto.randomUUID)();
|
|
175
|
+
this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, scope, source, access_count, pinned, needs_review, created_at, updated_at)
|
|
176
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);
|
|
177
|
+
this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
|
|
178
|
+
const row = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id);
|
|
179
|
+
return this.#rowToMemory(row);
|
|
180
|
+
}
|
|
181
|
+
async update(id, patch) {
|
|
182
|
+
const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
|
|
183
|
+
if (existing === void 0) throw new Error(`Memory not found: ${id}`);
|
|
184
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
185
|
+
const sets = ["updated_at = ?"];
|
|
186
|
+
const values = [now];
|
|
187
|
+
if (patch.content !== void 0) {
|
|
188
|
+
sets.push("content = ?");
|
|
189
|
+
values.push(patch.content);
|
|
190
|
+
}
|
|
191
|
+
if (patch.tags !== void 0) {
|
|
192
|
+
sets.push("tags = ?");
|
|
193
|
+
values.push(JSON.stringify(patch.tags));
|
|
194
|
+
}
|
|
195
|
+
values.push(id);
|
|
196
|
+
this.#db.db.prepare(`UPDATE memories SET ${sets.join(", ")} WHERE id = ?`).run(...values);
|
|
197
|
+
if (patch.content !== void 0) {
|
|
198
|
+
const embedding = await this.#embedding.embed(patch.content);
|
|
199
|
+
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
200
|
+
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
|
|
201
|
+
}
|
|
202
|
+
const updated = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id);
|
|
203
|
+
return this.#rowToMemory(updated);
|
|
204
|
+
}
|
|
205
|
+
delete(id) {
|
|
206
|
+
const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
|
|
207
|
+
if (row !== void 0) this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);
|
|
208
|
+
this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);
|
|
209
|
+
return Promise.resolve();
|
|
210
|
+
}
|
|
211
|
+
incrementAccessCount(id) {
|
|
212
|
+
this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
|
|
213
|
+
}
|
|
214
|
+
#rowToMemory(row) {
|
|
215
|
+
return {
|
|
216
|
+
id: row.id,
|
|
217
|
+
content: row.content,
|
|
218
|
+
type: row.type,
|
|
219
|
+
tags: JSON.parse(row.tags),
|
|
220
|
+
scope: row.scope,
|
|
221
|
+
sourceHarness: row.source,
|
|
222
|
+
accessCount: row.access_count,
|
|
223
|
+
pinned: row.pinned !== 0,
|
|
224
|
+
needsReview: row.needs_review !== 0,
|
|
225
|
+
createdAt: row.created_at,
|
|
226
|
+
updatedAt: row.updated_at
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/query/engine.ts
|
|
232
|
+
const TYPE_WEIGHTS = {
|
|
233
|
+
correction: 1,
|
|
234
|
+
preference: .8,
|
|
235
|
+
decision: .6,
|
|
236
|
+
learning: .4,
|
|
237
|
+
fact: .2
|
|
238
|
+
};
|
|
239
|
+
var QueryEngine = class {
|
|
240
|
+
#db;
|
|
241
|
+
#embedding;
|
|
242
|
+
#repo;
|
|
243
|
+
constructor(db, embeddingService, repo) {
|
|
244
|
+
this.#db = db;
|
|
245
|
+
this.#embedding = embeddingService;
|
|
246
|
+
this.#repo = repo;
|
|
247
|
+
}
|
|
248
|
+
async query(options) {
|
|
249
|
+
const { query, type, scope, limit = 10 } = options;
|
|
250
|
+
const queryEmbedding = await this.#embedding.embed(query);
|
|
251
|
+
const queryBlob = Buffer.from(queryEmbedding.buffer);
|
|
252
|
+
const whereClauses = [];
|
|
253
|
+
const params = [queryBlob];
|
|
254
|
+
if (type !== void 0) {
|
|
255
|
+
whereClauses.push("m.type = ?");
|
|
256
|
+
params.push(type);
|
|
257
|
+
}
|
|
258
|
+
if (scope !== void 0) {
|
|
259
|
+
whereClauses.push("m.scope = ?");
|
|
260
|
+
params.push(scope);
|
|
261
|
+
}
|
|
262
|
+
const sql = `
|
|
263
|
+
SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim
|
|
264
|
+
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
265
|
+
${whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : ""}
|
|
266
|
+
`;
|
|
267
|
+
const rows = this.#db.db.prepare(sql).all(...params);
|
|
268
|
+
const now = Date.now();
|
|
269
|
+
const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
|
|
270
|
+
const memory = this.#rowToMemory(row);
|
|
271
|
+
const score = this.#computeScore(memory, now);
|
|
272
|
+
return {
|
|
273
|
+
...memory,
|
|
274
|
+
score
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
scored.sort((a, b) => b.score - a.score);
|
|
278
|
+
const results = scored.slice(0, limit);
|
|
279
|
+
for (const result of results) this.#repo.incrementAccessCount(result.id);
|
|
280
|
+
return results;
|
|
281
|
+
}
|
|
282
|
+
#computeScore(memory, now) {
|
|
283
|
+
const typeWeight = TYPE_WEIGHTS[memory.type];
|
|
284
|
+
const accessCountNorm = memory.accessCount / (memory.accessCount + 10);
|
|
285
|
+
const recencyNorm = 1 / (1 + (now - new Date(memory.updatedAt).getTime()) / 864e5);
|
|
286
|
+
const pinned = memory.pinned ? 1 : 0;
|
|
287
|
+
return typeWeight * .4 + accessCountNorm * .3 + recencyNorm * .2 + pinned * .1;
|
|
288
|
+
}
|
|
289
|
+
#rowToMemory(row) {
|
|
290
|
+
return {
|
|
291
|
+
id: row.id,
|
|
292
|
+
content: row.content,
|
|
293
|
+
type: row.type,
|
|
294
|
+
tags: JSON.parse(row.tags),
|
|
295
|
+
scope: row.scope,
|
|
296
|
+
sourceHarness: row.source,
|
|
297
|
+
accessCount: row.access_count,
|
|
298
|
+
pinned: row.pinned !== 0,
|
|
299
|
+
needsReview: row.needs_review !== 0,
|
|
300
|
+
createdAt: row.created_at,
|
|
301
|
+
updatedAt: row.updated_at
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/scope/resolver.ts
|
|
307
|
+
const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
308
|
+
function sha256Truncated(input) {
|
|
309
|
+
return (0, node_crypto.createHash)("sha256").update(input).digest("hex").slice(0, 16);
|
|
310
|
+
}
|
|
311
|
+
async function resolveScope() {
|
|
312
|
+
try {
|
|
313
|
+
const { stdout } = await execFileAsync("git", [
|
|
314
|
+
"remote",
|
|
315
|
+
"get-url",
|
|
316
|
+
"origin"
|
|
317
|
+
]);
|
|
318
|
+
const url = stdout.trim();
|
|
319
|
+
if (url) return sha256Truncated(url);
|
|
320
|
+
} catch {}
|
|
321
|
+
return sha256Truncated(process.cwd());
|
|
322
|
+
}
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/session/builder.ts
|
|
325
|
+
const MEMORY_TYPES = [
|
|
326
|
+
"correction",
|
|
327
|
+
"preference",
|
|
328
|
+
"decision",
|
|
329
|
+
"learning",
|
|
330
|
+
"fact"
|
|
331
|
+
];
|
|
332
|
+
function rowToMemory(row) {
|
|
333
|
+
return {
|
|
334
|
+
id: row.id,
|
|
335
|
+
content: row.content,
|
|
336
|
+
type: row.type,
|
|
337
|
+
tags: JSON.parse(row.tags),
|
|
338
|
+
scope: row.scope,
|
|
339
|
+
sourceHarness: row.source,
|
|
340
|
+
accessCount: row.access_count,
|
|
341
|
+
pinned: row.pinned !== 0,
|
|
342
|
+
needsReview: row.needs_review !== 0,
|
|
343
|
+
createdAt: row.created_at,
|
|
344
|
+
updatedAt: row.updated_at
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function listMemoryTypes() {
|
|
348
|
+
return [...MEMORY_TYPES];
|
|
349
|
+
}
|
|
350
|
+
var SessionContextBuilder = class {
|
|
351
|
+
#db;
|
|
352
|
+
constructor(db) {
|
|
353
|
+
this.#db = db;
|
|
354
|
+
}
|
|
355
|
+
getSessionContext(projectScope) {
|
|
356
|
+
const pinnedGlobal = this.#db.db.prepare("SELECT * FROM memories WHERE scope = 'global' AND pinned = 1").all().map(rowToMemory);
|
|
357
|
+
const pinnedProject = this.#db.db.prepare("SELECT * FROM memories WHERE scope = ? AND pinned = 1").all(projectScope).map(rowToMemory);
|
|
358
|
+
const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
|
|
359
|
+
const stats = Object.fromEntries(MEMORY_TYPES.map((t) => [t, 0]));
|
|
360
|
+
for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
|
|
361
|
+
return {
|
|
362
|
+
stats,
|
|
363
|
+
pinnedGlobal,
|
|
364
|
+
pinnedProject
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
//#endregion
|
|
369
|
+
exports.DatabaseError = DatabaseError;
|
|
370
|
+
exports.DatabaseManager = DatabaseManager;
|
|
371
|
+
exports.EmbeddingService = EmbeddingService;
|
|
372
|
+
exports.MembankError = MembankError;
|
|
373
|
+
exports.MemoryRepository = MemoryRepository;
|
|
374
|
+
exports.QueryEngine = QueryEngine;
|
|
375
|
+
exports.SessionContextBuilder = SessionContextBuilder;
|
|
376
|
+
exports.listMemoryTypes = listMemoryTypes;
|
|
377
|
+
exports.resolveScope = resolveScope;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import BetterSqlite3 from "better-sqlite3";
|
|
2
|
+
|
|
3
|
+
//#region src/db/errors.d.ts
|
|
4
|
+
declare class MembankError extends Error {
|
|
5
|
+
constructor(message: string, options?: ErrorOptions);
|
|
6
|
+
}
|
|
7
|
+
declare class DatabaseError extends MembankError {
|
|
8
|
+
constructor(message: string, options?: ErrorOptions);
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/db/manager.d.ts
|
|
12
|
+
type VecLoader = (db: BetterSqlite3.Database) => void;
|
|
13
|
+
declare class DatabaseManager {
|
|
14
|
+
#private;
|
|
15
|
+
private constructor();
|
|
16
|
+
static open(dbPath?: string): DatabaseManager;
|
|
17
|
+
static openInMemory(): DatabaseManager;
|
|
18
|
+
/** For testing: inject a custom vec loader (e.g. a throwing stub). */
|
|
19
|
+
static _openInMemoryWithLoader(loader: VecLoader): DatabaseManager;
|
|
20
|
+
get db(): BetterSqlite3.Database;
|
|
21
|
+
close(): void;
|
|
22
|
+
}
|
|
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
|
+
//#region src/types.d.ts
|
|
39
|
+
type MemoryType = "correction" | "preference" | "decision" | "learning" | "fact";
|
|
40
|
+
interface Memory {
|
|
41
|
+
id: string;
|
|
42
|
+
content: string;
|
|
43
|
+
type: MemoryType;
|
|
44
|
+
tags: string[];
|
|
45
|
+
scope: string;
|
|
46
|
+
sourceHarness: string | null;
|
|
47
|
+
accessCount: number;
|
|
48
|
+
pinned: boolean;
|
|
49
|
+
needsReview: boolean;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
updatedAt: string;
|
|
52
|
+
}
|
|
53
|
+
interface QueryOptions {
|
|
54
|
+
query: string;
|
|
55
|
+
type?: MemoryType;
|
|
56
|
+
scope?: string;
|
|
57
|
+
limit?: number;
|
|
58
|
+
}
|
|
59
|
+
interface SaveOptions {
|
|
60
|
+
content: string;
|
|
61
|
+
type: MemoryType;
|
|
62
|
+
tags?: string[];
|
|
63
|
+
scope?: string;
|
|
64
|
+
sourceHarness?: string;
|
|
65
|
+
}
|
|
66
|
+
interface SessionContext {
|
|
67
|
+
stats: Record<MemoryType, number>;
|
|
68
|
+
pinnedGlobal: Memory[];
|
|
69
|
+
pinnedProject: Memory[];
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/memory/repository.d.ts
|
|
73
|
+
declare class MemoryRepository {
|
|
74
|
+
#private;
|
|
75
|
+
constructor(db: DatabaseManager, embeddingService: EmbeddingService);
|
|
76
|
+
save(options: SaveOptions): Promise<Memory>;
|
|
77
|
+
update(id: string, patch: {
|
|
78
|
+
content?: string;
|
|
79
|
+
tags?: string[];
|
|
80
|
+
}): Promise<Memory>;
|
|
81
|
+
delete(id: string): Promise<void>;
|
|
82
|
+
list(opts?: {
|
|
83
|
+
type?: MemoryType;
|
|
84
|
+
pinned?: boolean;
|
|
85
|
+
}): Memory[];
|
|
86
|
+
stats(): {
|
|
87
|
+
byType: Record<MemoryType, number>;
|
|
88
|
+
total: number;
|
|
89
|
+
needsReview: number;
|
|
90
|
+
};
|
|
91
|
+
incrementAccessCount(id: string): void;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/query/engine.d.ts
|
|
95
|
+
declare class QueryEngine {
|
|
96
|
+
#private;
|
|
97
|
+
constructor(db: DatabaseManager, embeddingService: EmbeddingService, repo: MemoryRepository);
|
|
98
|
+
query(options: QueryOptions): Promise<Array<Memory & {
|
|
99
|
+
score: number;
|
|
100
|
+
}>>;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/scope/resolver.d.ts
|
|
104
|
+
declare function resolveScope(): Promise<string>;
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/session/builder.d.ts
|
|
107
|
+
declare function listMemoryTypes(): MemoryType[];
|
|
108
|
+
declare class SessionContextBuilder {
|
|
109
|
+
#private;
|
|
110
|
+
constructor(db: DatabaseManager);
|
|
111
|
+
getSessionContext(projectScope: string): SessionContext;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, Memory, MemoryRepository, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope };
|
|
115
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/embedding/service.ts","../src/types.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,cA4BvB,eAAA;EAAA;UAGJ,WAAA,CAAA;EAAA,OAIA,IAAA,CAAK,MAAA,YAAkB,eAAA;EAAA,OAOvB,YAAA,CAAA,GAAgB,eAAA;EDnDS;EAAA,OCwDzB,uBAAA,CAAwB,MAAA,EAAQ,SAAA,GAAY,eAAA;EAAA,IAkD/C,EAAA,CAAA,GAAM,aAAA,CAAc,QAAA;EAIxB,KAAA,CAAA;AAAA;;;KC1GU,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;;;KC1BzB,UAAA;AAAA,UAEK,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;;;cCVJ,gBAAA;EAAA;cAIC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA;EAK7C,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,MAAA;EAgEpC,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;EAqBtD,KAAA,CAAA;IAAW,MAAA,EAAQ,MAAA,CAAO,UAAA;IAAqB,KAAA;IAAe,WAAA;EAAA;EA2B9D,oBAAA,CAAqB,EAAA;AAAA;;;cC9KV,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;;;iBC7BzC,YAAA,CAAA,GAAgB,OAAA;;;iBC8BtB,eAAA,CAAA,GAAmB,UAAA;AAAA,cAItB,qBAAA;EAAA;cAGC,EAAA,EAAI,eAAA;EAIhB,iBAAA,CAAkB,YAAA,WAAuB,cAAA;AAAA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import BetterSqlite3 from "better-sqlite3";
|
|
2
|
+
|
|
3
|
+
//#region src/db/errors.d.ts
|
|
4
|
+
declare class MembankError extends Error {
|
|
5
|
+
constructor(message: string, options?: ErrorOptions);
|
|
6
|
+
}
|
|
7
|
+
declare class DatabaseError extends MembankError {
|
|
8
|
+
constructor(message: string, options?: ErrorOptions);
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/db/manager.d.ts
|
|
12
|
+
type VecLoader = (db: BetterSqlite3.Database) => void;
|
|
13
|
+
declare class DatabaseManager {
|
|
14
|
+
#private;
|
|
15
|
+
private constructor();
|
|
16
|
+
static open(dbPath?: string): DatabaseManager;
|
|
17
|
+
static openInMemory(): DatabaseManager;
|
|
18
|
+
/** For testing: inject a custom vec loader (e.g. a throwing stub). */
|
|
19
|
+
static _openInMemoryWithLoader(loader: VecLoader): DatabaseManager;
|
|
20
|
+
get db(): BetterSqlite3.Database;
|
|
21
|
+
close(): void;
|
|
22
|
+
}
|
|
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
|
+
//#region src/types.d.ts
|
|
39
|
+
type MemoryType = "correction" | "preference" | "decision" | "learning" | "fact";
|
|
40
|
+
interface Memory {
|
|
41
|
+
id: string;
|
|
42
|
+
content: string;
|
|
43
|
+
type: MemoryType;
|
|
44
|
+
tags: string[];
|
|
45
|
+
scope: string;
|
|
46
|
+
sourceHarness: string | null;
|
|
47
|
+
accessCount: number;
|
|
48
|
+
pinned: boolean;
|
|
49
|
+
needsReview: boolean;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
updatedAt: string;
|
|
52
|
+
}
|
|
53
|
+
interface QueryOptions {
|
|
54
|
+
query: string;
|
|
55
|
+
type?: MemoryType;
|
|
56
|
+
scope?: string;
|
|
57
|
+
limit?: number;
|
|
58
|
+
}
|
|
59
|
+
interface SaveOptions {
|
|
60
|
+
content: string;
|
|
61
|
+
type: MemoryType;
|
|
62
|
+
tags?: string[];
|
|
63
|
+
scope?: string;
|
|
64
|
+
sourceHarness?: string;
|
|
65
|
+
}
|
|
66
|
+
interface SessionContext {
|
|
67
|
+
stats: Record<MemoryType, number>;
|
|
68
|
+
pinnedGlobal: Memory[];
|
|
69
|
+
pinnedProject: Memory[];
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/memory/repository.d.ts
|
|
73
|
+
declare class MemoryRepository {
|
|
74
|
+
#private;
|
|
75
|
+
constructor(db: DatabaseManager, embeddingService: EmbeddingService);
|
|
76
|
+
save(options: SaveOptions): Promise<Memory>;
|
|
77
|
+
update(id: string, patch: {
|
|
78
|
+
content?: string;
|
|
79
|
+
tags?: string[];
|
|
80
|
+
}): Promise<Memory>;
|
|
81
|
+
delete(id: string): Promise<void>;
|
|
82
|
+
list(opts?: {
|
|
83
|
+
type?: MemoryType;
|
|
84
|
+
pinned?: boolean;
|
|
85
|
+
}): Memory[];
|
|
86
|
+
stats(): {
|
|
87
|
+
byType: Record<MemoryType, number>;
|
|
88
|
+
total: number;
|
|
89
|
+
needsReview: number;
|
|
90
|
+
};
|
|
91
|
+
incrementAccessCount(id: string): void;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/query/engine.d.ts
|
|
95
|
+
declare class QueryEngine {
|
|
96
|
+
#private;
|
|
97
|
+
constructor(db: DatabaseManager, embeddingService: EmbeddingService, repo: MemoryRepository);
|
|
98
|
+
query(options: QueryOptions): Promise<Array<Memory & {
|
|
99
|
+
score: number;
|
|
100
|
+
}>>;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/scope/resolver.d.ts
|
|
104
|
+
declare function resolveScope(): Promise<string>;
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/session/builder.d.ts
|
|
107
|
+
declare function listMemoryTypes(): MemoryType[];
|
|
108
|
+
declare class SessionContextBuilder {
|
|
109
|
+
#private;
|
|
110
|
+
constructor(db: DatabaseManager);
|
|
111
|
+
getSessionContext(projectScope: string): SessionContext;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, Memory, MemoryRepository, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope };
|
|
115
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/embedding/service.ts","../src/types.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,cA4BvB,eAAA;EAAA;UAGJ,WAAA,CAAA;EAAA,OAIA,IAAA,CAAK,MAAA,YAAkB,eAAA;EAAA,OAOvB,YAAA,CAAA,GAAgB,eAAA;EDnDS;EAAA,OCwDzB,uBAAA,CAAwB,MAAA,EAAQ,SAAA,GAAY,eAAA;EAAA,IAkD/C,EAAA,CAAA,GAAM,aAAA,CAAc,QAAA;EAIxB,KAAA,CAAA;AAAA;;;KC1GU,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;;;KC1BzB,UAAA;AAAA,UAEK,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;;;cCVJ,gBAAA;EAAA;cAIC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA;EAK7C,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,MAAA;EAgEpC,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;EAqBtD,KAAA,CAAA;IAAW,MAAA,EAAQ,MAAA,CAAO,UAAA;IAAqB,KAAA;IAAe,WAAA;EAAA;EA2B9D,oBAAA,CAAqB,EAAA;AAAA;;;cC9KV,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;;;iBC7BzC,YAAA,CAAA,GAAgB,OAAA;;;iBC8BtB,eAAA,CAAA,GAAmB,UAAA;AAAA,cAItB,qBAAA;EAAA;cAGC,EAAA,EAAI,eAAA;EAIhB,iBAAA,CAAkB,YAAA,WAAuB,cAAA;AAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import BetterSqlite3 from "better-sqlite3";
|
|
5
|
+
import * as sqliteVec from "sqlite-vec";
|
|
6
|
+
import { pipeline } from "@huggingface/transformers";
|
|
7
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
8
|
+
import { execFile } from "node:child_process";
|
|
9
|
+
import { promisify } from "node:util";
|
|
10
|
+
//#region src/db/errors.ts
|
|
11
|
+
var MembankError = class extends Error {
|
|
12
|
+
constructor(message, options) {
|
|
13
|
+
super(message, options);
|
|
14
|
+
this.name = "MembankError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var DatabaseError = class extends MembankError {
|
|
18
|
+
constructor(message, options) {
|
|
19
|
+
super(message, options);
|
|
20
|
+
this.name = "DatabaseError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/db/manager.ts
|
|
25
|
+
const DEFAULT_DB_PATH = join(homedir(), ".membank", "memory.db");
|
|
26
|
+
const MIGRATIONS = [[1, `
|
|
27
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
content TEXT NOT NULL,
|
|
30
|
+
type TEXT NOT NULL,
|
|
31
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
32
|
+
scope TEXT NOT NULL,
|
|
33
|
+
source TEXT,
|
|
34
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
pinned INTEGER NOT NULL DEFAULT 0,
|
|
36
|
+
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
37
|
+
created_at TEXT NOT NULL,
|
|
38
|
+
updated_at TEXT NOT NULL
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(
|
|
42
|
+
embedding FLOAT[384]
|
|
43
|
+
);
|
|
44
|
+
`]];
|
|
45
|
+
var DatabaseManager = class DatabaseManager {
|
|
46
|
+
#db;
|
|
47
|
+
constructor(db) {
|
|
48
|
+
this.#db = db;
|
|
49
|
+
}
|
|
50
|
+
static open(dbPath) {
|
|
51
|
+
const resolvedPath = dbPath ?? DEFAULT_DB_PATH;
|
|
52
|
+
mkdirSync(dirname(resolvedPath), { recursive: true });
|
|
53
|
+
const db = new BetterSqlite3(resolvedPath);
|
|
54
|
+
return DatabaseManager.#init(db, sqliteVec.load);
|
|
55
|
+
}
|
|
56
|
+
static openInMemory() {
|
|
57
|
+
return DatabaseManager.#initInMemory(sqliteVec.load);
|
|
58
|
+
}
|
|
59
|
+
/** For testing: inject a custom vec loader (e.g. a throwing stub). */
|
|
60
|
+
static _openInMemoryWithLoader(loader) {
|
|
61
|
+
return DatabaseManager.#initInMemory(loader);
|
|
62
|
+
}
|
|
63
|
+
static #initInMemory(loader) {
|
|
64
|
+
const db = new BetterSqlite3(":memory:");
|
|
65
|
+
return DatabaseManager.#init(db, loader);
|
|
66
|
+
}
|
|
67
|
+
static #init(db, loader) {
|
|
68
|
+
try {
|
|
69
|
+
loader(db);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
throw new DatabaseError("Failed to load sqlite-vec extension", { cause: err });
|
|
72
|
+
}
|
|
73
|
+
db.pragma("journal_mode = WAL");
|
|
74
|
+
const manager = new DatabaseManager(db);
|
|
75
|
+
manager.#runMigrations();
|
|
76
|
+
return manager;
|
|
77
|
+
}
|
|
78
|
+
#runMigrations() {
|
|
79
|
+
this.#db.exec(`
|
|
80
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
81
|
+
key TEXT PRIMARY KEY,
|
|
82
|
+
value TEXT NOT NULL
|
|
83
|
+
);
|
|
84
|
+
`);
|
|
85
|
+
const row = this.#db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get();
|
|
86
|
+
const currentVersion = row ? Number.parseInt(row.value, 10) : 0;
|
|
87
|
+
for (const [targetVersion, sql] of MIGRATIONS) if (currentVersion < targetVersion) {
|
|
88
|
+
this.#db.exec(sql);
|
|
89
|
+
this.#db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)").run(String(targetVersion));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
get db() {
|
|
93
|
+
return this.#db;
|
|
94
|
+
}
|
|
95
|
+
close() {
|
|
96
|
+
this.#db.close();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/embedding/service.ts
|
|
101
|
+
var EmbeddingService = class {
|
|
102
|
+
modelCachePath;
|
|
103
|
+
onProgress;
|
|
104
|
+
pipelineInstance = null;
|
|
105
|
+
constructor(modelCachePath, onProgress) {
|
|
106
|
+
this.modelCachePath = modelCachePath ?? join(homedir(), ".membank", "models");
|
|
107
|
+
this.onProgress = onProgress;
|
|
108
|
+
}
|
|
109
|
+
async getPipeline() {
|
|
110
|
+
if (this.pipelineInstance === null) this.pipelineInstance = await pipeline("feature-extraction", "Xenova/bge-small-en-v1.5", {
|
|
111
|
+
cache_dir: this.modelCachePath,
|
|
112
|
+
progress_callback: this.onProgress
|
|
113
|
+
});
|
|
114
|
+
return this.pipelineInstance;
|
|
115
|
+
}
|
|
116
|
+
async embed(text) {
|
|
117
|
+
const flat = (await (await this.getPipeline())(text, {
|
|
118
|
+
pooling: "mean",
|
|
119
|
+
normalize: true
|
|
120
|
+
})).data;
|
|
121
|
+
return flat instanceof Float32Array ? flat : new Float32Array(flat);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/memory/repository.ts
|
|
126
|
+
var MemoryRepository = class {
|
|
127
|
+
#db;
|
|
128
|
+
#embedding;
|
|
129
|
+
constructor(db, embeddingService) {
|
|
130
|
+
this.#db = db;
|
|
131
|
+
this.#embedding = embeddingService;
|
|
132
|
+
}
|
|
133
|
+
async save(options) {
|
|
134
|
+
const { content, type, tags = [], scope = "global", sourceHarness } = options;
|
|
135
|
+
const embedding = await this.#embedding.embed(content);
|
|
136
|
+
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
137
|
+
const top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
|
|
138
|
+
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
139
|
+
WHERE m.type = ? AND m.scope = ?
|
|
140
|
+
ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, scope);
|
|
141
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
142
|
+
if (top !== void 0 && top.similarity > .92) {
|
|
143
|
+
this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
|
|
144
|
+
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
|
|
145
|
+
const updated = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id);
|
|
146
|
+
return this.#rowToMemory(updated);
|
|
147
|
+
}
|
|
148
|
+
if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
|
|
149
|
+
const id = randomUUID();
|
|
150
|
+
this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, scope, source, access_count, pinned, needs_review, created_at, updated_at)
|
|
151
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);
|
|
152
|
+
this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
|
|
153
|
+
const row = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id);
|
|
154
|
+
return this.#rowToMemory(row);
|
|
155
|
+
}
|
|
156
|
+
async update(id, patch) {
|
|
157
|
+
const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
|
|
158
|
+
if (existing === void 0) throw new Error(`Memory not found: ${id}`);
|
|
159
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
160
|
+
const sets = ["updated_at = ?"];
|
|
161
|
+
const values = [now];
|
|
162
|
+
if (patch.content !== void 0) {
|
|
163
|
+
sets.push("content = ?");
|
|
164
|
+
values.push(patch.content);
|
|
165
|
+
}
|
|
166
|
+
if (patch.tags !== void 0) {
|
|
167
|
+
sets.push("tags = ?");
|
|
168
|
+
values.push(JSON.stringify(patch.tags));
|
|
169
|
+
}
|
|
170
|
+
values.push(id);
|
|
171
|
+
this.#db.db.prepare(`UPDATE memories SET ${sets.join(", ")} WHERE id = ?`).run(...values);
|
|
172
|
+
if (patch.content !== void 0) {
|
|
173
|
+
const embedding = await this.#embedding.embed(patch.content);
|
|
174
|
+
const embeddingBlob = Buffer.from(embedding.buffer);
|
|
175
|
+
this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
|
|
176
|
+
}
|
|
177
|
+
const updated = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id);
|
|
178
|
+
return this.#rowToMemory(updated);
|
|
179
|
+
}
|
|
180
|
+
delete(id) {
|
|
181
|
+
const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
|
|
182
|
+
if (row !== void 0) this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);
|
|
183
|
+
this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);
|
|
184
|
+
return Promise.resolve();
|
|
185
|
+
}
|
|
186
|
+
list(opts) {
|
|
187
|
+
const conditions = [];
|
|
188
|
+
const params = [];
|
|
189
|
+
if (opts?.type !== void 0) {
|
|
190
|
+
conditions.push("type = ?");
|
|
191
|
+
params.push(opts.type);
|
|
192
|
+
}
|
|
193
|
+
if (opts?.pinned === true) conditions.push("pinned = 1");
|
|
194
|
+
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((r) => this.#rowToMemory(r));
|
|
196
|
+
}
|
|
197
|
+
stats() {
|
|
198
|
+
const byType = Object.fromEntries([
|
|
199
|
+
"correction",
|
|
200
|
+
"preference",
|
|
201
|
+
"decision",
|
|
202
|
+
"learning",
|
|
203
|
+
"fact"
|
|
204
|
+
].map((t) => [t, 0]));
|
|
205
|
+
const typeRows = this.#db.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();
|
|
206
|
+
for (const row of typeRows) if (row.type in byType) byType[row.type] = row.count;
|
|
207
|
+
const totals = this.#db.db.prepare(`SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`).get() ?? {
|
|
208
|
+
total: 0,
|
|
209
|
+
needsReview: 0
|
|
210
|
+
};
|
|
211
|
+
return {
|
|
212
|
+
byType,
|
|
213
|
+
total: totals.total,
|
|
214
|
+
needsReview: totals.needsReview ?? 0
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
incrementAccessCount(id) {
|
|
218
|
+
this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
|
|
219
|
+
}
|
|
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
|
+
};
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/query/engine.ts
|
|
238
|
+
const TYPE_WEIGHTS = {
|
|
239
|
+
correction: 1,
|
|
240
|
+
preference: .8,
|
|
241
|
+
decision: .6,
|
|
242
|
+
learning: .4,
|
|
243
|
+
fact: .2
|
|
244
|
+
};
|
|
245
|
+
var QueryEngine = class {
|
|
246
|
+
#db;
|
|
247
|
+
#embedding;
|
|
248
|
+
#repo;
|
|
249
|
+
constructor(db, embeddingService, repo) {
|
|
250
|
+
this.#db = db;
|
|
251
|
+
this.#embedding = embeddingService;
|
|
252
|
+
this.#repo = repo;
|
|
253
|
+
}
|
|
254
|
+
async query(options) {
|
|
255
|
+
const { query, type, scope, limit = 10 } = options;
|
|
256
|
+
const queryEmbedding = await this.#embedding.embed(query);
|
|
257
|
+
const queryBlob = Buffer.from(queryEmbedding.buffer);
|
|
258
|
+
const whereClauses = [];
|
|
259
|
+
const params = [queryBlob];
|
|
260
|
+
if (type !== void 0) {
|
|
261
|
+
whereClauses.push("m.type = ?");
|
|
262
|
+
params.push(type);
|
|
263
|
+
}
|
|
264
|
+
if (scope !== void 0) {
|
|
265
|
+
whereClauses.push("m.scope = ?");
|
|
266
|
+
params.push(scope);
|
|
267
|
+
}
|
|
268
|
+
const sql = `
|
|
269
|
+
SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim
|
|
270
|
+
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
271
|
+
${whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : ""}
|
|
272
|
+
`;
|
|
273
|
+
const rows = this.#db.db.prepare(sql).all(...params);
|
|
274
|
+
const now = Date.now();
|
|
275
|
+
const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
|
|
276
|
+
const memory = this.#rowToMemory(row);
|
|
277
|
+
const score = this.#computeScore(memory, now);
|
|
278
|
+
return {
|
|
279
|
+
...memory,
|
|
280
|
+
score
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
scored.sort((a, b) => b.score - a.score);
|
|
284
|
+
const results = scored.slice(0, limit);
|
|
285
|
+
for (const result of results) this.#repo.incrementAccessCount(result.id);
|
|
286
|
+
return results;
|
|
287
|
+
}
|
|
288
|
+
#computeScore(memory, now) {
|
|
289
|
+
const typeWeight = TYPE_WEIGHTS[memory.type];
|
|
290
|
+
const accessCountNorm = memory.accessCount / (memory.accessCount + 10);
|
|
291
|
+
const recencyNorm = 1 / (1 + (now - new Date(memory.updatedAt).getTime()) / 864e5);
|
|
292
|
+
const pinned = memory.pinned ? 1 : 0;
|
|
293
|
+
return typeWeight * .4 + accessCountNorm * .3 + recencyNorm * .2 + pinned * .1;
|
|
294
|
+
}
|
|
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
|
+
};
|
|
311
|
+
//#endregion
|
|
312
|
+
//#region src/scope/resolver.ts
|
|
313
|
+
const execFileAsync = promisify(execFile);
|
|
314
|
+
function sha256Truncated(input) {
|
|
315
|
+
return createHash("sha256").update(input).digest("hex").slice(0, 16);
|
|
316
|
+
}
|
|
317
|
+
async function resolveScope() {
|
|
318
|
+
try {
|
|
319
|
+
const { stdout } = await execFileAsync("git", [
|
|
320
|
+
"remote",
|
|
321
|
+
"get-url",
|
|
322
|
+
"origin"
|
|
323
|
+
]);
|
|
324
|
+
const url = stdout.trim();
|
|
325
|
+
if (url) return sha256Truncated(url);
|
|
326
|
+
} catch {}
|
|
327
|
+
return sha256Truncated(process.cwd());
|
|
328
|
+
}
|
|
329
|
+
//#endregion
|
|
330
|
+
//#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
|
+
function listMemoryTypes() {
|
|
354
|
+
return [...MEMORY_TYPES];
|
|
355
|
+
}
|
|
356
|
+
var SessionContextBuilder = class {
|
|
357
|
+
#db;
|
|
358
|
+
constructor(db) {
|
|
359
|
+
this.#db = db;
|
|
360
|
+
}
|
|
361
|
+
getSessionContext(projectScope) {
|
|
362
|
+
const pinnedGlobal = this.#db.db.prepare("SELECT * FROM memories WHERE scope = 'global' AND pinned = 1").all().map(rowToMemory);
|
|
363
|
+
const pinnedProject = this.#db.db.prepare("SELECT * FROM memories WHERE scope = ? AND pinned = 1").all(projectScope).map(rowToMemory);
|
|
364
|
+
const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
|
|
365
|
+
const stats = Object.fromEntries(MEMORY_TYPES.map((t) => [t, 0]));
|
|
366
|
+
for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
|
|
367
|
+
return {
|
|
368
|
+
stats,
|
|
369
|
+
pinnedGlobal,
|
|
370
|
+
pinnedProject
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
//#endregion
|
|
375
|
+
export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, MemoryRepository, QueryEngine, SessionContextBuilder, listMemoryTypes, resolveScope };
|
|
376
|
+
|
|
377
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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 {\r\n constructor(message: string, options?: ErrorOptions) {\r\n super(message, options);\r\n this.name = \"MembankError\";\r\n }\r\n}\r\n\r\nexport class DatabaseError extends MembankError {\r\n constructor(message: string, options?: ErrorOptions) {\r\n super(message, options);\r\n this.name = \"DatabaseError\";\r\n }\r\n}\r\n","import { mkdirSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { dirname, join } from \"node:path\";\r\nimport BetterSqlite3 from \"better-sqlite3\";\r\nimport * as sqliteVec from \"sqlite-vec\";\r\nimport { DatabaseError } from \"./errors.js\";\r\n\r\nconst DEFAULT_DB_PATH = join(homedir(), \".membank\", \"memory.db\");\r\n\r\ntype VecLoader = (db: BetterSqlite3.Database) => void;\r\n\r\n// Each entry is [targetVersion, sql]. Migrations are applied in ascending order.\r\nconst MIGRATIONS: [number, string][] = [\r\n [\r\n 1,\r\n `\r\nCREATE TABLE IF NOT EXISTS memories (\r\n id TEXT PRIMARY KEY,\r\n content TEXT NOT NULL,\r\n type TEXT NOT NULL,\r\n tags TEXT NOT NULL DEFAULT '[]',\r\n scope TEXT NOT NULL,\r\n source TEXT,\r\n access_count INTEGER NOT NULL DEFAULT 0,\r\n pinned INTEGER NOT NULL DEFAULT 0,\r\n needs_review INTEGER NOT NULL DEFAULT 0,\r\n created_at TEXT NOT NULL,\r\n updated_at TEXT NOT NULL\r\n);\r\n\r\nCREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(\r\n embedding FLOAT[384]\r\n);\r\n`,\r\n ],\r\n];\r\n\r\nexport class DatabaseManager {\r\n readonly #db: BetterSqlite3.Database;\r\n\r\n private constructor(db: BetterSqlite3.Database) {\r\n this.#db = db;\r\n }\r\n\r\n static open(dbPath?: string): DatabaseManager {\r\n const resolvedPath = dbPath ?? DEFAULT_DB_PATH;\r\n mkdirSync(dirname(resolvedPath), { recursive: true });\r\n const db = new BetterSqlite3(resolvedPath);\r\n return DatabaseManager.#init(db, sqliteVec.load);\r\n }\r\n\r\n static openInMemory(): DatabaseManager {\r\n return DatabaseManager.#initInMemory(sqliteVec.load);\r\n }\r\n\r\n /** For testing: inject a custom vec loader (e.g. a throwing stub). */\r\n static _openInMemoryWithLoader(loader: VecLoader): DatabaseManager {\r\n return DatabaseManager.#initInMemory(loader);\r\n }\r\n\r\n static #initInMemory(loader: VecLoader): DatabaseManager {\r\n const db = new BetterSqlite3(\":memory:\");\r\n return DatabaseManager.#init(db, loader);\r\n }\r\n\r\n static #init(db: BetterSqlite3.Database, loader: VecLoader): DatabaseManager {\r\n try {\r\n loader(db);\r\n } catch (err) {\r\n throw new DatabaseError(\"Failed to load sqlite-vec extension\", {\r\n cause: err,\r\n });\r\n }\r\n\r\n db.pragma(\"journal_mode = WAL\");\r\n\r\n const manager = new DatabaseManager(db);\r\n manager.#runMigrations();\r\n return manager;\r\n }\r\n\r\n #runMigrations(): void {\r\n // Bootstrap the meta table before reading schema_version from it\r\n this.#db.exec(`\r\n CREATE TABLE IF NOT EXISTS meta (\r\n key TEXT PRIMARY KEY,\r\n value TEXT NOT NULL\r\n );\r\n `);\r\n\r\n const row = this.#db\r\n .prepare<[], { value: string }>(\"SELECT value FROM meta WHERE key = 'schema_version'\")\r\n .get();\r\n\r\n const currentVersion = row ? Number.parseInt(row.value, 10) : 0;\r\n\r\n for (const [targetVersion, sql] of MIGRATIONS) {\r\n if (currentVersion < targetVersion) {\r\n this.#db.exec(sql);\r\n this.#db\r\n .prepare(\"INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)\")\r\n .run(String(targetVersion));\r\n }\r\n }\r\n }\r\n\r\n get db(): BetterSqlite3.Database {\r\n return this.#db;\r\n }\r\n\r\n close(): void {\r\n this.#db.close();\r\n }\r\n}\r\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\";\r\nimport type { DatabaseManager } from \"../db/manager.js\";\r\nimport type { EmbeddingService } from \"../embedding/service.js\";\r\nimport type { Memory, MemoryType, SaveOptions } from \"../types.js\";\r\n\r\ninterface MemoryRow {\r\n id: string;\r\n content: string;\r\n type: string;\r\n tags: string;\r\n scope: string;\r\n source: string | null;\r\n access_count: number;\r\n pinned: number;\r\n needs_review: number;\r\n created_at: string;\r\n updated_at: string;\r\n}\r\n\r\ninterface SimilarityRow extends MemoryRow {\r\n rowid: number;\r\n similarity: number;\r\n}\r\n\r\nexport class MemoryRepository {\r\n readonly #db: DatabaseManager;\r\n readonly #embedding: EmbeddingService;\r\n\r\n constructor(db: DatabaseManager, embeddingService: EmbeddingService) {\r\n this.#db = db;\r\n this.#embedding = embeddingService;\r\n }\r\n\r\n async save(options: SaveOptions): Promise<Memory> {\r\n const { content, type, tags = [], scope = \"global\", sourceHarness } = options;\r\n\r\n const embedding = await this.#embedding.embed(content);\r\n const embeddingBlob = Buffer.from(embedding.buffer);\r\n\r\n const top = this.#db.db\r\n .prepare<[Buffer, string, string], SimilarityRow>(\r\n `SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity\r\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\r\n WHERE m.type = ? AND m.scope = ?\r\n ORDER BY similarity DESC LIMIT 1`\r\n )\r\n .get(embeddingBlob, type, scope);\r\n\r\n const now = new Date().toISOString();\r\n\r\n if (top !== undefined && top.similarity > 0.92) {\r\n // Overwrite existing record\r\n this.#db.db\r\n .prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`)\r\n .run(content, now, top.id);\r\n\r\n this.#db.db\r\n .prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`)\r\n .run(embeddingBlob, top.rowid);\r\n\r\n const updated = this.#db.db\r\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\r\n .get(top.id);\r\n\r\n // updated is guaranteed to exist since we just updated it\r\n return this.#rowToMemory(updated as MemoryRow);\r\n }\r\n\r\n if (top !== undefined && top.similarity >= 0.75) {\r\n // Flag existing as needs_review\r\n this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);\r\n }\r\n\r\n // Insert new record\r\n const id = randomUUID();\r\n this.#db.db\r\n .prepare(\r\n `INSERT INTO memories (id, content, type, tags, scope, source, access_count, pinned, needs_review, created_at, updated_at)\r\n VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`\r\n )\r\n .run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);\r\n\r\n // sqlite-vec v0.1.9 does not accept parameterized rowid on INSERT into vec0 tables.\r\n // Use a SELECT subquery to copy the rowid from the memories row we just inserted.\r\n this.#db.db\r\n .prepare(\r\n `INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`\r\n )\r\n .run(embeddingBlob, id);\r\n\r\n const row = this.#db.db\r\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\r\n .get(id) as MemoryRow;\r\n\r\n return this.#rowToMemory(row);\r\n }\r\n\r\n async update(id: string, patch: { content?: string; tags?: string[] }): Promise<Memory> {\r\n const existing = this.#db.db\r\n .prepare<[string], MemoryRow & { rowid: number }>(\r\n `SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`\r\n )\r\n .get(id);\r\n\r\n if (existing === undefined) {\r\n throw new Error(`Memory not found: ${id}`);\r\n }\r\n\r\n const now = new Date().toISOString();\r\n const sets: string[] = [\"updated_at = ?\"];\r\n const values: string[] = [now];\r\n\r\n if (patch.content !== undefined) {\r\n sets.push(\"content = ?\");\r\n values.push(patch.content);\r\n }\r\n\r\n if (patch.tags !== undefined) {\r\n sets.push(\"tags = ?\");\r\n values.push(JSON.stringify(patch.tags));\r\n }\r\n\r\n values.push(id);\r\n this.#db.db.prepare(`UPDATE memories SET ${sets.join(\", \")} WHERE id = ?`).run(...values);\r\n\r\n if (patch.content !== undefined) {\r\n const embedding = await this.#embedding.embed(patch.content);\r\n const embeddingBlob = Buffer.from(embedding.buffer);\r\n this.#db.db\r\n .prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`)\r\n .run(embeddingBlob, existing.rowid);\r\n }\r\n\r\n const updated = this.#db.db\r\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\r\n .get(id) as MemoryRow;\r\n\r\n return this.#rowToMemory(updated);\r\n }\r\n\r\n delete(id: string): Promise<void> {\r\n const row = this.#db.db\r\n .prepare<[string], { rowid: number }>(`SELECT rowid FROM memories WHERE id = ?`)\r\n .get(id);\r\n\r\n if (row !== undefined) {\r\n this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);\r\n }\r\n\r\n this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);\r\n\r\n return Promise.resolve();\r\n }\r\n\r\n list(opts?: { type?: MemoryType; pinned?: boolean }): Memory[] {\r\n const conditions: string[] = [];\r\n const params: (string | number)[] = [];\r\n\r\n if (opts?.type !== undefined) {\r\n conditions.push(\"type = ?\");\r\n params.push(opts.type);\r\n }\r\n\r\n if (opts?.pinned === true) {\r\n conditions.push(\"pinned = 1\");\r\n }\r\n\r\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\r\n const rows = this.#db.db\r\n .prepare<(string | number)[], MemoryRow>(`SELECT * FROM memories ${where} ORDER BY created_at DESC`)\r\n .all(...params);\r\n\r\n return rows.map((r) => this.#rowToMemory(r));\r\n }\r\n\r\n stats(): { byType: Record<MemoryType, number>; total: number; needsReview: number } {\r\n const allTypes: MemoryType[] = [\"correction\", \"preference\", \"decision\", \"learning\", \"fact\"];\r\n const byType = Object.fromEntries(allTypes.map((t) => [t, 0])) as Record<MemoryType, number>;\r\n\r\n const typeRows = this.#db.db\r\n .prepare<[], { type: string; count: number }>(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`)\r\n .all();\r\n\r\n for (const row of typeRows) {\r\n if (row.type in byType) {\r\n byType[row.type as MemoryType] = row.count;\r\n }\r\n }\r\n\r\n const totals = this.#db.db\r\n .prepare<[], { total: number; needsReview: number }>(\r\n `SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`\r\n )\r\n .get() ?? { total: 0, needsReview: 0 };\r\n\r\n return {\r\n byType,\r\n total: totals.total,\r\n needsReview: totals.needsReview ?? 0,\r\n };\r\n }\r\n\r\n incrementAccessCount(id: string): void {\r\n this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);\r\n }\r\n\r\n #rowToMemory(row: MemoryRow): Memory {\r\n return {\r\n id: row.id,\r\n content: row.content,\r\n type: row.type as MemoryType,\r\n tags: JSON.parse(row.tags) as string[],\r\n scope: row.scope,\r\n sourceHarness: row.source,\r\n accessCount: row.access_count,\r\n pinned: row.pinned !== 0,\r\n needsReview: row.needs_review !== 0,\r\n createdAt: row.created_at,\r\n updatedAt: row.updated_at,\r\n };\r\n }\r\n}\r\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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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@membank/core",
|
|
3
|
+
"version": "0.0.0-dev-20260427133418",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./dist/index.mjs",
|
|
8
|
+
"require": "./dist/index.cjs",
|
|
9
|
+
"types": "./dist/index.d.mts"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@huggingface/transformers": "^4.2.0",
|
|
17
|
+
"better-sqlite3": "^12.9.0",
|
|
18
|
+
"sqlite-vec": "^0.1.9"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
22
|
+
"@types/node": "^25.6.0",
|
|
23
|
+
"typescript": "^6.0.3",
|
|
24
|
+
"vitest": "^3.2.4"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsdown",
|
|
28
|
+
"dev": "tsdown --watch",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"lint": "biome check ./src",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"test:integration": "vitest run --reporter=verbose src/**/*.integration.test.ts",
|
|
34
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
35
|
+
}
|
|
36
|
+
}
|