@smyslenny/agent-memory 2.2.0 → 3.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.
package/dist/index.js CHANGED
@@ -565,108 +565,6 @@ function deletePath(db, id) {
565
565
  return result.changes > 0;
566
566
  }
567
567
 
568
- // src/core/link.ts
569
- function createLink(db, sourceId, targetId, relation, weight = 1, agent_id) {
570
- const sourceAgent = db.prepare("SELECT agent_id FROM memories WHERE id = ?").get(sourceId)?.agent_id;
571
- const targetAgent = db.prepare("SELECT agent_id FROM memories WHERE id = ?").get(targetId)?.agent_id;
572
- if (!sourceAgent) throw new Error(`Source memory not found: ${sourceId}`);
573
- if (!targetAgent) throw new Error(`Target memory not found: ${targetId}`);
574
- if (sourceAgent !== targetAgent) throw new Error("Cross-agent links are not allowed");
575
- if (agent_id && agent_id !== sourceAgent) throw new Error("Agent mismatch for link");
576
- const agentId = agent_id ?? sourceAgent;
577
- db.prepare(
578
- `INSERT OR REPLACE INTO links (agent_id, source_id, target_id, relation, weight, created_at)
579
- VALUES (?, ?, ?, ?, ?, ?)`
580
- ).run(agentId, sourceId, targetId, relation, weight, now());
581
- return { agent_id: agentId, source_id: sourceId, target_id: targetId, relation, weight, created_at: now() };
582
- }
583
- function getLinks(db, memoryId, agent_id) {
584
- const agentId = agent_id ?? db.prepare("SELECT agent_id FROM memories WHERE id = ?").get(memoryId)?.agent_id ?? "default";
585
- return db.prepare("SELECT * FROM links WHERE agent_id = ? AND (source_id = ? OR target_id = ?)").all(agentId, memoryId, memoryId);
586
- }
587
- function getOutgoingLinks(db, sourceId, agent_id) {
588
- const agentId = agent_id ?? db.prepare("SELECT agent_id FROM memories WHERE id = ?").get(sourceId)?.agent_id ?? "default";
589
- return db.prepare("SELECT * FROM links WHERE agent_id = ? AND source_id = ?").all(agentId, sourceId);
590
- }
591
- function traverse(db, startId, maxHops = 2, agent_id) {
592
- const agentId = agent_id ?? db.prepare("SELECT agent_id FROM memories WHERE id = ?").get(startId)?.agent_id ?? "default";
593
- const visited = /* @__PURE__ */ new Set();
594
- const results = [];
595
- const queue = [
596
- { id: startId, hop: 0, relation: "self" }
597
- ];
598
- while (queue.length > 0) {
599
- const current = queue.shift();
600
- if (visited.has(current.id)) continue;
601
- visited.add(current.id);
602
- if (current.hop > 0) {
603
- results.push(current);
604
- }
605
- if (current.hop < maxHops) {
606
- const links = db.prepare("SELECT target_id, relation FROM links WHERE agent_id = ? AND source_id = ?").all(agentId, current.id);
607
- for (const link of links) {
608
- if (!visited.has(link.target_id)) {
609
- queue.push({
610
- id: link.target_id,
611
- hop: current.hop + 1,
612
- relation: link.relation
613
- });
614
- }
615
- }
616
- const reverseLinks = db.prepare("SELECT source_id, relation FROM links WHERE agent_id = ? AND target_id = ?").all(agentId, current.id);
617
- for (const link of reverseLinks) {
618
- if (!visited.has(link.source_id)) {
619
- queue.push({
620
- id: link.source_id,
621
- hop: current.hop + 1,
622
- relation: link.relation
623
- });
624
- }
625
- }
626
- }
627
- }
628
- return results;
629
- }
630
- function deleteLink(db, sourceId, targetId, agent_id) {
631
- const agentId = agent_id ?? db.prepare("SELECT agent_id FROM memories WHERE id = ?").get(sourceId)?.agent_id ?? "default";
632
- const result = db.prepare("DELETE FROM links WHERE agent_id = ? AND source_id = ? AND target_id = ?").run(agentId, sourceId, targetId);
633
- return result.changes > 0;
634
- }
635
-
636
- // src/core/snapshot.ts
637
- function createSnapshot(db, memoryId, action, changedBy) {
638
- const memory = db.prepare("SELECT content FROM memories WHERE id = ?").get(memoryId);
639
- if (!memory) throw new Error(`Memory not found: ${memoryId}`);
640
- const id = newId();
641
- db.prepare(
642
- `INSERT INTO snapshots (id, memory_id, content, changed_by, action, created_at)
643
- VALUES (?, ?, ?, ?, ?, ?)`
644
- ).run(id, memoryId, memory.content, changedBy ?? null, action, now());
645
- return { id, memory_id: memoryId, content: memory.content, changed_by: changedBy ?? null, action, created_at: now() };
646
- }
647
- function getSnapshots(db, memoryId) {
648
- return db.prepare("SELECT * FROM snapshots WHERE memory_id = ? ORDER BY created_at DESC").all(memoryId);
649
- }
650
- function getSnapshot(db, id) {
651
- return db.prepare("SELECT * FROM snapshots WHERE id = ?").get(id) ?? null;
652
- }
653
- function rollback(db, snapshotId) {
654
- const snapshot = getSnapshot(db, snapshotId);
655
- if (!snapshot) return false;
656
- createSnapshot(db, snapshot.memory_id, "update", "rollback");
657
- db.prepare("UPDATE memories SET content = ?, updated_at = ? WHERE id = ?").run(
658
- snapshot.content,
659
- now(),
660
- snapshot.memory_id
661
- );
662
- db.prepare("DELETE FROM memories_fts WHERE id = ?").run(snapshot.memory_id);
663
- db.prepare("INSERT INTO memories_fts (id, content) VALUES (?, ?)").run(
664
- snapshot.memory_id,
665
- tokenizeForIndex(snapshot.content)
666
- );
667
- return true;
668
- }
669
-
670
568
  // src/core/guard.ts
671
569
  function guard(db, input) {
672
570
  const hash = contentHash(input.content);
@@ -880,503 +778,314 @@ function buildFtsQuery(text) {
880
778
  return tokens.map((w) => `"${w}"`).join(" OR ");
881
779
  }
882
780
 
883
- // src/search/intent.ts
884
- var INTENT_PATTERNS = {
885
- factual: [
886
- // English
887
- /^(what|who|where|which|how much|how many)\b/i,
888
- /\b(name|address|number|password|config|setting)\b/i,
889
- // Chinese - questions about facts
890
- /是(什么|谁|哪|啥)/,
891
- /叫(什么|啥)/,
892
- /(名字|地址|号码|密码|配置|设置|账号|邮箱|链接|版本)/,
893
- /(多少|几个|哪个|哪些|哪里)/,
894
- // Chinese - lookup patterns
895
- /(查一下|找一下|看看|搜一下)/,
896
- /(.+)是什么$/
897
- ],
898
- temporal: [
899
- // English
900
- /^(when|what time|how long)\b/i,
901
- /\b(yesterday|today|tomorrow|last week|recently|ago|before|after)\b/i,
902
- /\b(first|latest|newest|oldest|previous|next)\b/i,
903
- // Chinese - time expressions
904
- /什么时候/,
905
- /(昨天|今天|明天|上周|下周|最近|以前|之前|之后|刚才|刚刚)/,
906
- /(几月|几号|几点|多久|多长时间)/,
907
- /(上次|下次|第一次|最后一次|那天|那时)/,
908
- // Date patterns
909
- /\d{4}[-/.]\d{1,2}/,
910
- /\d{1,2}月\d{1,2}[日号]/,
911
- // Chinese - temporal context
912
- /(历史|记录|日志|以来|至今|期间)/
913
- ],
914
- causal: [
915
- // English
916
- /^(why|how come|what caused)\b/i,
917
- /\b(because|due to|reason|cause|result)\b/i,
918
- // Chinese - causal questions
919
- /为(什么|啥|何)/,
920
- /(原因|导致|造成|引起|因为|所以|结果)/,
921
- /(怎么回事|怎么了|咋回事|咋了)/,
922
- /(为啥|凭啥|凭什么)/,
923
- // Chinese - problem/diagnosis
924
- /(出(了|了什么)?问题|报错|失败|出错|bug)/
925
- ],
926
- exploratory: [
927
- // English
928
- /^(how|tell me about|explain|describe|show me)\b/i,
929
- /^(what do you think|what about|any)\b/i,
930
- /\b(overview|summary|list|compare)\b/i,
931
- // Chinese - exploratory
932
- /(怎么样|怎样|如何)/,
933
- /(介绍|说说|讲讲|聊聊|谈谈)/,
934
- /(有哪些|有什么|有没有)/,
935
- /(关于|对于|至于|关联)/,
936
- /(总结|概括|梳理|回顾|盘点)/,
937
- // Chinese - opinion/analysis
938
- /(看法|想法|意见|建议|评价|感觉|觉得)/,
939
- /(对比|比较|区别|差异|优缺点)/
940
- ]
941
- };
942
- var CN_STRUCTURE_BOOSTS = {
943
- factual: [/^.{1,6}(是什么|叫什么|在哪)/, /^(谁|哪)/],
944
- temporal: [/^(什么时候|上次|最近)/, /(时间|日期)$/],
945
- causal: [/^(为什么|为啥)/, /(为什么|怎么回事)$/],
946
- exploratory: [/^(怎么|如何|说说)/, /(哪些|什么样)$/]
947
- };
948
- function classifyIntent(query) {
949
- const scores = {
950
- factual: 0,
951
- exploratory: 0,
952
- temporal: 0,
953
- causal: 0
781
+ // src/sleep/sync.ts
782
+ function syncOne(db, input) {
783
+ const memInput = {
784
+ content: input.content,
785
+ type: input.type ?? "event",
786
+ priority: input.priority,
787
+ emotion_val: input.emotion_val,
788
+ source: input.source,
789
+ agent_id: input.agent_id,
790
+ uri: input.uri
954
791
  };
955
- for (const [intent, patterns] of Object.entries(INTENT_PATTERNS)) {
956
- for (const pattern of patterns) {
957
- if (pattern.test(query)) {
958
- scores[intent] += 1;
792
+ const guardResult = guard(db, memInput);
793
+ switch (guardResult.action) {
794
+ case "skip":
795
+ return { action: "skipped", reason: guardResult.reason, memoryId: guardResult.existingId };
796
+ case "add": {
797
+ const mem = createMemory(db, memInput);
798
+ if (!mem) return { action: "skipped", reason: "createMemory returned null" };
799
+ if (input.uri) {
800
+ try {
801
+ createPath(db, mem.id, input.uri);
802
+ } catch {
803
+ }
959
804
  }
805
+ return { action: "added", memoryId: mem.id, reason: guardResult.reason };
960
806
  }
961
- }
962
- for (const [intent, patterns] of Object.entries(CN_STRUCTURE_BOOSTS)) {
963
- for (const pattern of patterns) {
964
- if (pattern.test(query)) {
965
- scores[intent] += 0.5;
966
- }
807
+ case "update": {
808
+ if (!guardResult.existingId) return { action: "skipped", reason: "No existing ID for update" };
809
+ updateMemory(db, guardResult.existingId, { content: input.content });
810
+ return { action: "updated", memoryId: guardResult.existingId, reason: guardResult.reason };
967
811
  }
968
- }
969
- const tokens = tokenize(query);
970
- const totalPatternScore = Object.values(scores).reduce((a, b) => a + b, 0);
971
- if (totalPatternScore === 0 && tokens.length <= 3) {
972
- scores.factual += 1;
973
- }
974
- let maxIntent = "factual";
975
- let maxScore = 0;
976
- for (const [intent, score] of Object.entries(scores)) {
977
- if (score > maxScore) {
978
- maxScore = score;
979
- maxIntent = intent;
812
+ case "merge": {
813
+ if (!guardResult.existingId || !guardResult.mergedContent) {
814
+ return { action: "skipped", reason: "Missing merge data" };
815
+ }
816
+ updateMemory(db, guardResult.existingId, { content: guardResult.mergedContent });
817
+ return { action: "merged", memoryId: guardResult.existingId, reason: guardResult.reason };
980
818
  }
981
819
  }
982
- const totalScore = Object.values(scores).reduce((a, b) => a + b, 0);
983
- const confidence = totalScore > 0 ? Math.min(0.95, maxScore / totalScore) : 0.5;
984
- return { intent: maxIntent, confidence };
985
- }
986
- function getStrategy(intent) {
987
- switch (intent) {
988
- case "factual":
989
- return { boostRecent: false, boostPriority: true, limit: 5 };
990
- case "temporal":
991
- return { boostRecent: true, boostPriority: false, limit: 10 };
992
- case "causal":
993
- return { boostRecent: false, boostPriority: false, limit: 10 };
994
- case "exploratory":
995
- return { boostRecent: false, boostPriority: false, limit: 15 };
996
- }
997
820
  }
998
-
999
- // src/search/rerank.ts
1000
- async function rerankWithProvider(results, query, provider) {
1001
- if (results.length === 0) return results;
1002
- const documents = results.map((r) => r.memory.content);
1003
- try {
1004
- const apiResults = await provider.rerank(query, documents);
1005
- const scoreMap = new Map(apiResults.map((r) => [r.index, r.relevance_score]));
1006
- return results.map((r, i) => {
1007
- const score = scoreMap.get(i);
1008
- if (score === void 0) return r;
1009
- return {
1010
- ...r,
1011
- score,
1012
- matchReason: `${r.matchReason}+rerank`
1013
- };
1014
- });
1015
- } catch (err) {
1016
- console.warn("[agent-memory] External rerank failed, falling back:", err);
1017
- return results;
1018
- }
1019
- }
1020
- function rerank(results, opts) {
1021
- const now2 = Date.now();
1022
- const scored = results.map((r) => {
1023
- let finalScore = r.score;
1024
- if (opts.boostPriority) {
1025
- const priorityMultiplier = [4, 3, 2, 1][r.memory.priority] ?? 1;
1026
- finalScore *= priorityMultiplier;
1027
- }
1028
- if (opts.boostRecent && r.memory.updated_at) {
1029
- const age = now2 - new Date(r.memory.updated_at).getTime();
1030
- const daysSinceUpdate = age / (1e3 * 60 * 60 * 24);
1031
- const recencyBoost = Math.max(0.1, 1 / (1 + daysSinceUpdate * 0.1));
1032
- finalScore *= recencyBoost;
821
+ function syncBatch(db, inputs) {
822
+ const results = [];
823
+ const transaction = db.transaction(() => {
824
+ for (const input of inputs) {
825
+ results.push(syncOne(db, input));
1033
826
  }
1034
- finalScore *= Math.max(0.1, r.memory.vitality);
1035
- return { ...r, score: finalScore };
1036
827
  });
1037
- scored.sort((a, b) => b.score - a.score);
1038
- return scored.slice(0, opts.limit);
1039
- }
1040
-
1041
- // src/search/embeddings.ts
1042
- function encodeEmbedding(vector) {
1043
- const arr = vector instanceof Float32Array ? vector : Float32Array.from(vector);
1044
- return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);
1045
- }
1046
- function decodeEmbedding(buf) {
1047
- const copy = Buffer.from(buf);
1048
- return new Float32Array(copy.buffer, copy.byteOffset, Math.floor(copy.byteLength / 4));
1049
- }
1050
- function upsertEmbedding(db, input) {
1051
- const ts = now();
1052
- const vec = input.vector instanceof Float32Array ? input.vector : Float32Array.from(input.vector);
1053
- const blob = encodeEmbedding(vec);
1054
- db.prepare(
1055
- `INSERT INTO embeddings (agent_id, memory_id, model, dim, vector, created_at, updated_at)
1056
- VALUES (?, ?, ?, ?, ?, ?, ?)
1057
- ON CONFLICT(agent_id, memory_id, model) DO UPDATE SET
1058
- dim = excluded.dim,
1059
- vector = excluded.vector,
1060
- updated_at = excluded.updated_at`
1061
- ).run(input.agent_id, input.memory_id, input.model, vec.length, blob, ts, ts);
1062
- }
1063
- function getEmbedding(db, agent_id, memory_id, model) {
1064
- const row = db.prepare(
1065
- "SELECT agent_id, memory_id, model, dim, vector, created_at, updated_at FROM embeddings WHERE agent_id = ? AND memory_id = ? AND model = ?"
1066
- ).get(agent_id, memory_id, model);
1067
- if (!row) return null;
1068
- return { ...row, vector: decodeEmbedding(row.vector) };
1069
- }
1070
- function listEmbeddings(db, agent_id, model) {
1071
- const rows = db.prepare(
1072
- "SELECT memory_id, vector FROM embeddings WHERE agent_id = ? AND model = ?"
1073
- ).all(agent_id, model);
1074
- return rows.map((r) => ({ memory_id: r.memory_id, vector: decodeEmbedding(r.vector) }));
828
+ transaction();
829
+ return results;
1075
830
  }
1076
831
 
1077
- // src/search/hybrid.ts
1078
- function cosine(a, b) {
1079
- const n = Math.min(a.length, b.length);
1080
- let dot = 0;
1081
- let na = 0;
1082
- let nb = 0;
1083
- for (let i = 0; i < n; i++) {
1084
- const x = a[i];
1085
- const y = b[i];
1086
- dot += x * y;
1087
- na += x * x;
1088
- nb += y * y;
1089
- }
1090
- if (na === 0 || nb === 0) return 0;
1091
- return dot / (Math.sqrt(na) * Math.sqrt(nb));
1092
- }
1093
- function rrfScore(rank, k) {
1094
- return 1 / (k + rank);
1095
- }
1096
- function fuseRrf(lists, k) {
1097
- const out = /* @__PURE__ */ new Map();
1098
- for (const list of lists) {
1099
- for (let i = 0; i < list.items.length; i++) {
1100
- const it = list.items[i];
1101
- const rank = i + 1;
1102
- const add = rrfScore(rank, k);
1103
- const prev = out.get(it.id);
1104
- if (!prev) out.set(it.id, { score: add, sources: [list.name] });
1105
- else {
1106
- prev.score += add;
1107
- if (!prev.sources.includes(list.name)) prev.sources.push(list.name);
1108
- }
832
+ // src/ingest/ingest.ts
833
+ function slugify(input) {
834
+ return input.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s-]/g, " ").trim().replace(/\s+/g, "-").slice(0, 64) || "item";
835
+ }
836
+ function classifyIngestType(text) {
837
+ const lower = text.toLowerCase();
838
+ if (/##\s*身份|\bidentity\b|\b我是\b|我是/.test(text)) {
839
+ return "identity";
840
+ }
841
+ if (/##\s*情感|❤️|💕|爱你|感动|难过|开心|害怕|想念|表白/.test(text)) {
842
+ return "emotion";
843
+ }
844
+ if (/##\s*决策|##\s*技术|选型|教训|\bknowledge\b|⚠️|复盘|经验/.test(text)) {
845
+ return "knowledge";
846
+ }
847
+ if (/\d{4}-\d{2}-\d{2}|发生了|完成了|今天|昨日|刚刚|部署|上线/.test(text)) {
848
+ return "event";
849
+ }
850
+ if (lower.length <= 12) return "event";
851
+ return "knowledge";
852
+ }
853
+ function splitIngestBlocks(text) {
854
+ const headingRegex = /^##\s+(.+)$/gm;
855
+ const matches = [...text.matchAll(headingRegex)];
856
+ const blocks = [];
857
+ if (matches.length > 0) {
858
+ for (let i = 0; i < matches.length; i++) {
859
+ const match = matches[i];
860
+ const start = match.index ?? 0;
861
+ const end = i + 1 < matches.length ? matches[i + 1].index ?? text.length : text.length;
862
+ const raw = text.slice(start, end).trim();
863
+ const lines = raw.split("\n");
864
+ const title = lines[0].replace(/^##\s+/, "").trim();
865
+ const content = lines.slice(1).join("\n").trim();
866
+ if (content) blocks.push({ title, content });
1109
867
  }
1110
- }
1111
- return out;
1112
- }
1113
- function fetchMemories(db, ids, agentId) {
1114
- if (ids.length === 0) return [];
1115
- const placeholders = ids.map(() => "?").join(", ");
1116
- const sql = agentId ? `SELECT * FROM memories WHERE id IN (${placeholders}) AND agent_id = ?` : `SELECT * FROM memories WHERE id IN (${placeholders})`;
1117
- const rows = db.prepare(sql).all(...agentId ? [...ids, agentId] : ids);
1118
- return rows;
1119
- }
1120
- async function searchHybrid(db, query, opts) {
1121
- const agentId = opts?.agent_id ?? "default";
1122
- const limit = opts?.limit ?? 10;
1123
- const bm25Mult = opts?.bm25CandidateMultiplier ?? 3;
1124
- const semanticCandidates = opts?.semanticCandidates ?? 50;
1125
- const rrfK = opts?.rrfK ?? 60;
1126
- const bm25 = searchBM25(db, query, {
1127
- agent_id: agentId,
1128
- limit: limit * bm25Mult
868
+ return blocks;
869
+ }
870
+ const bullets = text.split("\n").map((line) => line.trim()).filter((line) => /^[-*]\s+/.test(line)).map((line) => line.replace(/^[-*]\s+/, "").trim()).filter(Boolean);
871
+ if (bullets.length > 0) {
872
+ return bullets.map((content, i) => ({ title: `bullet-${i + 1}`, content }));
873
+ }
874
+ const plain = text.trim();
875
+ if (!plain) return [];
876
+ return [{ title: "ingest", content: plain }];
877
+ }
878
+ function extractIngestItems(text, source) {
879
+ const blocks = splitIngestBlocks(text);
880
+ return blocks.map((block, index) => {
881
+ const merged = `${block.title}
882
+ ${block.content}`;
883
+ const type = classifyIngestType(merged);
884
+ const domain = type === "identity" ? "core" : type;
885
+ const sourcePart = slugify(source ?? "ingest");
886
+ const uri = `${domain}://ingest/${sourcePart}/${index + 1}-${slugify(block.title)}`;
887
+ return {
888
+ index,
889
+ title: block.title,
890
+ content: block.content,
891
+ type,
892
+ uri
893
+ };
1129
894
  });
1130
- const provider = opts?.embeddingProvider ?? null;
1131
- const model = opts?.embeddingModel ?? provider?.model;
1132
- if (!provider || !model) {
1133
- return bm25.slice(0, limit);
1134
- }
1135
- const embedFn = provider.embedQuery ?? provider.embed;
1136
- const qVec = Float32Array.from(await embedFn.call(provider, query));
1137
- const embeddings = listEmbeddings(db, agentId, model);
1138
- const scored = [];
1139
- for (const e of embeddings) {
1140
- scored.push({ id: e.memory_id, score: cosine(qVec, e.vector) });
1141
- }
1142
- scored.sort((a, b) => b.score - a.score);
1143
- const semanticTop = scored.slice(0, semanticCandidates);
1144
- const fused = fuseRrf(
1145
- [
1146
- { name: "bm25", items: bm25.map((r) => ({ id: r.memory.id, score: r.score })) },
1147
- { name: "semantic", items: semanticTop }
1148
- ],
1149
- rrfK
1150
- );
1151
- const ids = [...fused.keys()];
1152
- const memories = fetchMemories(db, ids, agentId);
1153
- const byId = new Map(memories.map((m) => [m.id, m]));
1154
- const out = [];
1155
- for (const [id, meta] of fused) {
1156
- const mem = byId.get(id);
1157
- if (!mem) continue;
1158
- out.push({
1159
- memory: mem,
1160
- score: meta.score,
1161
- matchReason: meta.sources.sort().join("+")
1162
- });
1163
- }
1164
- out.sort((a, b) => b.score - a.score);
1165
- return out.slice(0, limit);
1166
- }
1167
-
1168
- // src/search/providers.ts
1169
- var QWEN_DEFAULT_INSTRUCTION = "Given a query, retrieve the most semantically relevant document";
1170
- function getDefaultInstruction(model) {
1171
- const m = model.toLowerCase();
1172
- if (m.includes("qwen")) return QWEN_DEFAULT_INSTRUCTION;
1173
- if (m.includes("gemini")) return null;
1174
- return null;
1175
- }
1176
- function resolveInstruction(model) {
1177
- const override = process.env.AGENT_MEMORY_EMBEDDINGS_INSTRUCTION;
1178
- if (override !== void 0) {
1179
- const normalized = override.trim();
1180
- if (!normalized) return null;
1181
- const lowered = normalized.toLowerCase();
1182
- if (lowered === "none" || lowered === "off" || lowered === "false" || lowered === "null") return null;
1183
- return normalized;
1184
- }
1185
- return getDefaultInstruction(model);
1186
895
  }
1187
- function buildQueryInput(query, instructionPrefix) {
1188
- if (!instructionPrefix) return query;
1189
- return `Instruct: ${instructionPrefix}
1190
- Query: ${query}`;
1191
- }
1192
- function getEmbeddingProviderFromEnv() {
1193
- const provider = (process.env.AGENT_MEMORY_EMBEDDINGS_PROVIDER ?? "none").toLowerCase();
1194
- if (provider === "none" || provider === "off" || provider === "false") return null;
1195
- if (provider === "openai") {
1196
- const apiKey = process.env.OPENAI_API_KEY;
1197
- const model = process.env.AGENT_MEMORY_EMBEDDINGS_MODEL ?? "text-embedding-3-small";
1198
- const baseUrl = process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1";
1199
- if (!apiKey) return null;
1200
- const instruction = resolveInstruction(model);
1201
- return createOpenAIProvider({ apiKey, model, baseUrl, instruction });
1202
- }
1203
- if (provider === "gemini" || provider === "google") {
1204
- const apiKey = process.env.GEMINI_API_KEY ?? process.env.OPENAI_API_KEY;
1205
- const model = process.env.AGENT_MEMORY_EMBEDDINGS_MODEL ?? "gemini-embedding-001";
1206
- const baseUrl = process.env.GEMINI_BASE_URL ?? process.env.OPENAI_BASE_URL ?? "https://generativelanguage.googleapis.com/v1beta";
1207
- if (!apiKey) return null;
1208
- const instruction = resolveInstruction(model);
1209
- return createOpenAIProvider({ id: "gemini", apiKey, model, baseUrl, instruction });
1210
- }
1211
- if (provider === "qwen" || provider === "dashscope" || provider === "tongyi") {
1212
- const apiKey = process.env.DASHSCOPE_API_KEY;
1213
- const model = process.env.AGENT_MEMORY_EMBEDDINGS_MODEL ?? "text-embedding-v3";
1214
- const baseUrl = process.env.DASHSCOPE_BASE_URL ?? "https://dashscope.aliyuncs.com";
1215
- if (!apiKey) return null;
1216
- const instruction = resolveInstruction(model);
1217
- return createDashScopeProvider({ apiKey, model, baseUrl, instruction });
1218
- }
1219
- return null;
1220
- }
1221
- function authHeader(apiKey) {
1222
- return apiKey.startsWith("Bearer ") ? apiKey : `Bearer ${apiKey}`;
1223
- }
1224
- function normalizeEmbedding(e) {
1225
- if (!Array.isArray(e)) throw new Error("Invalid embedding: not an array");
1226
- if (e.length === 0) throw new Error("Invalid embedding: empty");
1227
- return e.map((x) => {
1228
- if (typeof x !== "number" || !Number.isFinite(x)) throw new Error("Invalid embedding: non-numeric value");
1229
- return x;
1230
- });
1231
- }
1232
- function createOpenAIProvider(opts) {
1233
- const baseUrl = opts.baseUrl ?? "https://api.openai.com/v1";
1234
- const instructionPrefix = opts.instruction ?? null;
1235
- async function requestEmbedding(input) {
1236
- const resp = await fetch(`${baseUrl.replace(/\/$/, "")}/embeddings`, {
1237
- method: "POST",
1238
- headers: {
1239
- "content-type": "application/json",
1240
- authorization: authHeader(opts.apiKey)
1241
- },
1242
- body: JSON.stringify({ model: opts.model, input })
896
+ function ingestText(db, options) {
897
+ const extracted = extractIngestItems(options.text, options.source);
898
+ const dryRun = options.dryRun ?? false;
899
+ const agentId = options.agentId ?? "default";
900
+ if (dryRun) {
901
+ return {
902
+ extracted: extracted.length,
903
+ written: 0,
904
+ skipped: extracted.length,
905
+ dry_run: true,
906
+ details: extracted.map((item) => ({
907
+ index: item.index,
908
+ type: item.type,
909
+ uri: item.uri,
910
+ preview: item.content.slice(0, 80)
911
+ }))
912
+ };
913
+ }
914
+ let written = 0;
915
+ let skipped = 0;
916
+ const details = [];
917
+ for (const item of extracted) {
918
+ const result = syncOne(db, {
919
+ content: item.content,
920
+ type: item.type,
921
+ uri: item.uri,
922
+ source: `auto:${options.source ?? "ingest"}`,
923
+ agent_id: agentId
1243
924
  });
1244
- if (!resp.ok) {
1245
- const body = await resp.text().catch(() => "");
1246
- throw new Error(`OpenAI embeddings failed: ${resp.status} ${resp.statusText} ${body}`.trim());
925
+ if (result.action === "added" || result.action === "updated" || result.action === "merged") {
926
+ written++;
927
+ } else {
928
+ skipped++;
1247
929
  }
1248
- const data = await resp.json();
1249
- return normalizeEmbedding(data.data?.[0]?.embedding);
930
+ details.push({
931
+ index: item.index,
932
+ type: item.type,
933
+ uri: item.uri,
934
+ action: result.action,
935
+ reason: result.reason,
936
+ memoryId: result.memoryId
937
+ });
1250
938
  }
1251
939
  return {
1252
- id: opts.id ?? "openai",
1253
- model: opts.model,
1254
- instructionPrefix,
1255
- async embed(text) {
1256
- return requestEmbedding(text);
1257
- },
1258
- async embedQuery(query) {
1259
- return requestEmbedding(buildQueryInput(query, instructionPrefix));
1260
- }
940
+ extracted: extracted.length,
941
+ written,
942
+ skipped,
943
+ dry_run: false,
944
+ details
1261
945
  };
1262
946
  }
1263
- function createDashScopeProvider(opts) {
1264
- const baseUrl = opts.baseUrl ?? "https://dashscope.aliyuncs.com";
1265
- const instructionPrefix = opts.instruction ?? null;
1266
- async function requestEmbedding(text) {
1267
- const resp = await fetch(`${baseUrl.replace(/\/$/, "")}/api/v1/services/embeddings/text-embedding/text-embedding`, {
1268
- method: "POST",
1269
- headers: {
1270
- "content-type": "application/json",
1271
- authorization: authHeader(opts.apiKey)
1272
- },
1273
- body: JSON.stringify({
1274
- model: opts.model,
1275
- input: { texts: [text] }
1276
- })
1277
- });
1278
- if (!resp.ok) {
1279
- const body = await resp.text().catch(() => "");
1280
- throw new Error(`DashScope embeddings failed: ${resp.status} ${resp.statusText} ${body}`.trim());
947
+
948
+ // src/ingest/watcher.ts
949
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync, watch } from "fs";
950
+ import { join as join2, relative, resolve } from "path";
951
+ function runAutoIngestWatcher(options) {
952
+ const workspaceDir = resolve(options.workspaceDir);
953
+ const memoryDir = join2(workspaceDir, "memory");
954
+ const memoryMdPath = join2(workspaceDir, "MEMORY.md");
955
+ const debounceMs = options.debounceMs ?? 1200;
956
+ const initialScan = options.initialScan ?? true;
957
+ const logger = options.logger ?? console;
958
+ const timers = /* @__PURE__ */ new Map();
959
+ const watchers = [];
960
+ const stats = {
961
+ triggers: 0,
962
+ filesProcessed: 0,
963
+ extracted: 0,
964
+ written: 0,
965
+ skipped: 0,
966
+ errors: 0
967
+ };
968
+ let stopped = false;
969
+ let queue = Promise.resolve();
970
+ const toSource = (absPath) => {
971
+ const rel = relative(workspaceDir, absPath).replace(/\\/g, "/");
972
+ return rel || absPath;
973
+ };
974
+ const isTrackedMarkdownFile = (absPath) => {
975
+ if (!absPath.endsWith(".md")) return false;
976
+ if (resolve(absPath) === memoryMdPath) return true;
977
+ const rel = relative(memoryDir, absPath).replace(/\\/g, "/");
978
+ if (rel.startsWith("..") || rel === "") return false;
979
+ return !rel.includes("/");
980
+ };
981
+ const ingestFile = (absPath, reason) => {
982
+ if (stopped) return;
983
+ if (!existsSync2(absPath)) {
984
+ logger.log(`[auto-ingest] skip missing file: ${toSource(absPath)} (reason=${reason})`);
985
+ return;
1281
986
  }
1282
- const data = await resp.json();
1283
- const emb = data.output?.embeddings?.[0]?.embedding ?? data.output?.embeddings?.[0]?.vector ?? data.output?.embedding ?? data.data?.[0]?.embedding;
1284
- return normalizeEmbedding(emb);
1285
- }
1286
- return {
1287
- id: "dashscope",
1288
- model: opts.model,
1289
- instructionPrefix,
1290
- async embed(text) {
1291
- return requestEmbedding(text);
1292
- },
1293
- async embedQuery(query) {
1294
- return requestEmbedding(buildQueryInput(query, instructionPrefix));
987
+ let isFile = false;
988
+ try {
989
+ isFile = statSync(absPath).isFile();
990
+ } catch (err) {
991
+ stats.errors += 1;
992
+ logger.warn(`[auto-ingest] stat failed for ${toSource(absPath)}: ${String(err)}`);
993
+ return;
994
+ }
995
+ if (!isFile) return;
996
+ try {
997
+ const text = readFileSync2(absPath, "utf-8");
998
+ const source = toSource(absPath);
999
+ const result = ingestText(options.db, {
1000
+ text,
1001
+ source,
1002
+ agentId: options.agentId
1003
+ });
1004
+ stats.filesProcessed += 1;
1005
+ stats.extracted += result.extracted;
1006
+ stats.written += result.written;
1007
+ stats.skipped += result.skipped;
1008
+ logger.log(
1009
+ `[auto-ingest] file=${source} reason=${reason} extracted=${result.extracted} written=${result.written} skipped=${result.skipped}`
1010
+ );
1011
+ } catch (err) {
1012
+ stats.errors += 1;
1013
+ logger.error(`[auto-ingest] ingest failed for ${toSource(absPath)}: ${String(err)}`);
1295
1014
  }
1296
1015
  };
1297
- }
1298
-
1299
- // src/search/rerank-provider.ts
1300
- function authHeader2(apiKey) {
1301
- return apiKey.startsWith("Bearer ") ? apiKey : `Bearer ${apiKey}`;
1302
- }
1303
- function getRerankerProviderFromEnv() {
1304
- const provider = (process.env.AGENT_MEMORY_RERANK_PROVIDER ?? "none").toLowerCase();
1305
- if (provider === "none" || provider === "off") return null;
1306
- if (provider === "openai" || provider === "jina" || provider === "cohere") {
1307
- const apiKey = process.env.AGENT_MEMORY_RERANK_API_KEY ?? process.env.OPENAI_API_KEY;
1308
- const model = process.env.AGENT_MEMORY_RERANK_MODEL ?? "Qwen/Qwen3-Reranker-8B";
1309
- const baseUrl = process.env.AGENT_MEMORY_RERANK_BASE_URL ?? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1";
1310
- if (!apiKey) return null;
1311
- return createOpenAIRerankProvider({ apiKey, model, baseUrl });
1312
- }
1313
- return null;
1314
- }
1315
- function createOpenAIRerankProvider(opts) {
1316
- const baseUrl = opts.baseUrl ?? "https://api.openai.com/v1";
1317
- return {
1318
- id: "openai-rerank",
1319
- model: opts.model,
1320
- async rerank(query, documents) {
1321
- const resp = await fetch(`${baseUrl.replace(/\/$/, "")}/rerank`, {
1322
- method: "POST",
1323
- headers: {
1324
- "content-type": "application/json",
1325
- authorization: authHeader2(opts.apiKey)
1326
- },
1327
- body: JSON.stringify({ model: opts.model, query, documents })
1016
+ const scheduleIngest = (absPath, reason) => {
1017
+ if (stopped) return;
1018
+ if (!isTrackedMarkdownFile(absPath)) return;
1019
+ stats.triggers += 1;
1020
+ const previous = timers.get(absPath);
1021
+ if (previous) clearTimeout(previous);
1022
+ const timer = setTimeout(() => {
1023
+ timers.delete(absPath);
1024
+ queue = queue.then(() => {
1025
+ ingestFile(absPath, reason);
1026
+ }).catch((err) => {
1027
+ stats.errors += 1;
1028
+ logger.error(`[auto-ingest] queue error: ${String(err)}`);
1328
1029
  });
1329
- if (!resp.ok) {
1330
- const body = await resp.text().catch(() => "");
1331
- throw new Error(`Rerank API failed: ${resp.status} ${resp.statusText} ${body}`.trim());
1332
- }
1333
- const data = await resp.json();
1334
- const results = data.results ?? [];
1335
- return results.map((r) => {
1336
- const index = typeof r.index === "number" ? r.index : Number.NaN;
1337
- const relevance = typeof r.relevance_score === "number" ? r.relevance_score : Number.NaN;
1338
- return { index, relevance_score: relevance };
1339
- }).filter((r) => Number.isInteger(r.index) && Number.isFinite(r.relevance_score));
1030
+ }, debounceMs);
1031
+ timers.set(absPath, timer);
1032
+ };
1033
+ const safeWatch = (dir, onEvent) => {
1034
+ if (!existsSync2(dir)) {
1035
+ logger.warn(`[auto-ingest] watch path does not exist, skipping: ${dir}`);
1036
+ return;
1037
+ }
1038
+ try {
1039
+ const watcher = watch(dir, { persistent: true }, (eventType, filename) => {
1040
+ if (!filename) return;
1041
+ onEvent(eventType, filename.toString());
1042
+ });
1043
+ watchers.push(watcher);
1044
+ logger.log(`[auto-ingest] watching ${dir}`);
1045
+ } catch (err) {
1046
+ stats.errors += 1;
1047
+ logger.error(`[auto-ingest] failed to watch ${dir}: ${String(err)}`);
1340
1048
  }
1341
1049
  };
1342
- }
1343
-
1344
- // src/search/embed.ts
1345
- async function embedMemory(db, memoryId, provider, opts) {
1346
- const row = db.prepare("SELECT id, agent_id, content FROM memories WHERE id = ?").get(memoryId);
1347
- if (!row) return false;
1348
- if (opts?.agent_id && row.agent_id !== opts.agent_id) return false;
1349
- const model = opts?.model ?? provider.model;
1350
- const maxChars = opts?.maxChars ?? 2e3;
1351
- const text = row.content.length > maxChars ? row.content.slice(0, maxChars) : row.content;
1352
- const vector = await provider.embed(text);
1353
- upsertEmbedding(db, {
1354
- agent_id: row.agent_id,
1355
- memory_id: row.id,
1356
- model,
1357
- vector
1050
+ safeWatch(workspaceDir, (eventType, filename) => {
1051
+ if (filename === "MEMORY.md") {
1052
+ scheduleIngest(join2(workspaceDir, filename), `workspace:${eventType}`);
1053
+ }
1358
1054
  });
1359
- return true;
1360
- }
1361
- async function embedMissingForAgent(db, provider, opts) {
1362
- const agentId = opts?.agent_id ?? "default";
1363
- const model = opts?.model ?? provider.model;
1364
- const limit = opts?.limit ?? 1e3;
1365
- const rows = db.prepare(
1366
- `SELECT m.id
1367
- FROM memories m
1368
- LEFT JOIN embeddings e
1369
- ON e.memory_id = m.id AND e.agent_id = m.agent_id AND e.model = ?
1370
- WHERE m.agent_id = ? AND e.memory_id IS NULL
1371
- ORDER BY m.updated_at DESC
1372
- LIMIT ?`
1373
- ).all(model, agentId, limit);
1374
- let embedded = 0;
1375
- for (const r of rows) {
1376
- const ok = await embedMemory(db, r.id, provider, { agent_id: agentId, model, maxChars: opts?.maxChars });
1377
- if (ok) embedded++;
1055
+ safeWatch(memoryDir, (eventType, filename) => {
1056
+ if (filename.endsWith(".md")) {
1057
+ scheduleIngest(join2(memoryDir, filename), `memory:${eventType}`);
1058
+ }
1059
+ });
1060
+ if (initialScan) {
1061
+ scheduleIngest(memoryMdPath, "initial");
1062
+ if (existsSync2(memoryDir)) {
1063
+ for (const file of readdirSync(memoryDir)) {
1064
+ if (file.endsWith(".md")) {
1065
+ scheduleIngest(join2(memoryDir, file), "initial");
1066
+ }
1067
+ }
1068
+ }
1378
1069
  }
1379
- return { embedded, scanned: rows.length };
1070
+ return {
1071
+ close: () => {
1072
+ if (stopped) return;
1073
+ stopped = true;
1074
+ for (const timer of timers.values()) {
1075
+ clearTimeout(timer);
1076
+ }
1077
+ timers.clear();
1078
+ for (const watcher of watchers) {
1079
+ try {
1080
+ watcher.close();
1081
+ } catch {
1082
+ }
1083
+ }
1084
+ logger.log(
1085
+ `[auto-ingest] stopped triggers=${stats.triggers} files=${stats.filesProcessed} extracted=${stats.extracted} written=${stats.written} skipped=${stats.skipped} errors=${stats.errors}`
1086
+ );
1087
+ }
1088
+ };
1380
1089
  }
1381
1090
 
1382
1091
  // src/sleep/decay.ts
@@ -1439,74 +1148,15 @@ function getDecayedMemories(db, threshold = 0.05, opts) {
1439
1148
  ).all(...agentId ? [threshold, agentId] : [threshold]);
1440
1149
  }
1441
1150
 
1442
- // src/sleep/sync.ts
1443
- function syncOne(db, input) {
1444
- const memInput = {
1445
- content: input.content,
1446
- type: input.type ?? "event",
1447
- priority: input.priority,
1448
- emotion_val: input.emotion_val,
1449
- source: input.source,
1450
- agent_id: input.agent_id,
1451
- uri: input.uri
1452
- };
1453
- const guardResult = guard(db, memInput);
1454
- switch (guardResult.action) {
1455
- case "skip":
1456
- return { action: "skipped", reason: guardResult.reason, memoryId: guardResult.existingId };
1457
- case "add": {
1458
- const mem = createMemory(db, memInput);
1459
- if (!mem) return { action: "skipped", reason: "createMemory returned null" };
1460
- if (input.uri) {
1461
- try {
1462
- createPath(db, mem.id, input.uri);
1463
- } catch {
1464
- }
1465
- }
1466
- return { action: "added", memoryId: mem.id, reason: guardResult.reason };
1467
- }
1468
- case "update": {
1469
- if (!guardResult.existingId) return { action: "skipped", reason: "No existing ID for update" };
1470
- createSnapshot(db, guardResult.existingId, "update", "sync");
1471
- updateMemory(db, guardResult.existingId, { content: input.content });
1472
- return { action: "updated", memoryId: guardResult.existingId, reason: guardResult.reason };
1473
- }
1474
- case "merge": {
1475
- if (!guardResult.existingId || !guardResult.mergedContent) {
1476
- return { action: "skipped", reason: "Missing merge data" };
1477
- }
1478
- createSnapshot(db, guardResult.existingId, "merge", "sync");
1479
- updateMemory(db, guardResult.existingId, { content: guardResult.mergedContent });
1480
- return { action: "merged", memoryId: guardResult.existingId, reason: guardResult.reason };
1481
- }
1482
- }
1483
- }
1484
- function syncBatch(db, inputs) {
1485
- const results = [];
1486
- const transaction = db.transaction(() => {
1487
- for (const input of inputs) {
1488
- results.push(syncOne(db, input));
1489
- }
1490
- });
1491
- transaction();
1492
- return results;
1493
- }
1494
-
1495
1151
  // src/sleep/tidy.ts
1496
1152
  function runTidy(db, opts) {
1497
1153
  const threshold = opts?.vitalityThreshold ?? 0.05;
1498
- const maxSnapshots = opts?.maxSnapshotsPerMemory ?? 10;
1499
1154
  const agentId = opts?.agent_id;
1500
1155
  let archived = 0;
1501
1156
  let orphansCleaned = 0;
1502
- let snapshotsPruned = 0;
1503
1157
  const transaction = db.transaction(() => {
1504
1158
  const decayed = getDecayedMemories(db, threshold, agentId ? { agent_id: agentId } : void 0);
1505
1159
  for (const mem of decayed) {
1506
- try {
1507
- createSnapshot(db, mem.id, "delete", "tidy");
1508
- } catch {
1509
- }
1510
1160
  deleteMemory(db, mem.id);
1511
1161
  archived++;
1512
1162
  }
@@ -1518,35 +1168,15 @@ function runTidy(db, opts) {
1518
1168
  "DELETE FROM paths WHERE memory_id NOT IN (SELECT id FROM memories)"
1519
1169
  ).run();
1520
1170
  orphansCleaned = orphans.changes;
1521
- const memoriesWithSnapshots = agentId ? db.prepare(
1522
- `SELECT s.memory_id, COUNT(*) as cnt
1523
- FROM snapshots s
1524
- JOIN memories m ON m.id = s.memory_id
1525
- WHERE m.agent_id = ?
1526
- GROUP BY s.memory_id HAVING cnt > ?`
1527
- ).all(agentId, maxSnapshots) : db.prepare(
1528
- `SELECT memory_id, COUNT(*) as cnt FROM snapshots
1529
- GROUP BY memory_id HAVING cnt > ?`
1530
- ).all(maxSnapshots);
1531
- for (const { memory_id } of memoriesWithSnapshots) {
1532
- const pruned = db.prepare(
1533
- `DELETE FROM snapshots WHERE id NOT IN (
1534
- SELECT id FROM snapshots WHERE memory_id = ?
1535
- ORDER BY created_at DESC LIMIT ?
1536
- ) AND memory_id = ?`
1537
- ).run(memory_id, maxSnapshots, memory_id);
1538
- snapshotsPruned += pruned.changes;
1539
- }
1540
1171
  });
1541
1172
  transaction();
1542
- return { archived, orphansCleaned, snapshotsPruned };
1173
+ return { archived, orphansCleaned };
1543
1174
  }
1544
1175
 
1545
1176
  // src/sleep/govern.ts
1546
1177
  function runGovern(db, opts) {
1547
1178
  const agentId = opts?.agent_id;
1548
1179
  let orphanPaths = 0;
1549
- let orphanLinks = 0;
1550
1180
  let emptyMemories = 0;
1551
1181
  const transaction = db.transaction(() => {
1552
1182
  const pathResult = agentId ? db.prepare(
@@ -1555,23 +1185,11 @@ function runGovern(db, opts) {
1555
1185
  AND memory_id NOT IN (SELECT id FROM memories WHERE agent_id = ?)`
1556
1186
  ).run(agentId, agentId) : db.prepare("DELETE FROM paths WHERE memory_id NOT IN (SELECT id FROM memories)").run();
1557
1187
  orphanPaths = pathResult.changes;
1558
- const linkResult = agentId ? db.prepare(
1559
- `DELETE FROM links WHERE
1560
- agent_id = ? AND (
1561
- source_id NOT IN (SELECT id FROM memories WHERE agent_id = ?) OR
1562
- target_id NOT IN (SELECT id FROM memories WHERE agent_id = ?)
1563
- )`
1564
- ).run(agentId, agentId, agentId) : db.prepare(
1565
- `DELETE FROM links WHERE
1566
- source_id NOT IN (SELECT id FROM memories) OR
1567
- target_id NOT IN (SELECT id FROM memories)`
1568
- ).run();
1569
- orphanLinks = linkResult.changes;
1570
1188
  const emptyResult = agentId ? db.prepare("DELETE FROM memories WHERE agent_id = ? AND TRIM(content) = ''").run(agentId) : db.prepare("DELETE FROM memories WHERE TRIM(content) = ''").run();
1571
1189
  emptyMemories = emptyResult.changes;
1572
1190
  });
1573
1191
  transaction();
1574
- return { orphanPaths, orphanLinks, emptyMemories };
1192
+ return { orphanPaths, emptyMemories };
1575
1193
  }
1576
1194
 
1577
1195
  // src/sleep/boot.ts
@@ -1628,60 +1246,39 @@ function boot(db, opts) {
1628
1246
  export {
1629
1247
  boot,
1630
1248
  calculateVitality,
1631
- classifyIntent,
1249
+ classifyIngestType,
1632
1250
  contentHash,
1633
1251
  countMemories,
1634
- createDashScopeProvider,
1635
- createLink,
1636
1252
  createMemory,
1637
- createOpenAIProvider,
1638
- createOpenAIRerankProvider,
1639
1253
  createPath,
1640
- createSnapshot,
1641
- decodeEmbedding,
1642
- deleteLink,
1643
1254
  deleteMemory,
1644
1255
  deletePath,
1645
- embedMemory,
1646
- embedMissingForAgent,
1647
- encodeEmbedding,
1648
1256
  exportMemories,
1257
+ extractIngestItems,
1649
1258
  getDecayedMemories,
1650
- getDefaultInstruction,
1651
- getEmbedding,
1652
- getEmbeddingProviderFromEnv,
1653
- getLinks,
1654
1259
  getMemory,
1655
- getOutgoingLinks,
1656
1260
  getPath,
1657
1261
  getPathByUri,
1658
1262
  getPathsByDomain,
1659
1263
  getPathsByMemory,
1660
1264
  getPathsByPrefix,
1661
- getRerankerProviderFromEnv,
1662
- getSnapshot,
1663
- getSnapshots,
1664
- getStrategy,
1665
1265
  guard,
1266
+ ingestText,
1666
1267
  isCountRow,
1667
- listEmbeddings,
1668
1268
  listMemories,
1669
1269
  openDatabase,
1670
1270
  parseUri,
1671
1271
  recordAccess,
1672
- rerank,
1673
- rerankWithProvider,
1674
- rollback,
1272
+ runAutoIngestWatcher,
1675
1273
  runDecay,
1676
1274
  runGovern,
1677
1275
  runTidy,
1678
1276
  searchBM25,
1679
- searchHybrid,
1277
+ slugify,
1278
+ splitIngestBlocks,
1680
1279
  syncBatch,
1681
1280
  syncOne,
1682
1281
  tokenize,
1683
- traverse,
1684
- updateMemory,
1685
- upsertEmbedding
1282
+ updateMemory
1686
1283
  };
1687
1284
  //# sourceMappingURL=index.js.map