ziiagentmemory 0.1.0 → 0.2.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.
@@ -0,0 +1,206 @@
1
+ import { createRequire } from "node:module";
2
+ import { createHash } from "node:crypto";
3
+ //#region src/state/cjk-segmenter.ts
4
+ const cjkRequire = createRequire(import.meta.url);
5
+ const CJK_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
6
+ const KANA_RE = /[\p{Script=Hiragana}\p{Script=Katakana}]/u;
7
+ const HANGUL_RE = /\p{Script=Hangul}/u;
8
+ const CJK_RUN_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu;
9
+ const HANGUL_BLOCK_RE = /[가-힯]+/g;
10
+ const hintShown = /* @__PURE__ */ new Set();
11
+ function hasCjk(text) {
12
+ return CJK_RE.test(text);
13
+ }
14
+ function showHintOnce(key, message) {
15
+ if (hintShown.has(key)) return;
16
+ hintShown.add(key);
17
+ if (typeof process !== "undefined" && process.stderr?.write) process.stderr.write(`agentmemory: ${message}\n`);
18
+ }
19
+ let jiebaInstance = null;
20
+ let jiebaLoaded = false;
21
+ function getJieba() {
22
+ if (jiebaLoaded) return jiebaInstance;
23
+ jiebaLoaded = true;
24
+ try {
25
+ const mod = cjkRequire("@node-rs/jieba");
26
+ try {
27
+ const dictMod = cjkRequire("@node-rs/jieba/dict");
28
+ jiebaInstance = mod.Jieba.withDict(dictMod.dict);
29
+ } catch {
30
+ jiebaInstance = new mod.Jieba();
31
+ }
32
+ return jiebaInstance;
33
+ } catch {
34
+ showHintOnce("jieba", "install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization");
35
+ return null;
36
+ }
37
+ }
38
+ let jaSegmenterInstance = null;
39
+ let jaSegmenterLoaded = false;
40
+ function getJaSegmenter() {
41
+ if (jaSegmenterLoaded) return jaSegmenterInstance;
42
+ jaSegmenterLoaded = true;
43
+ try {
44
+ jaSegmenterInstance = new (cjkRequire("tiny-segmenter"))();
45
+ return jaSegmenterInstance;
46
+ } catch {
47
+ showHintOnce("tiny-segmenter", "install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization");
48
+ return null;
49
+ }
50
+ }
51
+ function cleanTokens(tokens) {
52
+ const out = [];
53
+ for (const t of tokens) {
54
+ const trimmed = t.trim();
55
+ if (trimmed) out.push(trimmed);
56
+ }
57
+ return out;
58
+ }
59
+ function segmentHan(text) {
60
+ const j = getJieba();
61
+ if (!j) return [text];
62
+ try {
63
+ return cleanTokens(j.cut(text, true));
64
+ } catch {
65
+ return [text];
66
+ }
67
+ }
68
+ function segmentKana(text) {
69
+ const s = getJaSegmenter();
70
+ if (!s) return [text];
71
+ try {
72
+ return cleanTokens(s.segment(text));
73
+ } catch {
74
+ return [text];
75
+ }
76
+ }
77
+ function segmentHangul(text) {
78
+ const out = [];
79
+ for (const m of text.matchAll(HANGUL_BLOCK_RE)) if (m[0]) out.push(m[0]);
80
+ return out;
81
+ }
82
+ function segmentCjk(text) {
83
+ if (!hasCjk(text)) return [text];
84
+ const out = [];
85
+ let cursor = 0;
86
+ for (const m of text.matchAll(CJK_RUN_RE)) {
87
+ const start = m.index ?? 0;
88
+ const run = m[0];
89
+ const end = start + run.length;
90
+ if (start > cursor) {
91
+ const piece = text.slice(cursor, start).trim();
92
+ if (piece) out.push(piece);
93
+ }
94
+ if (HANGUL_RE.test(run)) out.push(...segmentHangul(run));
95
+ else if (KANA_RE.test(run)) out.push(...segmentKana(run));
96
+ else out.push(...segmentHan(run));
97
+ cursor = end;
98
+ }
99
+ if (cursor < text.length) {
100
+ const trailing = text.slice(cursor).trim();
101
+ if (trailing) out.push(trailing);
102
+ }
103
+ return out;
104
+ }
105
+ //#endregion
106
+ //#region src/state/schema.ts
107
+ const KV = {
108
+ sessions: "mem:sessions",
109
+ observations: (sessionId) => `mem:obs:${sessionId}`,
110
+ memories: "mem:memories",
111
+ summaries: "mem:summaries",
112
+ config: "mem:config",
113
+ metrics: "mem:metrics",
114
+ health: "mem:health",
115
+ embeddings: (obsId) => `mem:emb:${obsId}`,
116
+ bm25Index: "mem:index:bm25",
117
+ relations: "mem:relations",
118
+ profiles: "mem:profiles",
119
+ claudeBridge: "mem:claude-bridge",
120
+ graphNodes: "mem:graph:nodes",
121
+ graphEdges: "mem:graph:edges",
122
+ graphSnapshot: "mem:graph:snapshot",
123
+ graphNameIndex: "mem:graph:name-index",
124
+ graphEdgeKey: "mem:graph:edge-key",
125
+ graphNodeDegree: "mem:graph:node-degree",
126
+ semantic: "mem:semantic",
127
+ procedural: "mem:procedural",
128
+ teamShared: (teamId) => `mem:team:${teamId}:shared`,
129
+ teamUsers: (teamId, userId) => `mem:team:${teamId}:users:${userId}`,
130
+ teamProfile: (teamId) => `mem:team:${teamId}:profile`,
131
+ audit: "mem:audit",
132
+ actions: "mem:actions",
133
+ actionEdges: "mem:action-edges",
134
+ leases: "mem:leases",
135
+ routines: "mem:routines",
136
+ routineRuns: "mem:routine-runs",
137
+ signals: "mem:signals",
138
+ checkpoints: "mem:checkpoints",
139
+ mesh: "mem:mesh",
140
+ sketches: "mem:sketches",
141
+ facets: "mem:facets",
142
+ sentinels: "mem:sentinels",
143
+ crystals: "mem:crystals",
144
+ lessons: "mem:lessons",
145
+ insights: "mem:insights",
146
+ graphEdgeHistory: "mem:graph:edge-history",
147
+ enrichedChunks: (sessionId) => `mem:enriched:${sessionId}`,
148
+ latentEmbeddings: (obsId) => `mem:latent:${obsId}`,
149
+ retentionScores: "mem:retention",
150
+ accessLog: "mem:access",
151
+ imageRefs: "mem:image-refs",
152
+ imageEmbeddings: "mem:image-embeddings",
153
+ slots: "mem:slots",
154
+ globalSlots: "mem:slots:global",
155
+ state: "mem:state",
156
+ commits: "mem:commits",
157
+ recentSearches: "mem:recent-searches"
158
+ };
159
+ const STREAM = {
160
+ name: "mem-live",
161
+ group: (sessionId) => sessionId,
162
+ viewerGroup: "viewer"
163
+ };
164
+ function generateId(prefix) {
165
+ return `${prefix}_${Date.now().toString(36)}_${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
166
+ }
167
+ function fingerprintId(prefix, content) {
168
+ return `${prefix}_${createHash("sha256").update(content).digest("hex").slice(0, 16)}`;
169
+ }
170
+ /**
171
+ * CJK-aware tokenizer for Jaccard similarity.
172
+ *
173
+ * For English/whitespace-delimited text we keep the original behaviour:
174
+ * split on whitespace, filter tokens longer than 2 characters.
175
+ *
176
+ * For CJK text (Chinese/Japanese/Korean) whitespace splitting produces
177
+ * near-disjoint sets because CJK text is typically written without spaces.
178
+ * We reuse the existing `segmentCjk` function and additionally emit
179
+ * character bigrams from CJK runs so that no-space Chinese text still
180
+ * produces useful overlap.
181
+ */
182
+ function tokenizeForJaccard(text) {
183
+ if (!hasCjk(text)) return new Set(text.split(/\s+/).filter((t) => t.length > 2));
184
+ const segments = segmentCjk(text);
185
+ const tokens = /* @__PURE__ */ new Set();
186
+ for (const seg of segments) {
187
+ const cleaned = seg.replace(/[^\p{L}\p{N}]/gu, "").toLowerCase();
188
+ if (cleaned.length > 2) tokens.add(cleaned);
189
+ const cjkChars = seg.match(/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu);
190
+ if (cjkChars && cjkChars.length >= 2) for (let i = 0; i < cjkChars.length - 1; i++) tokens.add(cjkChars[i] + cjkChars[i + 1]);
191
+ }
192
+ return tokens;
193
+ }
194
+ function jaccardSimilarity(a, b) {
195
+ const setA = tokenizeForJaccard(a);
196
+ const setB = tokenizeForJaccard(b);
197
+ if (setA.size === 0 && setB.size === 0) return 1;
198
+ if (setA.size === 0 || setB.size === 0) return 0;
199
+ let intersection = 0;
200
+ for (const word of setA) if (setB.has(word)) intersection++;
201
+ return intersection / (setA.size + setB.size - intersection);
202
+ }
203
+ //#endregion
204
+ export { jaccardSimilarity as a, generateId as i, STREAM as n, hasCjk as o, fingerprintId as r, segmentCjk as s, KV as t };
205
+
206
+ //# sourceMappingURL=schema-BQLY6d8C.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-BQLY6d8C.mjs","names":[],"sources":["../src/state/cjk-segmenter.ts","../src/state/schema.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\r\n\r\nconst cjkRequire = createRequire(import.meta.url);\r\n\r\nconst CJK_RE = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u;\r\nconst HAN_RE = /\\p{Script=Han}/u;\r\nconst KANA_RE = /[\\p{Script=Hiragana}\\p{Script=Katakana}]/u;\r\nconst HANGUL_RE = /\\p{Script=Hangul}/u;\r\nconst CJK_RUN_RE = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]+/gu;\r\nconst HANGUL_BLOCK_RE = /[가-힯]+/g;\r\n\r\ntype Script = \"han\" | \"kana\" | \"hangul\" | \"other\";\r\n\r\nconst hintShown = new Set<string>();\r\n\r\nexport function hasCjk(text: string): boolean {\r\n return CJK_RE.test(text);\r\n}\r\n\r\nexport function detectScript(text: string): Script {\r\n if (HAN_RE.test(text)) return \"han\";\r\n if (KANA_RE.test(text)) return \"kana\";\r\n if (HANGUL_RE.test(text)) return \"hangul\";\r\n return \"other\";\r\n}\r\n\r\nfunction showHintOnce(key: string, message: string): void {\r\n if (hintShown.has(key)) return;\r\n hintShown.add(key);\r\n if (typeof process !== \"undefined\" && process.stderr?.write) {\r\n process.stderr.write(`agentmemory: ${message}\\n`);\r\n }\r\n}\r\n\r\ninterface JiebaInstance {\r\n cut(text: string, hmm?: boolean): string[];\r\n}\r\n\r\nlet jiebaInstance: JiebaInstance | null = null;\r\nlet jiebaLoaded = false;\r\n\r\nfunction getJieba(): JiebaInstance | null {\r\n if (jiebaLoaded) return jiebaInstance;\r\n jiebaLoaded = true;\r\n try {\r\n const mod = cjkRequire(\"@node-rs/jieba\") as {\r\n Jieba: {\r\n new (): JiebaInstance;\r\n withDict(dict: Uint8Array): JiebaInstance;\r\n };\r\n };\r\n try {\r\n const dictMod = cjkRequire(\"@node-rs/jieba/dict\") as { dict: Uint8Array };\r\n jiebaInstance = mod.Jieba.withDict(dictMod.dict);\r\n } catch {\r\n jiebaInstance = new mod.Jieba();\r\n }\r\n return jiebaInstance;\r\n } catch {\r\n showHintOnce(\r\n \"jieba\",\r\n \"install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization\",\r\n );\r\n return null;\r\n }\r\n}\r\n\r\ninterface JaSegmenter {\r\n segment(text: string): string[];\r\n}\r\n\r\nlet jaSegmenterInstance: JaSegmenter | null = null;\r\nlet jaSegmenterLoaded = false;\r\n\r\nfunction getJaSegmenter(): JaSegmenter | null {\r\n if (jaSegmenterLoaded) return jaSegmenterInstance;\r\n jaSegmenterLoaded = true;\r\n try {\r\n const Ctor = cjkRequire(\"tiny-segmenter\") as new () => JaSegmenter;\r\n jaSegmenterInstance = new Ctor();\r\n return jaSegmenterInstance;\r\n } catch {\r\n showHintOnce(\r\n \"tiny-segmenter\",\r\n \"install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization\",\r\n );\r\n return null;\r\n }\r\n}\r\n\r\nfunction cleanTokens(tokens: string[]): string[] {\r\n const out: string[] = [];\r\n for (const t of tokens) {\r\n const trimmed = t.trim();\r\n if (trimmed) out.push(trimmed);\r\n }\r\n return out;\r\n}\r\n\r\nfunction segmentHan(text: string): string[] {\r\n const j = getJieba();\r\n if (!j) return [text];\r\n try {\r\n return cleanTokens(j.cut(text, true));\r\n } catch {\r\n return [text];\r\n }\r\n}\r\n\r\nfunction segmentKana(text: string): string[] {\r\n const s = getJaSegmenter();\r\n if (!s) return [text];\r\n try {\r\n return cleanTokens(s.segment(text));\r\n } catch {\r\n return [text];\r\n }\r\n}\r\n\r\nfunction segmentHangul(text: string): string[] {\r\n const out: string[] = [];\r\n for (const m of text.matchAll(HANGUL_BLOCK_RE)) {\r\n if (m[0]) out.push(m[0]);\r\n }\r\n return out;\r\n}\r\n\r\nexport function segmentCjk(text: string): string[] {\r\n if (!hasCjk(text)) return [text];\r\n\r\n const out: string[] = [];\r\n let cursor = 0;\r\n\r\n for (const m of text.matchAll(CJK_RUN_RE)) {\r\n const start = m.index ?? 0;\r\n const run = m[0];\r\n const end = start + run.length;\r\n\r\n if (start > cursor) {\r\n const piece = text.slice(cursor, start).trim();\r\n if (piece) out.push(piece);\r\n }\r\n\r\n if (HANGUL_RE.test(run)) {\r\n out.push(...segmentHangul(run));\r\n } else if (KANA_RE.test(run)) {\r\n out.push(...segmentKana(run));\r\n } else {\r\n out.push(...segmentHan(run));\r\n }\r\n\r\n cursor = end;\r\n }\r\n\r\n if (cursor < text.length) {\r\n const trailing = text.slice(cursor).trim();\r\n if (trailing) out.push(trailing);\r\n }\r\n\r\n return out;\r\n}\r\n\r\nexport function __resetCjkSegmenterStateForTests(): void {\r\n hintShown.clear();\r\n jiebaInstance = null;\r\n jiebaLoaded = false;\r\n jaSegmenterInstance = null;\r\n jaSegmenterLoaded = false;\r\n}\r\n","import { createHash } from \"node:crypto\";\r\nimport { hasCjk, segmentCjk } from \"./cjk-segmenter.js\";\r\n\r\nexport const KV = {\r\n sessions: \"mem:sessions\",\r\n observations: (sessionId: string) => `mem:obs:${sessionId}`,\r\n memories: \"mem:memories\",\r\n summaries: \"mem:summaries\",\r\n config: \"mem:config\",\r\n metrics: \"mem:metrics\",\r\n health: \"mem:health\",\r\n embeddings: (obsId: string) => `mem:emb:${obsId}`,\r\n bm25Index: \"mem:index:bm25\",\r\n relations: \"mem:relations\",\r\n profiles: \"mem:profiles\",\r\n claudeBridge: \"mem:claude-bridge\",\r\n graphNodes: \"mem:graph:nodes\",\r\n graphEdges: \"mem:graph:edges\",\r\n // #814: precomputed snapshot of the top-degree subgraph and aggregate\r\n // type counts. Saves /graph/query and /graph/stats from a full\r\n // kv.list enumeration over 75K+ node corpora, which exceeds the iii\r\n // invocation timeout and surfaces as \"Invocation stopped\" 500s.\r\n // Single fixed key (\"current\") so writes are read-modify-write under\r\n // the same keyed mutex as graph-extract.\r\n graphSnapshot: \"mem:graph:snapshot\",\r\n // #814 v2: targeted-lookup indexes so graph-extract never enumerates\r\n // the full nodes/edges scope. Each entry is a single small kv.get,\r\n // bounded payload — works at 75K+ nodes where kv.list would block\r\n // the worker event loop (37MB WS frame parse blocks heartbeat,\r\n // worker is declared dead before any Promise.race timer can fire).\r\n // - graphNameIndex: key `${type}|${name}` -> nodeId. Replaces the\r\n // existingNodes.find() O(n) dedup scan inside mem::graph-extract.\r\n // - graphEdgeKey: key `${src}|${tgt}|${type}` -> edgeId. Same for\r\n // edge dedup.\r\n // - graphNodeDegree: key nodeId -> incident-edge count. Read /\r\n // incremented on edge writes to maintain the snapshot top-N\r\n // ranking without scanning all edges.\r\n graphNameIndex: \"mem:graph:name-index\",\r\n graphEdgeKey: \"mem:graph:edge-key\",\r\n graphNodeDegree: \"mem:graph:node-degree\",\r\n semantic: \"mem:semantic\",\r\n procedural: \"mem:procedural\",\r\n teamShared: (teamId: string) => `mem:team:${teamId}:shared`,\r\n teamUsers: (teamId: string, userId: string) =>\r\n `mem:team:${teamId}:users:${userId}`,\r\n teamProfile: (teamId: string) => `mem:team:${teamId}:profile`,\r\n audit: \"mem:audit\",\r\n actions: \"mem:actions\",\r\n actionEdges: \"mem:action-edges\",\r\n leases: \"mem:leases\",\r\n routines: \"mem:routines\",\r\n routineRuns: \"mem:routine-runs\",\r\n signals: \"mem:signals\",\r\n checkpoints: \"mem:checkpoints\",\r\n mesh: \"mem:mesh\",\r\n sketches: \"mem:sketches\",\r\n facets: \"mem:facets\",\r\n sentinels: \"mem:sentinels\",\r\n crystals: \"mem:crystals\",\r\n lessons: \"mem:lessons\",\r\n insights: \"mem:insights\",\r\n graphEdgeHistory: \"mem:graph:edge-history\",\r\n enrichedChunks: (sessionId: string) => `mem:enriched:${sessionId}`,\r\n latentEmbeddings: (obsId: string) => `mem:latent:${obsId}`,\r\n retentionScores: \"mem:retention\",\r\n accessLog: \"mem:access\",\r\n imageRefs: \"mem:image-refs\",\r\n imageEmbeddings: \"mem:image-embeddings\",\r\n slots: \"mem:slots\",\r\n globalSlots: \"mem:slots:global\",\r\n state: \"mem:state\",\r\n commits: \"mem:commits\",\r\n // #771: tracks the most recent smart-search call per session, used by\r\n // the followup-rate diagnostic. Key = sessionId. TTL-swept hourly.\r\n recentSearches: \"mem:recent-searches\",\r\n} as const;\r\n\r\nexport const STREAM = {\r\n name: \"mem-live\",\r\n group: (sessionId: string) => sessionId,\r\n viewerGroup: \"viewer\",\r\n} as const;\r\n\r\nexport function generateId(prefix: string): string {\r\n const ts = Date.now().toString(36);\r\n const rand = crypto.randomUUID().replace(/-/g, \"\").slice(0, 12);\r\n return `${prefix}_${ts}_${rand}`;\r\n}\r\n\r\nexport function fingerprintId(prefix: string, content: string): string {\r\n const hash = createHash(\"sha256\").update(content).digest(\"hex\");\r\n return `${prefix}_${hash.slice(0, 16)}`;\r\n}\r\n\r\n/**\r\n * CJK-aware tokenizer for Jaccard similarity.\r\n *\r\n * For English/whitespace-delimited text we keep the original behaviour:\r\n * split on whitespace, filter tokens longer than 2 characters.\r\n *\r\n * For CJK text (Chinese/Japanese/Korean) whitespace splitting produces\r\n * near-disjoint sets because CJK text is typically written without spaces.\r\n * We reuse the existing `segmentCjk` function and additionally emit\r\n * character bigrams from CJK runs so that no-space Chinese text still\r\n * produces useful overlap.\r\n */\r\nfunction tokenizeForJaccard(text: string): Set<string> {\r\n // Fast path: no CJK characters → original whitespace split\r\n if (!hasCjk(text)) {\r\n return new Set(text.split(/\\s+/).filter((t) => t.length > 2));\r\n }\r\n\r\n // CJK path: segment + add character bigrams for overlap\r\n const segments = segmentCjk(text);\r\n const tokens = new Set<string>();\r\n\r\n for (const seg of segments) {\r\n // Keep the whole segment as a token (works for word-segmented Chinese\r\n // via jieba, and for English fragments in mixed text).\r\n const cleaned = seg.replace(/[^\\p{L}\\p{N}]/gu, \"\").toLowerCase();\r\n if (cleaned.length > 2) {\r\n tokens.add(cleaned);\r\n }\r\n\r\n // Add CJK character bigrams from each segment so unsegmented CJK\r\n // text can still produce meaningful overlap.\r\n const cjkChars = seg.match(/[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/gu);\r\n if (cjkChars && cjkChars.length >= 2) {\r\n for (let i = 0; i < cjkChars.length - 1; i++) {\r\n tokens.add(cjkChars[i] + cjkChars[i + 1]);\r\n }\r\n }\r\n }\r\n\r\n return tokens;\r\n}\r\n\r\nexport function jaccardSimilarity(a: string, b: string): number {\r\n const setA = tokenizeForJaccard(a);\r\n const setB = tokenizeForJaccard(b);\r\n if (setA.size === 0 && setB.size === 0) return 1;\r\n if (setA.size === 0 || setB.size === 0) return 0;\r\n let intersection = 0;\r\n for (const word of setA) {\r\n if (setB.has(word)) intersection++;\r\n }\r\n return intersection / (setA.size + setB.size - intersection);\r\n}\r\n"],"mappings":";;;AAEA,MAAM,aAAa,cAAc,OAAO,KAAK,IAAI;AAEjD,MAAM,SAAS;AAEf,MAAM,UAAU;AAChB,MAAM,YAAY;AAClB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AAIxB,MAAM,4BAAY,IAAI,KAAa;AAEnC,SAAgB,OAAO,MAAuB;AAC5C,QAAO,OAAO,KAAK,KAAK;;AAU1B,SAAS,aAAa,KAAa,SAAuB;AACxD,KAAI,UAAU,IAAI,IAAI,CAAE;AACxB,WAAU,IAAI,IAAI;AAClB,KAAI,OAAO,YAAY,eAAe,QAAQ,QAAQ,MACpD,SAAQ,OAAO,MAAM,gBAAgB,QAAQ,IAAI;;AAQrD,IAAI,gBAAsC;AAC1C,IAAI,cAAc;AAElB,SAAS,WAAiC;AACxC,KAAI,YAAa,QAAO;AACxB,eAAc;AACd,KAAI;EACF,MAAM,MAAM,WAAW,iBAAiB;AAMxC,MAAI;GACF,MAAM,UAAU,WAAW,sBAAsB;AACjD,mBAAgB,IAAI,MAAM,SAAS,QAAQ,KAAK;UAC1C;AACN,mBAAgB,IAAI,IAAI,OAAO;;AAEjC,SAAO;SACD;AACN,eACE,SACA,8FACD;AACD,SAAO;;;AAQX,IAAI,sBAA0C;AAC9C,IAAI,oBAAoB;AAExB,SAAS,iBAAqC;AAC5C,KAAI,kBAAmB,QAAO;AAC9B,qBAAoB;AACpB,KAAI;AAEF,wBAAsB,KADT,WAAW,iBACM,GAAE;AAChC,SAAO;SACD;AACN,eACE,kBACA,+FACD;AACD,SAAO;;;AAIX,SAAS,YAAY,QAA4B;CAC/C,MAAM,MAAgB,EAAE;AACxB,MAAK,MAAM,KAAK,QAAQ;EACtB,MAAM,UAAU,EAAE,MAAM;AACxB,MAAI,QAAS,KAAI,KAAK,QAAQ;;AAEhC,QAAO;;AAGT,SAAS,WAAW,MAAwB;CAC1C,MAAM,IAAI,UAAU;AACpB,KAAI,CAAC,EAAG,QAAO,CAAC,KAAK;AACrB,KAAI;AACF,SAAO,YAAY,EAAE,IAAI,MAAM,KAAK,CAAC;SAC/B;AACN,SAAO,CAAC,KAAK;;;AAIjB,SAAS,YAAY,MAAwB;CAC3C,MAAM,IAAI,gBAAgB;AAC1B,KAAI,CAAC,EAAG,QAAO,CAAC,KAAK;AACrB,KAAI;AACF,SAAO,YAAY,EAAE,QAAQ,KAAK,CAAC;SAC7B;AACN,SAAO,CAAC,KAAK;;;AAIjB,SAAS,cAAc,MAAwB;CAC7C,MAAM,MAAgB,EAAE;AACxB,MAAK,MAAM,KAAK,KAAK,SAAS,gBAAgB,CAC5C,KAAI,EAAE,GAAI,KAAI,KAAK,EAAE,GAAG;AAE1B,QAAO;;AAGT,SAAgB,WAAW,MAAwB;AACjD,KAAI,CAAC,OAAO,KAAK,CAAE,QAAO,CAAC,KAAK;CAEhC,MAAM,MAAgB,EAAE;CACxB,IAAI,SAAS;AAEb,MAAK,MAAM,KAAK,KAAK,SAAS,WAAW,EAAE;EACzC,MAAM,QAAQ,EAAE,SAAS;EACzB,MAAM,MAAM,EAAE;EACd,MAAM,MAAM,QAAQ,IAAI;AAExB,MAAI,QAAQ,QAAQ;GAClB,MAAM,QAAQ,KAAK,MAAM,QAAQ,MAAM,CAAC,MAAM;AAC9C,OAAI,MAAO,KAAI,KAAK,MAAM;;AAG5B,MAAI,UAAU,KAAK,IAAI,CACrB,KAAI,KAAK,GAAG,cAAc,IAAI,CAAC;WACtB,QAAQ,KAAK,IAAI,CAC1B,KAAI,KAAK,GAAG,YAAY,IAAI,CAAC;MAE7B,KAAI,KAAK,GAAG,WAAW,IAAI,CAAC;AAG9B,WAAS;;AAGX,KAAI,SAAS,KAAK,QAAQ;EACxB,MAAM,WAAW,KAAK,MAAM,OAAO,CAAC,MAAM;AAC1C,MAAI,SAAU,KAAI,KAAK,SAAS;;AAGlC,QAAO;;;;AC5JT,MAAa,KAAK;CAChB,UAAU;CACV,eAAe,cAAsB,WAAW;CAChD,UAAU;CACV,WAAW;CACX,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,aAAa,UAAkB,WAAW;CAC1C,WAAW;CACX,WAAW;CACX,UAAU;CACV,cAAc;CACd,YAAY;CACZ,YAAY;CAOZ,eAAe;CAaf,gBAAgB;CAChB,cAAc;CACd,iBAAiB;CACjB,UAAU;CACV,YAAY;CACZ,aAAa,WAAmB,YAAY,OAAO;CACnD,YAAY,QAAgB,WAC1B,YAAY,OAAO,SAAS;CAC9B,cAAc,WAAmB,YAAY,OAAO;CACpD,OAAO;CACP,SAAS;CACT,aAAa;CACb,QAAQ;CACR,UAAU;CACV,aAAa;CACb,SAAS;CACT,aAAa;CACb,MAAM;CACN,UAAU;CACV,QAAQ;CACR,WAAW;CACX,UAAU;CACV,SAAS;CACT,UAAU;CACV,kBAAkB;CAClB,iBAAiB,cAAsB,gBAAgB;CACvD,mBAAmB,UAAkB,cAAc;CACnD,iBAAiB;CACjB,WAAW;CACX,WAAW;CACX,iBAAiB;CACjB,OAAO;CACP,aAAa;CACb,OAAO;CACP,SAAS;CAGT,gBAAgB;CACjB;AAED,MAAa,SAAS;CACpB,MAAM;CACN,QAAQ,cAAsB;CAC9B,aAAa;CACd;AAED,SAAgB,WAAW,QAAwB;AAGjD,QAAO,GAAG,OAAO,GAFN,KAAK,KAAK,CAAC,SAAS,GAET,CAAC,GADV,OAAO,YAAY,CAAC,QAAQ,MAAM,GAAG,CAAC,MAAM,GAAG,GAC9B;;AAGhC,SAAgB,cAAc,QAAgB,SAAyB;AAErE,QAAO,GAAG,OAAO,GADJ,WAAW,SAAS,CAAC,OAAO,QAAQ,CAAC,OAAO,MACjC,CAAC,MAAM,GAAG,GAAG;;;;;;;;;;;;;;AAevC,SAAS,mBAAmB,MAA2B;AAErD,KAAI,CAAC,OAAO,KAAK,CACf,QAAO,IAAI,IAAI,KAAK,MAAM,MAAM,CAAC,QAAQ,MAAM,EAAE,SAAS,EAAE,CAAC;CAI/D,MAAM,WAAW,WAAW,KAAK;CACjC,MAAM,yBAAS,IAAI,KAAa;AAEhC,MAAK,MAAM,OAAO,UAAU;EAG1B,MAAM,UAAU,IAAI,QAAQ,mBAAmB,GAAG,CAAC,aAAa;AAChE,MAAI,QAAQ,SAAS,EACnB,QAAO,IAAI,QAAQ;EAKrB,MAAM,WAAW,IAAI,MAAM,4EAA4E;AACvG,MAAI,YAAY,SAAS,UAAU,EACjC,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,SAAS,GAAG,IACvC,QAAO,IAAI,SAAS,KAAK,SAAS,IAAI,GAAG;;AAK/C,QAAO;;AAGT,SAAgB,kBAAkB,GAAW,GAAmB;CAC9D,MAAM,OAAO,mBAAmB,EAAE;CAClC,MAAM,OAAO,mBAAmB,EAAE;AAClC,KAAI,KAAK,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAC/C,KAAI,KAAK,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;CAC/C,IAAI,eAAe;AACnB,MAAK,MAAM,QAAQ,KACjB,KAAI,KAAK,IAAI,KAAK,CAAE;AAEtB,QAAO,gBAAgB,KAAK,OAAO,KAAK,OAAO"}
@@ -1,9 +1,8 @@
1
- import { a as jaccardSimilarity, i as generateId, n as STREAM, r as fingerprintId, t as KV } from "./schema-CNiCWzV9.mjs";
1
+ import { a as jaccardSimilarity, i as generateId, n as STREAM, o as hasCjk, r as fingerprintId, s as segmentCjk, t as KV } from "./schema-BQLY6d8C.mjs";
2
2
  import { n as logger, t as bootLog } from "./logger-BEB7pCI9.mjs";
3
3
  import { i as VERSION, n as getAllTools, r as getVisibleTools } from "./tools-registry-DCt2KAWA.mjs";
4
4
  import { a as isManagedImagePath, i as getMaxBytes, n as IMAGES_DIR, o as saveImageToDisk, r as deleteImage, t as withKeyedLock } from "./keyed-mutex-DTHtcZrt.mjs";
5
5
  import { _ as loadFallbackConfig, a as getEnvVar, c as isAgentScopeIsolated, d as isContextInjectionEnabled, f as isDropStaleIndexEnabled, g as loadEmbeddingConfig, h as loadConfig, i as getConsolidationDecayDays, l as isAutoCompressEnabled, m as loadClaudeBridgeConfig, n as detectLlmProviderKind, o as getFollowupWindowSeconds, p as isGraphExtractionEnabled, r as getAgentId, t as detectEmbeddingProvider, u as isConsolidationEnabled, v as loadSnapshotConfig, y as loadTeamConfig } from "./config-ChC9EtgV.mjs";
6
- import { createRequire } from "node:module";
7
6
  import { execFile } from "node:child_process";
8
7
  import { constants, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
9
8
  import { basename, dirname, extname, join, resolve, sep } from "node:path";
@@ -2072,109 +2071,6 @@ function getSynonyms(stemmedTerm) {
2072
2071
  return syns ? [...syns] : [];
2073
2072
  }
2074
2073
  //#endregion
2075
- //#region src/state/cjk-segmenter.ts
2076
- const cjkRequire = createRequire(import.meta.url);
2077
- const CJK_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
2078
- const KANA_RE = /[\p{Script=Hiragana}\p{Script=Katakana}]/u;
2079
- const HANGUL_RE = /\p{Script=Hangul}/u;
2080
- const CJK_RUN_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu;
2081
- const HANGUL_BLOCK_RE = /[가-힯]+/g;
2082
- const hintShown = /* @__PURE__ */ new Set();
2083
- function hasCjk(text) {
2084
- return CJK_RE.test(text);
2085
- }
2086
- function showHintOnce(key, message) {
2087
- if (hintShown.has(key)) return;
2088
- hintShown.add(key);
2089
- if (typeof process !== "undefined" && process.stderr?.write) process.stderr.write(`agentmemory: ${message}\n`);
2090
- }
2091
- let jiebaInstance = null;
2092
- let jiebaLoaded = false;
2093
- function getJieba() {
2094
- if (jiebaLoaded) return jiebaInstance;
2095
- jiebaLoaded = true;
2096
- try {
2097
- const mod = cjkRequire("@node-rs/jieba");
2098
- try {
2099
- const dictMod = cjkRequire("@node-rs/jieba/dict");
2100
- jiebaInstance = mod.Jieba.withDict(dictMod.dict);
2101
- } catch {
2102
- jiebaInstance = new mod.Jieba();
2103
- }
2104
- return jiebaInstance;
2105
- } catch {
2106
- showHintOnce("jieba", "install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization");
2107
- return null;
2108
- }
2109
- }
2110
- let jaSegmenterInstance = null;
2111
- let jaSegmenterLoaded = false;
2112
- function getJaSegmenter() {
2113
- if (jaSegmenterLoaded) return jaSegmenterInstance;
2114
- jaSegmenterLoaded = true;
2115
- try {
2116
- jaSegmenterInstance = new (cjkRequire("tiny-segmenter"))();
2117
- return jaSegmenterInstance;
2118
- } catch {
2119
- showHintOnce("tiny-segmenter", "install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization");
2120
- return null;
2121
- }
2122
- }
2123
- function cleanTokens(tokens) {
2124
- const out = [];
2125
- for (const t of tokens) {
2126
- const trimmed = t.trim();
2127
- if (trimmed) out.push(trimmed);
2128
- }
2129
- return out;
2130
- }
2131
- function segmentHan(text) {
2132
- const j = getJieba();
2133
- if (!j) return [text];
2134
- try {
2135
- return cleanTokens(j.cut(text, true));
2136
- } catch {
2137
- return [text];
2138
- }
2139
- }
2140
- function segmentKana(text) {
2141
- const s = getJaSegmenter();
2142
- if (!s) return [text];
2143
- try {
2144
- return cleanTokens(s.segment(text));
2145
- } catch {
2146
- return [text];
2147
- }
2148
- }
2149
- function segmentHangul(text) {
2150
- const out = [];
2151
- for (const m of text.matchAll(HANGUL_BLOCK_RE)) if (m[0]) out.push(m[0]);
2152
- return out;
2153
- }
2154
- function segmentCjk(text) {
2155
- if (!hasCjk(text)) return [text];
2156
- const out = [];
2157
- let cursor = 0;
2158
- for (const m of text.matchAll(CJK_RUN_RE)) {
2159
- const start = m.index ?? 0;
2160
- const run = m[0];
2161
- const end = start + run.length;
2162
- if (start > cursor) {
2163
- const piece = text.slice(cursor, start).trim();
2164
- if (piece) out.push(piece);
2165
- }
2166
- if (HANGUL_RE.test(run)) out.push(...segmentHangul(run));
2167
- else if (KANA_RE.test(run)) out.push(...segmentKana(run));
2168
- else out.push(...segmentHan(run));
2169
- cursor = end;
2170
- }
2171
- if (cursor < text.length) {
2172
- const trailing = text.slice(cursor).trim();
2173
- if (trailing) out.push(trailing);
2174
- }
2175
- return out;
2176
- }
2177
- //#endregion
2178
2074
  //#region src/state/search-index.ts
2179
2075
  var SearchIndex = class SearchIndex {
2180
2076
  entries = /* @__PURE__ */ new Map();
@@ -3338,7 +3234,7 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
3338
3234
  if (pendingImageData && (pendingImageData.startsWith("data:image/") || pendingImageData.startsWith("iVBORw0KGgo") || pendingImageData.startsWith("/9j/"))) {
3339
3235
  const { filePath, bytesWritten } = await saveImageToDisk(pendingImageData);
3340
3236
  raw.imageData = filePath;
3341
- const { incrementImageRef } = await import("./image-refs-C2YU7BIm.mjs");
3237
+ const { incrementImageRef } = await import("./image-refs-Cc7-ua5j.mjs");
3342
3238
  await incrementImageRef(kv, filePath);
3343
3239
  sdk.trigger({
3344
3240
  function_id: "mem::disk-size-delta",
@@ -3359,7 +3255,7 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
3359
3255
  await kv.set(KV.observations(payload.sessionId), obsId, raw);
3360
3256
  } catch (error) {
3361
3257
  if (raw.imageData) try {
3362
- const { decrementImageRef } = await import("./image-refs-C2YU7BIm.mjs");
3258
+ const { decrementImageRef } = await import("./image-refs-Cc7-ua5j.mjs");
3363
3259
  await decrementImageRef(kv, sdk, raw.imageData);
3364
3260
  } catch (rollbackError) {
3365
3261
  logger.error("Failed to roll back image ref after observation write failure", {
@@ -3533,7 +3429,7 @@ function registerImageQuotaCleanup(sdk, kv) {
3533
3429
  await withKeyedLock(`imgRef:${f.filePath}`, async () => {
3534
3430
  let refCount;
3535
3431
  try {
3536
- const { getImageRefCount } = await import("./image-refs-C2YU7BIm.mjs");
3432
+ const { getImageRefCount } = await import("./image-refs-Cc7-ua5j.mjs");
3537
3433
  refCount = await getImageRefCount(kv, f.filePath);
3538
3434
  } catch (err) {
3539
3435
  logger.error("Failed to read refCount; skipping eviction", {
@@ -4945,7 +4841,7 @@ function registerSummarizeFunction(sdk, kv, provider, metricsStore) {
4945
4841
  error: "no_observations"
4946
4842
  };
4947
4843
  }
4948
- if (provider.name === "noop") {
4844
+ if (provider.name === "noop" || provider.name === "resilient(noop)") {
4949
4845
  logger.info("Summarize skipped — no LLM provider configured", { sessionId });
4950
4846
  return {
4951
4847
  success: false,
@@ -5556,6 +5452,34 @@ function registerPatternsFunction(sdk, kv) {
5556
5452
  });
5557
5453
  }
5558
5454
  //#endregion
5455
+ //#region src/state/memory-dedup.ts
5456
+ /**
5457
+ * Negation guard for memory dedup.
5458
+ *
5459
+ * Prevents "do X" from superseding "do not X" (and vice versa) even when
5460
+ * the two strings share enough tokens to exceed the Jaccard threshold.
5461
+ *
5462
+ * The guard is deliberately narrow and conservative:
5463
+ * - Only triggers when one side has a negation marker and the other does not.
5464
+ * - Supports English ("not", "never", "don't", "do not") and CJK
5465
+ * ("不要", "别", "無", "なし", "않") markers.
5466
+ * - If BOTH sides have negation markers, they are treated as compatible
5467
+ * (both say "don't do X") and the guard does not fire.
5468
+ */
5469
+ const EN_NEGATION = /\b(not|never|don't|do not|cannot|should not|must not|no)\b/i;
5470
+ const CJK_NEGATION = /不要|别|勿|無|无|不|なし|않|안|못/i;
5471
+ function hasNegation(text) {
5472
+ return EN_NEGATION.test(text) || CJK_NEGATION.test(text);
5473
+ }
5474
+ /**
5475
+ * Returns true if the two texts appear to be in a negation conflict
5476
+ * (one says do X, the other says do NOT do X) and should NOT be superseded.
5477
+ */
5478
+ function isNegationConflict(a, b) {
5479
+ if (hasNegation(a) === hasNegation(b)) return false;
5480
+ return true;
5481
+ }
5482
+ //#endregion
5559
5483
  //#region src/functions/remember.ts
5560
5484
  function registerRememberFunction(sdk, kv) {
5561
5485
  sdk.registerFunction("mem::remember", async (data) => {
@@ -5594,7 +5518,7 @@ function registerRememberFunction(sdk, kv) {
5594
5518
  for (const existing of existingMemories) {
5595
5519
  if (existing.isLatest === false) continue;
5596
5520
  if (project && existing.project && existing.project !== project) continue;
5597
- if (jaccardSimilarity(lowerContent, existing.content.toLowerCase()) > .7) {
5521
+ if (jaccardSimilarity(lowerContent, existing.content.toLowerCase()) > .7 && !isNegationConflict(lowerContent, existing.content.toLowerCase())) {
5598
5522
  supersededId = existing.id;
5599
5523
  supersededVersion = existing.version ?? 1;
5600
5524
  supersededMemory = existing;
@@ -5744,7 +5668,7 @@ function registerRememberFunction(sdk, kv) {
5744
5668
  const deletedMemoryIds = [];
5745
5669
  const deletedObservationIds = [];
5746
5670
  let deletedSession = false;
5747
- const { decrementImageRef } = await import("./image-refs-C2YU7BIm.mjs");
5671
+ const { decrementImageRef } = await import("./image-refs-Cc7-ua5j.mjs");
5748
5672
  if (data.memoryId) {
5749
5673
  const mem = await kv.get(KV.memories, data.memoryId);
5750
5674
  await kv.delete(KV.memories, data.memoryId);
@@ -5851,7 +5775,7 @@ async function runRecoveredSessionConsolidation(sdk) {
5851
5775
  function registerEvictFunction(sdk, kv) {
5852
5776
  sdk.registerFunction("mem::evict", async (data) => {
5853
5777
  const dryRun = data?.dryRun ?? false;
5854
- const { decrementImageRef } = await import("./image-refs-C2YU7BIm.mjs");
5778
+ const { decrementImageRef } = await import("./image-refs-Cc7-ua5j.mjs");
5855
5779
  const configOverride = await kv.get(KV.config, "eviction").catch(() => null);
5856
5780
  const cfg = {
5857
5781
  ...DEFAULTS$1,
@@ -6666,7 +6590,7 @@ function registerAutoForgetFunction(sdk, kv) {
6666
6590
  sdk.registerFunction("mem::auto-forget", async (data) => {
6667
6591
  const dryRun = data?.dryRun ?? false;
6668
6592
  const now = Date.now();
6669
- const { decrementImageRef } = await import("./image-refs-C2YU7BIm.mjs");
6593
+ const { decrementImageRef } = await import("./image-refs-Cc7-ua5j.mjs");
6670
6594
  const result = {
6671
6595
  ttlExpired: [],
6672
6596
  contradictions: [],
@@ -13804,7 +13728,7 @@ function registerRetentionFunctions(sdk, kv) {
13804
13728
  const threshold = typeof data?.threshold === "number" && Number.isFinite(data.threshold) ? data.threshold : DEFAULT_DECAY.tierThresholds.cold;
13805
13729
  const maxEvictRaw = typeof data?.maxEvict === "number" && Number.isInteger(data.maxEvict) ? data.maxEvict : 50;
13806
13730
  const maxEvict = Math.min(1e3, Math.max(0, maxEvictRaw));
13807
- const { decrementImageRef } = await import("./image-refs-C2YU7BIm.mjs");
13731
+ const { decrementImageRef } = await import("./image-refs-Cc7-ua5j.mjs");
13808
13732
  const candidates = (await kv.list(KV.retentionScores)).filter((s) => s.score < threshold).sort((a, b) => a.score - b.score).slice(0, maxEvict);
13809
13733
  if (data?.dryRun) return {
13810
13734
  success: true,
@@ -15017,6 +14941,10 @@ function parseOptionalInt(raw) {
15017
14941
  const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
15018
14942
  return Number.isFinite(n) ? n : void 0;
15019
14943
  }
14944
+ function parsePositiveLimit(raw) {
14945
+ const n = parseOptionalInt(raw);
14946
+ return n !== void 0 && n > 0 ? n : void 0;
14947
+ }
15020
14948
  function checkAuth(req, secret) {
15021
14949
  if (!secret) return null;
15022
14950
  const auth = req.headers?.["authorization"] || req.headers?.["Authorization"];
@@ -15405,11 +15333,12 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
15405
15333
  if (authErr) return authErr;
15406
15334
  const sessions = await kv.list(KV.sessions);
15407
15335
  sessions.sort((a, b) => (b.startedAt || "").localeCompare(a.startedAt || ""));
15336
+ const limit = parsePositiveLimit(req.query_params?.["limit"]);
15408
15337
  return {
15409
15338
  status_code: 200,
15410
15339
  body: {
15411
15340
  success: true,
15412
- sessions
15341
+ sessions: limit !== void 0 ? sessions.slice(0, limit) : sessions
15413
15342
  }
15414
15343
  };
15415
15344
  });
@@ -15681,10 +15610,12 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
15681
15610
  const wildcardAgent = normalizedAgentId === "*";
15682
15611
  const filterAgentId = wildcardAgent ? void 0 : (normalizedAgentId && !wildcardAgent ? normalizedAgentId : void 0) ?? (isAgentScopeIsolated() ? getAgentId() : void 0);
15683
15612
  const filtered = filterAgentId ? sessions.filter((s) => s.agentId === filterAgentId) : sessions;
15684
- const summaries = await Promise.all(filtered.map((s) => kv.get(KV.summaries, s.id).catch(() => null)));
15613
+ const limit = parsePositiveLimit(req.query_params?.["limit"]);
15614
+ const sliced = limit !== void 0 ? filtered.slice(0, limit) : filtered;
15615
+ const summaries = await Promise.all(sliced.map((s) => kv.get(KV.summaries, s.id).catch(() => null)));
15685
15616
  return {
15686
15617
  status_code: 200,
15687
- body: { sessions: filtered.map((s, i) => summaries[i] ? {
15618
+ body: { sessions: sliced.map((s, i) => summaries[i] ? {
15688
15619
  ...s,
15689
15620
  summary: summaries[i]
15690
15621
  } : s) }
@@ -20592,6 +20523,15 @@ async function main() {
20592
20523
  if (snapshotConfig.enabled) {
20593
20524
  registerSnapshotFunction(sdk, kv, snapshotConfig.dir);
20594
20525
  bootLog(`Git snapshots: ${snapshotConfig.dir} (every ${snapshotConfig.interval}s)`);
20526
+ const snapshotIntervalMs = snapshotConfig.interval * 1e3;
20527
+ setInterval(async () => {
20528
+ try {
20529
+ await sdk.trigger({
20530
+ function_id: "mem::snapshot-create",
20531
+ payload: {}
20532
+ });
20533
+ } catch {}
20534
+ }, snapshotIntervalMs).unref();
20595
20535
  }
20596
20536
  const bm25Index = getSearchIndex();
20597
20537
  const graphWeight = parseFloat(getEnvVar("AGENTMEMORY_GRAPH_WEIGHT") || "0.3");
@@ -20742,4 +20682,4 @@ main().catch((err) => {
20742
20682
  //#endregion
20743
20683
  export {};
20744
20684
 
20745
- //# sourceMappingURL=src-Cr3pH_uH.mjs.map
20685
+ //# sourceMappingURL=src-Dl4Re_-n.mjs.map