@agenticmail/core 0.9.14 → 0.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +893 -0
- package/dist/index.d.cts +210 -1
- package/dist/index.d.ts +210 -1
- package/dist/index.js +888 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1401,6 +1401,10 @@ var AccountManager = class {
|
|
|
1401
1401
|
}
|
|
1402
1402
|
const stmt = this.db.prepare("DELETE FROM agents WHERE id = ?");
|
|
1403
1403
|
const result = stmt.run(id);
|
|
1404
|
+
try {
|
|
1405
|
+
this.db.prepare("DELETE FROM agent_memory WHERE agent_id = ?").run(id);
|
|
1406
|
+
} catch {
|
|
1407
|
+
}
|
|
1404
1408
|
return result.changes > 0;
|
|
1405
1409
|
}
|
|
1406
1410
|
/**
|
|
@@ -9289,10 +9293,890 @@ function parse(raw) {
|
|
|
9289
9293
|
}
|
|
9290
9294
|
return out;
|
|
9291
9295
|
}
|
|
9296
|
+
|
|
9297
|
+
// src/memory/manager.ts
|
|
9298
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
9299
|
+
|
|
9300
|
+
// src/memory/text-search.ts
|
|
9301
|
+
var BM25_K1 = 1.2;
|
|
9302
|
+
var BM25_B = 0.75;
|
|
9303
|
+
var FIELD_WEIGHT_TITLE = 3;
|
|
9304
|
+
var FIELD_WEIGHT_TAGS = 2;
|
|
9305
|
+
var FIELD_WEIGHT_CONTENT = 1;
|
|
9306
|
+
var PREFIX_MATCH_PENALTY = 0.7;
|
|
9307
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
9308
|
+
"a",
|
|
9309
|
+
"about",
|
|
9310
|
+
"above",
|
|
9311
|
+
"after",
|
|
9312
|
+
"again",
|
|
9313
|
+
"against",
|
|
9314
|
+
"all",
|
|
9315
|
+
"am",
|
|
9316
|
+
"an",
|
|
9317
|
+
"and",
|
|
9318
|
+
"any",
|
|
9319
|
+
"are",
|
|
9320
|
+
"as",
|
|
9321
|
+
"at",
|
|
9322
|
+
"be",
|
|
9323
|
+
"because",
|
|
9324
|
+
"been",
|
|
9325
|
+
"before",
|
|
9326
|
+
"being",
|
|
9327
|
+
"below",
|
|
9328
|
+
"between",
|
|
9329
|
+
"both",
|
|
9330
|
+
"but",
|
|
9331
|
+
"by",
|
|
9332
|
+
"can",
|
|
9333
|
+
"could",
|
|
9334
|
+
"did",
|
|
9335
|
+
"do",
|
|
9336
|
+
"does",
|
|
9337
|
+
"doing",
|
|
9338
|
+
"down",
|
|
9339
|
+
"during",
|
|
9340
|
+
"each",
|
|
9341
|
+
"either",
|
|
9342
|
+
"every",
|
|
9343
|
+
"few",
|
|
9344
|
+
"for",
|
|
9345
|
+
"from",
|
|
9346
|
+
"further",
|
|
9347
|
+
"get",
|
|
9348
|
+
"got",
|
|
9349
|
+
"had",
|
|
9350
|
+
"has",
|
|
9351
|
+
"have",
|
|
9352
|
+
"having",
|
|
9353
|
+
"he",
|
|
9354
|
+
"her",
|
|
9355
|
+
"here",
|
|
9356
|
+
"hers",
|
|
9357
|
+
"herself",
|
|
9358
|
+
"him",
|
|
9359
|
+
"himself",
|
|
9360
|
+
"his",
|
|
9361
|
+
"how",
|
|
9362
|
+
"i",
|
|
9363
|
+
"if",
|
|
9364
|
+
"in",
|
|
9365
|
+
"into",
|
|
9366
|
+
"is",
|
|
9367
|
+
"it",
|
|
9368
|
+
"its",
|
|
9369
|
+
"itself",
|
|
9370
|
+
"just",
|
|
9371
|
+
"may",
|
|
9372
|
+
"me",
|
|
9373
|
+
"might",
|
|
9374
|
+
"more",
|
|
9375
|
+
"most",
|
|
9376
|
+
"must",
|
|
9377
|
+
"my",
|
|
9378
|
+
"myself",
|
|
9379
|
+
"neither",
|
|
9380
|
+
"no",
|
|
9381
|
+
"nor",
|
|
9382
|
+
"not",
|
|
9383
|
+
"now",
|
|
9384
|
+
"of",
|
|
9385
|
+
"off",
|
|
9386
|
+
"on",
|
|
9387
|
+
"once",
|
|
9388
|
+
"only",
|
|
9389
|
+
"or",
|
|
9390
|
+
"other",
|
|
9391
|
+
"ought",
|
|
9392
|
+
"our",
|
|
9393
|
+
"ours",
|
|
9394
|
+
"ourselves",
|
|
9395
|
+
"out",
|
|
9396
|
+
"over",
|
|
9397
|
+
"own",
|
|
9398
|
+
"same",
|
|
9399
|
+
"shall",
|
|
9400
|
+
"she",
|
|
9401
|
+
"should",
|
|
9402
|
+
"so",
|
|
9403
|
+
"some",
|
|
9404
|
+
"such",
|
|
9405
|
+
"than",
|
|
9406
|
+
"that",
|
|
9407
|
+
"the",
|
|
9408
|
+
"their",
|
|
9409
|
+
"theirs",
|
|
9410
|
+
"them",
|
|
9411
|
+
"themselves",
|
|
9412
|
+
"then",
|
|
9413
|
+
"there",
|
|
9414
|
+
"these",
|
|
9415
|
+
"they",
|
|
9416
|
+
"this",
|
|
9417
|
+
"those",
|
|
9418
|
+
"through",
|
|
9419
|
+
"to",
|
|
9420
|
+
"too",
|
|
9421
|
+
"under",
|
|
9422
|
+
"until",
|
|
9423
|
+
"up",
|
|
9424
|
+
"us",
|
|
9425
|
+
"very",
|
|
9426
|
+
"was",
|
|
9427
|
+
"we",
|
|
9428
|
+
"were",
|
|
9429
|
+
"what",
|
|
9430
|
+
"when",
|
|
9431
|
+
"where",
|
|
9432
|
+
"which",
|
|
9433
|
+
"while",
|
|
9434
|
+
"who",
|
|
9435
|
+
"whom",
|
|
9436
|
+
"why",
|
|
9437
|
+
"will",
|
|
9438
|
+
"with",
|
|
9439
|
+
"would",
|
|
9440
|
+
"yet",
|
|
9441
|
+
"you",
|
|
9442
|
+
"your",
|
|
9443
|
+
"yours",
|
|
9444
|
+
"yourself",
|
|
9445
|
+
"yourselves"
|
|
9446
|
+
]);
|
|
9447
|
+
var STEM_RULES = [
|
|
9448
|
+
// Step 1: plurals and past participles
|
|
9449
|
+
[/ies$/, "i", 3],
|
|
9450
|
+
// policies → polici,eries → eri
|
|
9451
|
+
[/sses$/, "ss", 4],
|
|
9452
|
+
// addresses → address
|
|
9453
|
+
[/([^s])s$/, "$1", 3],
|
|
9454
|
+
// items → item, but not "ss"
|
|
9455
|
+
[/eed$/, "ee", 4],
|
|
9456
|
+
// agreed → agree
|
|
9457
|
+
[/ed$/, "", 3],
|
|
9458
|
+
// configured → configur, but min length 3
|
|
9459
|
+
[/ing$/, "", 4],
|
|
9460
|
+
// running → runn → run (handled below)
|
|
9461
|
+
// Step 2: derivational suffixes
|
|
9462
|
+
[/ational$/, "ate", 6],
|
|
9463
|
+
// relational → relate
|
|
9464
|
+
[/tion$/, "t", 5],
|
|
9465
|
+
// adoption → adopt
|
|
9466
|
+
[/ness$/, "", 5],
|
|
9467
|
+
// awareness → aware
|
|
9468
|
+
[/ment$/, "", 5],
|
|
9469
|
+
// deployment → deploy
|
|
9470
|
+
[/able$/, "", 5],
|
|
9471
|
+
// configurable → configur
|
|
9472
|
+
[/ible$/, "", 5],
|
|
9473
|
+
// accessible → access
|
|
9474
|
+
[/ful$/, "", 5],
|
|
9475
|
+
// powerful → power
|
|
9476
|
+
[/ous$/, "", 5],
|
|
9477
|
+
// dangerous → danger
|
|
9478
|
+
[/ive$/, "", 5],
|
|
9479
|
+
// interactive → interact
|
|
9480
|
+
[/ize$/, "", 4],
|
|
9481
|
+
// normalize → normal
|
|
9482
|
+
[/ise$/, "", 4],
|
|
9483
|
+
// organise → organ
|
|
9484
|
+
[/ally$/, "", 5],
|
|
9485
|
+
// automatically → automat
|
|
9486
|
+
[/ly$/, "", 4],
|
|
9487
|
+
// quickly → quick
|
|
9488
|
+
[/er$/, "", 4]
|
|
9489
|
+
// handler → handl
|
|
9490
|
+
];
|
|
9491
|
+
var DOUBLE_CONSONANT = /([^aeiou])\1$/;
|
|
9492
|
+
function stem(word) {
|
|
9493
|
+
if (word.length < 3) return word;
|
|
9494
|
+
let stemmed = word;
|
|
9495
|
+
for (const [pattern, replacement, minLen] of STEM_RULES) {
|
|
9496
|
+
if (stemmed.length >= minLen && pattern.test(stemmed)) {
|
|
9497
|
+
stemmed = stemmed.replace(pattern, replacement);
|
|
9498
|
+
break;
|
|
9499
|
+
}
|
|
9500
|
+
}
|
|
9501
|
+
if (stemmed.length > 2 && DOUBLE_CONSONANT.test(stemmed)) {
|
|
9502
|
+
stemmed = stemmed.slice(0, -1);
|
|
9503
|
+
}
|
|
9504
|
+
return stemmed;
|
|
9505
|
+
}
|
|
9506
|
+
function tokenize(text) {
|
|
9507
|
+
return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1 && !STOP_WORDS.has(t)).map(stem);
|
|
9508
|
+
}
|
|
9509
|
+
var MemorySearchIndex = class {
|
|
9510
|
+
/** Posting lists: stemmed term → Set of memory IDs containing it */
|
|
9511
|
+
postings = /* @__PURE__ */ new Map();
|
|
9512
|
+
/** Per-document metadata for BM25 scoring */
|
|
9513
|
+
docs = /* @__PURE__ */ new Map();
|
|
9514
|
+
/** Pre-computed IDF values. Stale flag triggers lazy recomputation. */
|
|
9515
|
+
idf = /* @__PURE__ */ new Map();
|
|
9516
|
+
idfStale = true;
|
|
9517
|
+
/** 3-character prefix map for prefix matching: prefix → Set of full stems */
|
|
9518
|
+
prefixMap = /* @__PURE__ */ new Map();
|
|
9519
|
+
/** Total weighted document length (for computing average) */
|
|
9520
|
+
totalWeightedLen = 0;
|
|
9521
|
+
get docCount() {
|
|
9522
|
+
return this.docs.size;
|
|
9523
|
+
}
|
|
9524
|
+
get avgDocLen() {
|
|
9525
|
+
return this.docs.size > 0 ? this.totalWeightedLen / this.docs.size : 1;
|
|
9526
|
+
}
|
|
9527
|
+
/**
|
|
9528
|
+
* Index a memory entry. Extracts stems from title, content, and tags
|
|
9529
|
+
* with field-specific weighting and builds posting lists.
|
|
9530
|
+
*/
|
|
9531
|
+
addDocument(id, entry) {
|
|
9532
|
+
if (this.docs.has(id)) this.removeDocument(id);
|
|
9533
|
+
const titleTokens = tokenize(entry.title);
|
|
9534
|
+
const contentTokens = tokenize(entry.content);
|
|
9535
|
+
const tagTokens = entry.tags.flatMap((t) => tokenize(t));
|
|
9536
|
+
const weightedTf = /* @__PURE__ */ new Map();
|
|
9537
|
+
for (const t of titleTokens) weightedTf.set(t, (weightedTf.get(t) || 0) + FIELD_WEIGHT_TITLE);
|
|
9538
|
+
for (const t of tagTokens) weightedTf.set(t, (weightedTf.get(t) || 0) + FIELD_WEIGHT_TAGS);
|
|
9539
|
+
for (const t of contentTokens) weightedTf.set(t, (weightedTf.get(t) || 0) + FIELD_WEIGHT_CONTENT);
|
|
9540
|
+
const weightedLen = titleTokens.length * FIELD_WEIGHT_TITLE + tagTokens.length * FIELD_WEIGHT_TAGS + contentTokens.length * FIELD_WEIGHT_CONTENT;
|
|
9541
|
+
const allStems = /* @__PURE__ */ new Set();
|
|
9542
|
+
for (const t of weightedTf.keys()) allStems.add(t);
|
|
9543
|
+
const stemSequence = [...titleTokens, ...contentTokens];
|
|
9544
|
+
const docRecord = { weightedTf, weightedLen, allStems, stemSequence };
|
|
9545
|
+
this.docs.set(id, docRecord);
|
|
9546
|
+
this.totalWeightedLen += weightedLen;
|
|
9547
|
+
for (const term of allStems) {
|
|
9548
|
+
let posting = this.postings.get(term);
|
|
9549
|
+
if (!posting) {
|
|
9550
|
+
posting = /* @__PURE__ */ new Set();
|
|
9551
|
+
this.postings.set(term, posting);
|
|
9552
|
+
}
|
|
9553
|
+
posting.add(id);
|
|
9554
|
+
if (term.length >= 3) {
|
|
9555
|
+
const prefix = term.slice(0, 3);
|
|
9556
|
+
let prefixSet = this.prefixMap.get(prefix);
|
|
9557
|
+
if (!prefixSet) {
|
|
9558
|
+
prefixSet = /* @__PURE__ */ new Set();
|
|
9559
|
+
this.prefixMap.set(prefix, prefixSet);
|
|
9560
|
+
}
|
|
9561
|
+
prefixSet.add(term);
|
|
9562
|
+
}
|
|
9563
|
+
}
|
|
9564
|
+
this.idfStale = true;
|
|
9565
|
+
}
|
|
9566
|
+
/** Remove a document from the index. */
|
|
9567
|
+
removeDocument(id) {
|
|
9568
|
+
const doc = this.docs.get(id);
|
|
9569
|
+
if (!doc) return;
|
|
9570
|
+
this.totalWeightedLen -= doc.weightedLen;
|
|
9571
|
+
this.docs.delete(id);
|
|
9572
|
+
for (const term of doc.allStems) {
|
|
9573
|
+
const posting = this.postings.get(term);
|
|
9574
|
+
if (posting) {
|
|
9575
|
+
posting.delete(id);
|
|
9576
|
+
if (posting.size === 0) {
|
|
9577
|
+
this.postings.delete(term);
|
|
9578
|
+
if (term.length >= 3) {
|
|
9579
|
+
const prefixSet = this.prefixMap.get(term.slice(0, 3));
|
|
9580
|
+
if (prefixSet) {
|
|
9581
|
+
prefixSet.delete(term);
|
|
9582
|
+
if (prefixSet.size === 0) this.prefixMap.delete(term.slice(0, 3));
|
|
9583
|
+
}
|
|
9584
|
+
}
|
|
9585
|
+
}
|
|
9586
|
+
}
|
|
9587
|
+
}
|
|
9588
|
+
this.idfStale = true;
|
|
9589
|
+
}
|
|
9590
|
+
/** Recompute IDF values for all terms. Called lazily before search. */
|
|
9591
|
+
refreshIdf() {
|
|
9592
|
+
if (!this.idfStale) return;
|
|
9593
|
+
const N = this.docs.size;
|
|
9594
|
+
this.idf.clear();
|
|
9595
|
+
for (const [term, posting] of this.postings) {
|
|
9596
|
+
const df = posting.size;
|
|
9597
|
+
this.idf.set(term, Math.log((N - df + 0.5) / (df + 0.5) + 1));
|
|
9598
|
+
}
|
|
9599
|
+
this.idfStale = false;
|
|
9600
|
+
}
|
|
9601
|
+
/**
|
|
9602
|
+
* Expand query terms with prefix matches.
|
|
9603
|
+
* "deploy" → ["deploy", "deployment", "deploying", ...] (if they exist in the index)
|
|
9604
|
+
*/
|
|
9605
|
+
expandQueryTerms(queryStems) {
|
|
9606
|
+
const expanded = /* @__PURE__ */ new Map();
|
|
9607
|
+
for (const qs of queryStems) {
|
|
9608
|
+
if (this.postings.has(qs)) {
|
|
9609
|
+
expanded.set(qs, Math.max(expanded.get(qs) || 0, 1));
|
|
9610
|
+
}
|
|
9611
|
+
if (qs.length >= 3) {
|
|
9612
|
+
const prefix = qs.slice(0, 3);
|
|
9613
|
+
const candidates = this.prefixMap.get(prefix);
|
|
9614
|
+
if (candidates) {
|
|
9615
|
+
for (const candidate of candidates) {
|
|
9616
|
+
if (candidate !== qs && candidate.startsWith(qs)) {
|
|
9617
|
+
expanded.set(candidate, Math.max(expanded.get(candidate) || 0, PREFIX_MATCH_PENALTY));
|
|
9618
|
+
}
|
|
9619
|
+
}
|
|
9620
|
+
}
|
|
9621
|
+
}
|
|
9622
|
+
}
|
|
9623
|
+
return expanded;
|
|
9624
|
+
}
|
|
9625
|
+
/**
|
|
9626
|
+
* Compute bigram proximity boost: if two query terms appear adjacent
|
|
9627
|
+
* in the document's stem sequence, boost the score.
|
|
9628
|
+
*/
|
|
9629
|
+
bigramProximityBoost(docId, queryStems) {
|
|
9630
|
+
if (queryStems.length < 2) return 0;
|
|
9631
|
+
const doc = this.docs.get(docId);
|
|
9632
|
+
if (!doc || doc.stemSequence.length < 2) return 0;
|
|
9633
|
+
let boost = 0;
|
|
9634
|
+
const seq = doc.stemSequence;
|
|
9635
|
+
const querySet = new Set(queryStems);
|
|
9636
|
+
for (let i = 0; i < seq.length - 1; i++) {
|
|
9637
|
+
if (querySet.has(seq[i]) && querySet.has(seq[i + 1]) && seq[i] !== seq[i + 1]) {
|
|
9638
|
+
boost += 0.5;
|
|
9639
|
+
}
|
|
9640
|
+
}
|
|
9641
|
+
return Math.min(boost, 2);
|
|
9642
|
+
}
|
|
9643
|
+
/**
|
|
9644
|
+
* Search the index for documents matching a query.
|
|
9645
|
+
* Returns scored results sorted by BM25F relevance.
|
|
9646
|
+
*
|
|
9647
|
+
* @param query - Raw query string
|
|
9648
|
+
* @param candidateIds - Optional: only score these document IDs (for agent-scoped search)
|
|
9649
|
+
* @returns Array of { id, score } sorted by descending score
|
|
9650
|
+
*/
|
|
9651
|
+
search(query, candidateIds) {
|
|
9652
|
+
const queryStems = tokenize(query);
|
|
9653
|
+
if (queryStems.length === 0) return [];
|
|
9654
|
+
this.refreshIdf();
|
|
9655
|
+
const expandedTerms = this.expandQueryTerms(queryStems);
|
|
9656
|
+
if (expandedTerms.size === 0) return [];
|
|
9657
|
+
const avgDl = this.avgDocLen;
|
|
9658
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
9659
|
+
for (const term of expandedTerms.keys()) {
|
|
9660
|
+
const posting = this.postings.get(term);
|
|
9661
|
+
if (posting) {
|
|
9662
|
+
for (const docId of posting) {
|
|
9663
|
+
if (!candidateIds || candidateIds.has(docId)) candidates.add(docId);
|
|
9664
|
+
}
|
|
9665
|
+
}
|
|
9666
|
+
}
|
|
9667
|
+
const results = [];
|
|
9668
|
+
for (const docId of candidates) {
|
|
9669
|
+
const doc = this.docs.get(docId);
|
|
9670
|
+
if (!doc) continue;
|
|
9671
|
+
let score = 0;
|
|
9672
|
+
for (const [term, weight] of expandedTerms) {
|
|
9673
|
+
const tf = doc.weightedTf.get(term) || 0;
|
|
9674
|
+
if (tf === 0) continue;
|
|
9675
|
+
const termIdf = this.idf.get(term) || 0;
|
|
9676
|
+
const numerator = tf * (BM25_K1 + 1);
|
|
9677
|
+
const denominator = tf + BM25_K1 * (1 - BM25_B + BM25_B * (doc.weightedLen / avgDl));
|
|
9678
|
+
score += termIdf * (numerator / denominator) * weight;
|
|
9679
|
+
}
|
|
9680
|
+
score += this.bigramProximityBoost(docId, queryStems);
|
|
9681
|
+
if (score > 0) results.push({ id: docId, score });
|
|
9682
|
+
}
|
|
9683
|
+
results.sort((a, b) => b.score - a.score);
|
|
9684
|
+
return results;
|
|
9685
|
+
}
|
|
9686
|
+
/** Check if a document exists in the index. */
|
|
9687
|
+
has(id) {
|
|
9688
|
+
return this.docs.has(id);
|
|
9689
|
+
}
|
|
9690
|
+
};
|
|
9691
|
+
|
|
9692
|
+
// src/memory/manager.ts
|
|
9693
|
+
function sj(v, fb = {}) {
|
|
9694
|
+
if (!v) return fb;
|
|
9695
|
+
try {
|
|
9696
|
+
return JSON.parse(v);
|
|
9697
|
+
} catch {
|
|
9698
|
+
return fb;
|
|
9699
|
+
}
|
|
9700
|
+
}
|
|
9701
|
+
var MEMORY_CATEGORIES = {
|
|
9702
|
+
knowledge: {
|
|
9703
|
+
label: "Knowledge",
|
|
9704
|
+
description: "Facts, procedures, and reference information the agent has learned"
|
|
9705
|
+
},
|
|
9706
|
+
interaction_pattern: {
|
|
9707
|
+
label: "Interaction Patterns",
|
|
9708
|
+
description: "Learned patterns from past interactions"
|
|
9709
|
+
},
|
|
9710
|
+
preference: {
|
|
9711
|
+
label: "Preferences",
|
|
9712
|
+
description: "User and counterparty preferences"
|
|
9713
|
+
},
|
|
9714
|
+
correction: {
|
|
9715
|
+
label: "Corrections",
|
|
9716
|
+
description: "Corrections and feedback received"
|
|
9717
|
+
},
|
|
9718
|
+
skill: {
|
|
9719
|
+
label: "Skills",
|
|
9720
|
+
description: "Learned abilities and competencies"
|
|
9721
|
+
},
|
|
9722
|
+
context: {
|
|
9723
|
+
label: "Context",
|
|
9724
|
+
description: "Contextual information and background knowledge"
|
|
9725
|
+
},
|
|
9726
|
+
reflection: {
|
|
9727
|
+
label: "Reflections",
|
|
9728
|
+
description: "Self-reflective insights and learnings"
|
|
9729
|
+
},
|
|
9730
|
+
session_learning: {
|
|
9731
|
+
label: "Session Learnings",
|
|
9732
|
+
description: "Insights captured during conversation sessions"
|
|
9733
|
+
},
|
|
9734
|
+
system_notice: {
|
|
9735
|
+
label: "System Notices",
|
|
9736
|
+
description: "System-generated notifications about configuration changes"
|
|
9737
|
+
}
|
|
9738
|
+
};
|
|
9739
|
+
var VALID_CATEGORIES = new Set(Object.keys(MEMORY_CATEGORIES));
|
|
9740
|
+
var VALID_IMPORTANCE = /* @__PURE__ */ new Set(["critical", "high", "normal", "low"]);
|
|
9741
|
+
var IMPORTANCE_WEIGHT = {
|
|
9742
|
+
critical: 4,
|
|
9743
|
+
high: 3,
|
|
9744
|
+
normal: 2,
|
|
9745
|
+
low: 1
|
|
9746
|
+
};
|
|
9747
|
+
var AgentMemoryManager = class {
|
|
9748
|
+
constructor(db2) {
|
|
9749
|
+
this.db = db2;
|
|
9750
|
+
this.ensureTable();
|
|
9751
|
+
this.loadFromDb();
|
|
9752
|
+
}
|
|
9753
|
+
memories = /* @__PURE__ */ new Map();
|
|
9754
|
+
/** Per-agent index: agentId → Set of memory IDs for O(1) agent lookups */
|
|
9755
|
+
agentIndex = /* @__PURE__ */ new Map();
|
|
9756
|
+
/** Full-text search index (BM25F + stemming + inverted index) */
|
|
9757
|
+
searchIndex = new MemorySearchIndex();
|
|
9758
|
+
initialized = false;
|
|
9759
|
+
// ─── Database layer ─────────────────────────────────
|
|
9760
|
+
ensureTable() {
|
|
9761
|
+
if (this.initialized) return;
|
|
9762
|
+
this.db.exec(`
|
|
9763
|
+
CREATE TABLE IF NOT EXISTS agent_memory (
|
|
9764
|
+
id TEXT PRIMARY KEY,
|
|
9765
|
+
agent_id TEXT NOT NULL,
|
|
9766
|
+
category TEXT NOT NULL,
|
|
9767
|
+
title TEXT NOT NULL,
|
|
9768
|
+
content TEXT NOT NULL,
|
|
9769
|
+
source TEXT NOT NULL DEFAULT 'interaction',
|
|
9770
|
+
importance TEXT NOT NULL DEFAULT 'normal',
|
|
9771
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
9772
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
9773
|
+
last_accessed_at TEXT,
|
|
9774
|
+
expires_at TEXT,
|
|
9775
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
9776
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
9777
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
9778
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
9779
|
+
)
|
|
9780
|
+
`);
|
|
9781
|
+
try {
|
|
9782
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_agent_memory_agent ON agent_memory(agent_id)");
|
|
9783
|
+
} catch {
|
|
9784
|
+
}
|
|
9785
|
+
try {
|
|
9786
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_agent_memory_category ON agent_memory(category)");
|
|
9787
|
+
} catch {
|
|
9788
|
+
}
|
|
9789
|
+
this.initialized = true;
|
|
9790
|
+
}
|
|
9791
|
+
/** Run a write statement, swallowing errors with a log (memory must never crash a caller). */
|
|
9792
|
+
dbRun(sql, params) {
|
|
9793
|
+
try {
|
|
9794
|
+
this.db.prepare(sql).run(...params);
|
|
9795
|
+
} catch (err) {
|
|
9796
|
+
console.error("[agent-memory] DB write failed:", err.message);
|
|
9797
|
+
}
|
|
9798
|
+
}
|
|
9799
|
+
dbAll(sql, params = []) {
|
|
9800
|
+
try {
|
|
9801
|
+
return this.db.prepare(sql).all(...params);
|
|
9802
|
+
} catch (err) {
|
|
9803
|
+
console.error("[agent-memory] DB read failed:", err.message);
|
|
9804
|
+
return [];
|
|
9805
|
+
}
|
|
9806
|
+
}
|
|
9807
|
+
loadFromDb() {
|
|
9808
|
+
const rows = this.dbAll("SELECT * FROM agent_memory");
|
|
9809
|
+
for (const r of rows) {
|
|
9810
|
+
try {
|
|
9811
|
+
const entry = this.rowToEntry(r);
|
|
9812
|
+
this.memories.set(entry.id, entry);
|
|
9813
|
+
this.indexAdd(entry.agentId, entry.id);
|
|
9814
|
+
this.searchIndex.addDocument(entry.id, entry);
|
|
9815
|
+
} catch {
|
|
9816
|
+
}
|
|
9817
|
+
}
|
|
9818
|
+
}
|
|
9819
|
+
/** Add a memory ID to the per-agent index. */
|
|
9820
|
+
indexAdd(agentId, memoryId) {
|
|
9821
|
+
let set = this.agentIndex.get(agentId);
|
|
9822
|
+
if (!set) {
|
|
9823
|
+
set = /* @__PURE__ */ new Set();
|
|
9824
|
+
this.agentIndex.set(agentId, set);
|
|
9825
|
+
}
|
|
9826
|
+
set.add(memoryId);
|
|
9827
|
+
}
|
|
9828
|
+
/** Remove a memory ID from the per-agent index. */
|
|
9829
|
+
indexRemove(agentId, memoryId) {
|
|
9830
|
+
const set = this.agentIndex.get(agentId);
|
|
9831
|
+
if (set) {
|
|
9832
|
+
set.delete(memoryId);
|
|
9833
|
+
if (set.size === 0) this.agentIndex.delete(agentId);
|
|
9834
|
+
}
|
|
9835
|
+
}
|
|
9836
|
+
/** Get all memory entries for an agent via the index. */
|
|
9837
|
+
getAgentMemories(agentId) {
|
|
9838
|
+
const ids = this.agentIndex.get(agentId);
|
|
9839
|
+
if (!ids || ids.size === 0) return [];
|
|
9840
|
+
const result = [];
|
|
9841
|
+
for (const id of ids) {
|
|
9842
|
+
const entry = this.memories.get(id);
|
|
9843
|
+
if (entry) result.push(entry);
|
|
9844
|
+
}
|
|
9845
|
+
return result;
|
|
9846
|
+
}
|
|
9847
|
+
// ─── Convenience Methods ─────────────────────────────
|
|
9848
|
+
/** Store a memory with minimal input — the common "just remember this" case. */
|
|
9849
|
+
async storeMemory(agentId, opts) {
|
|
9850
|
+
const category = opts.category && VALID_CATEGORIES.has(opts.category) ? opts.category : "context";
|
|
9851
|
+
const importance = opts.importance && VALID_IMPORTANCE.has(opts.importance) ? opts.importance : "normal";
|
|
9852
|
+
return this.createMemory({
|
|
9853
|
+
agentId,
|
|
9854
|
+
content: opts.content,
|
|
9855
|
+
category,
|
|
9856
|
+
importance,
|
|
9857
|
+
confidence: opts.confidence ?? 1,
|
|
9858
|
+
title: opts.title || opts.content.slice(0, 80),
|
|
9859
|
+
source: "system",
|
|
9860
|
+
tags: opts.tags ?? [],
|
|
9861
|
+
metadata: {}
|
|
9862
|
+
});
|
|
9863
|
+
}
|
|
9864
|
+
/** Search memories by text query, sorted by relevance. */
|
|
9865
|
+
async recall(agentId, query, limit = 5) {
|
|
9866
|
+
return this.queryMemories({ agentId, query, limit });
|
|
9867
|
+
}
|
|
9868
|
+
// ─── CRUD Operations ────────────────────────────────
|
|
9869
|
+
/** Create a new memory entry with auto-generated id + timestamps. */
|
|
9870
|
+
async createMemory(input) {
|
|
9871
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9872
|
+
const entry = {
|
|
9873
|
+
...input,
|
|
9874
|
+
confidence: input.confidence ?? 0.8,
|
|
9875
|
+
tags: input.tags ?? [],
|
|
9876
|
+
metadata: input.metadata ?? {},
|
|
9877
|
+
id: randomUUID3(),
|
|
9878
|
+
accessCount: 0,
|
|
9879
|
+
createdAt: now,
|
|
9880
|
+
updatedAt: now
|
|
9881
|
+
};
|
|
9882
|
+
this.memories.set(entry.id, entry);
|
|
9883
|
+
this.indexAdd(entry.agentId, entry.id);
|
|
9884
|
+
this.searchIndex.addDocument(entry.id, entry);
|
|
9885
|
+
this.dbRun(
|
|
9886
|
+
`INSERT INTO agent_memory (id, agent_id, category, title, content, source, importance, confidence, access_count, last_accessed_at, expires_at, tags, metadata, created_at, updated_at)
|
|
9887
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
9888
|
+
[
|
|
9889
|
+
entry.id,
|
|
9890
|
+
entry.agentId,
|
|
9891
|
+
entry.category,
|
|
9892
|
+
entry.title,
|
|
9893
|
+
entry.content,
|
|
9894
|
+
entry.source,
|
|
9895
|
+
entry.importance,
|
|
9896
|
+
entry.confidence,
|
|
9897
|
+
entry.accessCount,
|
|
9898
|
+
entry.lastAccessedAt || null,
|
|
9899
|
+
entry.expiresAt || null,
|
|
9900
|
+
JSON.stringify(entry.tags),
|
|
9901
|
+
JSON.stringify(entry.metadata),
|
|
9902
|
+
entry.createdAt,
|
|
9903
|
+
entry.updatedAt
|
|
9904
|
+
]
|
|
9905
|
+
);
|
|
9906
|
+
return entry;
|
|
9907
|
+
}
|
|
9908
|
+
/** Update an existing memory entry by merging provided fields. */
|
|
9909
|
+
async updateMemory(id, updates) {
|
|
9910
|
+
const existing = this.memories.get(id);
|
|
9911
|
+
if (!existing) return null;
|
|
9912
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9913
|
+
const updated = {
|
|
9914
|
+
...existing,
|
|
9915
|
+
...updates,
|
|
9916
|
+
id: existing.id,
|
|
9917
|
+
agentId: existing.agentId,
|
|
9918
|
+
createdAt: existing.createdAt,
|
|
9919
|
+
updatedAt: now
|
|
9920
|
+
};
|
|
9921
|
+
this.memories.set(id, updated);
|
|
9922
|
+
if (updates.title !== void 0 || updates.content !== void 0 || updates.tags !== void 0) {
|
|
9923
|
+
this.searchIndex.addDocument(id, updated);
|
|
9924
|
+
}
|
|
9925
|
+
this.dbRun(
|
|
9926
|
+
`UPDATE agent_memory SET
|
|
9927
|
+
category = ?, title = ?, content = ?, source = ?,
|
|
9928
|
+
importance = ?, confidence = ?, access_count = ?,
|
|
9929
|
+
last_accessed_at = ?, expires_at = ?, tags = ?,
|
|
9930
|
+
metadata = ?, updated_at = ?
|
|
9931
|
+
WHERE id = ?`,
|
|
9932
|
+
[
|
|
9933
|
+
updated.category,
|
|
9934
|
+
updated.title,
|
|
9935
|
+
updated.content,
|
|
9936
|
+
updated.source,
|
|
9937
|
+
updated.importance,
|
|
9938
|
+
updated.confidence,
|
|
9939
|
+
updated.accessCount,
|
|
9940
|
+
updated.lastAccessedAt || null,
|
|
9941
|
+
updated.expiresAt || null,
|
|
9942
|
+
JSON.stringify(updated.tags),
|
|
9943
|
+
JSON.stringify(updated.metadata),
|
|
9944
|
+
updated.updatedAt,
|
|
9945
|
+
id
|
|
9946
|
+
]
|
|
9947
|
+
);
|
|
9948
|
+
return updated;
|
|
9949
|
+
}
|
|
9950
|
+
/** Delete a single memory entry. Returns true if it existed. */
|
|
9951
|
+
async deleteMemory(id) {
|
|
9952
|
+
const entry = this.memories.get(id);
|
|
9953
|
+
const existed = this.memories.delete(id);
|
|
9954
|
+
if (entry) this.indexRemove(entry.agentId, id);
|
|
9955
|
+
this.searchIndex.removeDocument(id);
|
|
9956
|
+
this.dbRun("DELETE FROM agent_memory WHERE id = ?", [id]);
|
|
9957
|
+
return existed;
|
|
9958
|
+
}
|
|
9959
|
+
/**
|
|
9960
|
+
* Purge every memory entry belonging to an agent — Map, per-agent
|
|
9961
|
+
* index, search index, and the database row. Called when an agent is
|
|
9962
|
+
* deleted so no orphaned memory is left behind.
|
|
9963
|
+
* Returns the number of entries removed.
|
|
9964
|
+
*/
|
|
9965
|
+
async deleteAgentMemories(agentId) {
|
|
9966
|
+
const ids = Array.from(this.agentIndex.get(agentId) ?? []);
|
|
9967
|
+
for (const id of ids) {
|
|
9968
|
+
this.memories.delete(id);
|
|
9969
|
+
this.searchIndex.removeDocument(id);
|
|
9970
|
+
}
|
|
9971
|
+
this.agentIndex.delete(agentId);
|
|
9972
|
+
this.dbRun("DELETE FROM agent_memory WHERE agent_id = ?", [agentId]);
|
|
9973
|
+
return ids.length;
|
|
9974
|
+
}
|
|
9975
|
+
/** Retrieve a single memory entry by id. */
|
|
9976
|
+
async getMemory(id) {
|
|
9977
|
+
return this.memories.get(id);
|
|
9978
|
+
}
|
|
9979
|
+
// ─── Query Operations ───────────────────────────────
|
|
9980
|
+
/** Query an agent's memory with optional category/importance/source filters + text search. */
|
|
9981
|
+
async queryMemories(opts) {
|
|
9982
|
+
let results = this.getAgentMemories(opts.agentId);
|
|
9983
|
+
if (opts.category) results = results.filter((m) => m.category === opts.category);
|
|
9984
|
+
if (opts.importance) results = results.filter((m) => m.importance === opts.importance);
|
|
9985
|
+
if (opts.source) results = results.filter((m) => m.source === opts.source);
|
|
9986
|
+
if (opts.query) {
|
|
9987
|
+
const candidateIds = new Set(results.map((m) => m.id));
|
|
9988
|
+
const searchResults = this.searchIndex.search(opts.query, candidateIds);
|
|
9989
|
+
if (searchResults.length > 0) {
|
|
9990
|
+
const scored = searchResults.map((r) => {
|
|
9991
|
+
const entry = this.memories.get(r.id);
|
|
9992
|
+
return entry ? { entry, score: r.score * IMPORTANCE_WEIGHT[entry.importance] } : null;
|
|
9993
|
+
}).filter((r) => r !== null);
|
|
9994
|
+
scored.sort((a, b) => b.score - a.score);
|
|
9995
|
+
return scored.slice(0, opts.limit || 100).map((d) => d.entry);
|
|
9996
|
+
}
|
|
9997
|
+
return [];
|
|
9998
|
+
}
|
|
9999
|
+
results.sort((a, b) => {
|
|
10000
|
+
const weightDiff = IMPORTANCE_WEIGHT[b.importance] - IMPORTANCE_WEIGHT[a.importance];
|
|
10001
|
+
if (weightDiff !== 0) return weightDiff;
|
|
10002
|
+
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
10003
|
+
});
|
|
10004
|
+
return results.slice(0, opts.limit || 100);
|
|
10005
|
+
}
|
|
10006
|
+
/** Memories created within the last N hours for an agent. */
|
|
10007
|
+
async getRecentMemories(agentId, hours = 24) {
|
|
10008
|
+
const cutoff = new Date(Date.now() - hours * 36e5).toISOString();
|
|
10009
|
+
return this.getAgentMemories(agentId).filter((m) => m.createdAt >= cutoff).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
10010
|
+
}
|
|
10011
|
+
// ─── Access Tracking ────────────────────────────────
|
|
10012
|
+
/** Bump access count + lastAccessedAt for a memory entry. */
|
|
10013
|
+
async recordAccess(memoryId) {
|
|
10014
|
+
const entry = this.memories.get(memoryId);
|
|
10015
|
+
if (!entry) return;
|
|
10016
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10017
|
+
entry.accessCount += 1;
|
|
10018
|
+
entry.lastAccessedAt = now;
|
|
10019
|
+
entry.updatedAt = now;
|
|
10020
|
+
this.dbRun(
|
|
10021
|
+
"UPDATE agent_memory SET access_count = ?, last_accessed_at = ?, updated_at = ? WHERE id = ?",
|
|
10022
|
+
[entry.accessCount, entry.lastAccessedAt, entry.updatedAt, memoryId]
|
|
10023
|
+
);
|
|
10024
|
+
}
|
|
10025
|
+
// ─── Context Generation ─────────────────────────────
|
|
10026
|
+
/**
|
|
10027
|
+
* Render an agent's memory as a markdown block for prompt injection.
|
|
10028
|
+
* Ranks entries by confidence × access × recency × importance, with a
|
|
10029
|
+
* BM25F relevance boost when a query is supplied, groups by category,
|
|
10030
|
+
* and truncates to ~maxTokens (estimated at 4 chars/token).
|
|
10031
|
+
*/
|
|
10032
|
+
async generateMemoryContext(agentId, query, maxTokens = 1500) {
|
|
10033
|
+
const entries = this.getAgentMemories(agentId).filter((m) => m.confidence >= 0.1);
|
|
10034
|
+
if (entries.length === 0) return "";
|
|
10035
|
+
const now = Date.now();
|
|
10036
|
+
let relevanceMap;
|
|
10037
|
+
if (query) {
|
|
10038
|
+
const candidateIds = new Set(entries.map((e) => e.id));
|
|
10039
|
+
const searchResults = this.searchIndex.search(query, candidateIds);
|
|
10040
|
+
if (searchResults.length > 0) {
|
|
10041
|
+
relevanceMap = /* @__PURE__ */ new Map();
|
|
10042
|
+
const maxScore = searchResults[0].score;
|
|
10043
|
+
for (const r of searchResults) {
|
|
10044
|
+
relevanceMap.set(r.id, maxScore > 0 ? r.score / maxScore : 0);
|
|
10045
|
+
}
|
|
10046
|
+
}
|
|
10047
|
+
}
|
|
10048
|
+
const scored = entries.map((entry) => {
|
|
10049
|
+
const accessWeight = 1 + Math.log1p(entry.accessCount) * 0.3;
|
|
10050
|
+
const lastTouch = entry.lastAccessedAt || entry.createdAt;
|
|
10051
|
+
const ageHours = Math.max(1, (now - new Date(lastTouch).getTime()) / 36e5);
|
|
10052
|
+
const recencyWeight = 1 / (1 + Math.log1p(ageHours / 24) * 0.2);
|
|
10053
|
+
let score = entry.confidence * accessWeight * recencyWeight;
|
|
10054
|
+
score *= IMPORTANCE_WEIGHT[entry.importance];
|
|
10055
|
+
if (relevanceMap) {
|
|
10056
|
+
const relevance = relevanceMap.get(entry.id) || 0;
|
|
10057
|
+
if (relevance > 0) score *= 1 + relevance * 3;
|
|
10058
|
+
}
|
|
10059
|
+
return { entry, score };
|
|
10060
|
+
});
|
|
10061
|
+
scored.sort((a, b) => b.score - a.score);
|
|
10062
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
10063
|
+
for (const { entry } of scored) {
|
|
10064
|
+
const group = grouped.get(entry.category) || [];
|
|
10065
|
+
group.push(entry);
|
|
10066
|
+
grouped.set(entry.category, group);
|
|
10067
|
+
}
|
|
10068
|
+
const maxChars = maxTokens * 4;
|
|
10069
|
+
const lines = ["## Agent Memory", ""];
|
|
10070
|
+
let charCount = lines.join("\n").length;
|
|
10071
|
+
for (const [category, categoryEntries] of Array.from(grouped.entries())) {
|
|
10072
|
+
const meta = MEMORY_CATEGORIES[category];
|
|
10073
|
+
if (!meta) continue;
|
|
10074
|
+
const header2 = `### ${meta.label}`;
|
|
10075
|
+
if (charCount + header2.length + 2 > maxChars) break;
|
|
10076
|
+
lines.push(header2, "");
|
|
10077
|
+
charCount += header2.length + 2;
|
|
10078
|
+
for (const entry of categoryEntries) {
|
|
10079
|
+
const badge = entry.importance === "critical" ? "[CRITICAL] " : entry.importance === "high" ? "[HIGH] " : "";
|
|
10080
|
+
const entryLine = `- **${badge}${entry.title}**: ${entry.content}`;
|
|
10081
|
+
if (charCount + entryLine.length + 1 > maxChars) break;
|
|
10082
|
+
lines.push(entryLine);
|
|
10083
|
+
charCount += entryLine.length + 1;
|
|
10084
|
+
}
|
|
10085
|
+
lines.push("");
|
|
10086
|
+
charCount += 1;
|
|
10087
|
+
}
|
|
10088
|
+
return lines.join("\n").trim();
|
|
10089
|
+
}
|
|
10090
|
+
// ─── Memory Lifecycle ───────────────────────────────
|
|
10091
|
+
/** Decay confidence for entries unaccessed for 7+ days. Critical entries are exempt. */
|
|
10092
|
+
async decayConfidence(agentId, decayRate = 0.05) {
|
|
10093
|
+
const cutoff = new Date(Date.now() - 7 * 864e5).toISOString();
|
|
10094
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10095
|
+
let decayed = 0;
|
|
10096
|
+
for (const entry of this.getAgentMemories(agentId)) {
|
|
10097
|
+
if (entry.importance === "critical") continue;
|
|
10098
|
+
const lastTouch = entry.lastAccessedAt || entry.createdAt;
|
|
10099
|
+
if (lastTouch >= cutoff) continue;
|
|
10100
|
+
const newConfidence = Math.max(0, entry.confidence - decayRate);
|
|
10101
|
+
if (newConfidence === entry.confidence) continue;
|
|
10102
|
+
entry.confidence = parseFloat(newConfidence.toFixed(4));
|
|
10103
|
+
entry.updatedAt = now;
|
|
10104
|
+
this.dbRun(
|
|
10105
|
+
"UPDATE agent_memory SET confidence = ?, updated_at = ? WHERE id = ?",
|
|
10106
|
+
[entry.confidence, now, entry.id]
|
|
10107
|
+
);
|
|
10108
|
+
decayed += 1;
|
|
10109
|
+
}
|
|
10110
|
+
return decayed;
|
|
10111
|
+
}
|
|
10112
|
+
/** Prune entries with confidence < 0.1 or past their expiresAt. */
|
|
10113
|
+
async pruneExpired(agentId) {
|
|
10114
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10115
|
+
const toDelete = [];
|
|
10116
|
+
const entries = agentId ? this.getAgentMemories(agentId) : Array.from(this.memories.values());
|
|
10117
|
+
for (const entry of entries) {
|
|
10118
|
+
const isLowConfidence = entry.confidence < 0.1;
|
|
10119
|
+
const isExpired = !!entry.expiresAt && entry.expiresAt <= now;
|
|
10120
|
+
if (isLowConfidence || isExpired) toDelete.push({ id: entry.id, agentId: entry.agentId });
|
|
10121
|
+
}
|
|
10122
|
+
for (const item of toDelete) {
|
|
10123
|
+
this.memories.delete(item.id);
|
|
10124
|
+
this.indexRemove(item.agentId, item.id);
|
|
10125
|
+
this.searchIndex.removeDocument(item.id);
|
|
10126
|
+
this.dbRun("DELETE FROM agent_memory WHERE id = ?", [item.id]);
|
|
10127
|
+
}
|
|
10128
|
+
return toDelete.length;
|
|
10129
|
+
}
|
|
10130
|
+
// ─── Statistics ─────────────────────────────────────
|
|
10131
|
+
/** Aggregate statistics for a specific agent's memory. */
|
|
10132
|
+
async getStats(agentId) {
|
|
10133
|
+
return this.computeStats(this.getAgentMemories(agentId));
|
|
10134
|
+
}
|
|
10135
|
+
computeStats(entries) {
|
|
10136
|
+
const byCategory = {};
|
|
10137
|
+
const byImportance = {};
|
|
10138
|
+
const bySource = {};
|
|
10139
|
+
let totalConfidence = 0;
|
|
10140
|
+
for (const entry of entries) {
|
|
10141
|
+
byCategory[entry.category] = (byCategory[entry.category] || 0) + 1;
|
|
10142
|
+
byImportance[entry.importance] = (byImportance[entry.importance] || 0) + 1;
|
|
10143
|
+
bySource[entry.source] = (bySource[entry.source] || 0) + 1;
|
|
10144
|
+
totalConfidence += entry.confidence;
|
|
10145
|
+
}
|
|
10146
|
+
return {
|
|
10147
|
+
totalEntries: entries.length,
|
|
10148
|
+
byCategory,
|
|
10149
|
+
byImportance,
|
|
10150
|
+
bySource,
|
|
10151
|
+
avgConfidence: entries.length > 0 ? parseFloat((totalConfidence / entries.length).toFixed(4)) : 0
|
|
10152
|
+
};
|
|
10153
|
+
}
|
|
10154
|
+
// ─── Row Mapper ─────────────────────────────────────
|
|
10155
|
+
rowToEntry(row) {
|
|
10156
|
+
return {
|
|
10157
|
+
id: row.id,
|
|
10158
|
+
agentId: row.agent_id,
|
|
10159
|
+
category: row.category,
|
|
10160
|
+
title: row.title,
|
|
10161
|
+
content: row.content,
|
|
10162
|
+
source: row.source,
|
|
10163
|
+
importance: row.importance,
|
|
10164
|
+
confidence: row.confidence,
|
|
10165
|
+
accessCount: row.access_count || 0,
|
|
10166
|
+
lastAccessedAt: row.last_accessed_at || void 0,
|
|
10167
|
+
expiresAt: row.expires_at || void 0,
|
|
10168
|
+
tags: Array.isArray(sj(row.tags)) ? sj(row.tags) : [],
|
|
10169
|
+
metadata: sj(row.metadata || "{}"),
|
|
10170
|
+
createdAt: row.created_at,
|
|
10171
|
+
updatedAt: row.updated_at
|
|
10172
|
+
};
|
|
10173
|
+
}
|
|
10174
|
+
};
|
|
9292
10175
|
export {
|
|
9293
10176
|
AGENT_ROLES,
|
|
9294
10177
|
AccountManager,
|
|
9295
10178
|
AgentDeletionService,
|
|
10179
|
+
AgentMemoryManager,
|
|
9296
10180
|
AgentMemoryStore,
|
|
9297
10181
|
AgenticMailClient,
|
|
9298
10182
|
BRIDGE_OPERATOR_LIVE_WINDOW_MS,
|
|
@@ -9309,8 +10193,10 @@ export {
|
|
|
9309
10193
|
EmailSearchIndex,
|
|
9310
10194
|
GatewayManager,
|
|
9311
10195
|
InboxWatcher,
|
|
10196
|
+
MEMORY_CATEGORIES,
|
|
9312
10197
|
MailReceiver,
|
|
9313
10198
|
MailSender,
|
|
10199
|
+
MemorySearchIndex,
|
|
9314
10200
|
PHONE_MAX_CONCURRENT_MISSIONS,
|
|
9315
10201
|
PHONE_MIN_WEBHOOK_SECRET_LENGTH,
|
|
9316
10202
|
PHONE_MISSION_STATES,
|
|
@@ -9401,7 +10287,9 @@ export {
|
|
|
9401
10287
|
setTelemetryVersion,
|
|
9402
10288
|
shouldSkipBridgeWakeForLiveOperator,
|
|
9403
10289
|
startRelayBridge,
|
|
10290
|
+
stem,
|
|
9404
10291
|
threadIdFor,
|
|
10292
|
+
tokenize,
|
|
9405
10293
|
tryJoin,
|
|
9406
10294
|
validateApiUrl,
|
|
9407
10295
|
validatePhoneMissionPolicy,
|