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.
- package/dist/cli.mjs +6 -6
- package/dist/{image-refs-C2YU7BIm.mjs → image-refs-Cc7-ua5j.mjs} +2 -2
- package/dist/{image-refs-C2YU7BIm.mjs.map → image-refs-Cc7-ua5j.mjs.map} +1 -1
- package/dist/index.mjs +178 -110
- package/dist/index.mjs.map +1 -1
- package/dist/schema-BQLY6d8C.mjs +206 -0
- package/dist/schema-BQLY6d8C.mjs.map +1 -0
- package/dist/{src-Cr3pH_uH.mjs → src-Dl4Re_-n.mjs} +58 -118
- package/dist/src-Dl4Re_-n.mjs.map +1 -0
- package/dist/{standalone-C6KzNkt5.mjs → standalone-CSnfnb1m.mjs} +2 -2
- package/dist/{standalone-C6KzNkt5.mjs.map → standalone-CSnfnb1m.mjs.map} +1 -1
- package/dist/standalone.mjs +2 -0
- package/dist/standalone.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/schema-CNiCWzV9.mjs +0 -78
- package/dist/schema-CNiCWzV9.mjs.map +0 -1
- package/dist/src-Cr3pH_uH.mjs.map +0 -1
|
@@ -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":"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"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { a as jaccardSimilarity, i as generateId, n as STREAM, r as fingerprintId, t as KV } from "./schema-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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:
|
|
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-
|
|
20685
|
+
//# sourceMappingURL=src-Dl4Re_-n.mjs.map
|