@mnemoai/core 1.1.0 → 1.1.1
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.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.d.ts.map +1 -0
- package/{index.ts → dist/index.js} +526 -1333
- package/dist/index.js.map +7 -0
- package/dist/src/access-tracker.d.ts +97 -0
- package/dist/src/access-tracker.d.ts.map +1 -0
- package/dist/src/access-tracker.js +184 -0
- package/dist/src/access-tracker.js.map +7 -0
- package/dist/src/adapters/chroma.d.ts +31 -0
- package/dist/src/adapters/chroma.d.ts.map +1 -0
- package/{src/adapters/chroma.ts → dist/src/adapters/chroma.js} +45 -107
- package/dist/src/adapters/chroma.js.map +7 -0
- package/dist/src/adapters/lancedb.d.ts +29 -0
- package/dist/src/adapters/lancedb.d.ts.map +1 -0
- package/{src/adapters/lancedb.ts → dist/src/adapters/lancedb.js} +41 -109
- package/dist/src/adapters/lancedb.js.map +7 -0
- package/dist/src/adapters/pgvector.d.ts +33 -0
- package/dist/src/adapters/pgvector.d.ts.map +1 -0
- package/{src/adapters/pgvector.ts → dist/src/adapters/pgvector.js} +42 -104
- package/dist/src/adapters/pgvector.js.map +7 -0
- package/dist/src/adapters/qdrant.d.ts +34 -0
- package/dist/src/adapters/qdrant.d.ts.map +1 -0
- package/dist/src/adapters/qdrant.js +132 -0
- package/dist/src/adapters/qdrant.js.map +7 -0
- package/dist/src/adaptive-retrieval.d.ts +14 -0
- package/dist/src/adaptive-retrieval.d.ts.map +1 -0
- package/dist/src/adaptive-retrieval.js +52 -0
- package/dist/src/adaptive-retrieval.js.map +7 -0
- package/dist/src/audit-log.d.ts +56 -0
- package/dist/src/audit-log.d.ts.map +1 -0
- package/dist/src/audit-log.js +139 -0
- package/dist/src/audit-log.js.map +7 -0
- package/dist/src/chunker.d.ts +45 -0
- package/dist/src/chunker.d.ts.map +1 -0
- package/dist/src/chunker.js +157 -0
- package/dist/src/chunker.js.map +7 -0
- package/dist/src/config.d.ts +70 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +142 -0
- package/dist/src/config.js.map +7 -0
- package/dist/src/decay-engine.d.ts +73 -0
- package/dist/src/decay-engine.d.ts.map +1 -0
- package/dist/src/decay-engine.js +119 -0
- package/dist/src/decay-engine.js.map +7 -0
- package/dist/src/embedder.d.ts +94 -0
- package/dist/src/embedder.d.ts.map +1 -0
- package/{src/embedder.ts → dist/src/embedder.js} +119 -317
- package/dist/src/embedder.js.map +7 -0
- package/dist/src/extraction-prompts.d.ts +12 -0
- package/dist/src/extraction-prompts.d.ts.map +1 -0
- package/dist/src/extraction-prompts.js +311 -0
- package/dist/src/extraction-prompts.js.map +7 -0
- package/dist/src/license.d.ts +29 -0
- package/dist/src/license.d.ts.map +1 -0
- package/{src/license.ts → dist/src/license.js} +42 -113
- package/dist/src/license.js.map +7 -0
- package/dist/src/llm-client.d.ts +23 -0
- package/dist/src/llm-client.d.ts.map +1 -0
- package/{src/llm-client.ts → dist/src/llm-client.js} +22 -55
- package/dist/src/llm-client.js.map +7 -0
- package/dist/src/logger.d.ts +33 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +35 -0
- package/dist/src/logger.js.map +7 -0
- package/dist/src/mcp-server.d.ts +16 -0
- package/dist/src/mcp-server.d.ts.map +1 -0
- package/{src/mcp-server.ts → dist/src/mcp-server.js} +81 -181
- package/dist/src/mcp-server.js.map +7 -0
- package/dist/src/memory-categories.d.ts +40 -0
- package/dist/src/memory-categories.d.ts.map +1 -0
- package/dist/src/memory-categories.js +33 -0
- package/dist/src/memory-categories.js.map +7 -0
- package/dist/src/memory-upgrader.d.ts +71 -0
- package/dist/src/memory-upgrader.d.ts.map +1 -0
- package/dist/src/memory-upgrader.js +238 -0
- package/dist/src/memory-upgrader.js.map +7 -0
- package/dist/src/migrate.d.ts +47 -0
- package/dist/src/migrate.d.ts.map +1 -0
- package/{src/migrate.ts → dist/src/migrate.js} +57 -165
- package/dist/src/migrate.js.map +7 -0
- package/dist/src/mnemo.d.ts +67 -0
- package/dist/src/mnemo.d.ts.map +1 -0
- package/dist/src/mnemo.js +66 -0
- package/dist/src/mnemo.js.map +7 -0
- package/dist/src/noise-filter.d.ts +23 -0
- package/dist/src/noise-filter.d.ts.map +1 -0
- package/dist/src/noise-filter.js +62 -0
- package/dist/src/noise-filter.js.map +7 -0
- package/dist/src/noise-prototypes.d.ts +40 -0
- package/dist/src/noise-prototypes.d.ts.map +1 -0
- package/dist/src/noise-prototypes.js +116 -0
- package/dist/src/noise-prototypes.js.map +7 -0
- package/dist/src/observability.d.ts +16 -0
- package/dist/src/observability.d.ts.map +1 -0
- package/dist/src/observability.js +53 -0
- package/dist/src/observability.js.map +7 -0
- package/dist/src/query-tracker.d.ts +27 -0
- package/dist/src/query-tracker.d.ts.map +1 -0
- package/dist/src/query-tracker.js +32 -0
- package/dist/src/query-tracker.js.map +7 -0
- package/dist/src/reflection-event-store.d.ts +44 -0
- package/dist/src/reflection-event-store.d.ts.map +1 -0
- package/dist/src/reflection-event-store.js +50 -0
- package/dist/src/reflection-event-store.js.map +7 -0
- package/dist/src/reflection-item-store.d.ts +58 -0
- package/dist/src/reflection-item-store.d.ts.map +1 -0
- package/dist/src/reflection-item-store.js +69 -0
- package/dist/src/reflection-item-store.js.map +7 -0
- package/dist/src/reflection-mapped-metadata.d.ts +47 -0
- package/dist/src/reflection-mapped-metadata.d.ts.map +1 -0
- package/dist/src/reflection-mapped-metadata.js +40 -0
- package/dist/src/reflection-mapped-metadata.js.map +7 -0
- package/dist/src/reflection-metadata.d.ts +11 -0
- package/dist/src/reflection-metadata.d.ts.map +1 -0
- package/dist/src/reflection-metadata.js +24 -0
- package/dist/src/reflection-metadata.js.map +7 -0
- package/dist/src/reflection-ranking.d.ts +13 -0
- package/dist/src/reflection-ranking.d.ts.map +1 -0
- package/{src/reflection-ranking.ts → dist/src/reflection-ranking.js} +12 -21
- package/dist/src/reflection-ranking.js.map +7 -0
- package/dist/src/reflection-retry.d.ts +30 -0
- package/dist/src/reflection-retry.d.ts.map +1 -0
- package/{src/reflection-retry.ts → dist/src/reflection-retry.js} +24 -64
- package/dist/src/reflection-retry.js.map +7 -0
- package/dist/src/reflection-slices.d.ts +42 -0
- package/dist/src/reflection-slices.d.ts.map +1 -0
- package/{src/reflection-slices.ts → dist/src/reflection-slices.js} +60 -136
- package/dist/src/reflection-slices.js.map +7 -0
- package/dist/src/reflection-store.d.ts +85 -0
- package/dist/src/reflection-store.d.ts.map +1 -0
- package/dist/src/reflection-store.js +407 -0
- package/dist/src/reflection-store.js.map +7 -0
- package/dist/src/resonance-state.d.ts +19 -0
- package/dist/src/resonance-state.d.ts.map +1 -0
- package/{src/resonance-state.ts → dist/src/resonance-state.js} +13 -42
- package/dist/src/resonance-state.js.map +7 -0
- package/dist/src/retriever.d.ts +228 -0
- package/dist/src/retriever.d.ts.map +1 -0
- package/dist/src/retriever.js +1006 -0
- package/dist/src/retriever.js.map +7 -0
- package/dist/src/scopes.d.ts +58 -0
- package/dist/src/scopes.d.ts.map +1 -0
- package/dist/src/scopes.js +252 -0
- package/dist/src/scopes.js.map +7 -0
- package/dist/src/self-improvement-files.d.ts +20 -0
- package/dist/src/self-improvement-files.d.ts.map +1 -0
- package/{src/self-improvement-files.ts → dist/src/self-improvement-files.js} +24 -49
- package/dist/src/self-improvement-files.js.map +7 -0
- package/dist/src/semantic-gate.d.ts +24 -0
- package/dist/src/semantic-gate.d.ts.map +1 -0
- package/dist/src/semantic-gate.js +86 -0
- package/dist/src/semantic-gate.js.map +7 -0
- package/dist/src/session-recovery.d.ts +9 -0
- package/dist/src/session-recovery.d.ts.map +1 -0
- package/{src/session-recovery.ts → dist/src/session-recovery.js} +40 -57
- package/dist/src/session-recovery.js.map +7 -0
- package/dist/src/smart-extractor.d.ts +107 -0
- package/dist/src/smart-extractor.d.ts.map +1 -0
- package/{src/smart-extractor.ts → dist/src/smart-extractor.js} +130 -383
- package/dist/src/smart-extractor.js.map +7 -0
- package/dist/src/smart-metadata.d.ts +103 -0
- package/dist/src/smart-metadata.d.ts.map +1 -0
- package/dist/src/smart-metadata.js +361 -0
- package/dist/src/smart-metadata.js.map +7 -0
- package/dist/src/storage-adapter.d.ts +102 -0
- package/dist/src/storage-adapter.d.ts.map +1 -0
- package/dist/src/storage-adapter.js +22 -0
- package/dist/src/storage-adapter.js.map +7 -0
- package/dist/src/store.d.ts +108 -0
- package/dist/src/store.d.ts.map +1 -0
- package/dist/src/store.js +939 -0
- package/dist/src/store.js.map +7 -0
- package/dist/src/tier-manager.d.ts +57 -0
- package/dist/src/tier-manager.d.ts.map +1 -0
- package/dist/src/tier-manager.js +80 -0
- package/dist/src/tier-manager.js.map +7 -0
- package/dist/src/tools.d.ts +43 -0
- package/dist/src/tools.d.ts.map +1 -0
- package/dist/src/tools.js +1075 -0
- package/dist/src/tools.js.map +7 -0
- package/dist/src/wal-recovery.d.ts +30 -0
- package/dist/src/wal-recovery.d.ts.map +1 -0
- package/{src/wal-recovery.ts → dist/src/wal-recovery.js} +26 -79
- package/dist/src/wal-recovery.js.map +7 -0
- package/package.json +21 -2
- package/openclaw.plugin.json +0 -815
- package/src/access-tracker.ts +0 -341
- package/src/adapters/README.md +0 -78
- package/src/adapters/qdrant.ts +0 -191
- package/src/adaptive-retrieval.ts +0 -90
- package/src/audit-log.ts +0 -238
- package/src/chunker.ts +0 -254
- package/src/config.ts +0 -271
- package/src/decay-engine.ts +0 -238
- package/src/extraction-prompts.ts +0 -339
- package/src/memory-categories.ts +0 -71
- package/src/memory-upgrader.ts +0 -388
- package/src/mnemo.ts +0 -142
- package/src/noise-filter.ts +0 -97
- package/src/noise-prototypes.ts +0 -164
- package/src/observability.ts +0 -81
- package/src/query-tracker.ts +0 -57
- package/src/reflection-event-store.ts +0 -98
- package/src/reflection-item-store.ts +0 -112
- package/src/reflection-mapped-metadata.ts +0 -84
- package/src/reflection-metadata.ts +0 -23
- package/src/reflection-store.ts +0 -602
- package/src/retriever.ts +0 -1510
- package/src/scopes.ts +0 -375
- package/src/semantic-gate.ts +0 -121
- package/src/smart-metadata.ts +0 -561
- package/src/storage-adapter.ts +0 -153
- package/src/store.ts +0 -1330
- package/src/tier-manager.ts +0 -189
- package/src/tools.ts +0 -1292
- package/test/core.test.mjs +0 -301
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const EMBEDDING_CONTEXT_LIMITS = {
|
|
2
|
+
// Jina v5
|
|
3
|
+
"jina-embeddings-v5-text-small": 8192,
|
|
4
|
+
"jina-embeddings-v5-text-nano": 8192,
|
|
5
|
+
// OpenAI
|
|
6
|
+
"text-embedding-3-small": 8192,
|
|
7
|
+
"text-embedding-3-large": 8192,
|
|
8
|
+
// Google
|
|
9
|
+
"text-embedding-004": 8192,
|
|
10
|
+
"gemini-embedding-001": 2048,
|
|
11
|
+
// Local/common
|
|
12
|
+
"nomic-embed-text": 8192,
|
|
13
|
+
"all-MiniLM-L6-v2": 512,
|
|
14
|
+
"all-mpnet-base-v2": 512
|
|
15
|
+
};
|
|
16
|
+
const DEFAULT_CHUNKER_CONFIG = {
|
|
17
|
+
maxChunkSize: 4e3,
|
|
18
|
+
overlapSize: 200,
|
|
19
|
+
minChunkSize: 200,
|
|
20
|
+
semanticSplit: true,
|
|
21
|
+
maxLinesPerChunk: 50
|
|
22
|
+
};
|
|
23
|
+
const SENTENCE_ENDING = /[.!?。!?]/;
|
|
24
|
+
function clamp(n, lo, hi) {
|
|
25
|
+
return Math.max(lo, Math.min(hi, n));
|
|
26
|
+
}
|
|
27
|
+
function countLines(s) {
|
|
28
|
+
return s.split(/\r\n|\n|\r/).length;
|
|
29
|
+
}
|
|
30
|
+
function findLastIndexWithin(text, re, start, end) {
|
|
31
|
+
let last = -1;
|
|
32
|
+
for (let i = end - 1; i >= start; i--) {
|
|
33
|
+
if (re.test(text[i])) return i;
|
|
34
|
+
}
|
|
35
|
+
return last;
|
|
36
|
+
}
|
|
37
|
+
function findSplitEnd(text, start, maxEnd, minEnd, config) {
|
|
38
|
+
const safeMinEnd = clamp(minEnd, start + 1, maxEnd);
|
|
39
|
+
const safeMaxEnd = clamp(maxEnd, safeMinEnd, text.length);
|
|
40
|
+
if (config.maxLinesPerChunk > 0) {
|
|
41
|
+
const candidate = text.slice(start, safeMaxEnd);
|
|
42
|
+
if (countLines(candidate) > config.maxLinesPerChunk) {
|
|
43
|
+
let breaks = 0;
|
|
44
|
+
for (let i = start; i < safeMaxEnd; i++) {
|
|
45
|
+
const ch = text[i];
|
|
46
|
+
if (ch === "\n") {
|
|
47
|
+
breaks++;
|
|
48
|
+
if (breaks >= config.maxLinesPerChunk) {
|
|
49
|
+
return Math.max(i + 1, safeMinEnd);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (config.semanticSplit) {
|
|
56
|
+
for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {
|
|
57
|
+
if (SENTENCE_ENDING.test(text[i])) {
|
|
58
|
+
let j = i + 1;
|
|
59
|
+
while (j < safeMaxEnd && /\s/.test(text[j])) j++;
|
|
60
|
+
return j;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {
|
|
64
|
+
if (text[i] === "\n") return i + 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {
|
|
68
|
+
if (/\s/.test(text[i])) return i;
|
|
69
|
+
}
|
|
70
|
+
return safeMaxEnd;
|
|
71
|
+
}
|
|
72
|
+
function sliceTrimWithIndices(text, start, end) {
|
|
73
|
+
const raw = text.slice(start, end);
|
|
74
|
+
const leading = raw.match(/^\s*/)?.[0]?.length ?? 0;
|
|
75
|
+
const trailing = raw.match(/\s*$/)?.[0]?.length ?? 0;
|
|
76
|
+
const chunk = raw.trim();
|
|
77
|
+
const trimmedStart = start + leading;
|
|
78
|
+
const trimmedEnd = end - trailing;
|
|
79
|
+
return {
|
|
80
|
+
chunk,
|
|
81
|
+
meta: {
|
|
82
|
+
startIndex: trimmedStart,
|
|
83
|
+
endIndex: Math.max(trimmedStart, trimmedEnd),
|
|
84
|
+
length: chunk.length
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function chunkDocument(text, config = DEFAULT_CHUNKER_CONFIG) {
|
|
89
|
+
if (!text || text.trim().length === 0) {
|
|
90
|
+
return { chunks: [], metadatas: [], totalOriginalLength: 0, chunkCount: 0 };
|
|
91
|
+
}
|
|
92
|
+
const totalOriginalLength = text.length;
|
|
93
|
+
const chunks = [];
|
|
94
|
+
const metadatas = [];
|
|
95
|
+
let pos = 0;
|
|
96
|
+
const maxGuard = Math.max(4, Math.ceil(text.length / Math.max(1, config.maxChunkSize - config.overlapSize)) + 5);
|
|
97
|
+
let guard = 0;
|
|
98
|
+
while (pos < text.length && guard < maxGuard) {
|
|
99
|
+
guard++;
|
|
100
|
+
const remaining = text.length - pos;
|
|
101
|
+
if (remaining <= config.maxChunkSize) {
|
|
102
|
+
const { chunk: chunk2, meta: meta2 } = sliceTrimWithIndices(text, pos, text.length);
|
|
103
|
+
if (chunk2.length > 0) {
|
|
104
|
+
chunks.push(chunk2);
|
|
105
|
+
metadatas.push(meta2);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
const maxEnd = Math.min(pos + config.maxChunkSize, text.length);
|
|
110
|
+
const minEnd = Math.min(pos + config.minChunkSize, maxEnd);
|
|
111
|
+
const end = findSplitEnd(text, pos, maxEnd, minEnd, config);
|
|
112
|
+
const { chunk, meta } = sliceTrimWithIndices(text, pos, end);
|
|
113
|
+
if (chunk.length < config.minChunkSize) {
|
|
114
|
+
const hardEnd = Math.min(pos + config.maxChunkSize, text.length);
|
|
115
|
+
const hard = sliceTrimWithIndices(text, pos, hardEnd);
|
|
116
|
+
if (hard.chunk.length > 0) {
|
|
117
|
+
chunks.push(hard.chunk);
|
|
118
|
+
metadatas.push(hard.meta);
|
|
119
|
+
}
|
|
120
|
+
if (hardEnd >= text.length) break;
|
|
121
|
+
pos = Math.max(hardEnd - config.overlapSize, pos + 1);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
chunks.push(chunk);
|
|
125
|
+
metadatas.push(meta);
|
|
126
|
+
if (end >= text.length) break;
|
|
127
|
+
const nextPos = Math.max(end - config.overlapSize, pos + 1);
|
|
128
|
+
pos = nextPos;
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
chunks,
|
|
132
|
+
metadatas,
|
|
133
|
+
totalOriginalLength,
|
|
134
|
+
chunkCount: chunks.length
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function smartChunk(text, embedderModel) {
|
|
138
|
+
const limit = embedderModel ? EMBEDDING_CONTEXT_LIMITS[embedderModel] : void 0;
|
|
139
|
+
const base = limit ?? 8192;
|
|
140
|
+
const config = {
|
|
141
|
+
maxChunkSize: Math.max(1e3, Math.floor(base * 0.7)),
|
|
142
|
+
overlapSize: Math.max(0, Math.floor(base * 0.05)),
|
|
143
|
+
minChunkSize: Math.max(100, Math.floor(base * 0.1)),
|
|
144
|
+
semanticSplit: true,
|
|
145
|
+
maxLinesPerChunk: 50
|
|
146
|
+
};
|
|
147
|
+
return chunkDocument(text, config);
|
|
148
|
+
}
|
|
149
|
+
var chunker_default = chunkDocument;
|
|
150
|
+
export {
|
|
151
|
+
DEFAULT_CHUNKER_CONFIG,
|
|
152
|
+
EMBEDDING_CONTEXT_LIMITS,
|
|
153
|
+
chunkDocument,
|
|
154
|
+
chunker_default as default,
|
|
155
|
+
smartChunk
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=chunker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/chunker.ts"],
|
|
4
|
+
"sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Long Context Chunking System\n *\n * Goal: split documents that exceed embedding model context limits into smaller,\n * semantically coherent chunks with overlap.\n *\n * Notes:\n * - We use *character counts* as a conservative proxy for tokens.\n * - The embedder triggers this only after a provider throws a context-length error.\n */\n\n// ============================================================================\n// Types & Constants\n// ============================================================================\n\nexport interface ChunkMetadata {\n startIndex: number;\n endIndex: number;\n length: number;\n}\n\nexport interface ChunkResult {\n chunks: string[];\n metadatas: ChunkMetadata[];\n totalOriginalLength: number;\n chunkCount: number;\n}\n\nexport interface ChunkerConfig {\n /** Maximum characters per chunk. */\n maxChunkSize: number;\n /** Overlap between chunks in characters. */\n overlapSize: number;\n /** Minimum chunk size (except the final chunk). */\n minChunkSize: number;\n /** Attempt to split on sentence boundaries for better semantic coherence. */\n semanticSplit: boolean;\n /** Max lines per chunk before we try to split earlier on a line boundary. */\n maxLinesPerChunk: number;\n}\n\n// Common embedding context limits (provider/model specific). These are typically\n// token limits, but we treat them as inputs to a conservative char-based heuristic.\nexport const EMBEDDING_CONTEXT_LIMITS: Record<string, number> = {\n // Jina v5\n \"jina-embeddings-v5-text-small\": 8192,\n \"jina-embeddings-v5-text-nano\": 8192,\n\n // OpenAI\n \"text-embedding-3-small\": 8192,\n \"text-embedding-3-large\": 8192,\n\n // Google\n \"text-embedding-004\": 8192,\n \"gemini-embedding-001\": 2048,\n\n // Local/common\n \"nomic-embed-text\": 8192,\n \"all-MiniLM-L6-v2\": 512,\n \"all-mpnet-base-v2\": 512,\n};\n\nexport const DEFAULT_CHUNKER_CONFIG: ChunkerConfig = {\n maxChunkSize: 4000,\n overlapSize: 200,\n minChunkSize: 200,\n semanticSplit: true,\n maxLinesPerChunk: 50,\n};\n\n// Sentence ending patterns (English + CJK-ish punctuation)\nconst SENTENCE_ENDING = /[.!?\u3002\uFF01\uFF1F]/;\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n\nfunction countLines(s: string): number {\n // Count \\n (treat CRLF as one line break)\n return s.split(/\\r\\n|\\n|\\r/).length;\n}\n\nfunction findLastIndexWithin(text: string, re: RegExp, start: number, end: number): number {\n // Find last match start index for regex within [start, end).\n // NOTE: `re` must NOT be global; we will scan manually.\n let last = -1;\n for (let i = end - 1; i >= start; i--) {\n if (re.test(text[i])) return i;\n }\n return last;\n}\n\nfunction findSplitEnd(text: string, start: number, maxEnd: number, minEnd: number, config: ChunkerConfig): number {\n const safeMinEnd = clamp(minEnd, start + 1, maxEnd);\n const safeMaxEnd = clamp(maxEnd, safeMinEnd, text.length);\n\n // Respect line limit: if we exceed maxLinesPerChunk, force earlier split at a line break.\n if (config.maxLinesPerChunk > 0) {\n const candidate = text.slice(start, safeMaxEnd);\n if (countLines(candidate) > config.maxLinesPerChunk) {\n // Find the position of the Nth line break.\n let breaks = 0;\n for (let i = start; i < safeMaxEnd; i++) {\n const ch = text[i];\n if (ch === \"\\n\") {\n breaks++;\n if (breaks >= config.maxLinesPerChunk) {\n // Split right after this newline.\n return Math.max(i + 1, safeMinEnd);\n }\n }\n }\n }\n }\n\n if (config.semanticSplit) {\n // Prefer a sentence boundary near the end.\n // Scan backward from safeMaxEnd to safeMinEnd.\n for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {\n if (SENTENCE_ENDING.test(text[i])) {\n // Include trailing whitespace after punctuation.\n let j = i + 1;\n while (j < safeMaxEnd && /\\s/.test(text[j])) j++;\n return j;\n }\n }\n\n // Next best: newline boundary.\n for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {\n if (text[i] === \"\\n\") return i + 1;\n }\n }\n\n // Fallback: last whitespace boundary.\n for (let i = safeMaxEnd - 1; i >= safeMinEnd; i--) {\n if (/\\s/.test(text[i])) return i;\n }\n\n return safeMaxEnd;\n}\n\nfunction sliceTrimWithIndices(text: string, start: number, end: number): { chunk: string; meta: ChunkMetadata } {\n const raw = text.slice(start, end);\n const leading = raw.match(/^\\s*/)?.[0]?.length ?? 0;\n const trailing = raw.match(/\\s*$/)?.[0]?.length ?? 0;\n const chunk = raw.trim();\n\n const trimmedStart = start + leading;\n const trimmedEnd = end - trailing;\n\n return {\n chunk,\n meta: {\n startIndex: trimmedStart,\n endIndex: Math.max(trimmedStart, trimmedEnd),\n length: chunk.length,\n },\n };\n}\n\n// ============================================================================\n// Chunking Core\n// ============================================================================\n\nexport function chunkDocument(text: string, config: ChunkerConfig = DEFAULT_CHUNKER_CONFIG): ChunkResult {\n if (!text || text.trim().length === 0) {\n return { chunks: [], metadatas: [], totalOriginalLength: 0, chunkCount: 0 };\n }\n\n const totalOriginalLength = text.length;\n const chunks: string[] = [];\n const metadatas: ChunkMetadata[] = [];\n\n let pos = 0;\n const maxGuard = Math.max(4, Math.ceil(text.length / Math.max(1, config.maxChunkSize - config.overlapSize)) + 5);\n let guard = 0;\n\n while (pos < text.length && guard < maxGuard) {\n guard++;\n\n const remaining = text.length - pos;\n if (remaining <= config.maxChunkSize) {\n const { chunk, meta } = sliceTrimWithIndices(text, pos, text.length);\n if (chunk.length > 0) {\n chunks.push(chunk);\n metadatas.push(meta);\n }\n break;\n }\n\n const maxEnd = Math.min(pos + config.maxChunkSize, text.length);\n const minEnd = Math.min(pos + config.minChunkSize, maxEnd);\n\n const end = findSplitEnd(text, pos, maxEnd, minEnd, config);\n const { chunk, meta } = sliceTrimWithIndices(text, pos, end);\n\n // If trimming made it too small, fall back to a hard split.\n if (chunk.length < config.minChunkSize) {\n const hardEnd = Math.min(pos + config.maxChunkSize, text.length);\n const hard = sliceTrimWithIndices(text, pos, hardEnd);\n if (hard.chunk.length > 0) {\n chunks.push(hard.chunk);\n metadatas.push(hard.meta);\n }\n if (hardEnd >= text.length) break;\n pos = Math.max(hardEnd - config.overlapSize, pos + 1);\n continue;\n }\n\n chunks.push(chunk);\n metadatas.push(meta);\n\n if (end >= text.length) break;\n\n // Move forward with overlap.\n const nextPos = Math.max(end - config.overlapSize, pos + 1);\n pos = nextPos;\n }\n\n return {\n chunks,\n metadatas,\n totalOriginalLength,\n chunkCount: chunks.length,\n };\n}\n\n/**\n * Smart chunker that adapts to model context limits.\n *\n * We intentionally pick conservative char limits (70% of the reported limit)\n * since token/char ratios vary.\n */\nexport function smartChunk(text: string, embedderModel?: string): ChunkResult {\n const limit = embedderModel ? EMBEDDING_CONTEXT_LIMITS[embedderModel] : undefined;\n const base = limit ?? 8192;\n\n const config: ChunkerConfig = {\n maxChunkSize: Math.max(1000, Math.floor(base * 0.7)),\n overlapSize: Math.max(0, Math.floor(base * 0.05)),\n minChunkSize: Math.max(100, Math.floor(base * 0.1)),\n semanticSplit: true,\n maxLinesPerChunk: 50,\n };\n\n return chunkDocument(text, config);\n}\n\nexport default chunkDocument;\n"],
|
|
5
|
+
"mappings": "AA4CO,MAAM,2BAAmD;AAAA;AAAA,EAE9D,iCAAiC;AAAA,EACjC,gCAAgC;AAAA;AAAA,EAGhC,0BAA0B;AAAA,EAC1B,0BAA0B;AAAA;AAAA,EAG1B,sBAAsB;AAAA,EACtB,wBAAwB;AAAA;AAAA,EAGxB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,qBAAqB;AACvB;AAEO,MAAM,yBAAwC;AAAA,EACnD,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,eAAe;AAAA,EACf,kBAAkB;AACpB;AAGA,MAAM,kBAAkB;AAMxB,SAAS,MAAM,GAAW,IAAY,IAAoB;AACxD,SAAO,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC;AACrC;AAEA,SAAS,WAAW,GAAmB;AAErC,SAAO,EAAE,MAAM,YAAY,EAAE;AAC/B;AAEA,SAAS,oBAAoB,MAAc,IAAY,OAAe,KAAqB;AAGzF,MAAI,OAAO;AACX,WAAS,IAAI,MAAM,GAAG,KAAK,OAAO,KAAK;AACrC,QAAI,GAAG,KAAK,KAAK,CAAC,CAAC,EAAG,QAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAc,OAAe,QAAgB,QAAgB,QAA+B;AAChH,QAAM,aAAa,MAAM,QAAQ,QAAQ,GAAG,MAAM;AAClD,QAAM,aAAa,MAAM,QAAQ,YAAY,KAAK,MAAM;AAGxD,MAAI,OAAO,mBAAmB,GAAG;AAC/B,UAAM,YAAY,KAAK,MAAM,OAAO,UAAU;AAC9C,QAAI,WAAW,SAAS,IAAI,OAAO,kBAAkB;AAEnD,UAAI,SAAS;AACb,eAAS,IAAI,OAAO,IAAI,YAAY,KAAK;AACvC,cAAM,KAAK,KAAK,CAAC;AACjB,YAAI,OAAO,MAAM;AACf;AACA,cAAI,UAAU,OAAO,kBAAkB;AAErC,mBAAO,KAAK,IAAI,IAAI,GAAG,UAAU;AAAA,UACnC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,eAAe;AAGxB,aAAS,IAAI,aAAa,GAAG,KAAK,YAAY,KAAK;AACjD,UAAI,gBAAgB,KAAK,KAAK,CAAC,CAAC,GAAG;AAEjC,YAAI,IAAI,IAAI;AACZ,eAAO,IAAI,cAAc,KAAK,KAAK,KAAK,CAAC,CAAC,EAAG;AAC7C,eAAO;AAAA,MACT;AAAA,IACF;AAGA,aAAS,IAAI,aAAa,GAAG,KAAK,YAAY,KAAK;AACjD,UAAI,KAAK,CAAC,MAAM,KAAM,QAAO,IAAI;AAAA,IACnC;AAAA,EACF;AAGA,WAAS,IAAI,aAAa,GAAG,KAAK,YAAY,KAAK;AACjD,QAAI,KAAK,KAAK,KAAK,CAAC,CAAC,EAAG,QAAO;AAAA,EACjC;AAEA,SAAO;AACT;AAEA,SAAS,qBAAqB,MAAc,OAAe,KAAqD;AAC9G,QAAM,MAAM,KAAK,MAAM,OAAO,GAAG;AACjC,QAAM,UAAU,IAAI,MAAM,MAAM,IAAI,CAAC,GAAG,UAAU;AAClD,QAAM,WAAW,IAAI,MAAM,MAAM,IAAI,CAAC,GAAG,UAAU;AACnD,QAAM,QAAQ,IAAI,KAAK;AAEvB,QAAM,eAAe,QAAQ;AAC7B,QAAM,aAAa,MAAM;AAEzB,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,MACJ,YAAY;AAAA,MACZ,UAAU,KAAK,IAAI,cAAc,UAAU;AAAA,MAC3C,QAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AACF;AAMO,SAAS,cAAc,MAAc,SAAwB,wBAAqC;AACvG,MAAI,CAAC,QAAQ,KAAK,KAAK,EAAE,WAAW,GAAG;AACrC,WAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,GAAG,qBAAqB,GAAG,YAAY,EAAE;AAAA,EAC5E;AAEA,QAAM,sBAAsB,KAAK;AACjC,QAAM,SAAmB,CAAC;AAC1B,QAAM,YAA6B,CAAC;AAEpC,MAAI,MAAM;AACV,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,SAAS,KAAK,IAAI,GAAG,OAAO,eAAe,OAAO,WAAW,CAAC,IAAI,CAAC;AAC/G,MAAI,QAAQ;AAEZ,SAAO,MAAM,KAAK,UAAU,QAAQ,UAAU;AAC5C;AAEA,UAAM,YAAY,KAAK,SAAS;AAChC,QAAI,aAAa,OAAO,cAAc;AACpC,YAAM,EAAE,OAAAA,QAAO,MAAAC,MAAK,IAAI,qBAAqB,MAAM,KAAK,KAAK,MAAM;AACnE,UAAID,OAAM,SAAS,GAAG;AACpB,eAAO,KAAKA,MAAK;AACjB,kBAAU,KAAKC,KAAI;AAAA,MACrB;AACA;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,IAAI,MAAM,OAAO,cAAc,KAAK,MAAM;AAC9D,UAAM,SAAS,KAAK,IAAI,MAAM,OAAO,cAAc,MAAM;AAEzD,UAAM,MAAM,aAAa,MAAM,KAAK,QAAQ,QAAQ,MAAM;AAC1D,UAAM,EAAE,OAAO,KAAK,IAAI,qBAAqB,MAAM,KAAK,GAAG;AAG3D,QAAI,MAAM,SAAS,OAAO,cAAc;AACtC,YAAM,UAAU,KAAK,IAAI,MAAM,OAAO,cAAc,KAAK,MAAM;AAC/D,YAAM,OAAO,qBAAqB,MAAM,KAAK,OAAO;AACpD,UAAI,KAAK,MAAM,SAAS,GAAG;AACzB,eAAO,KAAK,KAAK,KAAK;AACtB,kBAAU,KAAK,KAAK,IAAI;AAAA,MAC1B;AACA,UAAI,WAAW,KAAK,OAAQ;AAC5B,YAAM,KAAK,IAAI,UAAU,OAAO,aAAa,MAAM,CAAC;AACpD;AAAA,IACF;AAEA,WAAO,KAAK,KAAK;AACjB,cAAU,KAAK,IAAI;AAEnB,QAAI,OAAO,KAAK,OAAQ;AAGxB,UAAM,UAAU,KAAK,IAAI,MAAM,OAAO,aAAa,MAAM,CAAC;AAC1D,UAAM;AAAA,EACR;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,OAAO;AAAA,EACrB;AACF;AAQO,SAAS,WAAW,MAAc,eAAqC;AAC5E,QAAM,QAAQ,gBAAgB,yBAAyB,aAAa,IAAI;AACxE,QAAM,OAAO,SAAS;AAEtB,QAAM,SAAwB;AAAA,IAC5B,cAAc,KAAK,IAAI,KAAM,KAAK,MAAM,OAAO,GAAG,CAAC;AAAA,IACnD,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,IAAI,CAAC;AAAA,IAChD,cAAc,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,GAAG,CAAC;AAAA,IAClD,eAAe;AAAA,IACf,kBAAkB;AAAA,EACpB;AAEA,SAAO,cAAc,MAAM,MAAM;AACnC;AAEA,IAAO,kBAAQ;",
|
|
6
|
+
"names": ["chunk", "meta"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Configuration Helpers
|
|
3
|
+
* Extracted from index.ts for reuse by MCP server and gateway plugin.
|
|
4
|
+
*/
|
|
5
|
+
export interface PluginConfig {
|
|
6
|
+
embedding: {
|
|
7
|
+
provider: "openai-compatible";
|
|
8
|
+
apiKey: string | string[];
|
|
9
|
+
model?: string;
|
|
10
|
+
baseURL?: string;
|
|
11
|
+
dimensions?: number;
|
|
12
|
+
taskQuery?: string;
|
|
13
|
+
taskPassage?: string;
|
|
14
|
+
normalized?: boolean;
|
|
15
|
+
};
|
|
16
|
+
dbPath?: string;
|
|
17
|
+
autoCapture?: boolean;
|
|
18
|
+
autoRecall?: boolean;
|
|
19
|
+
autoRecallMinLength?: number;
|
|
20
|
+
autoRecallMinRepeated?: number;
|
|
21
|
+
captureAssistant?: boolean;
|
|
22
|
+
retrieval?: {
|
|
23
|
+
mode?: "hybrid" | "vector";
|
|
24
|
+
vectorWeight?: number;
|
|
25
|
+
bm25Weight?: number;
|
|
26
|
+
minScore?: number;
|
|
27
|
+
rerank?: "cross-encoder" | "lightweight" | "none";
|
|
28
|
+
candidatePoolSize?: number;
|
|
29
|
+
rerankApiKey?: string;
|
|
30
|
+
rerankModel?: string;
|
|
31
|
+
rerankEndpoint?: string;
|
|
32
|
+
rerankProvider?: "jina" | "siliconflow" | "voyage" | "pinecone";
|
|
33
|
+
recencyHalfLifeDays?: number;
|
|
34
|
+
recencyWeight?: number;
|
|
35
|
+
filterNoise?: boolean;
|
|
36
|
+
lengthNormAnchor?: number;
|
|
37
|
+
hardMinScore?: number;
|
|
38
|
+
timeDecayHalfLifeDays?: number;
|
|
39
|
+
reinforcementFactor?: number;
|
|
40
|
+
maxHalfLifeMultiplier?: number;
|
|
41
|
+
};
|
|
42
|
+
scopes?: {
|
|
43
|
+
default?: string;
|
|
44
|
+
definitions?: Record<string, {
|
|
45
|
+
description: string;
|
|
46
|
+
}>;
|
|
47
|
+
agentAccess?: Record<string, string[]>;
|
|
48
|
+
};
|
|
49
|
+
enableManagementTools?: boolean;
|
|
50
|
+
sessionMemory?: {
|
|
51
|
+
enabled?: boolean;
|
|
52
|
+
messageCount?: number;
|
|
53
|
+
};
|
|
54
|
+
mdMirror?: {
|
|
55
|
+
enabled?: boolean;
|
|
56
|
+
dir?: string;
|
|
57
|
+
};
|
|
58
|
+
autoCaptureLLM?: {
|
|
59
|
+
enabled?: boolean;
|
|
60
|
+
endpoint?: string;
|
|
61
|
+
model?: string;
|
|
62
|
+
timeoutMs?: number;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export declare function getDefaultDbPath(): string;
|
|
66
|
+
export declare function resolveEnvVars(value: string): string;
|
|
67
|
+
export declare function parsePositiveInt(value: unknown): number | undefined;
|
|
68
|
+
export declare function parsePluginConfig(value: unknown): PluginConfig;
|
|
69
|
+
export declare function loadConfigFromOpenClaw(): PluginConfig;
|
|
70
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AACA;;;GAGG;AAUH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE;QACT,QAAQ,EAAE,mBAAmB,CAAC;QAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE;QACV,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,eAAe,GAAG,aAAa,GAAG,MAAM,CAAC;QAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC;QAChE,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;IACF,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACtD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;KACxC,CAAC;IACF,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,QAAQ,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,cAAc,CAAC,EAAE;QACf,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAMD,wBAAgB,gBAAgB,IAAI,MAAM,CAWzC;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAYnE;AAMD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAuH9D;AAOD,wBAAgB,sBAAsB,IAAI,YAAY,CAgCrD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
function getDefaultDbPath() {
|
|
5
|
+
if (process.env.MNEMO_DB_PATH) return process.env.MNEMO_DB_PATH;
|
|
6
|
+
const mnemoPath = join(homedir(), ".mnemo", "memory-db");
|
|
7
|
+
const openclawPath = join(homedir(), ".openclaw", "memory", "lancedb-pro");
|
|
8
|
+
try {
|
|
9
|
+
const { existsSync } = require("fs");
|
|
10
|
+
if (existsSync(openclawPath)) return openclawPath;
|
|
11
|
+
} catch {
|
|
12
|
+
}
|
|
13
|
+
return mnemoPath;
|
|
14
|
+
}
|
|
15
|
+
function resolveEnvVars(value) {
|
|
16
|
+
return value.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
|
|
17
|
+
const envValue = process.env[envVar];
|
|
18
|
+
if (!envValue) {
|
|
19
|
+
throw new Error(`Environment variable ${envVar} is not set`);
|
|
20
|
+
}
|
|
21
|
+
return envValue;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function parsePositiveInt(value) {
|
|
25
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
26
|
+
return Math.floor(value);
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === "string") {
|
|
29
|
+
const s = value.trim();
|
|
30
|
+
if (!s) return void 0;
|
|
31
|
+
const resolved = resolveEnvVars(s);
|
|
32
|
+
const n = Number(resolved);
|
|
33
|
+
if (Number.isFinite(n) && n > 0) return Math.floor(n);
|
|
34
|
+
}
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
function parsePluginConfig(value) {
|
|
38
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
39
|
+
throw new Error("mnemo config required");
|
|
40
|
+
}
|
|
41
|
+
const cfg = value;
|
|
42
|
+
const embedding = cfg.embedding;
|
|
43
|
+
if (!embedding) {
|
|
44
|
+
throw new Error("embedding config is required");
|
|
45
|
+
}
|
|
46
|
+
let apiKey;
|
|
47
|
+
if (typeof embedding.apiKey === "string") {
|
|
48
|
+
apiKey = embedding.apiKey;
|
|
49
|
+
} else if (Array.isArray(embedding.apiKey) && embedding.apiKey.length > 0) {
|
|
50
|
+
const invalid = embedding.apiKey.findIndex(
|
|
51
|
+
(k) => typeof k !== "string" || k.trim().length === 0
|
|
52
|
+
);
|
|
53
|
+
if (invalid !== -1) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`embedding.apiKey[${invalid}] is invalid: expected non-empty string`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
apiKey = embedding.apiKey;
|
|
59
|
+
} else if (embedding.apiKey !== void 0) {
|
|
60
|
+
throw new Error("embedding.apiKey must be a string or non-empty array of strings");
|
|
61
|
+
} else {
|
|
62
|
+
apiKey = process.env.OPENAI_API_KEY || "";
|
|
63
|
+
}
|
|
64
|
+
if (!apiKey || Array.isArray(apiKey) && apiKey.length === 0) {
|
|
65
|
+
throw new Error("embedding.apiKey is required (set directly or via OPENAI_API_KEY env var)");
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
embedding: {
|
|
69
|
+
provider: "openai-compatible",
|
|
70
|
+
apiKey,
|
|
71
|
+
model: typeof embedding.model === "string" ? embedding.model : "text-embedding-3-small",
|
|
72
|
+
baseURL: typeof embedding.baseURL === "string" ? resolveEnvVars(embedding.baseURL) : void 0,
|
|
73
|
+
dimensions: parsePositiveInt(embedding.dimensions ?? cfg.dimensions),
|
|
74
|
+
taskQuery: typeof embedding.taskQuery === "string" ? embedding.taskQuery : void 0,
|
|
75
|
+
taskPassage: typeof embedding.taskPassage === "string" ? embedding.taskPassage : void 0,
|
|
76
|
+
normalized: typeof embedding.normalized === "boolean" ? embedding.normalized : void 0
|
|
77
|
+
},
|
|
78
|
+
dbPath: typeof cfg.dbPath === "string" ? cfg.dbPath : void 0,
|
|
79
|
+
autoCapture: cfg.autoCapture !== false,
|
|
80
|
+
autoRecall: cfg.autoRecall === true,
|
|
81
|
+
autoRecallMinLength: parsePositiveInt(cfg.autoRecallMinLength),
|
|
82
|
+
autoRecallMinRepeated: parsePositiveInt(cfg.autoRecallMinRepeated),
|
|
83
|
+
captureAssistant: cfg.captureAssistant === true,
|
|
84
|
+
retrieval: typeof cfg.retrieval === "object" && cfg.retrieval !== null ? cfg.retrieval : void 0,
|
|
85
|
+
scopes: typeof cfg.scopes === "object" && cfg.scopes !== null ? cfg.scopes : void 0,
|
|
86
|
+
enableManagementTools: cfg.enableManagementTools === true,
|
|
87
|
+
sessionMemory: typeof cfg.sessionMemory === "object" && cfg.sessionMemory !== null ? {
|
|
88
|
+
enabled: cfg.sessionMemory.enabled !== false,
|
|
89
|
+
messageCount: typeof cfg.sessionMemory.messageCount === "number" ? cfg.sessionMemory.messageCount : void 0
|
|
90
|
+
} : void 0,
|
|
91
|
+
mdMirror: typeof cfg.mdMirror === "object" && cfg.mdMirror !== null ? {
|
|
92
|
+
enabled: cfg.mdMirror.enabled === true,
|
|
93
|
+
dir: typeof cfg.mdMirror.dir === "string" ? cfg.mdMirror.dir : void 0
|
|
94
|
+
} : void 0,
|
|
95
|
+
autoCaptureLLM: typeof cfg.autoCaptureLLM === "object" && cfg.autoCaptureLLM !== null ? {
|
|
96
|
+
enabled: cfg.autoCaptureLLM.enabled === true,
|
|
97
|
+
endpoint: typeof cfg.autoCaptureLLM.endpoint === "string" ? cfg.autoCaptureLLM.endpoint : void 0,
|
|
98
|
+
model: typeof cfg.autoCaptureLLM.model === "string" ? cfg.autoCaptureLLM.model : void 0,
|
|
99
|
+
timeoutMs: parsePositiveInt(
|
|
100
|
+
cfg.autoCaptureLLM.timeoutMs
|
|
101
|
+
)
|
|
102
|
+
} : void 0
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function loadConfigFromOpenClaw() {
|
|
106
|
+
const { existsSync } = require("fs");
|
|
107
|
+
const envPath = process.env.MNEMO_CONFIG;
|
|
108
|
+
const mnemoPath = join(homedir(), ".mnemo", "mnemo.json");
|
|
109
|
+
const openclawPath = join(homedir(), ".openclaw", "openclaw.json");
|
|
110
|
+
const configPath = envPath || (existsSync(mnemoPath) ? mnemoPath : openclawPath);
|
|
111
|
+
let raw;
|
|
112
|
+
try {
|
|
113
|
+
raw = readFileSync(configPath, "utf8");
|
|
114
|
+
} catch (err) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Failed to read ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
let json;
|
|
120
|
+
try {
|
|
121
|
+
json = JSON.parse(raw);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Failed to parse ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const pluginConfig = json?.plugins?.entries?.["memory-lancedb-pro"]?.config;
|
|
128
|
+
if (!pluginConfig) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`No config found at plugins.entries["memory-lancedb-pro"].config in ${configPath}`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return parsePluginConfig(pluginConfig);
|
|
134
|
+
}
|
|
135
|
+
export {
|
|
136
|
+
getDefaultDbPath,
|
|
137
|
+
loadConfigFromOpenClaw,
|
|
138
|
+
parsePluginConfig,
|
|
139
|
+
parsePositiveInt,
|
|
140
|
+
resolveEnvVars
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/config.ts"],
|
|
4
|
+
"sourcesContent": ["// SPDX-License-Identifier: MIT\n/**\n * Shared Configuration Helpers\n * Extracted from index.ts for reuse by MCP server and gateway plugin.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\nexport interface PluginConfig {\n embedding: {\n provider: \"openai-compatible\";\n apiKey: string | string[];\n model?: string;\n baseURL?: string;\n dimensions?: number;\n taskQuery?: string;\n taskPassage?: string;\n normalized?: boolean;\n };\n dbPath?: string;\n autoCapture?: boolean;\n autoRecall?: boolean;\n autoRecallMinLength?: number;\n autoRecallMinRepeated?: number;\n captureAssistant?: boolean;\n retrieval?: {\n mode?: \"hybrid\" | \"vector\";\n vectorWeight?: number;\n bm25Weight?: number;\n minScore?: number;\n rerank?: \"cross-encoder\" | \"lightweight\" | \"none\";\n candidatePoolSize?: number;\n rerankApiKey?: string;\n rerankModel?: string;\n rerankEndpoint?: string;\n rerankProvider?: \"jina\" | \"siliconflow\" | \"voyage\" | \"pinecone\";\n recencyHalfLifeDays?: number;\n recencyWeight?: number;\n filterNoise?: boolean;\n lengthNormAnchor?: number;\n hardMinScore?: number;\n timeDecayHalfLifeDays?: number;\n reinforcementFactor?: number;\n maxHalfLifeMultiplier?: number;\n };\n scopes?: {\n default?: string;\n definitions?: Record<string, { description: string }>;\n agentAccess?: Record<string, string[]>;\n };\n enableManagementTools?: boolean;\n sessionMemory?: { enabled?: boolean; messageCount?: number };\n mdMirror?: { enabled?: boolean; dir?: string };\n autoCaptureLLM?: {\n enabled?: boolean;\n endpoint?: string;\n model?: string;\n timeoutMs?: number;\n };\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nexport function getDefaultDbPath(): string {\n // Prefer MNEMO_DB_PATH env, then ~/.mnemo/memory-db (open source default),\n // then ~/.openclaw/memory/lancedb-pro (OpenClaw integration fallback)\n if (process.env.MNEMO_DB_PATH) return process.env.MNEMO_DB_PATH;\n const mnemoPath = join(homedir(), \".mnemo\", \"memory-db\");\n const openclawPath = join(homedir(), \".openclaw\", \"memory\", \"lancedb-pro\");\n try {\n const { existsSync } = require(\"fs\");\n if (existsSync(openclawPath)) return openclawPath;\n } catch {}\n return mnemoPath;\n}\n\nexport function resolveEnvVars(value: string): string {\n return value.replace(/\\$\\{([^}]+)\\}/g, (_, envVar) => {\n const envValue = process.env[envVar];\n if (!envValue) {\n throw new Error(`Environment variable ${envVar} is not set`);\n }\n return envValue;\n });\n}\n\nexport function parsePositiveInt(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value) && value > 0) {\n return Math.floor(value);\n }\n if (typeof value === \"string\") {\n const s = value.trim();\n if (!s) return undefined;\n const resolved = resolveEnvVars(s);\n const n = Number(resolved);\n if (Number.isFinite(n) && n > 0) return Math.floor(n);\n }\n return undefined;\n}\n\n// ============================================================================\n// Config Parser\n// ============================================================================\n\nexport function parsePluginConfig(value: unknown): PluginConfig {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(\"mnemo config required\");\n }\n const cfg = value as Record<string, unknown>;\n\n const embedding = cfg.embedding as Record<string, unknown> | undefined;\n if (!embedding) {\n throw new Error(\"embedding config is required\");\n }\n\n // Accept single key (string) or array of keys for round-robin rotation\n let apiKey: string | string[];\n if (typeof embedding.apiKey === \"string\") {\n apiKey = embedding.apiKey;\n } else if (Array.isArray(embedding.apiKey) && embedding.apiKey.length > 0) {\n const invalid = embedding.apiKey.findIndex(\n (k: unknown) => typeof k !== \"string\" || (k as string).trim().length === 0,\n );\n if (invalid !== -1) {\n throw new Error(\n `embedding.apiKey[${invalid}] is invalid: expected non-empty string`,\n );\n }\n apiKey = embedding.apiKey as string[];\n } else if (embedding.apiKey !== undefined) {\n throw new Error(\"embedding.apiKey must be a string or non-empty array of strings\");\n } else {\n apiKey = process.env.OPENAI_API_KEY || \"\";\n }\n\n if (!apiKey || (Array.isArray(apiKey) && apiKey.length === 0)) {\n throw new Error(\"embedding.apiKey is required (set directly or via OPENAI_API_KEY env var)\");\n }\n\n return {\n embedding: {\n provider: \"openai-compatible\",\n apiKey,\n model:\n typeof embedding.model === \"string\"\n ? embedding.model\n : \"text-embedding-3-small\",\n baseURL:\n typeof embedding.baseURL === \"string\"\n ? resolveEnvVars(embedding.baseURL)\n : undefined,\n dimensions: parsePositiveInt(embedding.dimensions ?? cfg.dimensions),\n taskQuery:\n typeof embedding.taskQuery === \"string\"\n ? embedding.taskQuery\n : undefined,\n taskPassage:\n typeof embedding.taskPassage === \"string\"\n ? embedding.taskPassage\n : undefined,\n normalized:\n typeof embedding.normalized === \"boolean\"\n ? embedding.normalized\n : undefined,\n },\n dbPath: typeof cfg.dbPath === \"string\" ? cfg.dbPath : undefined,\n autoCapture: cfg.autoCapture !== false,\n autoRecall: cfg.autoRecall === true,\n autoRecallMinLength: parsePositiveInt(cfg.autoRecallMinLength),\n autoRecallMinRepeated: parsePositiveInt(cfg.autoRecallMinRepeated),\n captureAssistant: cfg.captureAssistant === true,\n retrieval:\n typeof cfg.retrieval === \"object\" && cfg.retrieval !== null\n ? (cfg.retrieval as any)\n : undefined,\n scopes:\n typeof cfg.scopes === \"object\" && cfg.scopes !== null\n ? (cfg.scopes as any)\n : undefined,\n enableManagementTools: cfg.enableManagementTools === true,\n sessionMemory:\n typeof cfg.sessionMemory === \"object\" && cfg.sessionMemory !== null\n ? {\n enabled:\n (cfg.sessionMemory as Record<string, unknown>).enabled !== false,\n messageCount:\n typeof (cfg.sessionMemory as Record<string, unknown>)\n .messageCount === \"number\"\n ? ((cfg.sessionMemory as Record<string, unknown>)\n .messageCount as number)\n : undefined,\n }\n : undefined,\n mdMirror:\n typeof cfg.mdMirror === \"object\" && cfg.mdMirror !== null\n ? {\n enabled:\n (cfg.mdMirror as Record<string, unknown>).enabled === true,\n dir:\n typeof (cfg.mdMirror as Record<string, unknown>).dir === \"string\"\n ? ((cfg.mdMirror as Record<string, unknown>).dir as string)\n : undefined,\n }\n : undefined,\n autoCaptureLLM:\n typeof cfg.autoCaptureLLM === \"object\" && cfg.autoCaptureLLM !== null\n ? {\n enabled:\n (cfg.autoCaptureLLM as Record<string, unknown>).enabled === true,\n endpoint:\n typeof (cfg.autoCaptureLLM as Record<string, unknown>).endpoint === \"string\"\n ? ((cfg.autoCaptureLLM as Record<string, unknown>).endpoint as string)\n : undefined,\n model:\n typeof (cfg.autoCaptureLLM as Record<string, unknown>).model === \"string\"\n ? ((cfg.autoCaptureLLM as Record<string, unknown>).model as string)\n : undefined,\n timeoutMs: parsePositiveInt(\n (cfg.autoCaptureLLM as Record<string, unknown>).timeoutMs,\n ),\n }\n : undefined,\n };\n}\n\n// ============================================================================\n// Load config from config file\n// Checks: MNEMO_CONFIG env \u2192 ~/.mnemo/mnemo.json \u2192 ~/.openclaw/openclaw.json\n// ============================================================================\n\nexport function loadConfigFromOpenClaw(): PluginConfig {\n const { existsSync } = require(\"fs\");\n const envPath = process.env.MNEMO_CONFIG;\n const mnemoPath = join(homedir(), \".mnemo\", \"mnemo.json\");\n const openclawPath = join(homedir(), \".openclaw\", \"openclaw.json\");\n const configPath = envPath || (existsSync(mnemoPath) ? mnemoPath : openclawPath);\n let raw: string;\n try {\n raw = readFileSync(configPath, \"utf8\");\n } catch (err) {\n throw new Error(\n `Failed to read ${configPath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n let json: any;\n try {\n json = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `Failed to parse ${configPath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const pluginConfig = json?.plugins?.entries?.[\"memory-lancedb-pro\"]?.config;\n if (!pluginConfig) {\n throw new Error(\n `No config found at plugins.entries[\"memory-lancedb-pro\"].config in ${configPath}`,\n );\n }\n\n return parsePluginConfig(pluginConfig);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decay Engine — Weibull stretched-exponential decay model
|
|
3
|
+
*
|
|
4
|
+
* Composite score = recencyWeight * recency + frequencyWeight * frequency + intrinsicWeight * intrinsic
|
|
5
|
+
*
|
|
6
|
+
* - Recency: Weibull decay with importance-modulated half-life and tier-specific beta
|
|
7
|
+
* - Frequency: Logarithmic saturation with time-weighted access pattern bonus
|
|
8
|
+
* - Intrinsic: importance × confidence
|
|
9
|
+
*/
|
|
10
|
+
import type { MemoryTier } from "./memory-categories.js";
|
|
11
|
+
export interface DecayConfig {
|
|
12
|
+
/** Days until recency score halves (default: 30) */
|
|
13
|
+
recencyHalfLifeDays: number;
|
|
14
|
+
/** Weight of recency in composite (default: 0.4) */
|
|
15
|
+
recencyWeight: number;
|
|
16
|
+
/** Weight of access frequency (default: 0.3) */
|
|
17
|
+
frequencyWeight: number;
|
|
18
|
+
/** Weight of importance × confidence (default: 0.3) */
|
|
19
|
+
intrinsicWeight: number;
|
|
20
|
+
/** Below this composite = stale (default: 0.3) */
|
|
21
|
+
staleThreshold: number;
|
|
22
|
+
/** Minimum search boost (default: 0.3) */
|
|
23
|
+
searchBoostMin: number;
|
|
24
|
+
/** Importance modulation coefficient for half-life (default: 1.5) */
|
|
25
|
+
importanceModulation: number;
|
|
26
|
+
/** Weibull beta for Core tier — sub-exponential (default: 0.8) */
|
|
27
|
+
betaCore: number;
|
|
28
|
+
/** Weibull beta for Working tier — standard exponential (default: 1.0) */
|
|
29
|
+
betaWorking: number;
|
|
30
|
+
/** Weibull beta for Peripheral tier — super-exponential (default: 1.3) */
|
|
31
|
+
betaPeripheral: number;
|
|
32
|
+
/** Decay floor for Core memories (default: 0.9) */
|
|
33
|
+
coreDecayFloor: number;
|
|
34
|
+
/** Decay floor for Working memories (default: 0.7) */
|
|
35
|
+
workingDecayFloor: number;
|
|
36
|
+
/** Decay floor for Peripheral memories (default: 0.5) */
|
|
37
|
+
peripheralDecayFloor: number;
|
|
38
|
+
}
|
|
39
|
+
export declare const DEFAULT_DECAY_CONFIG: DecayConfig;
|
|
40
|
+
export interface DecayScore {
|
|
41
|
+
memoryId: string;
|
|
42
|
+
recency: number;
|
|
43
|
+
frequency: number;
|
|
44
|
+
intrinsic: number;
|
|
45
|
+
composite: number;
|
|
46
|
+
}
|
|
47
|
+
/** Minimal memory fields needed for decay calculation. */
|
|
48
|
+
export interface DecayableMemory {
|
|
49
|
+
id: string;
|
|
50
|
+
importance: number;
|
|
51
|
+
confidence: number;
|
|
52
|
+
tier: MemoryTier;
|
|
53
|
+
accessCount: number;
|
|
54
|
+
createdAt: number;
|
|
55
|
+
lastAccessedAt: number;
|
|
56
|
+
/** Emotional salience (0-1). Modulates decay: high salience = slower decay. */
|
|
57
|
+
emotionalSalience: number;
|
|
58
|
+
}
|
|
59
|
+
export interface DecayEngine {
|
|
60
|
+
/** Calculate decay score for a single memory */
|
|
61
|
+
score(memory: DecayableMemory, now?: number): DecayScore;
|
|
62
|
+
/** Calculate decay scores for multiple memories */
|
|
63
|
+
scoreAll(memories: DecayableMemory[], now?: number): DecayScore[];
|
|
64
|
+
/** Apply decay boost to search results (multiplies each score by boost) */
|
|
65
|
+
applySearchBoost(results: Array<{
|
|
66
|
+
memory: DecayableMemory;
|
|
67
|
+
score: number;
|
|
68
|
+
}>, now?: number): void;
|
|
69
|
+
/** Find stale memories (composite below threshold) */
|
|
70
|
+
getStaleMemories(memories: DecayableMemory[], now?: number): DecayScore[];
|
|
71
|
+
}
|
|
72
|
+
export declare function createDecayEngine(config?: DecayConfig): DecayEngine;
|
|
73
|
+
//# sourceMappingURL=decay-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decay-engine.d.ts","sourceRoot":"","sources":["../../src/decay-engine.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAQzD,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,eAAe,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,cAAc,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAGD,eAAO,MAAM,oBAAoB,EAAE,WAclC,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0DAA0D;AAC1D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,gDAAgD;IAChD,KAAK,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACzD,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC;IAClE,2EAA2E;IAC3E,gBAAgB,CACd,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EAC1D,GAAG,CAAC,EAAE,MAAM,GACX,IAAI,CAAC;IACR,sDAAsD;IACtD,gBAAgB,CACd,QAAQ,EAAE,eAAe,EAAE,EAC3B,GAAG,CAAC,EAAE,MAAM,GACX,UAAU,EAAE,CAAC;CACjB;AAMD,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,WAAkC,GACzC,WAAW,CAgIb"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const MS_PER_DAY = 864e5;
|
|
2
|
+
const DEFAULT_DECAY_CONFIG = {
|
|
3
|
+
recencyHalfLifeDays: 30,
|
|
4
|
+
recencyWeight: 0.4,
|
|
5
|
+
// initial — pending optimization
|
|
6
|
+
frequencyWeight: 0.3,
|
|
7
|
+
// initial — pending optimization
|
|
8
|
+
intrinsicWeight: 0.3,
|
|
9
|
+
// initial — pending optimization
|
|
10
|
+
staleThreshold: 0.3,
|
|
11
|
+
searchBoostMin: 0.3,
|
|
12
|
+
importanceModulation: 1.5,
|
|
13
|
+
betaCore: 0.8,
|
|
14
|
+
betaWorking: 1,
|
|
15
|
+
betaPeripheral: 1.3,
|
|
16
|
+
coreDecayFloor: 0.9,
|
|
17
|
+
workingDecayFloor: 0.7,
|
|
18
|
+
peripheralDecayFloor: 0.5
|
|
19
|
+
};
|
|
20
|
+
function createDecayEngine(config = DEFAULT_DECAY_CONFIG) {
|
|
21
|
+
const {
|
|
22
|
+
recencyHalfLifeDays: halfLife,
|
|
23
|
+
recencyWeight: rw,
|
|
24
|
+
frequencyWeight: fw,
|
|
25
|
+
intrinsicWeight: iw,
|
|
26
|
+
staleThreshold,
|
|
27
|
+
searchBoostMin: boostMin,
|
|
28
|
+
importanceModulation: mu,
|
|
29
|
+
betaCore,
|
|
30
|
+
betaWorking,
|
|
31
|
+
betaPeripheral,
|
|
32
|
+
coreDecayFloor,
|
|
33
|
+
workingDecayFloor,
|
|
34
|
+
peripheralDecayFloor
|
|
35
|
+
} = config;
|
|
36
|
+
function getTierBeta(tier) {
|
|
37
|
+
switch (tier) {
|
|
38
|
+
case "core":
|
|
39
|
+
return betaCore;
|
|
40
|
+
case "working":
|
|
41
|
+
return betaWorking;
|
|
42
|
+
case "peripheral":
|
|
43
|
+
return betaPeripheral;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function getTierFloor(tier) {
|
|
47
|
+
switch (tier) {
|
|
48
|
+
case "core":
|
|
49
|
+
return coreDecayFloor;
|
|
50
|
+
case "working":
|
|
51
|
+
return workingDecayFloor;
|
|
52
|
+
case "peripheral":
|
|
53
|
+
return peripheralDecayFloor;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function recency(memory, now) {
|
|
57
|
+
const lastActive = memory.accessCount > 0 ? memory.lastAccessedAt : memory.createdAt;
|
|
58
|
+
const daysSince = Math.max(0, (now - lastActive) / MS_PER_DAY);
|
|
59
|
+
const salience = memory.emotionalSalience ?? 0.3;
|
|
60
|
+
const effectiveHL = halfLife * Math.exp(mu * memory.importance) * (1 + salience * 0.5);
|
|
61
|
+
const lambda = Math.LN2 / effectiveHL;
|
|
62
|
+
const beta = getTierBeta(memory.tier);
|
|
63
|
+
return Math.exp(-lambda * Math.pow(daysSince, beta));
|
|
64
|
+
}
|
|
65
|
+
function frequency(memory) {
|
|
66
|
+
const base = 1 - Math.exp(-memory.accessCount / 5);
|
|
67
|
+
if (memory.accessCount <= 1) return base;
|
|
68
|
+
const lastActive = memory.accessCount > 0 ? memory.lastAccessedAt : memory.createdAt;
|
|
69
|
+
const accessSpanDays = Math.max(
|
|
70
|
+
1,
|
|
71
|
+
(lastActive - memory.createdAt) / MS_PER_DAY
|
|
72
|
+
);
|
|
73
|
+
const avgGapDays = accessSpanDays / Math.max(memory.accessCount - 1, 1);
|
|
74
|
+
const recentnessBonus = Math.exp(-avgGapDays / 30);
|
|
75
|
+
return base * (0.5 + 0.5 * recentnessBonus);
|
|
76
|
+
}
|
|
77
|
+
function intrinsic(memory) {
|
|
78
|
+
const salience = memory.emotionalSalience ?? 0.3;
|
|
79
|
+
return memory.importance * memory.confidence * (1 + salience * 0.3);
|
|
80
|
+
}
|
|
81
|
+
function scoreOne(memory, now) {
|
|
82
|
+
const r = recency(memory, now);
|
|
83
|
+
const f = frequency(memory);
|
|
84
|
+
const i = intrinsic(memory);
|
|
85
|
+
const composite = rw * r + fw * f + iw * i;
|
|
86
|
+
return {
|
|
87
|
+
memoryId: memory.id,
|
|
88
|
+
recency: r,
|
|
89
|
+
frequency: f,
|
|
90
|
+
intrinsic: i,
|
|
91
|
+
composite
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
score(memory, now = Date.now()) {
|
|
96
|
+
return scoreOne(memory, now);
|
|
97
|
+
},
|
|
98
|
+
scoreAll(memories, now = Date.now()) {
|
|
99
|
+
return memories.map((m) => scoreOne(m, now));
|
|
100
|
+
},
|
|
101
|
+
applySearchBoost(results, now = Date.now()) {
|
|
102
|
+
for (const r of results) {
|
|
103
|
+
const ds = scoreOne(r.memory, now);
|
|
104
|
+
const tierFloor = Math.max(getTierFloor(r.memory.tier), ds.composite);
|
|
105
|
+
const multiplier = boostMin + (1 - boostMin) * tierFloor;
|
|
106
|
+
r.score *= Math.min(1, Math.max(boostMin, multiplier));
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
getStaleMemories(memories, now = Date.now()) {
|
|
110
|
+
const scores = memories.map((m) => scoreOne(m, now));
|
|
111
|
+
return scores.filter((s) => s.composite < staleThreshold).sort((a, b) => a.composite - b.composite);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
DEFAULT_DECAY_CONFIG,
|
|
117
|
+
createDecayEngine
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=decay-engine.js.map
|