@mnemonic-ai/core 0.1.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.
Files changed (47) hide show
  1. package/dist/chunk-5Z46NSNR.js +228 -0
  2. package/dist/chunk-5Z46NSNR.js.map +1 -0
  3. package/dist/chunk-CZDK53NR.js +24 -0
  4. package/dist/chunk-CZDK53NR.js.map +1 -0
  5. package/dist/chunk-L7SCUMC3.js +53 -0
  6. package/dist/chunk-L7SCUMC3.js.map +1 -0
  7. package/dist/chunk-M3IZJTMM.js +474 -0
  8. package/dist/chunk-M3IZJTMM.js.map +1 -0
  9. package/dist/chunk-NA7L5FQN.js +1581 -0
  10. package/dist/chunk-NA7L5FQN.js.map +1 -0
  11. package/dist/chunk-OEEEWS2M.js +375 -0
  12. package/dist/chunk-OEEEWS2M.js.map +1 -0
  13. package/dist/chunk-YZW6DYUY.js +46 -0
  14. package/dist/chunk-YZW6DYUY.js.map +1 -0
  15. package/dist/cli/main.cjs +1827 -0
  16. package/dist/cli/main.cjs.map +1 -0
  17. package/dist/cli/main.d.cts +1 -0
  18. package/dist/cli/main.d.ts +1 -0
  19. package/dist/cli/main.js +84 -0
  20. package/dist/cli/main.js.map +1 -0
  21. package/dist/client-b2Xhkqdl.d.cts +409 -0
  22. package/dist/client-b2Xhkqdl.d.ts +409 -0
  23. package/dist/index.cjs +2547 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +309 -0
  26. package/dist/index.d.ts +309 -0
  27. package/dist/index.js +160 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/local-MYLINANE.js +7 -0
  30. package/dist/local-MYLINANE.js.map +1 -0
  31. package/dist/mcp/main.cjs +2775 -0
  32. package/dist/mcp/main.cjs.map +1 -0
  33. package/dist/mcp/main.d.cts +1 -0
  34. package/dist/mcp/main.d.ts +1 -0
  35. package/dist/mcp/main.js +31 -0
  36. package/dist/mcp/main.js.map +1 -0
  37. package/dist/mcp/server.cjs +2765 -0
  38. package/dist/mcp/server.cjs.map +1 -0
  39. package/dist/mcp/server.d.cts +23 -0
  40. package/dist/mcp/server.d.ts +23 -0
  41. package/dist/mcp/server.js +12 -0
  42. package/dist/mcp/server.js.map +1 -0
  43. package/dist/openai-GDIC3YVT.js +7 -0
  44. package/dist/openai-GDIC3YVT.js.map +1 -0
  45. package/dist/postgres-GQ6DZDBW.js +8 -0
  46. package/dist/postgres-GQ6DZDBW.js.map +1 -0
  47. package/package.json +117 -0
@@ -0,0 +1,2775 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
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 __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
+ // If the importer is in node compatibility mode or this is not an ESM
26
+ // file that has been converted to a CommonJS file using a Babel-
27
+ // compatible transform (i.e. "__esModule" has not been set), then set
28
+ // "default" to the CommonJS "module.exports" for node compatibility.
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
+ mod
31
+ ));
32
+
33
+ // src/core/utils.ts
34
+ function cosineSimilarityVector(query, embeddings) {
35
+ const dim = query.length;
36
+ const n = embeddings.length;
37
+ const result = new Float64Array(n);
38
+ for (let ei = 0; ei < n; ei++) {
39
+ if (embeddings[ei].length !== dim) {
40
+ throw new Error(
41
+ `Embedding dimension mismatch: ${embeddings[ei].length} vs query ${dim}`
42
+ );
43
+ }
44
+ }
45
+ let normQ = 0;
46
+ for (let i = 0; i < dim; i++) normQ += query[i] * query[i];
47
+ normQ = Math.sqrt(normQ);
48
+ if (normQ === 0) return result;
49
+ const qNormed = new Float64Array(dim);
50
+ for (let i = 0; i < dim; i++) qNormed[i] = query[i] / normQ;
51
+ for (let ei = 0; ei < n; ei++) {
52
+ const emb = embeddings[ei];
53
+ let dot = 0;
54
+ let normE = 0;
55
+ for (let i = 0; i < dim; i++) {
56
+ dot += qNormed[i] * emb[i];
57
+ normE += emb[i] * emb[i];
58
+ }
59
+ normE = Math.sqrt(normE);
60
+ result[ei] = normE === 0 ? 0 : dot / normE;
61
+ }
62
+ return result;
63
+ }
64
+ function cosineSimilarityMatrix(embeddings) {
65
+ const n = embeddings.length;
66
+ if (n === 0) return new Float64Array(0);
67
+ const dim = embeddings[0].length;
68
+ for (let i = 1; i < n; i++) {
69
+ if (embeddings[i].length !== dim) {
70
+ throw new Error(
71
+ `Embedding dimension mismatch at index ${i}: ${embeddings[i].length} vs ${dim}`
72
+ );
73
+ }
74
+ }
75
+ const result = new Float64Array(n * n);
76
+ const normed = [];
77
+ const isZeroNorm = [];
78
+ for (let i = 0; i < n; i++) {
79
+ const emb = embeddings[i];
80
+ let norm = 0;
81
+ for (let d = 0; d < dim; d++) norm += emb[d] * emb[d];
82
+ norm = Math.sqrt(norm);
83
+ const row = new Float64Array(dim);
84
+ if (norm > 0) {
85
+ for (let d = 0; d < dim; d++) row[d] = emb[d] / norm;
86
+ isZeroNorm.push(false);
87
+ } else {
88
+ isZeroNorm.push(true);
89
+ }
90
+ normed.push(row);
91
+ }
92
+ for (let i = 0; i < n; i++) {
93
+ result[i * n + i] = isZeroNorm[i] ? 0 : 1;
94
+ for (let j = i + 1; j < n; j++) {
95
+ let dot = 0;
96
+ for (let d = 0; d < dim; d++) dot += normed[i][d] * normed[j][d];
97
+ result[i * n + j] = dot;
98
+ result[j * n + i] = dot;
99
+ }
100
+ }
101
+ return result;
102
+ }
103
+ function parseDuration(s) {
104
+ const match = s.trim().toLowerCase().match(DURATION_RE);
105
+ if (!match) {
106
+ throw new Error(
107
+ `Cannot parse duration: '${s}'. Use e.g. 2w, 30d, 24h, 60m, 120s.`
108
+ );
109
+ }
110
+ const n = parseInt(match[1], 10);
111
+ const unit = match[2];
112
+ return n * MS_MULTIPLIERS[unit];
113
+ }
114
+ function estimateTokens(text, charsPerToken = 4) {
115
+ return Math.max(1, Math.floor(text.length / charsPerToken));
116
+ }
117
+ function parseSqlitePath(uri) {
118
+ if (!uri.includes("///")) {
119
+ throw new Error(
120
+ `Invalid SQLite URI: '${uri}'. Expected format: sqlite:///path/to/db`
121
+ );
122
+ }
123
+ return uri.split("///")[1] || "mnemonic.db";
124
+ }
125
+ function bruteForceSimilarPairs(memories, threshold, maxCandidates) {
126
+ const cap = Math.max(0, maxCandidates);
127
+ const withEmb = memories.filter((m) => m.embedding != null).slice(0, cap);
128
+ if (withEmb.length < 2) return [];
129
+ const simMatrix = cosineSimilarityMatrix(withEmb.map((m) => m.embedding));
130
+ const n = withEmb.length;
131
+ const pairs = [];
132
+ for (let i = 0; i < n; i++) {
133
+ for (let j = i + 1; j < n; j++) {
134
+ const sim = simMatrix[i * n + j];
135
+ if (sim >= threshold) {
136
+ pairs.push({ idA: withEmb[i].id, idB: withEmb[j].id, similarity: sim });
137
+ }
138
+ }
139
+ }
140
+ return pairs;
141
+ }
142
+ function bruteForceCosineSearch(memories, embedding, limit) {
143
+ if (limit <= 0) return [];
144
+ const withEmb = memories.filter((m) => m.embedding != null);
145
+ if (withEmb.length === 0) return [];
146
+ const embList = withEmb.map((m) => m.embedding);
147
+ const sims = cosineSimilarityVector(embedding, embList);
148
+ const indices = Array.from({ length: withEmb.length }, (_, i) => i);
149
+ indices.sort((a, b) => sims[b] - sims[a]);
150
+ return indices.slice(0, limit).map((i) => withEmb[i]);
151
+ }
152
+ var DURATION_RE, MS_MULTIPLIERS;
153
+ var init_utils = __esm({
154
+ "src/core/utils.ts"() {
155
+ "use strict";
156
+ DURATION_RE = /^(\d+)\s*([wdhms])$/;
157
+ MS_MULTIPLIERS = {
158
+ w: 7 * 24 * 60 * 60 * 1e3,
159
+ d: 24 * 60 * 60 * 1e3,
160
+ h: 60 * 60 * 1e3,
161
+ m: 60 * 1e3,
162
+ s: 1e3
163
+ };
164
+ }
165
+ });
166
+
167
+ // src/core/memory.ts
168
+ function createEdge(sourceId, targetId, relation, weight = 1) {
169
+ return { sourceId, targetId, relation, weight, createdAt: /* @__PURE__ */ new Date() };
170
+ }
171
+ var import_node_crypto, MemoryTier, Memory;
172
+ var init_memory = __esm({
173
+ "src/core/memory.ts"() {
174
+ "use strict";
175
+ import_node_crypto = require("crypto");
176
+ init_utils();
177
+ MemoryTier = {
178
+ IDENTITY: "identity",
179
+ PROCEDURAL: "procedural",
180
+ STRUCTURAL: "structural",
181
+ EPISODIC: "episodic",
182
+ TRANSIENT: "transient"
183
+ };
184
+ Memory = class {
185
+ content;
186
+ id;
187
+ tier;
188
+ source;
189
+ actor;
190
+ sessionId;
191
+ createdAt;
192
+ lastAccessedAt;
193
+ accessCount;
194
+ importance;
195
+ supersededBy;
196
+ contradictedBy;
197
+ embedding;
198
+ metadata;
199
+ linkedIds;
200
+ agentId;
201
+ shared;
202
+ _charsPerToken;
203
+ constructor(init) {
204
+ const now = /* @__PURE__ */ new Date();
205
+ this.content = init.content;
206
+ this.id = init.id ?? (0, import_node_crypto.randomUUID)().replace(/-/g, "");
207
+ this.tier = init.tier ?? MemoryTier.EPISODIC;
208
+ this.source = init.source ?? "unknown";
209
+ this.actor = init.actor ?? "system";
210
+ this.sessionId = init.sessionId ?? null;
211
+ this.createdAt = init.createdAt ?? now;
212
+ this.lastAccessedAt = init.lastAccessedAt ?? now;
213
+ this.accessCount = init.accessCount ?? 0;
214
+ this.importance = init.importance ?? 1;
215
+ this.supersededBy = init.supersededBy ?? null;
216
+ this.contradictedBy = init.contradictedBy ?? null;
217
+ this.embedding = init.embedding ?? null;
218
+ this.metadata = init.metadata ?? {};
219
+ this.linkedIds = init.linkedIds ?? [];
220
+ this.agentId = init.agentId ?? "default";
221
+ this.shared = init.shared ?? false;
222
+ this._charsPerToken = init.charsPerToken ?? 4;
223
+ }
224
+ /** The tier as a plain string, regardless of whether it was set as an enum. */
225
+ get tierName() {
226
+ return this.tier;
227
+ }
228
+ /** Rough token count using configurable chars-per-token ratio. */
229
+ get tokenEstimate() {
230
+ return estimateTokens(this.content, this._charsPerToken);
231
+ }
232
+ /** Record an access. */
233
+ touch() {
234
+ this.lastAccessedAt = /* @__PURE__ */ new Date();
235
+ this.accessCount += 1;
236
+ }
237
+ };
238
+ }
239
+ });
240
+
241
+ // src/embeddings/openai.ts
242
+ var openai_exports = {};
243
+ __export(openai_exports, {
244
+ OpenAIEmbedder: () => OpenAIEmbedder
245
+ });
246
+ var MODEL_DIMENSIONS, OpenAIEmbedder;
247
+ var init_openai = __esm({
248
+ "src/embeddings/openai.ts"() {
249
+ "use strict";
250
+ MODEL_DIMENSIONS = {
251
+ "text-embedding-3-small": 1536,
252
+ "text-embedding-3-large": 3072,
253
+ "text-embedding-ada-002": 1536
254
+ };
255
+ OpenAIEmbedder = class {
256
+ _model;
257
+ _apiKey;
258
+ _client = null;
259
+ constructor(options) {
260
+ this._model = options?.model ?? "text-embedding-3-small";
261
+ this._apiKey = options?.apiKey;
262
+ }
263
+ get dimension() {
264
+ return MODEL_DIMENSIONS[this._model] ?? 1536;
265
+ }
266
+ async getClient() {
267
+ if (!this._client) {
268
+ const mod = await Function('return import("openai")')();
269
+ this._client = new mod.default({ apiKey: this._apiKey });
270
+ }
271
+ return this._client;
272
+ }
273
+ async embed(text) {
274
+ const client = await this.getClient();
275
+ const response = await client.embeddings.create({
276
+ model: this._model,
277
+ input: text
278
+ });
279
+ return response.data[0].embedding;
280
+ }
281
+ async embedBatch(texts) {
282
+ const client = await this.getClient();
283
+ const response = await client.embeddings.create({
284
+ model: this._model,
285
+ input: texts
286
+ });
287
+ return response.data.sort((a, b) => a.index - b.index).map((d) => d.embedding);
288
+ }
289
+ };
290
+ }
291
+ });
292
+
293
+ // src/embeddings/local.ts
294
+ var local_exports = {};
295
+ __export(local_exports, {
296
+ LocalEmbedder: () => LocalEmbedder
297
+ });
298
+ var MODEL_DIMENSIONS2, DEFAULT_MODEL, DEFAULT_DIMENSION, LocalEmbedder;
299
+ var init_local = __esm({
300
+ "src/embeddings/local.ts"() {
301
+ "use strict";
302
+ MODEL_DIMENSIONS2 = {
303
+ "Xenova/all-MiniLM-L6-v2": 384,
304
+ "Xenova/all-MiniLM-L12-v2": 384,
305
+ "Xenova/all-mpnet-base-v2": 768
306
+ };
307
+ DEFAULT_MODEL = "Xenova/all-MiniLM-L6-v2";
308
+ DEFAULT_DIMENSION = 384;
309
+ LocalEmbedder = class {
310
+ _modelName;
311
+ _pipeline = null;
312
+ _dimension;
313
+ constructor(options) {
314
+ this._modelName = options?.model ?? DEFAULT_MODEL;
315
+ this._dimension = MODEL_DIMENSIONS2[this._modelName] ?? DEFAULT_DIMENSION;
316
+ }
317
+ get dimension() {
318
+ return this._dimension;
319
+ }
320
+ async _getPipeline() {
321
+ if (this._pipeline) return this._pipeline;
322
+ let pipeline;
323
+ try {
324
+ const mod = await import("@huggingface/transformers");
325
+ pipeline = mod.pipeline ?? mod.default?.pipeline;
326
+ } catch {
327
+ throw new Error(
328
+ "LocalEmbedder requires '@huggingface/transformers'. Install it with: npm install @huggingface/transformers"
329
+ );
330
+ }
331
+ this._pipeline = await pipeline("feature-extraction", this._modelName, {
332
+ quantized: true
333
+ });
334
+ return this._pipeline;
335
+ }
336
+ async embed(text) {
337
+ const pipe = await this._getPipeline();
338
+ const output = await pipe(text, { pooling: "mean", normalize: true });
339
+ return Array.from(output.data).slice(0, this._dimension);
340
+ }
341
+ async embedBatch(texts) {
342
+ const results = [];
343
+ for (const text of texts) {
344
+ results.push(await this.embed(text));
345
+ }
346
+ return results;
347
+ }
348
+ };
349
+ }
350
+ });
351
+
352
+ // src/stores/postgres.ts
353
+ var postgres_exports = {};
354
+ __export(postgres_exports, {
355
+ PostgresStore: () => PostgresStore
356
+ });
357
+ async function loadPg() {
358
+ if (_pgModule) return _pgModule;
359
+ try {
360
+ _pgModule = await import("pg");
361
+ return _pgModule;
362
+ } catch {
363
+ throw new Error(
364
+ "PostgresStore requires the 'pg' package. Install it with: npm install pg pgvector"
365
+ );
366
+ }
367
+ }
368
+ async function loadPgvectorRegister() {
369
+ if (_registerPgvector) return _registerPgvector;
370
+ try {
371
+ const mod = await import("pgvector/pg");
372
+ _registerPgvector = mod.registerTypes ?? mod.default?.registerTypes ?? mod.registerType ?? mod.default?.registerType;
373
+ return _registerPgvector;
374
+ } catch {
375
+ throw new Error(
376
+ "PostgresStore requires the 'pgvector' package. Install it with: npm install pgvector"
377
+ );
378
+ }
379
+ }
380
+ function rowToMemory2(row) {
381
+ const embedding = row["embedding"];
382
+ let parsedEmbedding = null;
383
+ if (embedding != null) {
384
+ if (Array.isArray(embedding)) {
385
+ parsedEmbedding = embedding;
386
+ } else if (typeof embedding === "string") {
387
+ parsedEmbedding = JSON.parse(embedding);
388
+ }
389
+ }
390
+ const m = new Memory({
391
+ id: row["id"],
392
+ content: row["content"],
393
+ tier: row["tier"],
394
+ source: row["source"],
395
+ actor: row["actor"],
396
+ sessionId: row["session_id"],
397
+ createdAt: new Date(row["created_at"]),
398
+ lastAccessedAt: new Date(row["last_accessed"]),
399
+ accessCount: row["access_count"],
400
+ importance: row["importance"],
401
+ supersededBy: row["superseded_by"],
402
+ embedding: parsedEmbedding,
403
+ metadata: typeof row["metadata"] === "string" ? JSON.parse(row["metadata"]) : row["metadata"] ?? {},
404
+ linkedIds: typeof row["linked_ids"] === "string" ? JSON.parse(row["linked_ids"]) : row["linked_ids"] ?? [],
405
+ agentId: row["agent_id"] ?? "default",
406
+ shared: row["shared"] === true || row["shared"] === "t"
407
+ });
408
+ m.contradictedBy = row["contradicted_by"];
409
+ return m;
410
+ }
411
+ var _pgModule, _registerPgvector, PostgresStore;
412
+ var init_postgres = __esm({
413
+ "src/stores/postgres.ts"() {
414
+ "use strict";
415
+ init_memory();
416
+ init_utils();
417
+ _pgModule = null;
418
+ _registerPgvector = null;
419
+ PostgresStore = class {
420
+ _dsn;
421
+ _embeddingDim;
422
+ _poolMin;
423
+ _poolMax;
424
+ _pool = null;
425
+ _pgvectorRegistered = false;
426
+ constructor(options) {
427
+ this._dsn = options.dsn;
428
+ this._embeddingDim = options.embeddingDim ?? 1536;
429
+ this._poolMin = options.poolMin ?? 2;
430
+ this._poolMax = options.poolMax ?? 10;
431
+ }
432
+ async _getPool() {
433
+ if (this._pool) return this._pool;
434
+ const pg = await loadPg();
435
+ this._pool = new pg.Pool({
436
+ connectionString: this._dsn,
437
+ min: this._poolMin,
438
+ max: this._poolMax
439
+ });
440
+ return this._pool;
441
+ }
442
+ async _query(sql, params = []) {
443
+ const pool = await this._getPool();
444
+ const client = await pool.connect();
445
+ try {
446
+ return await client.query(sql, params);
447
+ } finally {
448
+ client.release();
449
+ }
450
+ }
451
+ /** Register pgvector on the pool's connect event so every connection gets it. */
452
+ async _setupPgvectorOnPool() {
453
+ if (this._pgvectorRegistered) return;
454
+ const pool = await this._getPool();
455
+ const register = await loadPgvectorRegister();
456
+ pool.on("connect", (client2) => {
457
+ register(client2).catch((err) => {
458
+ process.stderr.write(`pgvector registration failed: ${err.message}
459
+ `);
460
+ });
461
+ });
462
+ const client = await pool.connect();
463
+ try {
464
+ await register(client);
465
+ } finally {
466
+ client.release();
467
+ }
468
+ this._pgvectorRegistered = true;
469
+ }
470
+ async initialize() {
471
+ await this._query("CREATE EXTENSION IF NOT EXISTS vector");
472
+ await this._setupPgvectorOnPool();
473
+ await this._query(`
474
+ CREATE TABLE IF NOT EXISTS memories (
475
+ id TEXT PRIMARY KEY,
476
+ content TEXT NOT NULL,
477
+ tier TEXT NOT NULL,
478
+ source TEXT NOT NULL DEFAULT 'unknown',
479
+ actor TEXT NOT NULL DEFAULT 'system',
480
+ session_id TEXT,
481
+ created_at TIMESTAMPTZ NOT NULL,
482
+ last_accessed TIMESTAMPTZ NOT NULL,
483
+ access_count INTEGER NOT NULL DEFAULT 0,
484
+ importance DOUBLE PRECISION NOT NULL DEFAULT 1.0,
485
+ superseded_by TEXT,
486
+ contradicted_by TEXT,
487
+ embedding vector(${this._embeddingDim}),
488
+ metadata JSONB NOT NULL DEFAULT '{}',
489
+ linked_ids JSONB NOT NULL DEFAULT '[]',
490
+ agent_id TEXT NOT NULL DEFAULT 'default',
491
+ shared BOOLEAN NOT NULL DEFAULT FALSE
492
+ )
493
+ `);
494
+ await this._query(`
495
+ CREATE TABLE IF NOT EXISTS edges (
496
+ source_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
497
+ target_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
498
+ relation TEXT NOT NULL,
499
+ weight DOUBLE PRECISION NOT NULL DEFAULT 1.0,
500
+ created_at TIMESTAMPTZ NOT NULL,
501
+ PRIMARY KEY (source_id, target_id, relation)
502
+ )
503
+ `);
504
+ await this._query(`
505
+ CREATE TABLE IF NOT EXISTS meta (
506
+ key TEXT PRIMARY KEY,
507
+ value TEXT NOT NULL
508
+ )
509
+ `);
510
+ await this._migrateColumns();
511
+ await this._query(
512
+ "CREATE INDEX IF NOT EXISTS idx_memories_agent_id ON memories (agent_id)"
513
+ );
514
+ await this._query(
515
+ "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories (tier)"
516
+ );
517
+ await this._query(
518
+ "CREATE INDEX IF NOT EXISTS idx_memories_created_at ON memories (created_at)"
519
+ );
520
+ await this._query(
521
+ "CREATE INDEX IF NOT EXISTS idx_edges_source ON edges (source_id)"
522
+ );
523
+ await this._query(
524
+ "CREATE INDEX IF NOT EXISTS idx_edges_target ON edges (target_id)"
525
+ );
526
+ await this._query(
527
+ "CREATE INDEX IF NOT EXISTS idx_memories_shared ON memories (shared) WHERE shared = TRUE"
528
+ );
529
+ try {
530
+ await this._query(`
531
+ CREATE INDEX IF NOT EXISTS idx_memories_embedding_hnsw
532
+ ON memories USING hnsw (embedding vector_cosine_ops)
533
+ WITH (m = 16, ef_construction = 64)
534
+ `);
535
+ } catch {
536
+ }
537
+ }
538
+ async _migrateColumns() {
539
+ await this._query(
540
+ "ALTER TABLE memories ADD COLUMN IF NOT EXISTS agent_id TEXT NOT NULL DEFAULT 'default'"
541
+ );
542
+ await this._query(
543
+ "ALTER TABLE memories ADD COLUMN IF NOT EXISTS shared BOOLEAN NOT NULL DEFAULT FALSE"
544
+ );
545
+ await this._query(
546
+ "ALTER TABLE memories ADD COLUMN IF NOT EXISTS contradicted_by TEXT"
547
+ );
548
+ await this._query(
549
+ "UPDATE memories SET contradicted_by = metadata->>'contradicted_by' WHERE contradicted_by IS NULL AND metadata->>'contradicted_by' IS NOT NULL"
550
+ );
551
+ for (const col of ["source_id", "target_id"]) {
552
+ const name = `edges_${col}_fkey`;
553
+ try {
554
+ await this._query(
555
+ `ALTER TABLE edges ADD CONSTRAINT ${name} FOREIGN KEY (${col}) REFERENCES memories(id) ON DELETE CASCADE`
556
+ );
557
+ } catch (err) {
558
+ const code = err.code;
559
+ if (code !== "42710") throw err;
560
+ }
561
+ }
562
+ }
563
+ async close() {
564
+ if (this._pool) {
565
+ await this._pool.end();
566
+ this._pool = null;
567
+ this._pgvectorRegistered = false;
568
+ }
569
+ }
570
+ async save(memory) {
571
+ await this._query(
572
+ `INSERT INTO memories
573
+ (id, content, tier, source, actor, session_id, created_at,
574
+ last_accessed, access_count, importance, superseded_by,
575
+ contradicted_by, embedding, metadata, linked_ids, agent_id, shared)
576
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13::vector, $14, $15, $16, $17)
577
+ ON CONFLICT (id) DO UPDATE SET
578
+ content = EXCLUDED.content,
579
+ tier = EXCLUDED.tier,
580
+ source = EXCLUDED.source,
581
+ actor = EXCLUDED.actor,
582
+ session_id = EXCLUDED.session_id,
583
+ last_accessed = EXCLUDED.last_accessed,
584
+ access_count = EXCLUDED.access_count,
585
+ importance = EXCLUDED.importance,
586
+ superseded_by = EXCLUDED.superseded_by,
587
+ contradicted_by = EXCLUDED.contradicted_by,
588
+ embedding = EXCLUDED.embedding,
589
+ metadata = EXCLUDED.metadata,
590
+ linked_ids = EXCLUDED.linked_ids,
591
+ agent_id = EXCLUDED.agent_id,
592
+ shared = EXCLUDED.shared`,
593
+ [
594
+ memory.id,
595
+ memory.content,
596
+ memory.tierName,
597
+ memory.source,
598
+ memory.actor,
599
+ memory.sessionId,
600
+ memory.createdAt.toISOString(),
601
+ memory.lastAccessedAt.toISOString(),
602
+ memory.accessCount,
603
+ memory.importance,
604
+ memory.supersededBy,
605
+ memory.contradictedBy ?? null,
606
+ memory.embedding ? `[${memory.embedding.join(",")}]` : null,
607
+ JSON.stringify(memory.metadata),
608
+ JSON.stringify(memory.linkedIds),
609
+ memory.agentId,
610
+ memory.shared
611
+ ]
612
+ );
613
+ }
614
+ async get(memoryId) {
615
+ const result = await this._query(
616
+ "SELECT * FROM memories WHERE id = $1",
617
+ [memoryId]
618
+ );
619
+ if (result.rows.length === 0) return null;
620
+ return rowToMemory2(result.rows[0]);
621
+ }
622
+ async listAll(options) {
623
+ const clauses = [];
624
+ const params = [];
625
+ let paramIdx = 1;
626
+ if (options?.agentId) {
627
+ if (options.includeShared === false) {
628
+ clauses.push(`agent_id = $${paramIdx}`);
629
+ params.push(options.agentId);
630
+ paramIdx++;
631
+ } else {
632
+ clauses.push(`(agent_id = $${paramIdx} OR shared = TRUE)`);
633
+ params.push(options.agentId);
634
+ paramIdx++;
635
+ }
636
+ }
637
+ if (options?.tier != null) {
638
+ clauses.push(`tier = $${paramIdx}`);
639
+ params.push(options.tier);
640
+ paramIdx++;
641
+ }
642
+ const where = clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
643
+ let limitClause = "";
644
+ if (options?.limit != null) {
645
+ limitClause = ` LIMIT $${paramIdx}`;
646
+ params.push(options.limit);
647
+ paramIdx++;
648
+ }
649
+ const result = await this._query(
650
+ `SELECT * FROM memories${where}${limitClause}`,
651
+ params
652
+ );
653
+ return result.rows.map(rowToMemory2);
654
+ }
655
+ async delete(memoryId) {
656
+ await this._query("DELETE FROM memories WHERE id = $1", [memoryId]);
657
+ }
658
+ async deleteOlderThan(ageMs, tier, options) {
659
+ const cutoff = new Date(Date.now() - ageMs).toISOString();
660
+ const conditions = ["created_at < $1"];
661
+ const params = [cutoff];
662
+ let paramIdx = 2;
663
+ if (tier != null) {
664
+ conditions.push(`tier = $${paramIdx}`);
665
+ params.push(tier);
666
+ paramIdx++;
667
+ }
668
+ if (options?.agentId) {
669
+ conditions.push(`agent_id = $${paramIdx}`);
670
+ params.push(options.agentId);
671
+ paramIdx++;
672
+ }
673
+ const where = conditions.join(" AND ");
674
+ const result = await this._query(
675
+ `DELETE FROM memories WHERE ${where}`,
676
+ params
677
+ );
678
+ return result.rowCount ?? 0;
679
+ }
680
+ async saveEdge(edge) {
681
+ await this._query(
682
+ `INSERT INTO edges (source_id, target_id, relation, weight, created_at)
683
+ VALUES ($1, $2, $3, $4, $5)
684
+ ON CONFLICT (source_id, target_id, relation) DO UPDATE SET
685
+ weight = EXCLUDED.weight,
686
+ created_at = EXCLUDED.created_at`,
687
+ [
688
+ edge.sourceId,
689
+ edge.targetId,
690
+ edge.relation,
691
+ edge.weight,
692
+ edge.createdAt.toISOString()
693
+ ]
694
+ );
695
+ }
696
+ async getEdges(memoryId) {
697
+ const result = await this._query(
698
+ "SELECT * FROM edges WHERE source_id = $1 OR target_id = $1",
699
+ [memoryId]
700
+ );
701
+ return result.rows.map(
702
+ (r) => ({
703
+ sourceId: r["source_id"],
704
+ targetId: r["target_id"],
705
+ relation: r["relation"],
706
+ weight: r["weight"],
707
+ createdAt: new Date(r["created_at"])
708
+ })
709
+ );
710
+ }
711
+ async searchByEmbedding(embedding, limit = 50, options) {
712
+ try {
713
+ const embeddingStr = `[${embedding.join(",")}]`;
714
+ if (options?.agentId) {
715
+ const result2 = await this._query(
716
+ `SELECT * FROM memories
717
+ WHERE (agent_id = $1 OR shared = TRUE) AND embedding IS NOT NULL
718
+ ORDER BY embedding <=> $2::vector
719
+ LIMIT $3`,
720
+ [options.agentId, embeddingStr, limit]
721
+ );
722
+ return result2.rows.map(rowToMemory2);
723
+ }
724
+ const result = await this._query(
725
+ `SELECT * FROM memories
726
+ WHERE embedding IS NOT NULL
727
+ ORDER BY embedding <=> $1::vector
728
+ LIMIT $2`,
729
+ [embeddingStr, limit]
730
+ );
731
+ return result.rows.map(rowToMemory2);
732
+ } catch (err) {
733
+ const msg = err instanceof Error ? err.message : String(err);
734
+ if (msg.includes("operator") || msg.includes("vector") || msg.includes("does not exist")) {
735
+ const all = await this.listAll(options);
736
+ return bruteForceCosineSearch(all, embedding, limit);
737
+ }
738
+ throw err;
739
+ }
740
+ }
741
+ async findSimilarPairs(options) {
742
+ const threshold = options?.threshold ?? 0.7;
743
+ const maxCandidates = options?.maxCandidates ?? 1e3;
744
+ const maxNeighbors = options?.maxNeighborsPerCandidate ?? 50;
745
+ const distanceThreshold = 1 - threshold;
746
+ const conditions = ["embedding IS NOT NULL"];
747
+ const params = [];
748
+ let paramIdx = 1;
749
+ if (options?.agentId) {
750
+ conditions.push(`agent_id = $${paramIdx}`);
751
+ params.push(options.agentId);
752
+ paramIdx++;
753
+ }
754
+ if (options?.tier != null) {
755
+ conditions.push(`tier = $${paramIdx}`);
756
+ params.push(options.tier);
757
+ paramIdx++;
758
+ }
759
+ params.push(maxCandidates);
760
+ const limitIdx = paramIdx++;
761
+ params.push(distanceThreshold);
762
+ const distIdx = paramIdx++;
763
+ params.push(threshold);
764
+ const simIdx = paramIdx++;
765
+ params.push(maxNeighbors);
766
+ const neighborLimitIdx = paramIdx++;
767
+ const where = conditions.join(" AND ");
768
+ const sql = `
769
+ WITH candidates AS (
770
+ SELECT id, embedding
771
+ FROM memories
772
+ WHERE ${where}
773
+ LIMIT $${limitIdx}
774
+ )
775
+ SELECT c1.id AS id_a, c2_sub.id AS id_b, c2_sub.sim AS similarity
776
+ FROM candidates c1
777
+ CROSS JOIN LATERAL (
778
+ SELECT c2.id, 1.0 - (c1.embedding <=> c2.embedding) AS sim
779
+ FROM candidates c2
780
+ WHERE c2.id > c1.id
781
+ AND c1.embedding <=> c2.embedding <= $${distIdx}
782
+ ORDER BY c1.embedding <=> c2.embedding
783
+ LIMIT $${neighborLimitIdx}
784
+ ) c2_sub
785
+ WHERE c2_sub.sim >= $${simIdx}
786
+ `;
787
+ try {
788
+ const result = await this._query(sql, params);
789
+ return result.rows.map(
790
+ (r) => ({
791
+ idA: r["id_a"],
792
+ idB: r["id_b"],
793
+ similarity: r["similarity"]
794
+ })
795
+ );
796
+ } catch (err) {
797
+ const msg = err instanceof Error ? err.message : String(err);
798
+ if (msg.includes("operator") || msg.includes("vector") || msg.includes("does not exist")) {
799
+ const all = await this.listAll({
800
+ agentId: options?.agentId,
801
+ includeShared: false,
802
+ tier: options?.tier,
803
+ limit: maxCandidates
804
+ });
805
+ return bruteForceSimilarPairs(all, threshold, maxCandidates);
806
+ }
807
+ throw err;
808
+ }
809
+ }
810
+ async getMeta(key) {
811
+ const result = await this._query(
812
+ "SELECT value FROM meta WHERE key = $1",
813
+ [key]
814
+ );
815
+ return result.rows.length > 0 ? result.rows[0].value : null;
816
+ }
817
+ async setMeta(key, value) {
818
+ await this._query(
819
+ `INSERT INTO meta (key, value) VALUES ($1, $2)
820
+ ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`,
821
+ [key, value]
822
+ );
823
+ }
824
+ };
825
+ }
826
+ });
827
+
828
+ // src/client.ts
829
+ var import_node_crypto2 = require("crypto");
830
+
831
+ // src/core/config.ts
832
+ function createDefaultScorerConfig() {
833
+ return {
834
+ weightRecency: 0.4,
835
+ weightFrequency: 0.25,
836
+ weightLinks: 0.2,
837
+ weightBase: 0.15,
838
+ frequencyLogCap: 5,
839
+ linkSaturation: 5,
840
+ supersessionPenalty: 0.1,
841
+ contradictionPenalty: 0.5,
842
+ consolidationPenalty: 0.5,
843
+ tierBaseScores: {
844
+ identity: 1,
845
+ procedural: 0.9,
846
+ structural: 0.8,
847
+ episodic: 0.5,
848
+ transient: 0.2
849
+ }
850
+ };
851
+ }
852
+ function createDefaultClassifierConfig() {
853
+ return {
854
+ extraPatterns: {},
855
+ episodicMinWords: 10,
856
+ customClassifier: null
857
+ };
858
+ }
859
+ function createDefaultLinkerConfig() {
860
+ return {
861
+ similarityThreshold: 0.65,
862
+ maxLinks: 10,
863
+ keywordPenalty: 0.8,
864
+ contradictionThreshold: 0.7,
865
+ elaborationThreshold: 0.85,
866
+ supersessionThreshold: 0.8,
867
+ negationWords: /* @__PURE__ */ new Set([
868
+ "not",
869
+ "never",
870
+ "don't",
871
+ "doesn't",
872
+ "isn't",
873
+ "aren't",
874
+ "won't",
875
+ "no"
876
+ ]),
877
+ customRelationFn: null
878
+ };
879
+ }
880
+ function createDefaultAssemblerConfig() {
881
+ return {
882
+ defaultMaxTokens: 3e3,
883
+ diversityStrength: 0.3,
884
+ sortOrder: "chronological",
885
+ minRelevance: 0.25
886
+ };
887
+ }
888
+ function createDefaultConsolidationConfig() {
889
+ return {
890
+ minClusterSize: 3,
891
+ similarityThreshold: 0.7,
892
+ maxContentLength: 2e3,
893
+ prefix: "[Consolidated]",
894
+ sourceName: "consolidation",
895
+ actorName: "system",
896
+ maxCandidates: 1e3,
897
+ maxNeighborsPerCandidate: 50
898
+ };
899
+ }
900
+ function createDefaultPromotionConfig() {
901
+ return {
902
+ transientToEpisodicAccesses: 2,
903
+ episodicToStructuralAccesses: 5,
904
+ structuralToIdentityAccesses: 15,
905
+ proceduralToIdentityAccesses: 25,
906
+ demotionImportanceThreshold: 0.1
907
+ };
908
+ }
909
+ var SalienceSignal = class {
910
+ name;
911
+ patterns;
912
+ boost;
913
+ _compiled;
914
+ constructor(name, patterns, boost) {
915
+ this.name = name;
916
+ this.patterns = patterns;
917
+ this.boost = boost;
918
+ this._compiled = patterns.map((p) => new RegExp(p, "i"));
919
+ }
920
+ scan(text) {
921
+ return this._compiled.some((re) => re.test(text));
922
+ }
923
+ };
924
+ var BUILTIN_SALIENCE_SIGNALS = [
925
+ new SalienceSignal("error", [
926
+ "\\b(?:error|exception|traceback|stack\\s*trace|segfault|panic|crash)\\b",
927
+ "\\b(?:failed|failure|broken|bug|regression)\\b",
928
+ "\\b(?:404|500|502|503|ECONNREFUSED|OOM|timeout)\\b"
929
+ ], 0.15),
930
+ new SalienceSignal("urgency", [
931
+ "\\b(?:urgent|critical|blocker|p0|p1|sev[- ]?[01]|outage|incident)\\b",
932
+ "\\b(?:ASAP|immediately|right now|emergency)\\b",
933
+ "\\b(?:production is down|prod is down|site is down)\\b"
934
+ ], 0.25),
935
+ new SalienceSignal("correction", [
936
+ "\\b(?:actually|correction|i was wrong|that's wrong|that's not right)\\b",
937
+ "\\b(?:let me correct|i meant|i misspoke|sorry,? i meant)\\b",
938
+ "\\b(?:no,? (?:it's|it is|that's|that is)|wait,? (?:no|actually))\\b"
939
+ ], 0.2),
940
+ new SalienceSignal("decision", [
941
+ "\\b(?:we've decided|final decision|going with|approved|sign[- ]?off)\\b",
942
+ "\\b(?:agreement|consensus|resolved to|committed to)\\b",
943
+ "\\b(?:from now on|henceforth|going forward)\\b"
944
+ ], 0.15),
945
+ new SalienceSignal("frustration", [
946
+ "\\b(?:i(?:'ve| have) told you|already said|keep telling you)\\b",
947
+ "\\b(?:again\\?|how many times|stop (?:doing|suggesting))\\b",
948
+ "\\b(?:this is (?:wrong|bad|terrible|awful)|doesn't work)\\b"
949
+ ], 0.2)
950
+ ];
951
+ function createDefaultSalienceConfig() {
952
+ return {
953
+ enabled: true,
954
+ maxBoost: 0.4,
955
+ signals: [...BUILTIN_SALIENCE_SIGNALS]
956
+ };
957
+ }
958
+ var BUILTIN_TIERS = [
959
+ { name: "identity", defaultHalfLifeDays: 3650, description: "Name, role, company, tech stack \u2014 near-permanent" },
960
+ { name: "procedural", defaultHalfLifeDays: 365, description: "Learned workflows, multi-step procedures, how-to knowledge" },
961
+ { name: "structural", defaultHalfLifeDays: 180, description: "Architecture decisions, preferences, constraints" },
962
+ { name: "episodic", defaultHalfLifeDays: 30, description: "Specific conversations, tasks completed" },
963
+ { name: "transient", defaultHalfLifeDays: 7, description: "One-off questions, small talk" }
964
+ ];
965
+ var MnemonicConfig = class {
966
+ scorer;
967
+ classifier;
968
+ linker;
969
+ assembler;
970
+ consolidation;
971
+ promotion;
972
+ salience;
973
+ charsPerToken;
974
+ defaultTier;
975
+ embeddingSearchLimit;
976
+ _tiers;
977
+ constructor(overrides) {
978
+ this.scorer = { ...createDefaultScorerConfig(), ...overrides?.scorer };
979
+ this.classifier = { ...createDefaultClassifierConfig(), ...overrides?.classifier };
980
+ this.linker = { ...createDefaultLinkerConfig(), ...overrides?.linker };
981
+ this.assembler = { ...createDefaultAssemblerConfig(), ...overrides?.assembler };
982
+ this.consolidation = { ...createDefaultConsolidationConfig(), ...overrides?.consolidation };
983
+ this.promotion = { ...createDefaultPromotionConfig(), ...overrides?.promotion };
984
+ this.salience = { ...createDefaultSalienceConfig(), ...overrides?.salience };
985
+ this.charsPerToken = overrides?.charsPerToken ?? 4;
986
+ this.defaultTier = overrides?.defaultTier ?? "episodic";
987
+ this.embeddingSearchLimit = overrides?.embeddingSearchLimit ?? 100;
988
+ if (this.consolidation.maxCandidates < 1) {
989
+ throw new Error(`maxCandidates must be >= 1, got ${this.consolidation.maxCandidates}`);
990
+ }
991
+ if (this.consolidation.maxNeighborsPerCandidate < 1) {
992
+ throw new Error(
993
+ `maxNeighborsPerCandidate must be >= 1, got ${this.consolidation.maxNeighborsPerCandidate}`
994
+ );
995
+ }
996
+ this._tiers = /* @__PURE__ */ new Map();
997
+ for (const td of BUILTIN_TIERS) {
998
+ this._tiers.set(td.name, td);
999
+ }
1000
+ }
1001
+ registerTier(tier) {
1002
+ this._tiers.set(tier.name, tier);
1003
+ if (!(tier.name in this.scorer.tierBaseScores)) {
1004
+ this.scorer.tierBaseScores[tier.name] = 0.5;
1005
+ }
1006
+ }
1007
+ getTier(name) {
1008
+ const td = this._tiers.get(name);
1009
+ if (!td) throw new Error(`Unknown tier: '${name}'`);
1010
+ return td;
1011
+ }
1012
+ tierNames() {
1013
+ return [...this._tiers.keys()];
1014
+ }
1015
+ halfLifeDays(tierName) {
1016
+ return this.getTier(tierName).defaultHalfLifeDays;
1017
+ }
1018
+ };
1019
+
1020
+ // src/core/classifier.ts
1021
+ var IDENTITY_PATTERNS = [
1022
+ /\bmy name is\b/i,
1023
+ /\bi(?:'m| am) (?:a |an )?(?:software|data|ml|backend|frontend|devops|platform)\b/i,
1024
+ /\bi work (?:at|for)\b/i,
1025
+ /\bmy (?:role|title|position) is\b/i,
1026
+ /\bour (?:tech |technology )?stack (?:is|includes)\b/i,
1027
+ /\bi (?:use|prefer|always use)\b.+(?:language|framework|editor|ide|os)\b/i
1028
+ ];
1029
+ var PROCEDURAL_PATTERNS = [
1030
+ /\b(?:step\s+\d|first|then|next|finally|afterwards)\b.*\b(?:step|run|execute|do|click|open|type|enter)\b/i,
1031
+ /\bto\s+(?:do|deploy|set up|configure|install|build|run|create|fix)\b.+\b(?:first|then|after|before|finally)\b/i,
1032
+ /\bworkflow\b.*\b(?:is|goes|looks like|involves)\b/i,
1033
+ /\bprocedure\b.*\b(?:is|for|to)\b/i,
1034
+ /\bwhen(?:ever)?\b.+\b(?:always|you should|we|i) (?:run|do|execute|call|use)\b/i,
1035
+ /\brecipe\b.*\bfor\b/i,
1036
+ /\brunbook\b/i
1037
+ ];
1038
+ var STRUCTURAL_PATTERNS = [
1039
+ /\bwe (?:decided|chose|agreed) to\b/i,
1040
+ /\barchitectur(?:e|al)\b/i,
1041
+ /\bconstraint\b/i,
1042
+ /\brequirement\b/i,
1043
+ /\bprefer(?:s|ence)?\b.+(?:over|instead|rather)\b/i,
1044
+ /\balways\b.+\b(?:use|deploy|run)\b/i,
1045
+ /\bnever\b.+\b(?:use|deploy|run)\b/i,
1046
+ /\bstandard(?:s|ise|ize)?\b/i,
1047
+ /\bconvention\b/i
1048
+ ];
1049
+ var TRANSIENT_PATTERNS = [
1050
+ /\b(?:hey|hi|hello|thanks|thank you|cheers|bye|goodbye)\b/i,
1051
+ /\bwhat(?:'s| is) the (?:time|date|weather)\b/i,
1052
+ /\bjust (?:a )?quick\b/i,
1053
+ /\bnever ?mind\b/i
1054
+ ];
1055
+ var COMPILED_DEFAULTS = [
1056
+ ["identity", IDENTITY_PATTERNS],
1057
+ ["procedural", PROCEDURAL_PATTERNS],
1058
+ ["structural", STRUCTURAL_PATTERNS],
1059
+ ["transient", TRANSIENT_PATTERNS]
1060
+ ];
1061
+ function buildTierPatterns(cfg) {
1062
+ if (Object.keys(cfg.extraPatterns).length === 0) {
1063
+ return COMPILED_DEFAULTS;
1064
+ }
1065
+ return COMPILED_DEFAULTS.map(([name, compiled]) => {
1066
+ const extra = (cfg.extraPatterns[name] ?? []).map((p) => new RegExp(p, "i"));
1067
+ return [name, [...compiled, ...extra]];
1068
+ });
1069
+ }
1070
+ function classify(content, options) {
1071
+ const config = options?.config ?? new MnemonicConfig();
1072
+ const cfg = config.classifier;
1073
+ const actor = options?.actor ?? "";
1074
+ if (cfg.customClassifier) {
1075
+ return cfg.customClassifier(content, options?.source ?? "", actor);
1076
+ }
1077
+ for (const [tierName, patterns] of buildTierPatterns(cfg)) {
1078
+ for (const pat of patterns) {
1079
+ if (pat.test(content)) {
1080
+ return tierName;
1081
+ }
1082
+ }
1083
+ }
1084
+ if (actor === "user" && content.split(/\s+/).length > cfg.episodicMinWords) {
1085
+ return "episodic";
1086
+ }
1087
+ return config.defaultTier;
1088
+ }
1089
+
1090
+ // src/core/scorer.ts
1091
+ function recencyFactor(memory, halfLifeDays) {
1092
+ const now = Date.now();
1093
+ const elapsedDays = Math.max(0, (now - memory.lastAccessedAt.getTime()) / 864e5);
1094
+ return Math.pow(0.5, elapsedDays / halfLifeDays);
1095
+ }
1096
+ function frequencyFactor(memory, logCap) {
1097
+ if (logCap <= 0) return 1;
1098
+ return Math.min(1, Math.log2(1 + memory.accessCount) / logCap);
1099
+ }
1100
+ function linkFactor(memory, saturation) {
1101
+ return Math.min(1, memory.linkedIds.length / saturation);
1102
+ }
1103
+ function score(memory, config) {
1104
+ const cfg = config ?? new MnemonicConfig();
1105
+ const sc = cfg.scorer;
1106
+ const tierName = memory.tierName;
1107
+ let halfLife;
1108
+ try {
1109
+ halfLife = cfg.halfLifeDays(tierName);
1110
+ } catch {
1111
+ halfLife = 30;
1112
+ }
1113
+ const base = sc.tierBaseScores[tierName] ?? 0.5;
1114
+ const composite = sc.weightRecency * recencyFactor(memory, halfLife) + sc.weightFrequency * frequencyFactor(memory, sc.frequencyLogCap) + sc.weightLinks * linkFactor(memory, sc.linkSaturation) + sc.weightBase * base;
1115
+ const boost = memory.metadata["boost"] ?? 0;
1116
+ let result = Math.max(0, composite + boost);
1117
+ if (memory.supersededBy != null) {
1118
+ result *= Math.min(1, Math.max(0, sc.supersessionPenalty));
1119
+ }
1120
+ if (memory.supersededBy == null && memory.contradictedBy != null) {
1121
+ result *= Math.min(1, Math.max(0, sc.contradictionPenalty));
1122
+ }
1123
+ if (memory.metadata["consolidated_into"] != null) {
1124
+ result *= Math.min(1, Math.max(0, sc.consolidationPenalty));
1125
+ }
1126
+ return result;
1127
+ }
1128
+
1129
+ // src/core/linker.ts
1130
+ init_memory();
1131
+ init_utils();
1132
+ var NUMBER_RE = /\b\d+(?:[.,]\d+)*/g;
1133
+ var CORRECTION_RE = /\b(?:correction|actually|i was wrong|that was wrong|that's wrong|that's not right)\b|\b(?:that isn't right|that isn't correct)\b|\b(?:let me correct|i meant|i misspoke|sorry,? i meant)\b|\b(?:no,? (?:it's|it is|that's|that is)|wait,? (?:no|actually))\b|\b(?:disregard|ignore what i said|scratch that|on second thought)\b/i;
1134
+ function keywordOverlap(a, b) {
1135
+ const sa = new Set(a.toLowerCase().split(/\s+/));
1136
+ const sb = new Set(b.toLowerCase().split(/\s+/));
1137
+ if (sa.size === 0 || sb.size === 0) return 0;
1138
+ let intersection = 0;
1139
+ for (const word of sa) {
1140
+ if (sb.has(word)) intersection++;
1141
+ }
1142
+ const union = (/* @__PURE__ */ new Set([...sa, ...sb])).size;
1143
+ return intersection / union;
1144
+ }
1145
+ function extractNumbers(text) {
1146
+ return new Set(text.match(NUMBER_RE) ?? []);
1147
+ }
1148
+ function hasNumberDisagreement(a, b) {
1149
+ const aNums = extractNumbers(a);
1150
+ const bNums = extractNumbers(b);
1151
+ if (aNums.size === 0 || bNums.size === 0) return false;
1152
+ if (aNums.size !== bNums.size) return true;
1153
+ for (const n of aNums) {
1154
+ if (!bNums.has(n)) return true;
1155
+ }
1156
+ return false;
1157
+ }
1158
+ function hasCorrectionSignal(text) {
1159
+ return CORRECTION_RE.test(text);
1160
+ }
1161
+ function inferRelation(a, b, similarity, cfg) {
1162
+ if (cfg.customRelationFn) {
1163
+ return cfg.customRelationFn(a, b, similarity);
1164
+ }
1165
+ const aWords = new Set(a.content.toLowerCase().split(/\s+/));
1166
+ const bWords = new Set(b.content.toLowerCase().split(/\s+/));
1167
+ const hasNegation = [...aWords].some((w) => cfg.negationWords.has(w)) !== [...bWords].some((w) => cfg.negationWords.has(w));
1168
+ const hasNumbers = hasNumberDisagreement(a.content, b.content);
1169
+ const hasCorrection = hasCorrectionSignal(a.content) || hasCorrectionSignal(b.content);
1170
+ const hasContradiction = (hasNegation || hasNumbers || hasCorrection) && similarity > cfg.contradictionThreshold;
1171
+ if (hasContradiction && a.createdAt > b.createdAt) {
1172
+ if (hasNumbers || hasCorrection || similarity > cfg.supersessionThreshold) {
1173
+ return "supersedes";
1174
+ }
1175
+ }
1176
+ if (hasContradiction) return "contradicts";
1177
+ if (similarity > cfg.elaborationThreshold) return "elaborates";
1178
+ return "related_to";
1179
+ }
1180
+ function findLinks(newMemory, existing, options) {
1181
+ const cfg = (options?.config ?? new MnemonicConfig()).linker;
1182
+ const threshold = options?.similarityThreshold ?? cfg.similarityThreshold;
1183
+ const limit = options?.maxLinks ?? cfg.maxLinks;
1184
+ const others = existing.filter((m) => m.id !== newMemory.id);
1185
+ if (others.length === 0) return [];
1186
+ const scored = [];
1187
+ if (newMemory.embedding) {
1188
+ const withEmb = others.filter((m) => m.embedding != null);
1189
+ const withoutEmb = others.filter((m) => m.embedding == null);
1190
+ if (withEmb.length > 0) {
1191
+ const embList = withEmb.map((m) => m.embedding);
1192
+ const sims = cosineSimilarityVector(newMemory.embedding, embList);
1193
+ for (let i = 0; i < withEmb.length; i++) {
1194
+ if (sims[i] >= threshold) {
1195
+ scored.push([sims[i], withEmb[i]]);
1196
+ }
1197
+ }
1198
+ }
1199
+ for (const mem of withoutEmb) {
1200
+ const sim = keywordOverlap(newMemory.content, mem.content) * cfg.keywordPenalty;
1201
+ if (sim >= threshold) scored.push([sim, mem]);
1202
+ }
1203
+ } else {
1204
+ for (const mem of others) {
1205
+ const sim = keywordOverlap(newMemory.content, mem.content) * cfg.keywordPenalty;
1206
+ if (sim >= threshold) scored.push([sim, mem]);
1207
+ }
1208
+ }
1209
+ scored.sort((a, b) => b[0] - a[0]);
1210
+ return scored.slice(0, limit).map(([sim, mem]) => {
1211
+ const relation = inferRelation(newMemory, mem, sim, cfg);
1212
+ return createEdge(newMemory.id, mem.id, relation, sim);
1213
+ });
1214
+ }
1215
+
1216
+ // src/core/assembler.ts
1217
+ init_utils();
1218
+ function getSortFn(sortOrder) {
1219
+ if (typeof sortOrder === "function") return sortOrder;
1220
+ if (sortOrder === "importance") return (a, b) => b.importance - a.importance;
1221
+ if (sortOrder === "relevance") return () => 0;
1222
+ return (a, b) => a.createdAt.getTime() - b.createdAt.getTime();
1223
+ }
1224
+ function assemble(_query, candidates, options) {
1225
+ const config = options?.config ?? new MnemonicConfig();
1226
+ const cfg = config.assembler;
1227
+ const budget = options?.maxTokens ?? cfg.defaultMaxTokens;
1228
+ const divStrength = options?.diversityStrength ?? cfg.diversityStrength;
1229
+ const relevanceFloor = options?.minRelevance ?? cfg.minRelevance;
1230
+ const qEmbed = options?.queryEmbedding ?? null;
1231
+ let n = candidates.length;
1232
+ if (n === 0) return [];
1233
+ let importances = new Float64Array(n);
1234
+ for (let i = 0; i < n; i++) {
1235
+ importances[i] = score(candidates[i], config);
1236
+ }
1237
+ let embIndices = [];
1238
+ for (let i = 0; i < n; i++) {
1239
+ if (candidates[i].embedding) embIndices.push(i);
1240
+ }
1241
+ let relevances = new Float64Array(n);
1242
+ for (let i = 0; i < n; i++) {
1243
+ relevances[i] = keywordOverlap(_query, candidates[i].content);
1244
+ }
1245
+ if (qEmbed && embIndices.length > 0) {
1246
+ const embList = embIndices.map((i) => candidates[i].embedding);
1247
+ const sims = cosineSimilarityVector(qEmbed, embList);
1248
+ for (let pos = 0; pos < embIndices.length; pos++) {
1249
+ const idx = embIndices[pos];
1250
+ relevances[idx] = Math.max(Math.max(0, sims[pos]), relevances[idx]);
1251
+ }
1252
+ }
1253
+ if (relevanceFloor > 0) {
1254
+ const keep = [];
1255
+ for (let i = 0; i < n; i++) {
1256
+ if (relevances[i] >= relevanceFloor) keep.push(i);
1257
+ }
1258
+ if (keep.length < n) {
1259
+ const oldToNew = /* @__PURE__ */ new Map();
1260
+ keep.forEach((old, idx) => oldToNew.set(old, idx));
1261
+ candidates = keep.map((i) => candidates[i]);
1262
+ importances = Float64Array.from(keep.map((i) => importances[i]));
1263
+ relevances = Float64Array.from(keep.map((i) => relevances[i]));
1264
+ embIndices = embIndices.filter((i) => oldToNew.has(i)).map((i) => oldToNew.get(i));
1265
+ n = candidates.length;
1266
+ }
1267
+ }
1268
+ let simMatrix = null;
1269
+ const idxToPos = /* @__PURE__ */ new Map();
1270
+ if (embIndices.length > 0 && divStrength > 0) {
1271
+ const embList = embIndices.map((i) => candidates[i].embedding);
1272
+ simMatrix = cosineSimilarityMatrix(embList);
1273
+ for (let pos = 0; pos < embIndices.length; pos++) {
1274
+ idxToPos.set(embIndices[pos], pos);
1275
+ }
1276
+ }
1277
+ const nEmb = embIndices.length;
1278
+ const remaining = Array.from({ length: n }, (_, i) => i);
1279
+ const selectedEmbPositions = [];
1280
+ const selectedIndices = [];
1281
+ let budgetLeft = budget;
1282
+ while (remaining.length > 0 && budgetLeft > 0) {
1283
+ let bestRi = -1;
1284
+ let bestValue = -1;
1285
+ for (let ri = 0; ri < remaining.length; ri++) {
1286
+ const ci = remaining[ri];
1287
+ if (candidates[ci].tokenEstimate > budgetLeft) continue;
1288
+ let value = relevances[ci] * importances[ci];
1289
+ if (selectedEmbPositions.length > 0 && simMatrix && idxToPos.has(ci)) {
1290
+ const pos = idxToPos.get(ci);
1291
+ let maxSim = 0;
1292
+ for (const sp of selectedEmbPositions) {
1293
+ const s = simMatrix[pos * nEmb + sp];
1294
+ if (s > maxSim) maxSim = s;
1295
+ }
1296
+ value *= Math.min(1, Math.max(0, 1 - divStrength * maxSim));
1297
+ }
1298
+ if (value > bestValue) {
1299
+ bestValue = value;
1300
+ bestRi = ri;
1301
+ }
1302
+ }
1303
+ if (bestRi < 0) break;
1304
+ const chosenIdx = remaining.splice(bestRi, 1)[0];
1305
+ candidates[chosenIdx].touch();
1306
+ selectedIndices.push(chosenIdx);
1307
+ if (idxToPos.has(chosenIdx)) {
1308
+ selectedEmbPositions.push(idxToPos.get(chosenIdx));
1309
+ }
1310
+ budgetLeft -= candidates[chosenIdx].tokenEstimate;
1311
+ }
1312
+ const selected = selectedIndices.map((i) => candidates[i]);
1313
+ selected.sort(getSortFn(cfg.sortOrder));
1314
+ return selected;
1315
+ }
1316
+ function detectConflicts(memories, config) {
1317
+ const cfg = config ?? new MnemonicConfig();
1318
+ const negWords = cfg.linker.negationWords;
1319
+ const conflicts = [];
1320
+ const seenPairs = /* @__PURE__ */ new Set();
1321
+ const n = memories.length;
1322
+ const pairKey = (a, b) => `${Math.min(a, b)},${Math.max(a, b)}`;
1323
+ const idToIdx = /* @__PURE__ */ new Map();
1324
+ for (let i = 0; i < n; i++) idToIdx.set(memories[i].id, i);
1325
+ for (let i = 0; i < n; i++) {
1326
+ const mem = memories[i];
1327
+ if (mem.contradictedBy && idToIdx.has(mem.contradictedBy)) {
1328
+ const j = idToIdx.get(mem.contradictedBy);
1329
+ const key = pairKey(i, j);
1330
+ if (!seenPairs.has(key)) {
1331
+ seenPairs.add(key);
1332
+ conflicts.push([Math.min(i, j), Math.max(i, j), "contradicted by newer memory"]);
1333
+ }
1334
+ }
1335
+ }
1336
+ for (let i = 0; i < n; i++) {
1337
+ for (let j = i + 1; j < n; j++) {
1338
+ const key = pairKey(i, j);
1339
+ if (seenPairs.has(key)) continue;
1340
+ const a = memories[i], b = memories[j];
1341
+ const kwSim = keywordOverlap(a.content, b.content);
1342
+ let embSim = 0;
1343
+ if (a.embedding && b.embedding) {
1344
+ const sims = cosineSimilarityVector(a.embedding, [b.embedding]);
1345
+ embSim = sims[0];
1346
+ }
1347
+ const maxSim = Math.max(kwSim, embSim);
1348
+ if (maxSim < 0.6) continue;
1349
+ if (hasNumberDisagreement(a.content, b.content)) {
1350
+ conflicts.push([i, j, "numbers differ on similar topic"]);
1351
+ seenPairs.add(key);
1352
+ } else if (hasCorrectionSignal(a.content) || hasCorrectionSignal(b.content)) {
1353
+ conflicts.push([i, j, "correction language detected"]);
1354
+ seenPairs.add(key);
1355
+ } else if (maxSim > 0.8) {
1356
+ const aWords = new Set(a.content.toLowerCase().split(/\s+/));
1357
+ const bWords = new Set(b.content.toLowerCase().split(/\s+/));
1358
+ const aNeg = [...aWords].some((w) => negWords.has(w));
1359
+ const bNeg = [...bWords].some((w) => negWords.has(w));
1360
+ if (aNeg !== bNeg) {
1361
+ conflicts.push([i, j, "contradictory statements"]);
1362
+ seenPairs.add(key);
1363
+ }
1364
+ }
1365
+ }
1366
+ }
1367
+ return conflicts;
1368
+ }
1369
+ function formatContext(memories, header = "Relevant context:", conflicts) {
1370
+ if (memories.length === 0) return "";
1371
+ const lines = [header, ""];
1372
+ for (const mem of memories) {
1373
+ lines.push(`- [${mem.tierName}] ${mem.content}`);
1374
+ }
1375
+ if (conflicts && conflicts.length > 0) {
1376
+ lines.push("");
1377
+ for (const [idxA, idxB, reason] of conflicts) {
1378
+ lines.push(`[!] Potential conflict between items ${idxA + 1} and ${idxB + 1}: ${reason}`);
1379
+ }
1380
+ }
1381
+ return lines.join("\n");
1382
+ }
1383
+
1384
+ // src/client.ts
1385
+ init_memory();
1386
+
1387
+ // src/core/promotion.ts
1388
+ var TIER_TRANSITIONS = {
1389
+ transient: { promoteTo: "episodic", demoteTo: null, promoteThresholdKey: "transientToEpisodicAccesses" },
1390
+ episodic: { promoteTo: "structural", demoteTo: "transient", promoteThresholdKey: "episodicToStructuralAccesses" },
1391
+ structural: { promoteTo: "identity", demoteTo: "episodic", promoteThresholdKey: "structuralToIdentityAccesses" },
1392
+ procedural: { promoteTo: "identity", demoteTo: "structural", promoteThresholdKey: "proceduralToIdentityAccesses" },
1393
+ identity: { promoteTo: null, demoteTo: "structural", promoteThresholdKey: null }
1394
+ };
1395
+ function checkPromotions(memories, config) {
1396
+ const cfg = (config ?? new MnemonicConfig()).promotion;
1397
+ const results = [];
1398
+ for (const mem of memories) {
1399
+ const transition = TIER_TRANSITIONS[mem.tierName];
1400
+ if (!transition?.promoteTo || !transition.promoteThresholdKey) continue;
1401
+ const threshold = cfg[transition.promoteThresholdKey];
1402
+ if (mem.accessCount >= threshold) {
1403
+ results.push({
1404
+ memoryId: mem.id,
1405
+ oldTier: mem.tierName,
1406
+ newTier: transition.promoteTo,
1407
+ reason: `access_count (${mem.accessCount}) >= ${threshold}`
1408
+ });
1409
+ }
1410
+ }
1411
+ return results;
1412
+ }
1413
+ function checkDemotions(memories, config) {
1414
+ const fullCfg = config ?? new MnemonicConfig();
1415
+ const cfg = fullCfg.promotion;
1416
+ const results = [];
1417
+ for (const mem of memories) {
1418
+ if (mem.supersededBy != null) continue;
1419
+ const transition = TIER_TRANSITIONS[mem.tierName];
1420
+ if (!transition?.demoteTo) continue;
1421
+ const importance = score(mem, fullCfg);
1422
+ if (importance < cfg.demotionImportanceThreshold) {
1423
+ results.push({
1424
+ memoryId: mem.id,
1425
+ oldTier: mem.tierName,
1426
+ newTier: transition.demoteTo,
1427
+ reason: `importance (${importance.toFixed(4)}) < ${cfg.demotionImportanceThreshold}`
1428
+ });
1429
+ }
1430
+ }
1431
+ return results;
1432
+ }
1433
+ function applyTierChange(memory, newTier) {
1434
+ const oldTier = memory.tierName;
1435
+ const history = memory.metadata["previous_tiers"] ?? [];
1436
+ history.push(oldTier);
1437
+ memory.metadata["previous_tiers"] = history;
1438
+ memory.tier = newTier;
1439
+ }
1440
+
1441
+ // src/core/salience.ts
1442
+ function detectSalience(content, config) {
1443
+ const cfg = config ?? createDefaultSalienceConfig();
1444
+ if (!cfg.enabled) {
1445
+ return { boost: 0, signals: [] };
1446
+ }
1447
+ let totalBoost = 0;
1448
+ const fired = [];
1449
+ for (const signal of cfg.signals) {
1450
+ if (signal.scan(content)) {
1451
+ totalBoost += signal.boost;
1452
+ fired.push(signal.name);
1453
+ }
1454
+ }
1455
+ return {
1456
+ boost: Math.min(totalBoost, cfg.maxBoost),
1457
+ signals: fired
1458
+ };
1459
+ }
1460
+
1461
+ // src/client.ts
1462
+ init_utils();
1463
+
1464
+ // src/consolidation/engine.ts
1465
+ init_memory();
1466
+ init_utils();
1467
+ var TRANSIENT_ERRORS = /* @__PURE__ */ new Set([
1468
+ "RateLimitError",
1469
+ "APITimeoutError",
1470
+ "APIConnectionTimeoutError",
1471
+ // OpenAI Node SDK timeout subclass
1472
+ "APIConnectionError",
1473
+ "InternalServerError"
1474
+ ]);
1475
+ function findConsolidationCandidates(memories, config, pairs) {
1476
+ const cfg = (config ?? new MnemonicConfig()).consolidation;
1477
+ const episodic = memories.filter(
1478
+ (m) => m.tierName === "episodic" && m.embedding != null
1479
+ );
1480
+ if (episodic.length < cfg.minClusterSize) return [];
1481
+ if (pairs != null) {
1482
+ return clusterFromPairs(episodic, pairs, cfg.minClusterSize);
1483
+ }
1484
+ return clusterFromMatrix(episodic, cfg.similarityThreshold, cfg.minClusterSize);
1485
+ }
1486
+ function unionFindClusters(items, edges, minClusterSize) {
1487
+ const n = items.length;
1488
+ const parent = Array.from({ length: n }, (_, i) => i);
1489
+ function find(x) {
1490
+ while (parent[x] !== x) {
1491
+ parent[x] = parent[parent[x]];
1492
+ x = parent[x];
1493
+ }
1494
+ return x;
1495
+ }
1496
+ for (const [a, b] of edges) {
1497
+ const ra = find(a);
1498
+ const rb = find(b);
1499
+ if (ra !== rb) parent[ra] = rb;
1500
+ }
1501
+ const clusters = /* @__PURE__ */ new Map();
1502
+ for (let i = 0; i < n; i++) {
1503
+ const root = find(i);
1504
+ if (!clusters.has(root)) clusters.set(root, []);
1505
+ clusters.get(root).push(items[i]);
1506
+ }
1507
+ return [...clusters.values()].filter((c) => c.length >= minClusterSize);
1508
+ }
1509
+ function clusterFromPairs(episodic, pairs, minClusterSize) {
1510
+ const idToIdx = /* @__PURE__ */ new Map();
1511
+ for (let i = 0; i < episodic.length; i++) {
1512
+ idToIdx.set(episodic[i].id, i);
1513
+ }
1514
+ const edges = [];
1515
+ for (const p of pairs) {
1516
+ const ia = idToIdx.get(p.idA);
1517
+ const ib = idToIdx.get(p.idB);
1518
+ if (ia != null && ib != null) {
1519
+ edges.push([ia, ib]);
1520
+ }
1521
+ }
1522
+ return unionFindClusters(episodic, edges, minClusterSize);
1523
+ }
1524
+ function clusterFromMatrix(episodic, similarityThreshold, minClusterSize) {
1525
+ const n = episodic.length;
1526
+ const simMatrix = cosineSimilarityMatrix(episodic.map((m) => m.embedding));
1527
+ const edges = [];
1528
+ for (let i = 0; i < n; i++) {
1529
+ for (let j = i + 1; j < n; j++) {
1530
+ if (simMatrix[i * n + j] >= similarityThreshold) {
1531
+ edges.push([i, j]);
1532
+ }
1533
+ }
1534
+ }
1535
+ return unionFindClusters(episodic, edges, minClusterSize);
1536
+ }
1537
+ function defaultJoin(cluster, maxLength) {
1538
+ const sorted = [...cluster].sort(
1539
+ (a, b) => b.createdAt.getTime() - a.createdAt.getTime()
1540
+ );
1541
+ const unique = [];
1542
+ for (const mem of sorted) {
1543
+ if (unique.some((prev) => keywordOverlap(mem.content, prev) > 0.8)) continue;
1544
+ unique.push(mem.content);
1545
+ }
1546
+ const header = `(${cluster.length} memories, ${unique.length} unique)`;
1547
+ let combined = header + "\n" + unique.join("\n");
1548
+ if (combined.length > maxLength) {
1549
+ combined = combined.slice(0, maxLength - 3) + "...";
1550
+ }
1551
+ return combined;
1552
+ }
1553
+ async function mergeCluster(cluster, config) {
1554
+ const cfg = (config ?? new MnemonicConfig()).consolidation;
1555
+ let combined;
1556
+ if (cfg.summarizer != null) {
1557
+ try {
1558
+ combined = (await cfg.summarizer(cluster.map((m) => m.content))).trim();
1559
+ if (!combined) {
1560
+ throw new Error("Summarizer returned empty content");
1561
+ }
1562
+ } catch (err) {
1563
+ const errName = err instanceof Error ? err.constructor.name : "";
1564
+ if (TRANSIENT_ERRORS.has(errName)) {
1565
+ throw err;
1566
+ }
1567
+ console.warn(`Summarizer failed permanently with ${errName}; falling back to default join`);
1568
+ combined = defaultJoin(cluster, cfg.maxContentLength);
1569
+ }
1570
+ if (combined.length > cfg.maxContentLength) {
1571
+ combined = combined.slice(0, cfg.maxContentLength - 3) + "...";
1572
+ }
1573
+ } else {
1574
+ combined = defaultJoin(cluster, cfg.maxContentLength);
1575
+ }
1576
+ const summary = new Memory({
1577
+ content: `${cfg.prefix} ${combined}`,
1578
+ tier: "structural",
1579
+ source: cfg.sourceName,
1580
+ actor: cfg.actorName,
1581
+ metadata: {
1582
+ consolidated_from: cluster.map((m) => m.id),
1583
+ original_count: cluster.length
1584
+ }
1585
+ });
1586
+ summary.importance = Math.max(...cluster.map((m) => m.importance));
1587
+ const linkedSet = new Set(cluster.flatMap((m) => m.linkedIds));
1588
+ summary.linkedIds = [...linkedSet];
1589
+ return summary;
1590
+ }
1591
+
1592
+ // src/stores/sync-adapter.ts
1593
+ function syncToAsync(store) {
1594
+ return {
1595
+ async initialize() {
1596
+ store.initialize();
1597
+ },
1598
+ async close() {
1599
+ store.close();
1600
+ },
1601
+ async save(memory) {
1602
+ store.save(memory);
1603
+ },
1604
+ async get(memoryId) {
1605
+ return store.get(memoryId);
1606
+ },
1607
+ async listAll(options) {
1608
+ return store.listAll(options);
1609
+ },
1610
+ async delete(memoryId) {
1611
+ store.delete(memoryId);
1612
+ },
1613
+ async deleteOlderThan(ageMs, tier, options) {
1614
+ return store.deleteOlderThan(ageMs, tier, options);
1615
+ },
1616
+ async saveEdge(edge) {
1617
+ store.saveEdge(edge);
1618
+ },
1619
+ async getEdges(memoryId) {
1620
+ return store.getEdges(memoryId);
1621
+ },
1622
+ async searchByEmbedding(embedding, limit, options) {
1623
+ return store.searchByEmbedding(embedding, limit, options);
1624
+ },
1625
+ async findSimilarPairs(options) {
1626
+ return store.findSimilarPairs(options);
1627
+ },
1628
+ async getMeta(key) {
1629
+ return store.getMeta(key);
1630
+ },
1631
+ async setMeta(key, value) {
1632
+ store.setMeta(key, value);
1633
+ }
1634
+ };
1635
+ }
1636
+
1637
+ // src/stores/sqlite.ts
1638
+ var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
1639
+ init_memory();
1640
+ init_utils();
1641
+ var SCHEMA = `
1642
+ CREATE TABLE IF NOT EXISTS memories (
1643
+ id TEXT PRIMARY KEY,
1644
+ content TEXT NOT NULL,
1645
+ tier TEXT NOT NULL,
1646
+ source TEXT NOT NULL DEFAULT 'unknown',
1647
+ actor TEXT NOT NULL DEFAULT 'system',
1648
+ session_id TEXT,
1649
+ created_at TEXT NOT NULL,
1650
+ last_accessed TEXT NOT NULL,
1651
+ access_count INTEGER NOT NULL DEFAULT 0,
1652
+ importance REAL NOT NULL DEFAULT 1.0,
1653
+ superseded_by TEXT,
1654
+ contradicted_by TEXT,
1655
+ embedding TEXT,
1656
+ metadata TEXT NOT NULL DEFAULT '{}',
1657
+ linked_ids TEXT NOT NULL DEFAULT '[]',
1658
+ agent_id TEXT NOT NULL DEFAULT 'default',
1659
+ shared INTEGER NOT NULL DEFAULT 0
1660
+ );
1661
+
1662
+ CREATE TABLE IF NOT EXISTS edges (
1663
+ source_id TEXT NOT NULL,
1664
+ target_id TEXT NOT NULL,
1665
+ relation TEXT NOT NULL,
1666
+ weight REAL NOT NULL DEFAULT 1.0,
1667
+ created_at TEXT NOT NULL,
1668
+ PRIMARY KEY (source_id, target_id, relation)
1669
+ );
1670
+
1671
+ CREATE TABLE IF NOT EXISTS meta (
1672
+ key TEXT PRIMARY KEY,
1673
+ value TEXT NOT NULL
1674
+ );
1675
+
1676
+ CREATE INDEX IF NOT EXISTS idx_memories_agent_id ON memories (agent_id);
1677
+ `;
1678
+ var SQLiteStore = class {
1679
+ _db;
1680
+ constructor(path = "mnemonic.db") {
1681
+ this._db = new import_better_sqlite3.default(path);
1682
+ this._db.pragma("journal_mode = WAL");
1683
+ }
1684
+ initialize() {
1685
+ this._db.exec(SCHEMA);
1686
+ this._migrate();
1687
+ }
1688
+ close() {
1689
+ this._db.close();
1690
+ }
1691
+ /** Add missing columns/tables for stores created before multi-agent support. */
1692
+ _migrate() {
1693
+ const cols = this._db.prepare("PRAGMA table_info(memories)").all();
1694
+ const colNames = new Set(cols.map((c) => c.name));
1695
+ if (!colNames.has("agent_id")) {
1696
+ this._db.exec(
1697
+ "ALTER TABLE memories ADD COLUMN agent_id TEXT NOT NULL DEFAULT 'default'"
1698
+ );
1699
+ this._db.exec(
1700
+ "CREATE INDEX IF NOT EXISTS idx_memories_agent_id ON memories (agent_id)"
1701
+ );
1702
+ }
1703
+ if (!colNames.has("shared")) {
1704
+ this._db.exec(
1705
+ "ALTER TABLE memories ADD COLUMN shared INTEGER NOT NULL DEFAULT 0"
1706
+ );
1707
+ }
1708
+ if (!colNames.has("contradicted_by")) {
1709
+ this._db.exec(
1710
+ "ALTER TABLE memories ADD COLUMN contradicted_by TEXT"
1711
+ );
1712
+ this._db.exec(
1713
+ "UPDATE memories SET contradicted_by = json_extract(metadata, '$.contradicted_by') WHERE contradicted_by IS NULL AND json_extract(metadata, '$.contradicted_by') IS NOT NULL"
1714
+ );
1715
+ }
1716
+ this._db.exec(
1717
+ "CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
1718
+ );
1719
+ }
1720
+ save(memory) {
1721
+ this._db.prepare(
1722
+ `INSERT OR REPLACE INTO memories
1723
+ (id, content, tier, source, actor, session_id, created_at,
1724
+ last_accessed, access_count, importance, superseded_by,
1725
+ contradicted_by, embedding, metadata, linked_ids, agent_id, shared)
1726
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
1727
+ ).run(
1728
+ memory.id,
1729
+ memory.content,
1730
+ memory.tierName,
1731
+ memory.source,
1732
+ memory.actor,
1733
+ memory.sessionId,
1734
+ memory.createdAt.toISOString(),
1735
+ memory.lastAccessedAt.toISOString(),
1736
+ memory.accessCount,
1737
+ memory.importance,
1738
+ memory.supersededBy,
1739
+ memory.contradictedBy ?? null,
1740
+ memory.embedding ? JSON.stringify(memory.embedding) : null,
1741
+ JSON.stringify(memory.metadata),
1742
+ JSON.stringify(memory.linkedIds),
1743
+ memory.agentId,
1744
+ memory.shared ? 1 : 0
1745
+ );
1746
+ }
1747
+ get(memoryId) {
1748
+ const row = this._db.prepare("SELECT * FROM memories WHERE id = ?").get(memoryId);
1749
+ if (!row) return null;
1750
+ return rowToMemory(row);
1751
+ }
1752
+ listAll(options) {
1753
+ const clauses = [];
1754
+ const params = [];
1755
+ if (options?.agentId) {
1756
+ if (options.includeShared === false) {
1757
+ clauses.push("agent_id = ?");
1758
+ params.push(options.agentId);
1759
+ } else {
1760
+ clauses.push("(agent_id = ? OR shared = 1)");
1761
+ params.push(options.agentId);
1762
+ }
1763
+ }
1764
+ if (options?.tier != null) {
1765
+ clauses.push("tier = ?");
1766
+ params.push(options.tier);
1767
+ }
1768
+ const where = clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
1769
+ let limitClause = "";
1770
+ if (options?.limit != null) {
1771
+ limitClause = " LIMIT ?";
1772
+ params.push(options.limit);
1773
+ }
1774
+ const rows = this._db.prepare(`SELECT * FROM memories${where}${limitClause}`).all(...params);
1775
+ return rows.map(rowToMemory);
1776
+ }
1777
+ delete(memoryId) {
1778
+ this._db.transaction(() => {
1779
+ this._db.prepare("DELETE FROM memories WHERE id = ?").run(memoryId);
1780
+ this._db.prepare("DELETE FROM edges WHERE source_id = ? OR target_id = ?").run(memoryId, memoryId);
1781
+ })();
1782
+ }
1783
+ deleteOlderThan(ageMs, tier, options) {
1784
+ const cutoff = new Date(Date.now() - ageMs).toISOString();
1785
+ const conditions = ["created_at < ?"];
1786
+ const params = [cutoff];
1787
+ if (tier != null) {
1788
+ conditions.push("tier = ?");
1789
+ params.push(tier);
1790
+ }
1791
+ if (options?.agentId) {
1792
+ conditions.push("agent_id = ?");
1793
+ params.push(options.agentId);
1794
+ }
1795
+ const where = conditions.join(" AND ");
1796
+ const ids = this._db.prepare(`SELECT id FROM memories WHERE ${where}`).all(...params);
1797
+ if (ids.length === 0) return 0;
1798
+ const idList = ids.map((r) => r.id);
1799
+ const placeholders = idList.map(() => "?").join(",");
1800
+ const txn = this._db.transaction(() => {
1801
+ this._db.prepare(
1802
+ `DELETE FROM edges WHERE source_id IN (${placeholders}) OR target_id IN (${placeholders})`
1803
+ ).run(...idList, ...idList);
1804
+ const result = this._db.prepare(`DELETE FROM memories WHERE ${where}`).run(...params);
1805
+ return result.changes;
1806
+ });
1807
+ return txn();
1808
+ }
1809
+ saveEdge(edge) {
1810
+ this._db.prepare(
1811
+ `INSERT OR REPLACE INTO edges (source_id, target_id, relation, weight, created_at)
1812
+ VALUES (?, ?, ?, ?, ?)`
1813
+ ).run(
1814
+ edge.sourceId,
1815
+ edge.targetId,
1816
+ edge.relation,
1817
+ edge.weight,
1818
+ edge.createdAt.toISOString()
1819
+ );
1820
+ }
1821
+ getEdges(memoryId) {
1822
+ const rows = this._db.prepare("SELECT * FROM edges WHERE source_id = ? OR target_id = ?").all(memoryId, memoryId);
1823
+ return rows.map((r) => ({
1824
+ sourceId: r["source_id"],
1825
+ targetId: r["target_id"],
1826
+ relation: r["relation"],
1827
+ weight: r["weight"],
1828
+ createdAt: new Date(r["created_at"])
1829
+ }));
1830
+ }
1831
+ searchByEmbedding(embedding, limit = 50, options) {
1832
+ const all = this.listAll(options);
1833
+ return bruteForceCosineSearch(all, embedding, limit);
1834
+ }
1835
+ findSimilarPairs(options) {
1836
+ const maxCandidates = options?.maxCandidates ?? 1e3;
1837
+ const memories = this.listAll({
1838
+ agentId: options?.agentId,
1839
+ includeShared: false,
1840
+ tier: options?.tier
1841
+ });
1842
+ return bruteForceSimilarPairs(
1843
+ memories,
1844
+ options?.threshold ?? 0.7,
1845
+ maxCandidates
1846
+ );
1847
+ }
1848
+ getMeta(key) {
1849
+ const row = this._db.prepare("SELECT value FROM meta WHERE key = ?").get(key);
1850
+ return row?.value ?? null;
1851
+ }
1852
+ setMeta(key, value) {
1853
+ this._db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(key, value);
1854
+ }
1855
+ };
1856
+ function rowToMemory(row) {
1857
+ const embRaw = row["embedding"];
1858
+ const m = new Memory({
1859
+ id: row["id"],
1860
+ content: row["content"],
1861
+ tier: row["tier"],
1862
+ source: row["source"],
1863
+ actor: row["actor"],
1864
+ sessionId: row["session_id"],
1865
+ createdAt: new Date(row["created_at"]),
1866
+ lastAccessedAt: new Date(row["last_accessed"]),
1867
+ accessCount: row["access_count"],
1868
+ importance: row["importance"],
1869
+ supersededBy: row["superseded_by"],
1870
+ embedding: embRaw ? JSON.parse(embRaw) : null,
1871
+ metadata: JSON.parse(row["metadata"]),
1872
+ linkedIds: JSON.parse(row["linked_ids"]),
1873
+ agentId: row["agent_id"] ?? "default",
1874
+ shared: row["shared"] === 1 || row["shared"] === true
1875
+ });
1876
+ m.contradictedBy = row["contradicted_by"];
1877
+ return m;
1878
+ }
1879
+
1880
+ // src/client.ts
1881
+ var _storeRegistry = /* @__PURE__ */ new Map();
1882
+ function _registeredStores() {
1883
+ return _storeRegistry.entries();
1884
+ }
1885
+ function sanitizeUri(uri) {
1886
+ try {
1887
+ const url = new URL(uri);
1888
+ if (url.username || url.password) {
1889
+ url.username = "***";
1890
+ url.password = "";
1891
+ }
1892
+ return url.toString();
1893
+ } catch {
1894
+ return uri.includes("://") ? uri.split("://")[0] + "://***" : uri;
1895
+ }
1896
+ }
1897
+ function parseStoreUri(uri, embeddingDim) {
1898
+ if (uri.startsWith("sqlite:")) {
1899
+ return new SQLiteStore(parseSqlitePath(uri));
1900
+ }
1901
+ if (uri.startsWith("postgres://") || uri.startsWith("postgresql://")) {
1902
+ throw new Error(
1903
+ "PostgresStore cannot be resolved from URI synchronously. Import PostgresStore directly and pass it as the store option."
1904
+ );
1905
+ }
1906
+ for (const [scheme, factory] of _storeRegistry) {
1907
+ if (uri.startsWith(`${scheme}://`) || uri.startsWith(`${scheme}+`)) {
1908
+ const result = factory(uri, embeddingDim);
1909
+ if (result instanceof Promise) {
1910
+ throw new Error(
1911
+ `Store factory for '${scheme}' returned a Promise. Async store factories must be resolved before passing to Mnemonic.`
1912
+ );
1913
+ }
1914
+ return result;
1915
+ }
1916
+ }
1917
+ throw new Error(`Unsupported store URI: '${sanitizeUri(uri)}'`);
1918
+ }
1919
+ var Mnemonic = class _Mnemonic {
1920
+ _config;
1921
+ _store;
1922
+ _embedder;
1923
+ _autoClassify;
1924
+ _agentId;
1925
+ _resolvedAgentId = null;
1926
+ _initPromise = null;
1927
+ constructor(options) {
1928
+ this._config = options?.config ?? new MnemonicConfig();
1929
+ this._embedder = options?.embedder ?? null;
1930
+ this._autoClassify = options?.autoClassify ?? true;
1931
+ this._agentId = options?.agentId ?? null;
1932
+ const storeOpt = options?.store ?? "sqlite:///mnemonic.db";
1933
+ if (typeof storeOpt === "string") {
1934
+ const raw = parseStoreUri(storeOpt, this._embedder?.dimension);
1935
+ this._store = _wrapIfSync(raw);
1936
+ } else {
1937
+ this._store = _wrapIfSync(storeOpt);
1938
+ }
1939
+ }
1940
+ get config() {
1941
+ return this._config;
1942
+ }
1943
+ /** The underlying async store. */
1944
+ get store() {
1945
+ return this._store;
1946
+ }
1947
+ /** The embedder (if configured). */
1948
+ get embedder() {
1949
+ return this._embedder;
1950
+ }
1951
+ /** The resolved agent ID (available after init()). */
1952
+ get agentId() {
1953
+ if (!this._resolvedAgentId) {
1954
+ throw new Error("Client not initialized. Call init() first.");
1955
+ }
1956
+ return this._resolvedAgentId;
1957
+ }
1958
+ /**
1959
+ * Initialize the store and resolve agent identity. Must be called before use.
1960
+ * Pass `{ skipStoreInit: true }` when reusing an already-initialized store
1961
+ * (e.g. creating per-agent clients that share a single backend).
1962
+ */
1963
+ async init(options) {
1964
+ if (this._resolvedAgentId) return;
1965
+ if (this._initPromise) {
1966
+ await this._initPromise;
1967
+ return;
1968
+ }
1969
+ this._initPromise = (async () => {
1970
+ if (!options?.skipStoreInit) {
1971
+ await this._store.initialize();
1972
+ }
1973
+ await this._resolveAgentId();
1974
+ })();
1975
+ try {
1976
+ await this._initPromise;
1977
+ } finally {
1978
+ this._initPromise = null;
1979
+ }
1980
+ }
1981
+ async _resolveAgentId() {
1982
+ if (this._agentId) {
1983
+ this._resolvedAgentId = this._agentId;
1984
+ return;
1985
+ }
1986
+ const persisted = await this._store.getMeta("agent_id");
1987
+ if (persisted) {
1988
+ this._resolvedAgentId = persisted;
1989
+ return;
1990
+ }
1991
+ const generated = (0, import_node_crypto2.randomUUID)().replace(/-/g, "");
1992
+ await this._store.setMeta("agent_id", generated);
1993
+ this._resolvedAgentId = generated;
1994
+ }
1995
+ _ensureInitialized() {
1996
+ if (!this._resolvedAgentId) {
1997
+ throw new Error("Client not initialized. Call init() first.");
1998
+ }
1999
+ }
2000
+ /** Return only memories owned by this agent (excludes shared from others). */
2001
+ async _ownMemories() {
2002
+ return this._store.listAll({
2003
+ agentId: this._resolvedAgentId,
2004
+ includeShared: false
2005
+ });
2006
+ }
2007
+ static _contentHash(content) {
2008
+ return (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex");
2009
+ }
2010
+ /**
2011
+ * Store a memory with automatic classification and linking.
2012
+ * If identical content already exists for this agent, returns the existing memory.
2013
+ */
2014
+ async add(content, options) {
2015
+ this._ensureInitialized();
2016
+ const contentHash = _Mnemonic._contentHash(content);
2017
+ const knownMemories = await this._store.listAll({ agentId: this._resolvedAgentId });
2018
+ for (const mem of knownMemories) {
2019
+ if (mem.agentId === this._resolvedAgentId && _Mnemonic._contentHash(mem.content) === contentHash) {
2020
+ return mem;
2021
+ }
2022
+ }
2023
+ const source = options?.source ?? "unknown";
2024
+ const actor = options?.actor ?? "system";
2025
+ let resolvedTier;
2026
+ if (options?.tier != null) {
2027
+ resolvedTier = options.tier;
2028
+ } else if (this._autoClassify) {
2029
+ resolvedTier = classify(content, {
2030
+ source,
2031
+ actor,
2032
+ config: this._config
2033
+ });
2034
+ } else {
2035
+ resolvedTier = this._config.defaultTier;
2036
+ }
2037
+ const memory = new Memory({
2038
+ content,
2039
+ tier: resolvedTier,
2040
+ source,
2041
+ actor,
2042
+ sessionId: options?.sessionId ?? null,
2043
+ metadata: options?.metadata ?? {},
2044
+ agentId: this._resolvedAgentId,
2045
+ shared: options?.shared ?? false,
2046
+ charsPerToken: this._config.charsPerToken
2047
+ });
2048
+ if (this._embedder) {
2049
+ memory.embedding = await this._embedder.embed(content);
2050
+ }
2051
+ const salience = detectSalience(content, this._config.salience);
2052
+ if (salience.boost > 0) {
2053
+ memory.metadata["salience_signals"] = salience.signals;
2054
+ memory.metadata["boost"] = (memory.metadata["boost"] ?? 0) + salience.boost;
2055
+ }
2056
+ memory.importance = score(memory, this._config);
2057
+ const existing = await this._store.listAll({
2058
+ agentId: this._resolvedAgentId
2059
+ });
2060
+ const edges = findLinks(memory, existing, { config: this._config });
2061
+ await this._store.save(memory);
2062
+ for (const edge of edges) {
2063
+ memory.linkedIds.push(edge.targetId);
2064
+ await this._store.saveEdge(edge);
2065
+ const linked = await this._store.get(edge.targetId);
2066
+ if (linked) {
2067
+ if (linked.agentId === this._resolvedAgentId) {
2068
+ if (!linked.linkedIds.includes(memory.id)) {
2069
+ linked.linkedIds.push(memory.id);
2070
+ }
2071
+ if (edge.relation === "supersedes" && linked.supersededBy == null) {
2072
+ linked.supersededBy = memory.id;
2073
+ } else if (edge.relation === "contradicts" && linked.contradictedBy == null) {
2074
+ linked.contradictedBy = memory.id;
2075
+ }
2076
+ await this._store.save(linked);
2077
+ }
2078
+ }
2079
+ }
2080
+ await this._store.save(memory);
2081
+ return memory;
2082
+ }
2083
+ async _assemble(query, options) {
2084
+ let queryEmbedding = null;
2085
+ const limit = this._config.embeddingSearchLimit;
2086
+ let candidates;
2087
+ if (this._embedder) {
2088
+ queryEmbedding = await this._embedder.embed(query);
2089
+ candidates = await this._store.searchByEmbedding(
2090
+ queryEmbedding,
2091
+ limit,
2092
+ { agentId: this._resolvedAgentId }
2093
+ );
2094
+ } else {
2095
+ candidates = await this._store.listAll({
2096
+ agentId: this._resolvedAgentId
2097
+ });
2098
+ }
2099
+ if (options?.excludeSuperseded) {
2100
+ candidates = candidates.filter((m) => m.supersededBy == null);
2101
+ }
2102
+ const selected = assemble(query, candidates, {
2103
+ queryEmbedding,
2104
+ maxTokens: options?.maxTokens,
2105
+ minRelevance: options?.minRelevance,
2106
+ config: this._config
2107
+ });
2108
+ await Promise.all(selected.map((mem) => this._store.save(mem)));
2109
+ return selected;
2110
+ }
2111
+ /** Retrieve optimised context for a given query within a token budget. */
2112
+ async recall(query, options) {
2113
+ this._ensureInitialized();
2114
+ const selected = await this._assemble(query, {
2115
+ ...options,
2116
+ excludeSuperseded: true
2117
+ });
2118
+ const conflicts = detectConflicts(selected, this._config);
2119
+ return formatContext(selected, options?.header, conflicts);
2120
+ }
2121
+ /** Like recall but returns the raw Memory objects. */
2122
+ async recallMemories(query, options) {
2123
+ this._ensureInitialized();
2124
+ return this._assemble(query, options);
2125
+ }
2126
+ /** Delete memories matching the given criteria. Returns count deleted. */
2127
+ async forget(options) {
2128
+ this._ensureInitialized();
2129
+ if (!options?.olderThan && !options?.tier && options?.belowImportance == null) {
2130
+ throw new Error(
2131
+ "forget() requires at least one filter (olderThan, tier, or belowImportance)"
2132
+ );
2133
+ }
2134
+ const tier = options?.tier;
2135
+ if (options?.belowImportance == null) {
2136
+ const olderThan = options?.olderThan?.trim();
2137
+ if (olderThan === "") {
2138
+ throw new Error("olderThan must be a valid duration string (e.g. '30d'), not empty");
2139
+ }
2140
+ const ageMs = olderThan ? parseDuration(olderThan) : 0;
2141
+ return this._store.deleteOlderThan(ageMs, tier, {
2142
+ agentId: this._resolvedAgentId
2143
+ });
2144
+ }
2145
+ let candidates = await this._store.listAll({
2146
+ agentId: this._resolvedAgentId,
2147
+ includeShared: false,
2148
+ tier: tier ?? void 0
2149
+ });
2150
+ if (options?.olderThan != null) {
2151
+ const ageMs = parseDuration(options.olderThan);
2152
+ const cutoff = Date.now() - ageMs;
2153
+ candidates = candidates.filter((m) => m.createdAt.getTime() < cutoff);
2154
+ }
2155
+ let deleted = 0;
2156
+ for (const mem of candidates) {
2157
+ const currentImportance = score(mem, this._config);
2158
+ if (currentImportance < options.belowImportance) {
2159
+ await this._store.delete(mem.id);
2160
+ deleted++;
2161
+ }
2162
+ }
2163
+ return deleted;
2164
+ }
2165
+ /**
2166
+ * Merge similar episodic memories into higher-tier summaries.
2167
+ *
2168
+ * Originals are kept alive but deprioritised via a scoring penalty
2169
+ * and `consolidated_into` metadata. A `consolidates` edge links
2170
+ * the summary to each original for graph traversal.
2171
+ */
2172
+ async consolidate() {
2173
+ this._ensureInitialized();
2174
+ const ownEpisodic = await this._store.listAll({
2175
+ agentId: this._resolvedAgentId,
2176
+ includeShared: false,
2177
+ tier: MemoryTier.EPISODIC
2178
+ });
2179
+ const eligible = [];
2180
+ for (const m of ownEpisodic) {
2181
+ const summaryId = m.metadata["consolidated_into"];
2182
+ if (summaryId == null || await this._store.get(summaryId) == null) {
2183
+ eligible.push(m);
2184
+ }
2185
+ }
2186
+ const privateMemories = eligible.filter((m) => !m.shared);
2187
+ const sharedMemories = eligible.filter((m) => m.shared);
2188
+ const cfg = this._config.consolidation;
2189
+ const eligibleIds = new Set(eligible.map((m) => m.id));
2190
+ const allPairs = await this._store.findSimilarPairs({
2191
+ threshold: cfg.similarityThreshold,
2192
+ agentId: this._resolvedAgentId,
2193
+ tier: MemoryTier.EPISODIC,
2194
+ maxCandidates: cfg.maxCandidates,
2195
+ maxNeighborsPerCandidate: cfg.maxNeighborsPerCandidate
2196
+ });
2197
+ const eligiblePairs = allPairs.filter(
2198
+ (p) => eligibleIds.has(p.idA) && eligibleIds.has(p.idB)
2199
+ );
2200
+ const privateIds = new Set(privateMemories.map((m) => m.id));
2201
+ const sharedIds = new Set(sharedMemories.map((m) => m.id));
2202
+ const privatePairs = eligiblePairs.filter(
2203
+ (p) => privateIds.has(p.idA) && privateIds.has(p.idB)
2204
+ );
2205
+ const sharedPairs = eligiblePairs.filter(
2206
+ (p) => sharedIds.has(p.idA) && sharedIds.has(p.idB)
2207
+ );
2208
+ let totalClusters = 0;
2209
+ for (const [memories, pairs, isShared] of [
2210
+ [privateMemories, privatePairs, false],
2211
+ [sharedMemories, sharedPairs, true]
2212
+ ]) {
2213
+ const clusters = findConsolidationCandidates(memories, this._config, pairs);
2214
+ const CONCURRENCY = 8;
2215
+ const prepared = [];
2216
+ const processBatch = async (batch) => {
2217
+ const results = await Promise.all(
2218
+ batch.map(async (cluster) => {
2219
+ const summary = await mergeCluster(cluster, this._config);
2220
+ summary.agentId = this._resolvedAgentId;
2221
+ summary.shared = isShared;
2222
+ for (const mem of cluster) {
2223
+ if (!summary.linkedIds.includes(mem.id)) {
2224
+ summary.linkedIds.push(mem.id);
2225
+ }
2226
+ }
2227
+ if (this._embedder) {
2228
+ summary.embedding = await this._embedder.embed(summary.content);
2229
+ }
2230
+ summary.importance = score(summary, this._config);
2231
+ return { cluster, summary };
2232
+ })
2233
+ );
2234
+ prepared.push(...results);
2235
+ };
2236
+ for (let i = 0; i < clusters.length; i += CONCURRENCY) {
2237
+ await processBatch(clusters.slice(i, i + CONCURRENCY));
2238
+ }
2239
+ for (const { cluster, summary } of prepared) {
2240
+ await this._store.save(summary);
2241
+ for (const mem of cluster) {
2242
+ const edge = createEdge(summary.id, mem.id, "consolidates");
2243
+ await this._store.saveEdge(edge);
2244
+ mem.metadata["consolidated_into"] = summary.id;
2245
+ mem.linkedIds.push(summary.id);
2246
+ mem.importance = score(mem, this._config);
2247
+ await this._store.save(mem);
2248
+ }
2249
+ }
2250
+ totalClusters += clusters.length;
2251
+ }
2252
+ return totalClusters;
2253
+ }
2254
+ /** Run the tier promotion/demotion pipeline on own memories. */
2255
+ async promote() {
2256
+ this._ensureInitialized();
2257
+ const ownMemories = await this._ownMemories();
2258
+ let changes = await this._applyTierResults(
2259
+ checkPromotions(ownMemories, this._config)
2260
+ );
2261
+ const refreshed = await this._ownMemories();
2262
+ changes += await this._applyTierResults(
2263
+ checkDemotions(refreshed, this._config)
2264
+ );
2265
+ return changes;
2266
+ }
2267
+ /** Re-run the linker across all memories to retroactively set edges. */
2268
+ async relink() {
2269
+ this._ensureInitialized();
2270
+ const ownMemories = await this._ownMemories();
2271
+ const visible = await this._store.listAll({ agentId: this._resolvedAgentId });
2272
+ const ownById = new Map(ownMemories.map((m) => [m.id, m]));
2273
+ const existingRelations = /* @__PURE__ */ new Map();
2274
+ for (const mem of ownMemories) {
2275
+ const edges = await this._store.getEdges(mem.id);
2276
+ for (const e of edges) existingRelations.set(`${e.sourceId}:${e.targetId}`, e.relation);
2277
+ }
2278
+ let newEdges = 0;
2279
+ for (const mem of ownMemories) {
2280
+ const edges = findLinks(mem, visible, { config: this._config });
2281
+ for (const edge of edges) {
2282
+ const key = `${edge.sourceId}:${edge.targetId}`;
2283
+ const existingRel = existingRelations.get(key);
2284
+ if (existingRel == null) {
2285
+ await this._store.saveEdge(edge);
2286
+ existingRelations.set(key, edge.relation);
2287
+ newEdges++;
2288
+ } else if (existingRel !== edge.relation) {
2289
+ await this._store.saveEdge(edge);
2290
+ existingRelations.set(key, edge.relation);
2291
+ }
2292
+ if (edge.targetId !== mem.id && !mem.linkedIds.includes(edge.targetId)) {
2293
+ mem.linkedIds.push(edge.targetId);
2294
+ }
2295
+ const linked = ownById.get(edge.targetId);
2296
+ if (linked) {
2297
+ if (!linked.linkedIds.includes(mem.id)) linked.linkedIds.push(mem.id);
2298
+ if (edge.relation === "supersedes" && linked.supersededBy == null) {
2299
+ linked.supersededBy = mem.id;
2300
+ } else if (edge.relation === "contradicts" && linked.contradictedBy == null) {
2301
+ linked.contradictedBy = mem.id;
2302
+ }
2303
+ }
2304
+ }
2305
+ }
2306
+ await Promise.all(ownMemories.map((m) => this._store.save(m)));
2307
+ return newEdges;
2308
+ }
2309
+ /** Return aggregate statistics about own memories. */
2310
+ async stats() {
2311
+ this._ensureInitialized();
2312
+ const memories = await this._ownMemories();
2313
+ const byTier = {};
2314
+ let totalTokens = 0;
2315
+ for (const m of memories) {
2316
+ byTier[m.tierName] = (byTier[m.tierName] ?? 0) + 1;
2317
+ totalTokens += m.tokenEstimate;
2318
+ }
2319
+ return {
2320
+ totalMemories: memories.length,
2321
+ totalTokens,
2322
+ byTier,
2323
+ agentId: this._resolvedAgentId
2324
+ };
2325
+ }
2326
+ /** Make a memory visible to all agents. */
2327
+ async share(memoryId) {
2328
+ this._ensureInitialized();
2329
+ const memory = await this._store.get(memoryId);
2330
+ if (!memory) {
2331
+ throw new Error(`Memory not found: ${memoryId}`);
2332
+ }
2333
+ if (memory.agentId !== this._resolvedAgentId) {
2334
+ throw new Error("Cannot share another agent's memory");
2335
+ }
2336
+ memory.shared = true;
2337
+ await this._store.save(memory);
2338
+ return memory;
2339
+ }
2340
+ /** Make a memory private (only visible to owner). */
2341
+ async unshare(memoryId) {
2342
+ this._ensureInitialized();
2343
+ const memory = await this._store.get(memoryId);
2344
+ if (!memory) {
2345
+ throw new Error(`Memory not found: ${memoryId}`);
2346
+ }
2347
+ if (memory.agentId !== this._resolvedAgentId) {
2348
+ throw new Error("Cannot unshare another agent's memory");
2349
+ }
2350
+ memory.shared = false;
2351
+ await this._store.save(memory);
2352
+ return memory;
2353
+ }
2354
+ async _applyTierResults(results) {
2355
+ let applied = 0;
2356
+ for (const result of results) {
2357
+ const mem = await this._store.get(result.memoryId);
2358
+ if (!mem) continue;
2359
+ applyTierChange(mem, result.newTier);
2360
+ mem.importance = score(mem, this._config);
2361
+ await this._store.save(mem);
2362
+ applied++;
2363
+ }
2364
+ return applied;
2365
+ }
2366
+ };
2367
+ function _wrapIfSync(store) {
2368
+ if (store instanceof SQLiteStore) {
2369
+ return syncToAsync(store);
2370
+ }
2371
+ return store;
2372
+ }
2373
+
2374
+ // src/embeddings/factory.ts
2375
+ async function buildEmbedder(provider) {
2376
+ const resolved = (provider ?? process.env["MNEMONIC_EMBEDDER"] ?? "none").trim().toLowerCase();
2377
+ if (resolved === "none" || resolved === "") return null;
2378
+ if (resolved === "openai") {
2379
+ const { OpenAIEmbedder: OpenAIEmbedder2 } = await Promise.resolve().then(() => (init_openai(), openai_exports));
2380
+ return new OpenAIEmbedder2({
2381
+ model: process.env["MNEMONIC_OPENAI_MODEL"],
2382
+ apiKey: process.env["MNEMONIC_OPENAI_API_KEY"]
2383
+ });
2384
+ }
2385
+ if (resolved === "local") {
2386
+ const { LocalEmbedder: LocalEmbedder2 } = await Promise.resolve().then(() => (init_local(), local_exports));
2387
+ return new LocalEmbedder2();
2388
+ }
2389
+ throw new Error(
2390
+ `Unknown embedder provider: '${resolved}'. Valid options: none, openai, local`
2391
+ );
2392
+ }
2393
+
2394
+ // src/mcp/server.ts
2395
+ init_utils();
2396
+ var MAX_AGENT_CACHE = 256;
2397
+ var AGENT_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
2398
+ function validateAgentId(agentId) {
2399
+ const trimmed = agentId.trim();
2400
+ if (!AGENT_ID_RE.test(trimmed)) {
2401
+ throw new Error(
2402
+ "Invalid agent_id: must be 1-128 alphanumeric/underscore/hyphen characters"
2403
+ );
2404
+ }
2405
+ return trimmed;
2406
+ }
2407
+ function serializeMemory(memory, callerAgentId) {
2408
+ return {
2409
+ id: memory.id,
2410
+ content: memory.content,
2411
+ tier: memory.tierName,
2412
+ source: memory.source,
2413
+ actor: memory.actor,
2414
+ session_id: memory.sessionId,
2415
+ created_at: memory.createdAt.toISOString(),
2416
+ last_accessed_at: memory.lastAccessedAt.toISOString(),
2417
+ access_count: memory.accessCount,
2418
+ importance: Math.round(memory.importance * 1e4) / 1e4,
2419
+ superseded_by: memory.supersededBy,
2420
+ contradicted_by: memory.contradictedBy,
2421
+ has_embedding: memory.embedding != null,
2422
+ metadata: memory.metadata,
2423
+ linked_ids: memory.linkedIds,
2424
+ token_estimate: memory.tokenEstimate,
2425
+ owned_by_caller: callerAgentId != null ? memory.agentId === callerAgentId : false,
2426
+ shared: memory.shared
2427
+ };
2428
+ }
2429
+ async function buildFromUri(uri) {
2430
+ const embedder = await buildEmbedder();
2431
+ const dim = embedder?.dimension ?? 1536;
2432
+ if (uri.startsWith("sqlite:")) {
2433
+ return { store: new SQLiteStore(parseSqlitePath(uri)), embedder };
2434
+ }
2435
+ if (uri.startsWith("postgres://") || uri.startsWith("postgresql://")) {
2436
+ const { PostgresStore: PostgresStore2 } = await Promise.resolve().then(() => (init_postgres(), postgres_exports));
2437
+ return { store: new PostgresStore2({ dsn: uri, embeddingDim: dim }), embedder };
2438
+ }
2439
+ for (const [scheme, factory] of _registeredStores()) {
2440
+ if (uri.startsWith(`${scheme}://`) || uri.startsWith(`${scheme}+`)) {
2441
+ const result = factory(uri, dim);
2442
+ const store = result instanceof Promise ? await result : result;
2443
+ return { store, embedder };
2444
+ }
2445
+ }
2446
+ throw new Error(
2447
+ `Unsupported store URI: '${uri}'. Use sqlite:///path, postgres[ql]://..., or register a custom store.`
2448
+ );
2449
+ }
2450
+ async function createServer(options) {
2451
+ let mcpSdk;
2452
+ try {
2453
+ const mcpPath = "@modelcontextprotocol/sdk/server/mcp.js";
2454
+ mcpSdk = await import(mcpPath);
2455
+ } catch {
2456
+ throw new Error(
2457
+ "MCP server requires '@modelcontextprotocol/sdk'. Install it with: npm install @modelcontextprotocol/sdk"
2458
+ );
2459
+ }
2460
+ const { McpServer } = mcpSdk;
2461
+ let z;
2462
+ try {
2463
+ const zodPath = "zod";
2464
+ const zod = await import(zodPath);
2465
+ z = zod.z ?? zod.default?.z;
2466
+ } catch {
2467
+ throw new Error(
2468
+ "MCP server requires 'zod'. Install it with: npm install zod"
2469
+ );
2470
+ }
2471
+ let baseClient;
2472
+ if (options?.mnemonic) {
2473
+ baseClient = options.mnemonic;
2474
+ try {
2475
+ baseClient.agentId;
2476
+ } catch {
2477
+ await baseClient.init();
2478
+ }
2479
+ } else {
2480
+ const storeUri = process.env["MNEMONIC_STORE_URI"] ?? "sqlite:///mnemonic.db";
2481
+ const agentId = process.env["MNEMONIC_AGENT_ID"] || void 0;
2482
+ const { store, embedder } = await buildFromUri(storeUri);
2483
+ baseClient = new Mnemonic({ store, embedder, agentId });
2484
+ await baseClient.init();
2485
+ }
2486
+ const agentCache = /* @__PURE__ */ new Map();
2487
+ async function getAgent(agentId) {
2488
+ if (!agentId) return baseClient;
2489
+ agentId = validateAgentId(agentId);
2490
+ const cached = agentCache.get(agentId);
2491
+ if (cached) {
2492
+ agentCache.delete(agentId);
2493
+ agentCache.set(agentId, cached);
2494
+ return cached;
2495
+ }
2496
+ if (agentCache.size >= MAX_AGENT_CACHE) {
2497
+ const lru = agentCache.keys().next().value;
2498
+ agentCache.delete(lru);
2499
+ }
2500
+ const agent = new Mnemonic({
2501
+ store: baseClient.store,
2502
+ embedder: baseClient.embedder,
2503
+ config: baseClient.config,
2504
+ agentId
2505
+ });
2506
+ await agent.init({ skipStoreInit: true });
2507
+ agentCache.set(agentId, agent);
2508
+ return agent;
2509
+ }
2510
+ function jsonResponse(data) {
2511
+ return {
2512
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
2513
+ };
2514
+ }
2515
+ function errorResponse(err) {
2516
+ const message = err instanceof Error ? err.message : String(err);
2517
+ let hint = "";
2518
+ if (message.includes("not found")) hint = " Use memory_search to find valid IDs.";
2519
+ else if (message.includes("not the owner") || message.includes("permission")) hint = " Only the owning agent can modify this memory.";
2520
+ else if (message.includes("at least one filter")) hint = " Provide older_than, tier, or below_importance.";
2521
+ return {
2522
+ content: [
2523
+ {
2524
+ type: "text",
2525
+ text: JSON.stringify({ error: message + hint }, null, 2)
2526
+ }
2527
+ ],
2528
+ isError: true
2529
+ };
2530
+ }
2531
+ const server = new McpServer({
2532
+ name: "mnemonic",
2533
+ version: "0.1.0"
2534
+ });
2535
+ server.tool(
2536
+ "memory_store",
2537
+ {
2538
+ description: "Save information to long-term memory. Call proactively when the user shares preferences, identity facts, decisions, corrections, or notable events \u2014 also when they correct prior knowledge or express a strong preference. Auto-classifies into tiers, scores importance, links to related memories, and detects supersession.",
2539
+ annotations: { readOnlyHint: false, idempotentHint: false }
2540
+ },
2541
+ {
2542
+ content: z.string().describe("Clear standalone statement to remember, e.g. 'User prefers Kubernetes over ECS' or 'Project uses Next.js 14 with App Router'"),
2543
+ source: z.string().default("conversation").describe("Where this came from. Use lowercase_snake values. Common: 'conversation', 'observation', 'document', 'tool_output', 'system'. Custom sources welcome: 'slack', 'email', 'jira', 'meeting_notes', etc."),
2544
+ actor: z.string().default("user").describe("Who produced this. Common: 'user', 'assistant', 'system'. Can also be a specific name or agent ID."),
2545
+ tier: z.enum(["identity", "procedural", "structural", "episodic", "transient"]).optional().describe("Override auto-classification. Usually omit \u2014 the classifier handles it."),
2546
+ session_id: z.string().optional().describe("Group memories by conversation or task"),
2547
+ metadata: z.record(z.unknown()).optional().describe("Key-value pairs, e.g. {project: 'api', priority: 'high'}"),
2548
+ agent_id: z.string().optional().describe("Target agent's memory space. Omit for default."),
2549
+ shared: z.boolean().optional().describe("Make visible to all agents. Default: false.")
2550
+ },
2551
+ async (params) => {
2552
+ try {
2553
+ const agent = await getAgent(params.agent_id);
2554
+ const memory = await agent.add(params.content, {
2555
+ source: params.source,
2556
+ actor: params.actor,
2557
+ tier: params.tier,
2558
+ sessionId: params.session_id,
2559
+ metadata: params.metadata,
2560
+ shared: params.shared
2561
+ });
2562
+ return jsonResponse(serializeMemory(memory, agent.agentId));
2563
+ } catch (err) {
2564
+ return errorResponse(err);
2565
+ }
2566
+ }
2567
+ );
2568
+ server.tool(
2569
+ "memory_recall",
2570
+ {
2571
+ description: "Retrieve relevant memories as formatted text, ranked by relevance and importance within a token budget. Call at the start of every conversation to load context. Also call when the topic shifts or when you need to verify what you already know. Returns a ready-to-use text block. Prefer over memory_search unless you need structured data or memory IDs.",
2572
+ annotations: { readOnlyHint: true, idempotentHint: true }
2573
+ },
2574
+ {
2575
+ query: z.string().describe("What you need to know, e.g. 'deployment preferences' or 'user's tech stack'"),
2576
+ max_tokens: z.number().int().min(100).max(16e3).optional().describe("Token budget for returned context. Default: 2000."),
2577
+ min_relevance: z.number().min(0).max(1).optional().describe("Minimum relevance score (0.0-1.0) for a memory to be included. Filters out low-relevance noise."),
2578
+ header: z.string().optional().describe("Header prepended to context, e.g. '## Relevant Memory'"),
2579
+ agent_id: z.string().optional().describe("Target agent's memory space. Omit for default.")
2580
+ },
2581
+ async (params) => {
2582
+ try {
2583
+ const agent = await getAgent(params.agent_id);
2584
+ const context = await agent.recall(params.query, {
2585
+ maxTokens: params.max_tokens,
2586
+ minRelevance: params.min_relevance,
2587
+ header: params.header
2588
+ });
2589
+ return { content: [{ type: "text", text: context }] };
2590
+ } catch (err) {
2591
+ return errorResponse(err);
2592
+ }
2593
+ }
2594
+ );
2595
+ server.tool(
2596
+ "memory_search",
2597
+ {
2598
+ description: "Search memories and return structured JSON with full metadata (id, tier, importance, timestamps, linked_ids). Use instead of memory_recall when you need memory IDs for share/unshare/forget, or need to inspect importance scores and graph links.",
2599
+ annotations: { readOnlyHint: true, idempotentHint: true }
2600
+ },
2601
+ {
2602
+ query: z.string().describe("What to search for, e.g. 'infrastructure decisions' or 'recent debugging sessions'"),
2603
+ max_tokens: z.number().int().min(100).max(16e3).optional().describe("Token budget for candidate selection. Default: 2000."),
2604
+ min_relevance: z.number().min(0).max(1).optional().describe("Minimum relevance score (0.0-1.0) for a memory to be included. Filters out low-relevance noise."),
2605
+ agent_id: z.string().optional().describe("Target agent's memory space. Omit for default.")
2606
+ },
2607
+ async (params) => {
2608
+ try {
2609
+ const agent = await getAgent(params.agent_id);
2610
+ const memories = await agent.recallMemories(params.query, {
2611
+ maxTokens: params.max_tokens,
2612
+ minRelevance: params.min_relevance
2613
+ });
2614
+ return jsonResponse(memories.map((m) => serializeMemory(m, agent.agentId)));
2615
+ } catch (err) {
2616
+ return errorResponse(err);
2617
+ }
2618
+ }
2619
+ );
2620
+ server.tool(
2621
+ "memory_forget",
2622
+ {
2623
+ description: "Delete memories matching filters. Memories naturally fade via decay \u2014 only explicitly delete when content is wrong, sensitive, or the user requests removal. At least one filter is required.",
2624
+ annotations: { destructiveHint: true, idempotentHint: false }
2625
+ },
2626
+ {
2627
+ older_than: z.string().optional().describe("Delete older than duration: '30d', '24h', '2w', '90m', '120s'"),
2628
+ tier: z.enum(["identity", "procedural", "structural", "episodic", "transient"]).optional().describe("Only delete memories in this tier"),
2629
+ below_importance: z.number().min(0).max(1).optional().describe("Delete below this importance score (0.0\u20131.0)"),
2630
+ agent_id: z.string().optional().describe("Target agent's memory space. Omit for default.")
2631
+ },
2632
+ async (params) => {
2633
+ try {
2634
+ const agent = await getAgent(params.agent_id);
2635
+ const deleted = await agent.forget({
2636
+ olderThan: params.older_than,
2637
+ tier: params.tier,
2638
+ belowImportance: params.below_importance
2639
+ });
2640
+ return jsonResponse({ deleted });
2641
+ } catch (err) {
2642
+ return errorResponse(err);
2643
+ }
2644
+ }
2645
+ );
2646
+ server.tool(
2647
+ "memory_consolidate",
2648
+ {
2649
+ description: "Merge similar episodic memories into structural summaries. Originals are kept but deprioritised \u2014 they fade naturally via decay while the summary takes over in recall. Run after storing 10+ memories in a session, or when memory_stats shows many episodic memories.",
2650
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
2651
+ },
2652
+ { agent_id: z.string().optional().describe("Target agent. Omit for default.") },
2653
+ async (params) => {
2654
+ try {
2655
+ const agent = await getAgent(params.agent_id);
2656
+ return jsonResponse({ consolidated_groups: await agent.consolidate() });
2657
+ } catch (err) {
2658
+ return errorResponse(err);
2659
+ }
2660
+ }
2661
+ );
2662
+ server.tool(
2663
+ "memory_promote",
2664
+ {
2665
+ description: "Promote frequently accessed memories to higher tiers and demote inactive ones. Tiers affect recall ranking and decay rate. Run at the end of sessions to update tier rankings based on access patterns.",
2666
+ annotations: { readOnlyHint: false, idempotentHint: false }
2667
+ },
2668
+ { agent_id: z.string().optional().describe("Target agent. Omit for default.") },
2669
+ async (params) => {
2670
+ try {
2671
+ const agent = await getAgent(params.agent_id);
2672
+ return jsonResponse({ tier_changes: await agent.promote() });
2673
+ } catch (err) {
2674
+ return errorResponse(err);
2675
+ }
2676
+ }
2677
+ );
2678
+ server.tool(
2679
+ "memory_relink",
2680
+ {
2681
+ description: "Re-run the linker across all memories to retroactively detect supersession and contradiction edges. Use after upgrading or when existing memories lack relationship metadata.",
2682
+ annotations: { readOnlyHint: false, idempotentHint: true }
2683
+ },
2684
+ { agent_id: z.string().optional().describe("Target agent. Omit for default.") },
2685
+ async (params) => {
2686
+ try {
2687
+ const agent = await getAgent(params.agent_id);
2688
+ return jsonResponse({ new_edges: await agent.relink() });
2689
+ } catch (err) {
2690
+ return errorResponse(err);
2691
+ }
2692
+ }
2693
+ );
2694
+ server.tool(
2695
+ "memory_stats",
2696
+ {
2697
+ description: "Get memory health: total count, tier breakdown, token usage. Call when the user asks 'what do you remember?' or to decide if consolidation or pruning is needed.",
2698
+ annotations: { readOnlyHint: true, idempotentHint: true }
2699
+ },
2700
+ { agent_id: z.string().optional().describe("Target agent. Omit for default.") },
2701
+ async (params) => {
2702
+ try {
2703
+ const agent = await getAgent(params.agent_id);
2704
+ return jsonResponse(await agent.stats());
2705
+ } catch (err) {
2706
+ return errorResponse(err);
2707
+ }
2708
+ }
2709
+ );
2710
+ server.tool(
2711
+ "memory_share",
2712
+ {
2713
+ description: "Make a memory visible to all agents. Only the owning agent can share. Use memory_search first to get the memory ID.",
2714
+ annotations: { readOnlyHint: false, idempotentHint: true }
2715
+ },
2716
+ {
2717
+ memory_id: z.string().describe("Memory ID from memory_search results"),
2718
+ agent_id: z.string().optional().describe("Owning agent. Omit for default.")
2719
+ },
2720
+ async (params) => {
2721
+ try {
2722
+ const agent = await getAgent(params.agent_id);
2723
+ const memory = await agent.share(params.memory_id);
2724
+ return jsonResponse(serializeMemory(memory, agent.agentId));
2725
+ } catch (err) {
2726
+ return errorResponse(err);
2727
+ }
2728
+ }
2729
+ );
2730
+ server.tool(
2731
+ "memory_unshare",
2732
+ {
2733
+ description: "Make a shared memory private again. Only the owning agent can unshare. Use memory_search first to get the memory ID.",
2734
+ annotations: { readOnlyHint: false, idempotentHint: true }
2735
+ },
2736
+ {
2737
+ memory_id: z.string().describe("Memory ID from memory_search results"),
2738
+ agent_id: z.string().optional().describe("Owning agent. Omit for default.")
2739
+ },
2740
+ async (params) => {
2741
+ try {
2742
+ const agent = await getAgent(params.agent_id);
2743
+ const memory = await agent.unshare(params.memory_id);
2744
+ return jsonResponse(serializeMemory(memory, agent.agentId));
2745
+ } catch (err) {
2746
+ return errorResponse(err);
2747
+ }
2748
+ }
2749
+ );
2750
+ return { server, baseClient, agentCache };
2751
+ }
2752
+
2753
+ // src/mcp/main.ts
2754
+ async function main() {
2755
+ const { server, baseClient } = await createServer();
2756
+ const transportPath = "@modelcontextprotocol/sdk/server/stdio.js";
2757
+ const mcpTransport = await import(transportPath);
2758
+ const { StdioServerTransport } = mcpTransport;
2759
+ const transport = new StdioServerTransport();
2760
+ for (const sig of ["SIGINT", "SIGTERM"]) {
2761
+ process.on(sig, async () => {
2762
+ await baseClient.store.close().catch(() => {
2763
+ });
2764
+ process.exit(0);
2765
+ });
2766
+ }
2767
+ await server.connect(transport);
2768
+ }
2769
+ main().catch((err) => {
2770
+ const msg = err instanceof Error ? err.message : String(err);
2771
+ process.stderr.write(`Fatal: ${msg}
2772
+ `);
2773
+ process.exit(1);
2774
+ });
2775
+ //# sourceMappingURL=main.cjs.map