@membank/core 0.9.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,70 +1,4 @@
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 zod = require("zod");
32
- let _huggingface_transformers = require("@huggingface/transformers");
33
- let node_crypto = require("node:crypto");
34
- let node_child_process = require("node:child_process");
35
- let node_util = require("node:util");
36
- //#region src/config/loader.ts
37
- function loadConfig() {
38
- const configPath = (0, node_path.join)((0, node_os.homedir)(), ".membank", "config.json");
39
- try {
40
- const raw = (0, node_fs.readFileSync)(configPath, "utf8");
41
- return JSON.parse(raw);
42
- } catch {
43
- return null;
44
- }
45
- }
46
- function isSynthesisEnabled() {
47
- return loadConfig()?.synthesis?.enabled === true;
48
- }
49
- //#endregion
50
- //#region src/db/errors.ts
51
- var MembankError = class extends Error {
52
- constructor(message, options) {
53
- super(message, options);
54
- this.name = "MembankError";
55
- }
56
- };
57
- var DatabaseError = class extends MembankError {
58
- constructor(message, options) {
59
- super(message, options);
60
- this.name = "DatabaseError";
61
- }
62
- };
63
- //#endregion
64
- //#region src/db/manager.ts
65
- const DEFAULT_DB_PATH = (0, node_path.join)((0, node_os.homedir)(), ".membank", "memory.db");
66
- const MIGRATIONS$1 = [
67
- [1, `
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`node:fs`),l=require(`node:os`),u=require(`node:path`),d=require(`zod`),f=require(`better-sqlite3`);f=s(f,1);let p=require(`sqlite-vec`);p=s(p,1);let ee=require(`node:events`),m=require(`@huggingface/transformers`),h=require(`@anthropic-ai/claude-agent-sdk`),te=require(`node:fs/promises`),g=require(`node:crypto`),_=require(`node:child_process`),ne=require(`node:util`);function re(){let e=(0,u.join)((0,l.homedir)(),`.membank`,`config.json`);try{let t=(0,c.readFileSync)(e,`utf8`);return JSON.parse(t)}catch{return null}}function ie(){return re()?.synthesis?.enabled===!0}const v=[`correction`,`preference`,`decision`,`learning`,`fact`],y=d.z.enum(v),b=d.z.array(d.z.string()),x=d.z.object({id:d.z.string(),name:d.z.string(),scopeHash:d.z.string(),createdAt:d.z.string(),updatedAt:d.z.string()}),S=d.z.enum([`similarity_dedup`]),C=d.z.object({id:d.z.string(),memoryId:d.z.string(),conflictingMemoryId:d.z.string().nullable(),similarity:d.z.number(),conflictContentSnapshot:d.z.string(),reason:S,createdAt:d.z.string(),resolvedAt:d.z.string().nullable()}),w=d.z.object({id:d.z.string(),memory_id:d.z.string(),conflicting_memory_id:d.z.string().nullable(),similarity:d.z.number(),conflict_content_snapshot:d.z.string(),reason:S,created_at:d.z.string(),resolved_at:d.z.string().nullable()}),T=d.z.object({id:d.z.string(),content:d.z.string(),type:y,tags:d.z.array(d.z.string()),projects:d.z.array(x),sourceHarness:d.z.string().nullable(),accessCount:d.z.number().int().nonnegative(),pinned:d.z.boolean(),reviewEvents:d.z.array(C),createdAt:d.z.string(),updatedAt:d.z.string()}),E=d.z.object({query:d.z.string().min(1),type:y.optional(),projectHash:d.z.string().optional(),limit:d.z.number().int().positive().optional(),includePinned:d.z.boolean().optional()}),D=d.z.object({content:d.z.string().min(1),type:y,tags:d.z.array(d.z.string()).optional(),projectScope:d.z.object({hash:d.z.string(),name:d.z.string()}).optional(),sourceHarness:d.z.string().optional()}),O=d.z.object({content:d.z.string().min(1).optional(),tags:d.z.array(d.z.string()).optional(),type:y.optional()}),k=d.z.object({id:d.z.string(),scope:d.z.string(),content:d.z.string(),sourceMemoryHash:d.z.string(),synthesizedAt:d.z.string(),expiresAt:d.z.string(),inFlightSince:d.z.string().nullable(),createdAt:d.z.string(),updatedAt:d.z.string()}),ae=d.z.object({stats:d.z.record(y,d.z.number()),pinnedGlobal:d.z.array(T),pinnedProject:d.z.array(T),synthesis:d.z.string().optional()}),A=d.z.object({id:d.z.string(),content:d.z.string(),type:d.z.string(),tags:d.z.string(),source:d.z.string().nullable(),access_count:d.z.number(),pinned:d.z.number(),created_at:d.z.string(),updated_at:d.z.string()}),j=d.z.object({id:d.z.string(),name:d.z.string(),scope_hash:d.z.string(),created_at:d.z.string(),updated_at:d.z.string()});function M(e,t,n=[]){return{id:e.id,content:e.content,type:y.parse(e.type),tags:b.parse(JSON.parse(e.tags)),projects:t,sourceHarness:e.source,accessCount:e.access_count,pinned:e.pinned!==0,reviewEvents:n,createdAt:e.created_at,updatedAt:e.updated_at}}function N(e){let t=w.parse(e);return{id:t.id,memoryId:t.memory_id,conflictingMemoryId:t.conflicting_memory_id,similarity:t.similarity,conflictContentSnapshot:t.conflict_content_snapshot,reason:t.reason,createdAt:t.created_at,resolvedAt:t.resolved_at}}function P(e){return{id:e.id,name:e.name,scopeHash:e.scope_hash,createdAt:e.created_at,updatedAt:e.updated_at}}var F=class extends Error{constructor(e,t){super(e,t),this.name=`MembankError`}},I=class extends F{constructor(e,t){super(e,t),this.name=`DatabaseError`}};const oe=(0,u.join)((0,l.homedir)(),`.membank`,`memory.db`),se=[[1,`
68
2
  CREATE TABLE IF NOT EXISTS memories (
69
3
  id TEXT PRIMARY KEY,
70
4
  content TEXT NOT NULL,
@@ -82,8 +16,7 @@ CREATE TABLE IF NOT EXISTS memories (
82
16
  CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(
83
17
  embedding FLOAT[384]
84
18
  );
85
- `],
86
- [2, `
19
+ `],[2,`
87
20
  CREATE TABLE IF NOT EXISTS projects (
88
21
  id TEXT PRIMARY KEY,
89
22
  name TEXT NOT NULL,
@@ -116,8 +49,7 @@ JOIN projects p ON p.scope_hash = m.scope
116
49
  WHERE m.scope != 'global';
117
50
 
118
51
  ALTER TABLE memories DROP COLUMN scope;
119
- `],
120
- [3, `
52
+ `],[3,`
121
53
  CREATE TABLE IF NOT EXISTS memory_review_events (
122
54
  id TEXT PRIMARY KEY,
123
55
  memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
@@ -133,8 +65,7 @@ CREATE INDEX IF NOT EXISTS idx_review_events_memory_open
133
65
  ON memory_review_events(memory_id) WHERE resolved_at IS NULL;
134
66
 
135
67
  ALTER TABLE memories DROP COLUMN needs_review;
136
- `],
137
- [4, `
68
+ `],[4,`
138
69
  CREATE TABLE IF NOT EXISTS syntheses (
139
70
  id TEXT PRIMARY KEY,
140
71
  scope TEXT NOT NULL,
@@ -154,8 +85,7 @@ CREATE INDEX IF NOT EXISTS idx_syntheses_expires_at
154
85
 
155
86
  CREATE INDEX IF NOT EXISTS idx_syntheses_scope_inflight
156
87
  ON syntheses(scope) WHERE in_flight_since IS NOT NULL;
157
- `],
158
- [5, `
88
+ `],[5,`
159
89
  PRAGMA foreign_keys = OFF;
160
90
 
161
91
  BEGIN;
@@ -213,773 +143,83 @@ ALTER TABLE projects_new RENAME TO projects;
213
143
  COMMIT;
214
144
 
215
145
  PRAGMA foreign_keys = ON;
216
- `]
217
- ];
218
- var DatabaseManager = class DatabaseManager {
219
- #db;
220
- constructor(db) {
221
- this.#db = db;
222
- }
223
- static open(dbPath) {
224
- const resolvedPath = dbPath ?? DEFAULT_DB_PATH;
225
- (0, node_fs.mkdirSync)((0, node_path.dirname)(resolvedPath), { recursive: true });
226
- const db = new better_sqlite3.default(resolvedPath);
227
- return DatabaseManager.#init(db, sqlite_vec.load);
228
- }
229
- static openInMemory() {
230
- return DatabaseManager.#initInMemory(sqlite_vec.load);
231
- }
232
- /** For testing: inject a custom vec loader (e.g. a throwing stub). */
233
- static _openInMemoryWithLoader(loader) {
234
- return DatabaseManager.#initInMemory(loader);
235
- }
236
- static #initInMemory(loader) {
237
- const db = new better_sqlite3.default(":memory:");
238
- return DatabaseManager.#init(db, loader);
239
- }
240
- static #init(db, loader) {
241
- try {
242
- loader(db);
243
- } catch (err) {
244
- throw new DatabaseError("Failed to load sqlite-vec extension", { cause: err });
245
- }
246
- db.pragma("journal_mode = WAL");
247
- db.pragma("foreign_keys = ON");
248
- const manager = new DatabaseManager(db);
249
- manager.#runMigrations();
250
- return manager;
251
- }
252
- #runMigrations() {
253
- this.#db.exec(`
146
+ `],[6,`
147
+ CREATE TABLE IF NOT EXISTS extraction_runs (
148
+ session_id TEXT PRIMARY KEY,
149
+ started_at TEXT NOT NULL,
150
+ completed_at TEXT,
151
+ status TEXT NOT NULL CHECK(status IN ('in_flight', 'completed', 'failed')),
152
+ error TEXT
153
+ );
154
+
155
+ CREATE INDEX IF NOT EXISTS idx_extraction_runs_status
156
+ ON extraction_runs(status) WHERE status = 'in_flight';
157
+ `]];var ce=class e{#e;constructor(e){this.#e=e}static open(t){let n=t??oe;(0,c.mkdirSync)((0,u.dirname)(n),{recursive:!0});let r=new f.default(n);return e.#n(r,p.load)}static openInMemory(){return e.#t(p.load)}static _openInMemoryWithLoader(t){return e.#t(t)}static#t(t){let n=new f.default(`:memory:`);return e.#n(n,t)}static#n(t,n){try{n(t)}catch(e){throw new I(`Failed to load sqlite-vec extension`,{cause:e})}t.pragma(`journal_mode = WAL`),t.pragma(`foreign_keys = ON`);let r=new e(t);return r.#r(),r}#r(){this.#e.exec(`
254
158
  CREATE TABLE IF NOT EXISTS meta (
255
159
  key TEXT PRIMARY KEY,
256
160
  value TEXT NOT NULL
257
161
  );
258
- `);
259
- const row = this.#db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get();
260
- const currentVersion = row ? Number.parseInt(row.value, 10) : 0;
261
- for (const [targetVersion, sql] of MIGRATIONS$1) if (currentVersion < targetVersion) {
262
- this.#db.exec(sql);
263
- this.#db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)").run(String(targetVersion));
264
- }
265
- }
266
- get db() {
267
- return this.#db;
268
- }
269
- close() {
270
- this.#db.close();
271
- }
272
- };
273
- //#endregion
274
- //#region src/schemas.ts
275
- const MEMORY_TYPE_VALUES = [
276
- "correction",
277
- "preference",
278
- "decision",
279
- "learning",
280
- "fact"
281
- ];
282
- const MemoryTypeSchema = zod.z.enum(MEMORY_TYPE_VALUES);
283
- const TagsJsonSchema = zod.z.array(zod.z.string());
284
- const ProjectSchema = zod.z.object({
285
- id: zod.z.string(),
286
- name: zod.z.string(),
287
- scopeHash: zod.z.string(),
288
- createdAt: zod.z.string(),
289
- updatedAt: zod.z.string()
290
- });
291
- const ReviewReasonSchema = zod.z.enum(["similarity_dedup"]);
292
- const ReviewEventSchema = zod.z.object({
293
- id: zod.z.string(),
294
- memoryId: zod.z.string(),
295
- conflictingMemoryId: zod.z.string().nullable(),
296
- similarity: zod.z.number(),
297
- conflictContentSnapshot: zod.z.string(),
298
- reason: ReviewReasonSchema,
299
- createdAt: zod.z.string(),
300
- resolvedAt: zod.z.string().nullable()
301
- });
302
- const ReviewEventRowSchema = zod.z.object({
303
- id: zod.z.string(),
304
- memory_id: zod.z.string(),
305
- conflicting_memory_id: zod.z.string().nullable(),
306
- similarity: zod.z.number(),
307
- conflict_content_snapshot: zod.z.string(),
308
- reason: ReviewReasonSchema,
309
- created_at: zod.z.string(),
310
- resolved_at: zod.z.string().nullable()
311
- });
312
- const MemorySchema = zod.z.object({
313
- id: zod.z.string(),
314
- content: zod.z.string(),
315
- type: MemoryTypeSchema,
316
- tags: zod.z.array(zod.z.string()),
317
- projects: zod.z.array(ProjectSchema),
318
- sourceHarness: zod.z.string().nullable(),
319
- accessCount: zod.z.number().int().nonnegative(),
320
- pinned: zod.z.boolean(),
321
- reviewEvents: zod.z.array(ReviewEventSchema),
322
- createdAt: zod.z.string(),
323
- updatedAt: zod.z.string()
324
- });
325
- const QueryOptionsSchema = zod.z.object({
326
- query: zod.z.string().min(1),
327
- type: MemoryTypeSchema.optional(),
328
- projectHash: zod.z.string().optional(),
329
- limit: zod.z.number().int().positive().optional(),
330
- includePinned: zod.z.boolean().optional()
331
- });
332
- const SaveOptionsSchema = zod.z.object({
333
- content: zod.z.string().min(1),
334
- type: MemoryTypeSchema,
335
- tags: zod.z.array(zod.z.string()).optional(),
336
- projectScope: zod.z.object({
337
- hash: zod.z.string(),
338
- name: zod.z.string()
339
- }).optional(),
340
- sourceHarness: zod.z.string().optional()
341
- });
342
- const MemoryPatchSchema = zod.z.object({
343
- content: zod.z.string().min(1).optional(),
344
- tags: zod.z.array(zod.z.string()).optional(),
345
- type: MemoryTypeSchema.optional()
346
- });
347
- const SynthesisSchema = zod.z.object({
348
- id: zod.z.string(),
349
- scope: zod.z.string(),
350
- content: zod.z.string(),
351
- sourceMemoryHash: zod.z.string(),
352
- synthesizedAt: zod.z.string(),
353
- expiresAt: zod.z.string(),
354
- inFlightSince: zod.z.string().nullable(),
355
- createdAt: zod.z.string(),
356
- updatedAt: zod.z.string()
357
- });
358
- const SessionContextSchema = zod.z.object({
359
- stats: zod.z.record(MemoryTypeSchema, zod.z.number()),
360
- pinnedGlobal: zod.z.array(MemorySchema),
361
- pinnedProject: zod.z.array(MemorySchema),
362
- synthesis: zod.z.string().optional()
363
- });
364
- const MemoryRowSchema = zod.z.object({
365
- id: zod.z.string(),
366
- content: zod.z.string(),
367
- type: zod.z.string(),
368
- tags: zod.z.string(),
369
- source: zod.z.string().nullable(),
370
- access_count: zod.z.number(),
371
- pinned: zod.z.number(),
372
- created_at: zod.z.string(),
373
- updated_at: zod.z.string()
374
- });
375
- const ProjectRowSchema = zod.z.object({
376
- id: zod.z.string(),
377
- name: zod.z.string(),
378
- scope_hash: zod.z.string(),
379
- created_at: zod.z.string(),
380
- updated_at: zod.z.string()
381
- });
382
- //#endregion
383
- //#region src/db/row-types.ts
384
- function rowToMemory(row, projects, reviewEvents = []) {
385
- return {
386
- id: row.id,
387
- content: row.content,
388
- type: MemoryTypeSchema.parse(row.type),
389
- tags: TagsJsonSchema.parse(JSON.parse(row.tags)),
390
- projects,
391
- sourceHarness: row.source,
392
- accessCount: row.access_count,
393
- pinned: row.pinned !== 0,
394
- reviewEvents,
395
- createdAt: row.created_at,
396
- updatedAt: row.updated_at
397
- };
398
- }
399
- function rowToReviewEvent(row) {
400
- const parsed = ReviewEventRowSchema.parse(row);
401
- return {
402
- id: parsed.id,
403
- memoryId: parsed.memory_id,
404
- conflictingMemoryId: parsed.conflicting_memory_id,
405
- similarity: parsed.similarity,
406
- conflictContentSnapshot: parsed.conflict_content_snapshot,
407
- reason: parsed.reason,
408
- createdAt: parsed.created_at,
409
- resolvedAt: parsed.resolved_at
410
- };
411
- }
412
- function rowToProject(row) {
413
- return {
414
- id: row.id,
415
- name: row.name,
416
- scopeHash: row.scope_hash,
417
- createdAt: row.created_at,
418
- updatedAt: row.updated_at
419
- };
420
- }
421
- //#endregion
422
- //#region src/embedding/service.ts
423
- var EmbeddingService = class {
424
- modelCachePath;
425
- onProgress;
426
- pipelineInstance = null;
427
- constructor(modelCachePath, onProgress) {
428
- this.modelCachePath = modelCachePath ?? (0, node_path.join)((0, node_os.homedir)(), ".membank", "models");
429
- this.onProgress = onProgress;
430
- }
431
- async getPipeline() {
432
- if (this.pipelineInstance === null) this.pipelineInstance = await (0, _huggingface_transformers.pipeline)("feature-extraction", "Xenova/bge-small-en-v1.5", {
433
- cache_dir: this.modelCachePath,
434
- progress_callback: this.onProgress
435
- });
436
- return this.pipelineInstance;
437
- }
438
- async embed(text) {
439
- const flat = (await (await this.getPipeline())(text, {
440
- pooling: "mean",
441
- normalize: true
442
- })).data;
443
- return flat instanceof Float32Array ? flat : new Float32Array(flat);
444
- }
445
- };
446
- //#endregion
447
- //#region src/memory/repository.ts
448
- const PIN_BUDGET_THRESHOLD = 8e3;
449
- var MemoryRepository = class {
450
- #db;
451
- #embedding;
452
- #projects;
453
- constructor(db, embeddingService, projects) {
454
- this.#db = db;
455
- this.#embedding = embeddingService;
456
- this.#projects = projects;
457
- }
458
- async save(options) {
459
- const { content, type, tags = [], projectScope, sourceHarness } = SaveOptionsSchema.parse(options);
460
- const embedding = await this.#embedding.embed(content);
461
- const embeddingBlob = Buffer.from(embedding.buffer);
462
- let top;
463
- if (projectScope !== void 0) top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
162
+ `);let e=this.#e.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get(),t=e?Number.parseInt(e.value,10):0;for(let[e,n]of se)t<e&&(this.#e.exec(n),this.#e.prepare(`INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)`).run(String(e)))}get db(){return this.#e}close(){this.#e.close()}};const L=`Xenova/bge-small-en-v1.5`;var R=class extends Error{constructor(e,t){super(e,t),this.name=`ModelDownloadError`}};function le(){return(0,u.join)((0,l.homedir)(),`.membank`,`models`)}function z(e){if(!(0,c.existsSync)(e))return!1;try{return(0,c.readdirSync)(e).length>0}catch{return!1}}var B=class extends ee.EventEmitter{modelPath;constructor(e){super(),this.modelPath=e??le()}isAlreadyCached(){return z(this.modelPath)}get cachePath(){return this.modelPath}async download(){if(z(this.modelPath))return{skipped:!0};let e=Date.now(),t=0,n=e;try{await(0,m.pipeline)(`feature-extraction`,L,{cache_dir:this.modelPath,progress_callback:e=>{if(e.status!==`progress`||e.total==null||e.loaded==null)return;let r=e.total,i=e.loaded,a=r>0?i/r*100:0,o=Date.now(),s=o-n,c=i-t,l=0;if(s>0&&c>0){let e=c/s;l=(r-i)/e/1e3}t=i,n=o;let u={totalBytes:r,downloadedBytes:i,percentage:a,estimatedSecondsRemaining:l};this.emit(`progress`,u)}})}catch(e){throw new R(`Failed to download model`,{cause:e})}return{skipped:!1}}},V=class{modelCachePath;onProgress;pipelineInstance=null;constructor(e,t){this.modelCachePath=e??(0,u.join)((0,l.homedir)(),`.membank`,`models`),this.onProgress=t}async getPipeline(){return this.pipelineInstance===null&&(this.pipelineInstance=await(0,m.pipeline)(`feature-extraction`,`Xenova/bge-small-en-v1.5`,{cache_dir:this.modelCachePath,progress_callback:this.onProgress})),this.pipelineInstance}async embed(e){let t=(await(await this.getPipeline())(e,{pooling:`mean`,normalize:!0})).data;return t instanceof Float32Array?t:new Float32Array(t)}};async function H(e,t){let n=t.now??(()=>new Date);if(!t.repo.tryClaim(e.sessionId,n(),t.config)){let n=t.repo.get(e.sessionId);return{status:`skipped`,reason:n?.status===`completed`&&n.completedAt!==null?`recently_completed`:`in_flight`}}try{let r=await t.transcripts.read(e.transcriptPath);return await t.agent.run({transcript:r,projectHash:e.projectHash,sessionId:e.sessionId}),t.repo.markCompleted(e.sessionId,n()),{status:`completed`}}catch(r){let i=r instanceof Error?r.message:String(r);return t.repo.markFailed(e.sessionId,n(),i),{status:`failed`,error:i}}}const U=[`You are a memory extractor that runs after a coding session ends. You read the session transcript and CALL save_memory for every durable fact, preference, correction, decision, or learning the user expressed, so that future sessions inherit them.`,``,`Memory types (pick the closest match):`,`- correction: the user told the assistant to stop doing something or to do it differently.`,`- preference: the user stated how they want work done (tools, style, conventions).`,`- decision: the user committed to a choice future work should respect (tech pick, architectural direction, scope cut).`,`- learning: a non-obvious fact about the codebase or tooling that future sessions would otherwise rediscover.`,`- fact: stable info about the user or their project not derivable from the code.`,``,`Bias strongly toward saving. If the user said it in plain language and it would be useful in a future session, save it. Phrasing like 'stop X', 'always Y', 'we use Z', 'don't suggest W', 'we decided', 'from now on' is a clear save signal — even when the assistant in the transcript already acknowledged it, save it so the NEXT session also knows.`,``,`Process:`,`1. Read the supplied transcript end-to-end.`,`2. Identify every distinct durable signal. List them mentally before calling tools.`,`3. Optional: call query_memory with focused search terms to avoid duplicates. If a near-duplicate exists, call update_memory instead of save_memory.`,`4. Call save_memory for each new durable signal. Phrase the content as a standalone instruction or fact — strip session framing ("in this session", "just now"). Good: "Use pnpm, not npm, for all dependency operations." Bad: "User said stop using npm."`,"5. Use `global: true` only when the fact is about the user themselves or applies across every project. Otherwise default to project scope (omit `global`).",``,`Only return without saving when the transcript truly contains no durable signal — pure greetings, time-of-day questions, abandoned tasks. Do not invent facts. If in doubt and the signal is concrete, save it.`].join(`
163
+ `);var W=class{#e;constructor(e){this.#e=e}async run(e){let t=(0,h.createSdkMcpServer)({name:`membank-extraction-tools`,version:`1.0.0`,tools:[(0,h.tool)(`query_memory`,`Search memories by semantic similarity to check for existing entries before saving.`,{query:d.z.string().describe(`Search text`),limit:d.z.number().optional().describe(`Maximum results to return`),global:d.z.boolean().optional().describe(`Query global memories when true, otherwise current project scope`)},async({query:t,limit:n,global:r})=>({content:[{type:`text`,text:await this.#e.queryMemory({query:t,limit:n,global:r,projectHash:e.projectHash})}]}),{annotations:{readOnlyHint:!0}}),(0,h.tool)(`save_memory`,`Persist a new memory. The system handles dedup automatically.`,{content:d.z.string().describe(`Memory content — concise, decontextualised`),type:d.z.enum([`correction`,`preference`,`decision`,`learning`,`fact`]).describe(`Memory type`),tags:d.z.array(d.z.string()).optional().describe(`Optional tags`),global:d.z.boolean().optional().describe(`Save as global memory rather than project-scoped`)},async({content:e,type:t,tags:n,global:r})=>({content:[{type:`text`,text:await this.#e.saveMemory({content:e,type:t,tags:n,global:r})}]})),(0,h.tool)(`update_memory`,`Refine an existing memory by id rather than creating a near-duplicate.`,{id:d.z.string().describe(`Memory id`),content:d.z.string().optional(),type:d.z.enum([`correction`,`preference`,`decision`,`learning`,`fact`]).optional(),tags:d.z.array(d.z.string()).optional()},async({id:e,content:t,type:n,tags:r})=>({content:[{type:`text`,text:await this.#e.updateMemory({id:e,content:t,type:n,tags:r})}]}))]}),n=[`Session id: ${e.sessionId}`,``,`Transcript (most recent turns):`,`---`,e.transcript,`---`,``,`Extract durable memories from this transcript following the system instructions.`].join(`
164
+ `),r=Object.fromEntries(Object.entries(process.env).filter(e=>e[1]!==void 0)),i=Date.now(),a=(0,h.query)({prompt:n,options:{model:`claude-haiku-4-5-20251001`,systemPrompt:U,mcpServers:{"membank-extraction-tools":t},allowedTools:[`mcp__membank-extraction-tools__query_memory`,`mcp__membank-extraction-tools__save_memory`,`mcp__membank-extraction-tools__update_memory`],disallowedTools:[`mcp__membank__*`],settingSources:[],permissionMode:`bypassPermissions`,env:r}}),o=process.env.MEMBANK_EXTRACTION_DEBUG===`true`;for await(let e of a)if(o&&process.stderr.write(`[extraction debug] ${JSON.stringify(e).slice(0,600)}\n`),e.type===`result`&&e.subtype!==`success`){let t=`errors`in e&&Array.isArray(e.errors)?`: ${e.errors.join(`; `)}`:``;throw Error(`Extraction agent failed: ${e.subtype}${t}`)}let s=Date.now()-i;process.stderr.write(`membank extraction: session=${e.sessionId} duration=${s}ms\n`)}};function ue(e){return new W(e)}function de(e,t,n,r){return e===void 0?{kind:`claim`}:e.status===`in_flight`?t.getTime()-e.startedAt.getTime()<n?{kind:`skip`,reason:`in_flight`}:{kind:`claim`}:e.status===`completed`&&e.completedAt!==null&&t.getTime()-e.completedAt.getTime()<r?{kind:`skip`,reason:`recently_completed`}:{kind:`claim`}}function fe(e){return{sessionId:e.session_id,startedAt:e.started_at,completedAt:e.completed_at,status:e.status,error:e.error}}var pe=class{#e;constructor(e){this.#e=e}tryClaim(e,t,n){let r=this.#t(e);if(de(r===void 0?void 0:{startedAt:new Date(r.started_at),completedAt:r.completed_at===null?null:new Date(r.completed_at),status:r.status},t,n.inFlightTimeoutMs??6e5,n.recentCompletionMs??6e4).kind===`skip`)return!1;let i=t.toISOString();return this.#e.db.prepare(`INSERT INTO extraction_runs (session_id, started_at, completed_at, status, error)
165
+ VALUES (?, ?, NULL, 'in_flight', NULL)
166
+ ON CONFLICT(session_id) DO UPDATE SET
167
+ started_at = excluded.started_at,
168
+ completed_at = NULL,
169
+ status = 'in_flight',
170
+ error = NULL`).run(e,i),!0}markCompleted(e,t){this.#e.db.prepare(`UPDATE extraction_runs
171
+ SET status = 'completed', completed_at = ?, error = NULL
172
+ WHERE session_id = ?`).run(t.toISOString(),e)}markFailed(e,t,n){this.#e.db.prepare(`UPDATE extraction_runs
173
+ SET status = 'failed', completed_at = ?, error = ?
174
+ WHERE session_id = ?`).run(t.toISOString(),n,e)}get(e){let t=this.#t(e);return t===void 0?void 0:fe(t)}#t(e){return this.#e.db.prepare(`SELECT * FROM extraction_runs WHERE session_id = ?`).get(e)}};function me(e){return new pe(e)}function he(e){if(typeof e==`string`)return e;if(!Array.isArray(e))return``;let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n;e.type===`text`&&typeof e.text==`string`?t.push(e.text):e.type===`tool_use`&&typeof e.name==`string`?t.push(`[tool_use: ${e.name}]`):e.type===`tool_result`&&t.push(`[tool_result]`)}return t.join(`
175
+ `)}var ge=class{#e;#t;constructor(e={}){this.#e=e.maxTurns??80,this.#t=e.maxChars??6e4}async read(e){let t=(await(0,te.readFile)(e,`utf8`)).split(`
176
+ `).filter(e=>e.length>0),n=[];for(let e of t){let t;try{t=JSON.parse(e)}catch{continue}let r=t.message?.role;if(r!==`user`&&r!==`assistant`)continue;let i=he(t.message?.content).trim();i.length!==0&&n.push(`${r}: ${i}`)}let r=n.slice(-this.#e).join(`
177
+
178
+ `);return r.length<=this.#t?r:r.slice(r.length-this.#t)}};function _e(e={}){return new ge(e)}function ve(e,t){return t.delete(e),Promise.resolve()}function ye(e,t){t.resolveReviewEvents(e)}function be(e){return e>.92?`overwrite`:e>=.75?`flag`:`none`}async function xe(e,t){let{content:n,type:r,tags:i=[],projectScope:a,sourceHarness:o}=D.parse(e),{repo:s,embedder:c}=t,l=await c.embed(n),[u]=s.findSimilar(l,r,a?.hash);if(u!==void 0){let e=be(u.similarity);if(e===`overwrite`)return s.overwrite(u.id,n,l);if(e===`flag`){let e=s.create({id:(0,g.randomUUID)(),content:n,type:r,tags:i,sourceHarness:o??null,embedding:l,projectScope:a});return s.createReviewEvent({memoryId:u.id,conflictingMemoryId:e.id,similarity:u.similarity,conflictContentSnapshot:n}),e}}return s.create({id:(0,g.randomUUID)(),content:n,type:r,tags:i,sourceHarness:o??null,embedding:l,projectScope:a})}async function Se(e,t,n){let{repo:r,embedder:i}=n,a=O.parse(t),o=a.content===void 0?void 0:await i.embed(a.content);return r.update(e,a,o)}const G=8e3;function Ce(e){return e>=G}var K=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}findSimilar(e,t,n){let r=Buffer.from(e.buffer),i;return i=n===void 0?this.#e.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
179
+ FROM memories m JOIN embeddings e ON e.rowid = m.rowid
180
+ WHERE m.type = ?
181
+ AND m.id NOT IN (SELECT memory_id FROM memory_projects)
182
+ ORDER BY similarity DESC LIMIT 1`).get(r,t):this.#e.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
464
183
  FROM memories m JOIN embeddings e ON e.rowid = m.rowid
465
184
  JOIN memory_projects mp ON mp.memory_id = m.id
466
185
  JOIN projects p ON p.id = mp.project_id
467
186
  WHERE m.type = ? AND p.scope_hash = ?
468
- ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, projectScope.hash);
469
- else top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
470
- FROM memories m JOIN embeddings e ON e.rowid = m.rowid
471
- WHERE m.type = ?
472
- AND m.id NOT IN (SELECT memory_id FROM memory_projects)
473
- ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type);
474
- const now = (/* @__PURE__ */ new Date()).toISOString();
475
- if (top !== void 0 && top.similarity > .92) {
476
- this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
477
- this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
478
- const updated = MemoryRowSchema.parse(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id));
479
- const projectMap = this.#projects.getProjectsForMemories([top.id]);
480
- const events = this.#getEventsForMemories([top.id]);
481
- return rowToMemory(updated, projectMap.get(top.id) ?? [], events.get(top.id) ?? []);
482
- }
483
- const id = (0, node_crypto.randomUUID)();
484
- this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, source, access_count, pinned, created_at, updated_at)
485
- VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), sourceHarness ?? null, now, now);
486
- if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`INSERT INTO memory_review_events
487
- (id, memory_id, conflicting_memory_id, similarity, conflict_content_snapshot, reason, created_at)
488
- VALUES (?, ?, ?, ?, ?, 'similarity_dedup', ?)`).run((0, node_crypto.randomUUID)(), top.id, id, top.similarity, content, now);
489
- this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
490
- if (projectScope !== void 0) {
491
- const project = this.#projects.upsertByHash(projectScope.hash, projectScope.name);
492
- this.#projects.addAssociation(id, project.id);
493
- }
494
- return rowToMemory(MemoryRowSchema.parse(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id)), this.#projects.getProjectsForMemories([id]).get(id) ?? [], []);
495
- }
496
- async update(id, patch) {
497
- const { content, tags, type } = MemoryPatchSchema.parse(patch);
498
- const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
499
- if (existing === void 0) throw new Error(`Memory not found: ${id}`);
500
- const now = (/* @__PURE__ */ new Date()).toISOString();
501
- const sets = ["updated_at = ?"];
502
- const values = [now];
503
- if (content !== void 0) {
504
- sets.push("content = ?");
505
- values.push(content);
506
- }
507
- if (tags !== void 0) {
508
- sets.push("tags = ?");
509
- values.push(JSON.stringify(tags));
510
- }
511
- if (type !== void 0) {
512
- sets.push("type = ?");
513
- values.push(type);
514
- }
515
- values.push(id);
516
- this.#db.db.prepare(`UPDATE memories SET ${sets.join(", ")} WHERE id = ?`).run(...values);
517
- if (content !== void 0) {
518
- const embedding = await this.#embedding.embed(content);
519
- const embeddingBlob = Buffer.from(embedding.buffer);
520
- this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
521
- }
522
- const updated = MemoryRowSchema.parse(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id));
523
- const projectMap = this.#projects.getProjectsForMemories([id]);
524
- const events = this.#getEventsForMemories([id]);
525
- return rowToMemory(updated, projectMap.get(id) ?? [], events.get(id) ?? []);
526
- }
527
- delete(id) {
528
- const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
529
- if (row !== void 0) this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);
530
- this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ?`).run(id);
531
- this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);
532
- return Promise.resolve();
533
- }
534
- list(opts) {
535
- const conditions = [];
536
- const params = [];
537
- if (opts?.type !== void 0) {
538
- conditions.push("type = ?");
539
- params.push(opts.type);
540
- }
541
- if (opts?.pinned === true) conditions.push("pinned = 1");
542
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
543
- const rows = this.#db.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC`).all(...params);
544
- if (rows.length === 0) return [];
545
- const ids = rows.map((r) => r.id);
546
- const projectMap = this.#projects.getProjectsForMemories(ids);
547
- const eventMap = this.#getEventsForMemories(ids);
548
- return rows.map((row) => rowToMemory(row, projectMap.get(row.id) ?? [], eventMap.get(row.id) ?? []));
549
- }
550
- listFlagged() {
551
- const rows = this.#db.db.prepare(`SELECT * FROM memories
187
+ ORDER BY similarity DESC LIMIT 1`).get(r,t,n),i===void 0?[]:[{id:i.id,similarity:i.similarity}]}create(e){let{id:t,content:n,type:r,tags:i,sourceHarness:a,embedding:o,projectScope:s}=e,c=new Date().toISOString(),l=Buffer.from(o.buffer);if(this.#e.db.prepare(`INSERT INTO memories (id, content, type, tags, source, access_count, pinned, created_at, updated_at)
188
+ VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?)`).run(t,n,r,JSON.stringify(i),a,c,c),this.#e.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(l,t),s!==void 0){let e=this.#t.upsertByHash(s.hash,s.name);this.#t.addAssociation(t,e.id)}return M(A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(t)),this.#t.getProjectsForMemories([t]).get(t)??[])}overwrite(e,t,n){let r=new Date().toISOString(),i=Buffer.from(n.buffer);this.#e.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(t,r,e);let a=this.#e.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(e)?.rowid;a!==void 0&&this.#e.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(i,a);let o=A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),s=this.#t.getProjectsForMemories([e]),c=this.#n([e]);return M(o,s.get(e)??[],c.get(e)??[])}findById(e){let t=this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e);if(t===void 0)return;let n=this.#t.getProjectsForMemories([e]),r=this.#n([e]);return M(t,n.get(e)??[],r.get(e)??[])}update(e,t,n){let{content:r,tags:i,type:a}=O.parse(t),o=this.#e.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(e);if(o===void 0)throw Error(`Memory not found: ${e}`);let s=new Date().toISOString(),c=[`updated_at = ?`],l=[s];if(r!==void 0&&(c.push(`content = ?`),l.push(r)),i!==void 0&&(c.push(`tags = ?`),l.push(JSON.stringify(i))),a!==void 0&&(c.push(`type = ?`),l.push(a)),l.push(e),this.#e.db.prepare(`UPDATE memories SET ${c.join(`, `)} WHERE id = ?`).run(...l),n!==void 0){let e=Buffer.from(n.buffer);this.#e.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(e,o.rowid)}let u=A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),d=this.#t.getProjectsForMemories([e]),f=this.#n([e]);return M(u,d.get(e)??[],f.get(e)??[])}delete(e){let t=this.#e.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(e);t!==void 0&&this.#e.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(t.rowid),this.#e.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ?`).run(e),this.#e.db.prepare(`DELETE FROM memories WHERE id = ?`).run(e)}list(e){let t=[],n=[];e?.type!==void 0&&(t.push(`m.type = ?`),n.push(e.type)),e?.pinned===!0&&t.push(`m.pinned = 1`),e?.needsReview===!0&&t.push(`EXISTS (SELECT 1 FROM memory_review_events e WHERE e.memory_id = m.id AND e.resolved_at IS NULL)`),e?.projectId===`global`?t.push(`m.id NOT IN (SELECT memory_id FROM memory_projects)`):e?.projectId!==void 0&&(t.push(`m.id IN (SELECT memory_id FROM memory_projects WHERE project_id = ?)`),n.push(e.projectId));let r=t.length>0?`WHERE ${t.join(` AND `)}`:``,i=this.#e.db.prepare(`SELECT m.* FROM memories m ${r} ORDER BY m.created_at DESC`).all(...n);if(i.length===0)return[];let a=i.map(e=>e.id),o=this.#t.getProjectsForMemories(a),s=this.#n(a);return i.map(e=>M(e,o.get(e.id)??[],s.get(e.id)??[]))}listPinnedGlobal(){return this.#e.db.prepare(`SELECT * FROM memories
189
+ WHERE id NOT IN (SELECT memory_id FROM memory_projects)
190
+ AND pinned = 1`).all().map(e=>M(e,[]))}listPinnedForProject(e){return this.#e.db.prepare(`SELECT m.* FROM memories m
191
+ JOIN memory_projects mp ON mp.memory_id = m.id
192
+ JOIN projects p ON p.id = mp.project_id
193
+ WHERE p.scope_hash = ? AND m.pinned = 1`).all(e).map(e=>M(e,[]))}listFlagged(){let e=this.#e.db.prepare(`SELECT * FROM memories
552
194
  WHERE EXISTS (
553
195
  SELECT 1 FROM memory_review_events e
554
196
  WHERE e.memory_id = memories.id AND e.resolved_at IS NULL
555
197
  )
556
- ORDER BY created_at DESC`).all();
557
- if (rows.length === 0) return [];
558
- const ids = rows.map((r) => r.id);
559
- const projectMap = this.#projects.getProjectsForMemories(ids);
560
- const eventMap = this.#getEventsForMemories(ids, { unresolvedOnly: true });
561
- return rows.map((row) => rowToMemory(row, projectMap.get(row.id) ?? [], eventMap.get(row.id) ?? []));
562
- }
563
- listReviewEvents(memoryId, opts) {
564
- const where = opts?.unresolvedOnly === true ? "WHERE memory_id = ? AND resolved_at IS NULL" : "WHERE memory_id = ?";
565
- return this.#db.db.prepare(`SELECT * FROM memory_review_events ${where} ORDER BY created_at DESC`).all(memoryId).map((r) => rowToReviewEvent(ReviewEventRowSchema.parse(r)));
566
- }
567
- resolveReviewEvents(memoryId) {
568
- const now = (/* @__PURE__ */ new Date()).toISOString();
569
- this.#db.db.prepare(`UPDATE memory_review_events SET resolved_at = ? WHERE memory_id = ? AND resolved_at IS NULL`).run(now, memoryId);
570
- }
571
- #getEventsForMemories(ids, opts) {
572
- if (ids.length === 0) return /* @__PURE__ */ new Map();
573
- const placeholders = ids.map(() => "?").join(", ");
574
- const unresolvedClause = opts?.unresolvedOnly === true ? "AND resolved_at IS NULL" : "";
575
- const rows = this.#db.db.prepare(`SELECT * FROM memory_review_events
576
- WHERE memory_id IN (${placeholders}) ${unresolvedClause}
577
- ORDER BY created_at DESC`).all(...ids);
578
- const map = /* @__PURE__ */ new Map();
579
- for (const row of rows) {
580
- const event = rowToReviewEvent(ReviewEventRowSchema.parse(row));
581
- const existing = map.get(event.memoryId) ?? [];
582
- existing.push(event);
583
- map.set(event.memoryId, existing);
584
- }
585
- return map;
586
- }
587
- getPinnedCharCount() {
588
- return (this.#db.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) as total FROM memories WHERE pinned = 1`).get() ?? { total: 0 }).total;
589
- }
590
- stats() {
591
- const byType = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
592
- const typeRows = this.#db.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();
593
- for (const row of typeRows) {
594
- const parsed = MemoryTypeSchema.safeParse(row.type);
595
- if (parsed.success) byType[parsed.data] = row.count;
596
- }
597
- const aggregates = this.#db.db.prepare(`SELECT COUNT(*) as total, SUM(pinned) as pinned FROM memories`).get() ?? {
598
- total: 0,
599
- pinned: 0
600
- };
601
- const reviewRow = this.#db.db.prepare(`SELECT COUNT(DISTINCT memory_id) as needsReview FROM memory_review_events WHERE resolved_at IS NULL`).get() ?? { needsReview: 0 };
602
- return {
603
- byType,
604
- total: aggregates.total,
605
- pinned: aggregates.pinned ?? 0,
606
- needsReview: reviewRow.needsReview,
607
- pinBudgetChars: this.getPinnedCharCount()
608
- };
609
- }
610
- setPin(id, pinned) {
611
- if (this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id) === void 0) throw new Error(`Memory not found: ${id}`);
612
- const now = (/* @__PURE__ */ new Date()).toISOString();
613
- this.#db.db.prepare(`UPDATE memories SET pinned = ?, updated_at = ? WHERE id = ?`).run(pinned ? 1 : 0, now, id);
614
- const updated = MemoryRowSchema.parse(this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id));
615
- const projectMap = this.#projects.getProjectsForMemories([id]);
616
- const events = this.#getEventsForMemories([id]);
617
- return rowToMemory(updated, projectMap.get(id) ?? [], events.get(id) ?? []);
618
- }
619
- incrementAccessCount(id) {
620
- this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
621
- }
622
- };
623
- //#endregion
624
- //#region src/scope/resolver.ts
625
- const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
626
- function sha256Truncated(input) {
627
- return (0, node_crypto.createHash)("sha256").update(input).digest("hex").slice(0, 16);
628
- }
629
- async function resolveProject() {
630
- try {
631
- const { stdout } = await execFileAsync("git", [
632
- "remote",
633
- "get-url",
634
- "origin"
635
- ]);
636
- const url = stdout.trim();
637
- if (url) {
638
- const hash = sha256Truncated(url);
639
- return {
640
- hash,
641
- name: url.split("/").pop()?.replace(/\.git$/, "") ?? hash.slice(0, 8)
642
- };
643
- }
644
- } catch {}
645
- const cwd = process.cwd();
646
- const hash = sha256Truncated(cwd);
647
- return {
648
- hash,
649
- name: cwd.split(/[/\\]/).filter(Boolean).pop() ?? hash.slice(0, 8)
650
- };
651
- }
652
- async function resolveScope() {
653
- try {
654
- const { stdout } = await execFileAsync("git", [
655
- "remote",
656
- "get-url",
657
- "origin"
658
- ]);
659
- const url = stdout.trim();
660
- if (url) return sha256Truncated(url);
661
- } catch {}
662
- return sha256Truncated(process.cwd());
663
- }
664
- //#endregion
665
- //#region src/migrations/index.ts
666
- const MIGRATIONS = [{
667
- name: "scope-to-projects",
668
- description: "Rename the auto-migrated project for the current directory from its generic hash-derived name to the resolved repo/directory name."
669
- }];
670
- async function runScopeToProjectsMigration(projects) {
671
- const resolved = await resolveProject();
672
- const project = projects.getByHash(resolved.hash);
673
- if (project === void 0) return null;
674
- const oldName = project.name;
675
- const memoryCount = projects.countMemories(project.id);
676
- projects.rename(project.id, resolved.name);
677
- return {
678
- migration: "scope-to-projects",
679
- oldName,
680
- newName: resolved.name,
681
- memoryCount
682
- };
683
- }
684
- //#endregion
685
- //#region src/project/repository.ts
686
- var ProjectRepository = class {
687
- #db;
688
- constructor(db) {
689
- this.#db = db;
690
- }
691
- upsertByHash(hash, name) {
692
- if (!/^[0-9a-f]{16}$/.test(hash)) throw new Error(`Invalid scope hash "${hash}": expected 16 lowercase hex characters`);
693
- const now = (/* @__PURE__ */ new Date()).toISOString();
694
- const id = (0, node_crypto.randomUUID)();
695
- this.#db.db.prepare(`INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`).run(id, name, hash, now, now);
696
- return rowToProject(ProjectRowSchema.parse(this.#db.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(hash)));
697
- }
698
- rename(id, name) {
699
- const now = (/* @__PURE__ */ new Date()).toISOString();
700
- this.#db.db.prepare(`UPDATE projects SET name = ?, updated_at = ? WHERE id = ?`).run(name, now, id);
701
- const row = this.#db.db.prepare(`SELECT * FROM projects WHERE id = ?`).get(id);
702
- if (row === void 0) throw new Error(`Project not found: ${id}`);
703
- return rowToProject(row);
704
- }
705
- list() {
706
- return this.#db.db.prepare(`SELECT * FROM projects ORDER BY name ASC`).all().map(rowToProject);
707
- }
708
- getByHash(hash) {
709
- const row = this.#db.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(hash);
710
- return row !== void 0 ? rowToProject(row) : void 0;
711
- }
712
- getByName(name) {
713
- const row = this.#db.db.prepare(`SELECT * FROM projects WHERE name = ? LIMIT 1`).get(name);
714
- return row !== void 0 ? rowToProject(row) : void 0;
715
- }
716
- addAssociation(memoryId, projectId) {
717
- this.#db.db.prepare(`INSERT OR IGNORE INTO memory_projects (memory_id, project_id) VALUES (?, ?)`).run(memoryId, projectId);
718
- }
719
- removeAssociation(memoryId, projectId) {
720
- this.#db.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`).run(memoryId, projectId);
721
- }
722
- countMemories(projectId) {
723
- return this.#db.db.prepare(`SELECT COUNT(*) AS count FROM memory_projects WHERE project_id = ?`).get(projectId)?.count ?? 0;
724
- }
725
- getProjectsForMemories(memoryIds) {
726
- if (memoryIds.length === 0) return /* @__PURE__ */ new Map();
727
- const placeholders = memoryIds.map(() => "?").join(",");
728
- const rows = this.#db.db.prepare(`SELECT p.*, mp.memory_id FROM projects p
198
+ ORDER BY created_at DESC`).all();if(e.length===0)return[];let t=e.map(e=>e.id),n=this.#t.getProjectsForMemories(t),r=this.#n(t,{unresolvedOnly:!0});return e.map(e=>M(e,n.get(e.id)??[],r.get(e.id)??[]))}listReviewEvents(e,t){let n=t?.unresolvedOnly===!0?`WHERE memory_id = ? AND resolved_at IS NULL`:`WHERE memory_id = ?`;return this.#e.db.prepare(`SELECT * FROM memory_review_events ${n} ORDER BY created_at DESC`).all(e).map(e=>N(w.parse(e)))}createReviewEvent(e){let t=new Date().toISOString();this.#e.db.prepare(`INSERT INTO memory_review_events
199
+ (id, memory_id, conflicting_memory_id, similarity, conflict_content_snapshot, reason, created_at)
200
+ VALUES (?, ?, ?, ?, ?, 'similarity_dedup', ?)`).run((0,g.randomUUID)(),e.memoryId,e.conflictingMemoryId,e.similarity,e.conflictContentSnapshot,t)}resolveReviewEvents(e){let t=new Date().toISOString();this.#e.db.prepare(`UPDATE memory_review_events SET resolved_at = ? WHERE memory_id = ? AND resolved_at IS NULL`).run(t,e)}getPinnedCharCount(){return(this.#e.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) as total FROM memories WHERE pinned = 1`).get()??{total:0}).total}stats(){let e=Object.fromEntries(v.map(e=>[e,0])),t=this.#e.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();for(let n of t){let t=y.safeParse(n.type);t.success&&(e[t.data]=n.count)}let n=this.#e.db.prepare(`SELECT COUNT(*) as total, SUM(pinned) as pinned FROM memories`).get()??{total:0,pinned:0},r=this.#e.db.prepare(`SELECT COUNT(DISTINCT memory_id) as needsReview FROM memory_review_events WHERE resolved_at IS NULL`).get()??{needsReview:0};return{byType:e,total:n.total,pinned:n.pinned??0,needsReview:r.needsReview,pinBudgetChars:this.getPinnedCharCount()}}setPin(e,t){if(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)===void 0)throw Error(`Memory not found: ${e}`);let n=new Date().toISOString();this.#e.db.prepare(`UPDATE memories SET pinned = ?, updated_at = ? WHERE id = ?`).run(+!!t,n,e);let r=A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),i=this.#t.getProjectsForMemories([e]),a=this.#n([e]);return M(r,i.get(e)??[],a.get(e)??[])}incrementAccessCount(e){this.#e.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(e)}exportAll(){return this.#e.db.prepare(`SELECT m.*, e.embedding FROM memories m LEFT JOIN embeddings e ON e.rowid = m.rowid ORDER BY m.created_at DESC`).all().map(e=>({id:e.id,content:e.content,type:y.parse(e.type),tags:b.parse(JSON.parse(e.tags)),sourceHarness:e.source,accessCount:e.access_count,pinned:e.pinned!==0,createdAt:e.created_at,updatedAt:e.updated_at,embedding:e.embedding===null?null:new Float32Array(e.embedding.buffer,e.embedding.byteOffset,e.embedding.byteLength/4)}))}importAll(e){let t=this.#e.db.prepare(`INSERT OR REPLACE INTO memories (id, content, type, tags, source, access_count, pinned, created_at, updated_at)
201
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),n=this.#e.db.prepare(`INSERT OR REPLACE INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`);this.#e.db.transaction(()=>{for(let r of e)t.run(r.id,r.content,r.type,JSON.stringify(r.tags),r.sourceHarness,r.accessCount,+!!r.pinned,r.createdAt,r.updatedAt),r.embedding!==null&&n.run(Buffer.from(r.embedding.buffer,r.embedding.byteOffset,r.embedding.byteLength),r.id)})()}#n(e,t){if(e.length===0)return new Map;let n=e.map(()=>`?`).join(`, `),r=t?.unresolvedOnly===!0?`AND resolved_at IS NULL`:``,i=this.#e.db.prepare(`SELECT * FROM memory_review_events
202
+ WHERE memory_id IN (${n}) ${r}
203
+ ORDER BY created_at DESC`).all(...e),a=new Map;for(let e of i){let t=N(w.parse(e)),n=a.get(t.memoryId)??[];n.push(t),a.set(t.memoryId,n)}return a}};function we(e,t){return new K(e,t)}const q=(0,ne.promisify)(_.execFile);function J(e){return(0,g.createHash)(`sha256`).update(e).digest(`hex`).slice(0,16)}async function Y(){try{let{stdout:e}=await q(`git`,[`remote`,`get-url`,`origin`]),t=e.trim();if(t){let e=J(t);return{hash:e,name:t.split(`/`).pop()?.replace(/\.git$/,``)??e.slice(0,8)}}}catch{}let e=process.cwd(),t=J(e);return{hash:t,name:e.split(/[/\\]/).filter(Boolean).pop()??t.slice(0,8)}}async function Te(){try{let{stdout:e}=await q(`git`,[`remote`,`get-url`,`origin`]),t=e.trim();if(t)return J(t)}catch{}return J(process.cwd())}const Ee=[{name:`scope-to-projects`,description:`Rename the auto-migrated project for the current directory from its generic hash-derived name to the resolved repo/directory name.`}];async function De(e){let t=await Y(),n=e.getByHash(t.hash);if(n===void 0)return null;let r=n.name,i=e.countMemories(n.id);return e.rename(n.id,t.name),{migration:`scope-to-projects`,oldName:r,newName:t.name,memoryCount:i}}var Oe=class{#e;constructor(e){this.#e=e}upsertByHash(e,t){if(!/^[0-9a-f]{16}$/.test(e))throw Error(`Invalid scope hash "${e}": expected 16 lowercase hex characters`);let n=new Date().toISOString(),r=(0,g.randomUUID)();return this.#e.db.prepare(`INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`).run(r,t,e,n,n),P(j.parse(this.#e.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(e)))}rename(e,t){let n=new Date().toISOString();this.#e.db.prepare(`UPDATE projects SET name = ?, updated_at = ? WHERE id = ?`).run(t,n,e);let r=this.#e.db.prepare(`SELECT * FROM projects WHERE id = ?`).get(e);if(r===void 0)throw Error(`Project not found: ${e}`);return P(r)}list(){return this.#e.db.prepare(`SELECT * FROM projects ORDER BY name ASC`).all().map(P)}getByHash(e){let t=this.#e.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(e);return t===void 0?void 0:P(t)}getByName(e){let t=this.#e.db.prepare(`SELECT * FROM projects WHERE name = ? LIMIT 1`).get(e);return t===void 0?void 0:P(t)}addAssociation(e,t){this.#e.db.prepare(`INSERT OR IGNORE INTO memory_projects (memory_id, project_id) VALUES (?, ?)`).run(e,t)}removeAssociation(e,t){this.#e.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`).run(e,t)}countMemories(e){return this.#e.db.prepare(`SELECT COUNT(*) AS count FROM memory_projects WHERE project_id = ?`).get(e)?.count??0}getProjectsForMemories(e){if(e.length===0)return new Map;let t=e.map(()=>`?`).join(`,`),n=this.#e.db.prepare(`SELECT p.*, mp.memory_id FROM projects p
729
204
  JOIN memory_projects mp ON mp.project_id = p.id
730
- WHERE mp.memory_id IN (${placeholders})`).all(...memoryIds);
731
- const result = /* @__PURE__ */ new Map();
732
- for (const row of rows) {
733
- const list = result.get(row.memory_id) ?? [];
734
- list.push(rowToProject(row));
735
- result.set(row.memory_id, list);
736
- }
737
- return result;
738
- }
739
- };
740
- //#endregion
741
- //#region src/query/engine.ts
742
- const TYPE_WEIGHTS = {
743
- correction: 1,
744
- preference: .8,
745
- decision: .6,
746
- learning: .4,
747
- fact: .2
748
- };
749
- var QueryEngine = class {
750
- #db;
751
- #embedding;
752
- #repo;
753
- constructor(db, embeddingService, repo) {
754
- this.#db = db;
755
- this.#embedding = embeddingService;
756
- this.#repo = repo;
757
- }
758
- async query(options) {
759
- const { query, type, projectHash, limit = 10, includePinned } = QueryOptionsSchema.parse(options);
760
- const queryEmbedding = await this.#embedding.embed(query);
761
- const queryBlob = Buffer.from(queryEmbedding.buffer);
762
- const whereClauses = [];
763
- const params = [queryBlob];
764
- let joinClause = "";
765
- if (!includePinned) whereClauses.push("m.pinned = 0");
766
- if (type !== void 0) {
767
- whereClauses.push("m.type = ?");
768
- params.push(type);
769
- }
770
- if (projectHash !== void 0) {
771
- joinClause = "LEFT JOIN memory_projects mp ON mp.memory_id = m.id LEFT JOIN projects p ON p.id = mp.project_id";
772
- whereClauses.push("p.scope_hash = ?");
773
- params.push(projectHash);
774
- }
775
- const whereSQL = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
776
- const sql = `
205
+ WHERE mp.memory_id IN (${t})`).all(...e),r=new Map;for(let e of n){let t=r.get(e.memory_id)??[];t.push(P(e)),r.set(e.memory_id,t)}return r}};function ke(e){return new Oe(e)}const Ae={correction:1,preference:.8,decision:.6,learning:.4,fact:.2};function X(e,t,n){let r=Ae[e.type],i=e.accessCount/(e.accessCount+10),a=1/(1+(n-new Date(e.updatedAt).getTime())/864e5),o=+!!e.pinned;return t*.4+r*.25+i*.2+a*.1+o*.05}async function Z(e,t){let{query:n,type:r,projectHash:i,limit:a=10,includePinned:o}=E.parse(e),s=await t.embedder.embed(n),c=Buffer.from(s.buffer),l=t.adapter.findByEmbedding(c,{type:r,projectHash:i,includePinned:o}),u=Date.now(),d=l.filter(e=>e.cosineSim>0).map(e=>{let{cosineSim:t,...n}=e;return{...n,score:X(n,t,u)}});d.sort((e,t)=>t.score-e.score);let f=d.slice(0,a);for(let e of f)t.repo.incrementAccessCount(e.id);return f}var je=class{#e;constructor(e){this.#e=e}findByEmbedding(e,t){let{type:n,projectHash:r,includePinned:i}=t,a=[],o=[e],s=``;i||a.push(`m.pinned = 0`),n!==void 0&&(a.push(`m.type = ?`),o.push(n)),r!==void 0&&(s=`LEFT JOIN memory_projects mp ON mp.memory_id = m.id LEFT JOIN projects p ON p.id = mp.project_id`,a.push(`p.scope_hash = ?`),o.push(r));let c=a.length>0?`WHERE ${a.join(` AND `)}`:``,l=`
777
206
  SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim
778
207
  FROM memories m JOIN embeddings e ON e.rowid = m.rowid
779
- ${joinClause}
780
- ${whereSQL}
781
- `;
782
- const rows = this.#db.db.prepare(sql).all(...params);
783
- const now = Date.now();
784
- const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
785
- const memory = rowToMemory(row, []);
786
- const score = this.#computeScore(memory, row.cosine_sim, now);
787
- return {
788
- ...memory,
789
- score
790
- };
791
- });
792
- scored.sort((a, b) => b.score - a.score);
793
- const results = scored.slice(0, limit);
794
- for (const result of results) this.#repo.incrementAccessCount(result.id);
795
- return results;
796
- }
797
- #computeScore(memory, cosine_sim, now) {
798
- const typeWeight = TYPE_WEIGHTS[memory.type];
799
- const accessCountNorm = memory.accessCount / (memory.accessCount + 10);
800
- const recencyNorm = 1 / (1 + (now - new Date(memory.updatedAt).getTime()) / 864e5);
801
- const pinned = memory.pinned ? 1 : 0;
802
- return cosine_sim * .4 + typeWeight * .25 + accessCountNorm * .2 + recencyNorm * .1 + pinned * .05;
803
- }
804
- };
805
- //#endregion
806
- //#region src/session/builder.ts
807
- function listMemoryTypes() {
808
- return [...MEMORY_TYPE_VALUES];
809
- }
810
- var SessionContextBuilder = class {
811
- #db;
812
- constructor(db) {
813
- this.#db = db;
814
- }
815
- getSessionContext(projectHash, synthesis) {
816
- const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
817
- const stats = Object.fromEntries(MEMORY_TYPE_VALUES.map((t) => [t, 0]));
818
- for (const row of typeCounts) {
819
- const parsed = MemoryTypeSchema.safeParse(row.type);
820
- if (parsed.success) stats[parsed.data] = row.count;
821
- }
822
- if (synthesis !== void 0 && synthesis.length > 0) return {
823
- stats,
824
- pinnedGlobal: [],
825
- pinnedProject: [],
826
- synthesis
827
- };
828
- return {
829
- stats,
830
- pinnedGlobal: this.#db.db.prepare(`SELECT * FROM memories
831
- WHERE id NOT IN (SELECT memory_id FROM memory_projects)
832
- AND pinned = 1`).all().map((row) => rowToMemory(row, [])),
833
- pinnedProject: this.#db.db.prepare(`SELECT m.* FROM memories m
834
- JOIN memory_projects mp ON mp.memory_id = m.id
835
- JOIN projects p ON p.id = mp.project_id
836
- WHERE p.scope_hash = ? AND m.pinned = 1`).all(projectHash).map((row) => rowToMemory(row, []))
837
- };
838
- }
839
- };
840
- //#endregion
841
- //#region src/synthesis/repository.ts
842
- function rowToSynthesis(row) {
843
- return SynthesisSchema.parse({
844
- id: row.id,
845
- scope: row.scope,
846
- content: row.content,
847
- sourceMemoryHash: row.source_memory_hash,
848
- synthesizedAt: row.synthesized_at,
849
- expiresAt: row.expires_at,
850
- inFlightSince: row.in_flight_since,
851
- createdAt: row.created_at,
852
- updatedAt: row.updated_at
853
- });
854
- }
855
- const STALENESS_DAYS = 30;
856
- var SynthesisRepository = class {
857
- #db;
858
- constructor(db) {
859
- this.#db = db;
860
- }
861
- saveSynthesis(scope, content, sourceHash) {
862
- const now = (/* @__PURE__ */ new Date()).toISOString();
863
- const expiresAt = new Date(Date.now() + STALENESS_DAYS * 24 * 60 * 60 * 1e3).toISOString();
864
- if (this.#db.db.prepare("SELECT id FROM syntheses WHERE scope = ?").get(scope) !== void 0) this.#db.db.prepare(`UPDATE syntheses
208
+ ${s}
209
+ ${c}
210
+ `;return this.#e.db.prepare(l).all(...o).map(e=>({...M(e,[]),cosineSim:e.cosine_sim}))}},Me=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async query(e){return Z(e,{adapter:new je(this.#e),repo:this.#n,embedder:this.#t})}};function Q(e,t){let n=t.repo.stats();return e.synthesis!==void 0&&e.synthesis.length>0?{stats:n.byType,pinnedGlobal:[],pinnedProject:[],synthesis:e.synthesis}:{stats:n.byType,pinnedGlobal:t.repo.listPinnedGlobal(),pinnedProject:t.repo.listPinnedForProject(e.projectHash)}}function Ne(){return[...v]}var Pe=class{#e;constructor(e){this.#e=e}getSessionContext(e,t){return Q({projectHash:e,synthesis:t},{repo:this.#e})}},Fe=class{#e;#t;#n;#r=new Set;#i=new Map;#a=!1;#o;#s=new Map;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async init(){this.#e.clearStaleInFlight(this.#t.inFlightTimeoutMs??12e4),this.#e.expireStale();let e=this.#e.getExpiredOrDirtyScopes();for(let{scope:t}of e)this.#r.add(t);this.#a=!0,await this.#l()}shutdown(){this.#a=!1,this.#o!==void 0&&(clearTimeout(this.#o),this.#o=void 0);let e=[...this.#s.values()];return e.length===0?Promise.resolve():Promise.race([Promise.allSettled(e).then(()=>void 0),new Promise(e=>setTimeout(e,5e3))])}markDirty(e){this.#r.add(e)}#c(){if(!this.#a)return;let e=this.#t.debounceMs??45e3;this.#o=setTimeout(()=>{this.#l()},e)}async#l(){let e=[...this.#r];for(let t of e){let e=this.#t.inFlightTimeoutMs??12e4,n=this.#e.getSynthesis(t);if(n?.inFlightSince!==null&&n?.inFlightSince!==void 0){if(Date.now()-new Date(n.inFlightSince).getTime()<e)continue;this.#e.clearInFlight(t)}this.#r.delete(t);let r=this.#u(t).finally(()=>{this.#s.delete(t)});this.#s.set(t,r)}this.#c()}async#u(e){this.#e.markInFlight(e);try{let t=e===`global`?void 0:e,n=await this.#n.run(e,t),r=this.#e.computeSourceMemoryHash(e);this.#e.saveSynthesis(e,n,r),this.#i.delete(e)}catch(t){let n=(this.#i.get(e)??0)+1;this.#i.set(e,n);let r=Math.min(n,5),i=(this.#t.debounceMs??45e3)*r;process.stderr.write(`membank synthesis: error for scope=${e} failures=${n} backoff=${i}ms: ${t instanceof Error?t.message:String(t)}\n`),setTimeout(()=>{this.#r.add(e)},i),this.#e.clearInFlight(e)}}};async function Ie(e,t){let n=e===`global`?void 0:e;t.synthRepo.markInFlight(e);try{let[r,i]=await Promise.all([t.agentRunner.run(e,n),Promise.resolve(t.synthRepo.computeSourceMemoryHash(e))]);return t.synthRepo.saveSynthesis(e,r,i),r}catch(n){throw t.synthRepo.clearInFlight(e),n}}var Le=class{#e;constructor(e,t){this.#e=e}async run(e,t){let n=(0,h.createSdkMcpServer)({name:`membank-synthesis-tools`,version:`1.0.0`,tools:[(0,h.tool)(`query_memory`,`Search memories by semantic similarity`,{query:d.z.string().describe(`Search text`),limit:d.z.number().optional().describe(`Maximum results to return`),global:d.z.boolean().optional().describe(`Query global memories only when true, otherwise current project scope`)},async({query:e,limit:n,global:r})=>({content:[{type:`text`,text:await this.#e.queryMemory({query:e,limit:n,global:r,projectHash:t})}]}),{annotations:{readOnlyHint:!0}}),(0,h.tool)(`get_memory_summary`,`Returns aggregate stats: total memories, counts by type, pinned count, review queue size`,{},async()=>({content:[{type:`text`,text:await this.#e.getMemorySummary()}]}),{annotations:{readOnlyHint:!0}})]}),r=`Synthesize the memories for ${e===`global`?`global (across all projects)`:`project scope: ${e}`}. Use get_memory_summary first to understand the overall state, then use query_memory to retrieve relevant memories (query with broad terms like "preferences", "corrections", "decisions", "key facts"). After gathering information, produce a concise synthesis of the most important things to remember about this user. Output only the synthesis text — no preamble, no metadata.`,i=Date.now(),a=Object.fromEntries(Object.entries(process.env).filter(e=>e[1]!==void 0)),o=(0,h.query)({prompt:r,options:{model:`claude-haiku-4-5-20251001`,systemPrompt:`You are a memory synthesizer. Your job is to read the user's stored memories and produce a concise, well-structured summary of what's most important to remember about this user — their preferences, corrections, decisions, and key facts. Pinned memories are higher fidelity and should be weighted more heavily. Exclude transient or ephemeral details. Output plain text suitable for injection into an LLM context window. Be concise — target 200-400 words.`,mcpServers:{"membank-synthesis-tools":n},allowedTools:[`mcp__membank-synthesis-tools__query_memory`,`mcp__membank-synthesis-tools__get_memory_summary`],disallowedTools:[`mcp__membank__*`],settingSources:[],permissionMode:`bypassPermissions`,env:a}}),s=``;for await(let e of o)if(e.type===`result`)if(e.subtype===`success`)s=e.result;else{let t=`errors`in e&&Array.isArray(e.errors)?`: ${e.errors.join(`; `)}`:``;throw Error(`Synthesis agent failed: ${e.subtype}${t}`)}let c=Date.now()-i;if(process.stderr.write(`membank synthesis: scope=${e} duration=${c}ms\n`),s===``)throw Error(`Synthesis agent returned empty result`);return s}};function Re(e,t){return new Le(e,t)}function $(e){return k.parse({id:e.id,scope:e.scope,content:e.content,sourceMemoryHash:e.source_memory_hash,synthesizedAt:e.synthesized_at,expiresAt:e.expires_at,inFlightSince:e.in_flight_since,createdAt:e.created_at,updatedAt:e.updated_at})}var ze=class{#e;constructor(e){this.#e=e}saveSynthesis(e,t,n){let r=new Date().toISOString(),i=new Date(Date.now()+720*60*60*1e3).toISOString();if(this.#e.db.prepare(`SELECT id FROM syntheses WHERE scope = ?`).get(e)!==void 0)this.#e.db.prepare(`UPDATE syntheses
865
211
  SET content = ?, source_memory_hash = ?, synthesized_at = ?, expires_at = ?,
866
212
  in_flight_since = NULL, updated_at = ?
867
- WHERE scope = ?`).run(content, sourceHash, now, expiresAt, now, scope);
868
- else {
869
- const id = (0, node_crypto.randomUUID)();
870
- this.#db.db.prepare(`INSERT INTO syntheses
213
+ WHERE scope = ?`).run(t,n,r,i,r,e);else{let a=(0,g.randomUUID)();this.#e.db.prepare(`INSERT INTO syntheses
871
214
  (id, scope, content, source_memory_hash, synthesized_at, expires_at,
872
215
  in_flight_since, created_at, updated_at)
873
- VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)`).run(id, scope, content, sourceHash, now, expiresAt, now, now);
874
- }
875
- const row = this.#db.db.prepare("SELECT * FROM syntheses WHERE scope = ?").get(scope);
876
- if (row === void 0) throw new Error(`Failed to save synthesis for scope: ${scope}`);
877
- return rowToSynthesis(row);
878
- }
879
- getSynthesis(scope) {
880
- const row = this.#db.db.prepare("SELECT * FROM syntheses WHERE scope = ?").get(scope);
881
- return row !== void 0 ? rowToSynthesis(row) : void 0;
882
- }
883
- markInFlight(scope) {
884
- const now = (/* @__PURE__ */ new Date()).toISOString();
885
- if (this.#db.db.prepare("SELECT id FROM syntheses WHERE scope = ?").get(scope) !== void 0) this.#db.db.prepare("UPDATE syntheses SET in_flight_since = ?, updated_at = ? WHERE scope = ?").run(now, now, scope);
886
- else {
887
- const id = (0, node_crypto.randomUUID)();
888
- const placeholder = "pending";
889
- const future = new Date(Date.now() + STALENESS_DAYS * 24 * 60 * 60 * 1e3).toISOString();
890
- this.#db.db.prepare(`INSERT INTO syntheses
216
+ VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)`).run(a,e,t,n,r,i,r,r)}let a=this.#e.db.prepare(`SELECT * FROM syntheses WHERE scope = ?`).get(e);if(a===void 0)throw Error(`Failed to save synthesis for scope: ${e}`);return $(a)}getSynthesis(e){let t=this.#e.db.prepare(`SELECT * FROM syntheses WHERE scope = ?`).get(e);return t===void 0?void 0:$(t)}listAll(){return this.#e.db.prepare(`SELECT * FROM syntheses ORDER BY scope`).all().map($)}markInFlight(e){let t=new Date().toISOString();if(this.#e.db.prepare(`SELECT id FROM syntheses WHERE scope = ?`).get(e)!==void 0)this.#e.db.prepare(`UPDATE syntheses SET in_flight_since = ?, updated_at = ? WHERE scope = ?`).run(t,t,e);else{let n=(0,g.randomUUID)(),r=new Date(Date.now()+720*60*60*1e3).toISOString();this.#e.db.prepare(`INSERT INTO syntheses
891
217
  (id, scope, content, source_memory_hash, synthesized_at, expires_at,
892
218
  in_flight_since, created_at, updated_at)
893
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, scope, placeholder, "", now, future, now, now, now);
894
- }
895
- }
896
- clearInFlight(scope) {
897
- const now = (/* @__PURE__ */ new Date()).toISOString();
898
- this.#db.db.prepare("UPDATE syntheses SET in_flight_since = NULL, updated_at = ? WHERE scope = ?").run(now, scope);
899
- }
900
- clearStaleInFlight(thresholdMs) {
901
- const cutoff = new Date(Date.now() - thresholdMs).toISOString();
902
- const now = (/* @__PURE__ */ new Date()).toISOString();
903
- this.#db.db.prepare("UPDATE syntheses SET in_flight_since = NULL, updated_at = ? WHERE in_flight_since IS NOT NULL AND in_flight_since < ?").run(now, cutoff);
904
- }
905
- computeSourceMemoryHash(scope) {
906
- let contents;
907
- if (scope === "global") contents = this.#db.db.prepare(`SELECT content FROM memories
219
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(n,e,`pending`,``,t,r,t,t,t)}}clearInFlight(e){let t=new Date().toISOString();this.#e.db.prepare(`UPDATE syntheses SET in_flight_since = NULL, updated_at = ? WHERE scope = ?`).run(t,e)}clearStaleInFlight(e){let t=new Date(Date.now()-e).toISOString(),n=new Date().toISOString();this.#e.db.prepare(`UPDATE syntheses SET in_flight_since = NULL, updated_at = ? WHERE in_flight_since IS NOT NULL AND in_flight_since < ?`).run(n,t)}computeSourceMemoryHash(e){let t;return t=e===`global`?this.#e.db.prepare(`SELECT content FROM memories
908
220
  WHERE id NOT IN (SELECT memory_id FROM memory_projects)
909
- ORDER BY id`).all();
910
- else contents = this.#db.db.prepare(`SELECT m.content FROM memories m
221
+ ORDER BY id`).all():this.#e.db.prepare(`SELECT m.content FROM memories m
911
222
  JOIN memory_projects mp ON mp.memory_id = m.id
912
223
  JOIN projects p ON p.id = mp.project_id
913
224
  WHERE p.scope_hash = ?
914
- ORDER BY m.id`).all(scope);
915
- return (0, node_crypto.createHash)("sha256").update(JSON.stringify(contents.map((r) => r.content))).digest("hex");
916
- }
917
- getExpiredOrDirtyScopes() {
918
- const allScopes = this.getAllActiveScopes();
919
- const now = (/* @__PURE__ */ new Date()).toISOString();
920
- const results = [];
921
- for (const scope of allScopes) {
922
- const row = this.#db.db.prepare("SELECT * FROM syntheses WHERE scope = ?").get(scope);
923
- if (row === void 0 || row.content === "pending" && row.source_memory_hash === "") {
924
- results.push({
925
- scope,
926
- reason: "missing"
927
- });
928
- continue;
929
- }
930
- if (row.expires_at <= now) {
931
- results.push({
932
- scope,
933
- reason: "expired"
934
- });
935
- continue;
936
- }
937
- if (this.computeSourceMemoryHash(scope) !== row.source_memory_hash) results.push({
938
- scope,
939
- reason: "dirty"
940
- });
941
- }
942
- return results;
943
- }
944
- getAllActiveScopes() {
945
- return ["global", ...this.#db.db.prepare("SELECT DISTINCT scope_hash FROM projects").all().map((r) => r.scope_hash)];
946
- }
947
- expireStale() {
948
- const now = (/* @__PURE__ */ new Date()).toISOString();
949
- this.#db.db.prepare("DELETE FROM syntheses WHERE expires_at < ?").run(now);
950
- }
951
- };
952
- //#endregion
953
- exports.DatabaseError = DatabaseError;
954
- exports.DatabaseManager = DatabaseManager;
955
- exports.EmbeddingService = EmbeddingService;
956
- exports.MEMORY_TYPE_VALUES = MEMORY_TYPE_VALUES;
957
- exports.MIGRATIONS = MIGRATIONS;
958
- exports.MembankError = MembankError;
959
- exports.MemoryPatchSchema = MemoryPatchSchema;
960
- exports.MemoryRepository = MemoryRepository;
961
- exports.MemoryRowSchema = MemoryRowSchema;
962
- exports.MemorySchema = MemorySchema;
963
- exports.MemoryTypeSchema = MemoryTypeSchema;
964
- exports.PIN_BUDGET_THRESHOLD = PIN_BUDGET_THRESHOLD;
965
- exports.ProjectRepository = ProjectRepository;
966
- exports.ProjectRowSchema = ProjectRowSchema;
967
- exports.ProjectSchema = ProjectSchema;
968
- exports.QueryEngine = QueryEngine;
969
- exports.QueryOptionsSchema = QueryOptionsSchema;
970
- exports.ReviewEventRowSchema = ReviewEventRowSchema;
971
- exports.ReviewEventSchema = ReviewEventSchema;
972
- exports.ReviewReasonSchema = ReviewReasonSchema;
973
- exports.SaveOptionsSchema = SaveOptionsSchema;
974
- exports.SessionContextBuilder = SessionContextBuilder;
975
- exports.SessionContextSchema = SessionContextSchema;
976
- exports.SynthesisRepository = SynthesisRepository;
977
- exports.SynthesisSchema = SynthesisSchema;
978
- exports.TagsJsonSchema = TagsJsonSchema;
979
- exports.isSynthesisEnabled = isSynthesisEnabled;
980
- exports.listMemoryTypes = listMemoryTypes;
981
- exports.resolveProject = resolveProject;
982
- exports.resolveScope = resolveScope;
983
- exports.rowToMemory = rowToMemory;
984
- exports.rowToProject = rowToProject;
985
- exports.runScopeToProjectsMigration = runScopeToProjectsMigration;
225
+ ORDER BY m.id`).all(e),(0,g.createHash)(`sha256`).update(JSON.stringify(t.map(e=>e.content))).digest(`hex`)}getExpiredOrDirtyScopes(){let e=this.getAllActiveScopes(),t=new Date().toISOString(),n=[];for(let r of e){let e=this.#e.db.prepare(`SELECT * FROM syntheses WHERE scope = ?`).get(r);if(e===void 0||e.content===`pending`&&e.source_memory_hash===``){n.push({scope:r,reason:`missing`});continue}if(e.expires_at<=t){n.push({scope:r,reason:`expired`});continue}this.computeSourceMemoryHash(r)!==e.source_memory_hash&&n.push({scope:r,reason:`dirty`})}return n}getAllActiveScopes(){return[`global`,...this.#e.db.prepare(`SELECT DISTINCT scope_hash FROM projects`).all().map(e=>e.scope_hash)]}expireStale(){let e=new Date().toISOString();this.#e.db.prepare(`DELETE FROM syntheses WHERE expires_at < ?`).run(e)}};function Be(e){return new ze(e)}exports.DatabaseError=I,exports.DatabaseManager=ce,exports.EmbeddingService=V,exports.MEMORY_TYPE_VALUES=v,exports.MIGRATIONS=Ee,exports.MODEL_NAME=L,exports.MembankError=F,exports.MemoryPatchSchema=O,exports.MemoryRowSchema=A,exports.MemorySchema=T,exports.MemoryTypeSchema=y,exports.ModelDownloadError=R,exports.ModelDownloader=B,exports.PIN_BUDGET_THRESHOLD=G,exports.ProjectRowSchema=j,exports.ProjectSchema=x,exports.QueryEngine=Me,exports.QueryOptionsSchema=E,exports.ReviewEventRowSchema=w,exports.ReviewEventSchema=C,exports.ReviewReasonSchema=S,exports.SaveOptionsSchema=D,exports.SessionContextBuilder=Pe,exports.SessionContextSchema=ae,exports.SqliteMemoryRepository=K,exports.SynthesisEngine=Fe,exports.SynthesisSchema=k,exports.TagsJsonSchema=b,exports.createClaudeCodeTranscriptReader=_e,exports.createExtractionAgentRunner=ue,exports.createExtractionRunRepository=me,exports.createMemoryRepository=we,exports.createProjectRepository=ke,exports.createSynthesisAgentRunner=Re,exports.createSynthesisRepository=Be,exports.deleteMemory=ve,exports.getSessionContext=Q,exports.isOverBudget=Ce,exports.isSynthesisEnabled=ie,exports.listMemoryTypes=Ne,exports.queryMemories=Z,exports.resolveProject=Y,exports.resolveReview=ye,exports.resolveScope=Te,exports.rowToMemory=M,exports.rowToProject=P,exports.runExtraction=H,exports.runScopeToProjectsMigration=De,exports.runSynthesis=Ie,exports.saveMemory=xe,exports.updateMemory=Se;