@danielsimonjr/memoryjs 1.9.0 → 1.9.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/index.js +241 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +241 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +241 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -15984,7 +15984,7 @@ var init_ContextWindowManager = __esm({
|
|
|
15984
15984
|
init_esm_shims();
|
|
15985
15985
|
init_agent_memory();
|
|
15986
15986
|
init_ContextProfileManager();
|
|
15987
|
-
ContextWindowManager = class {
|
|
15987
|
+
ContextWindowManager = class _ContextWindowManager {
|
|
15988
15988
|
storage;
|
|
15989
15989
|
salienceEngine;
|
|
15990
15990
|
config;
|
|
@@ -16751,9 +16751,249 @@ var init_ContextWindowManager = __esm({
|
|
|
16751
16751
|
console.error("[ContextWindowManager.wakeUp] L1 entity loading failed:", err);
|
|
16752
16752
|
}
|
|
16753
16753
|
}
|
|
16754
|
+
if (options.compress && l1) {
|
|
16755
|
+
try {
|
|
16756
|
+
const level = typeof options.compress === "string" ? options.compress : "medium";
|
|
16757
|
+
const compressResult = this.compressForContext(l1, { level });
|
|
16758
|
+
if (compressResult.stats.savedTokens > 0) {
|
|
16759
|
+
l1 = compressResult.compressed;
|
|
16760
|
+
}
|
|
16761
|
+
} catch (err) {
|
|
16762
|
+
console.error("[ContextWindowManager.wakeUp] Compression failed, using uncompressed:", err);
|
|
16763
|
+
}
|
|
16764
|
+
}
|
|
16754
16765
|
const totalTokens = this.estimateStringTokens(l0) + this.estimateStringTokens(l1);
|
|
16755
16766
|
return { l0, l1, totalTokens, entityCount };
|
|
16756
16767
|
}
|
|
16768
|
+
// ==================== Context Compression ====================
|
|
16769
|
+
/** Unicode abbreviation map for aggressive-level compression (code keywords). */
|
|
16770
|
+
static COMMON_PATTERNS = {
|
|
16771
|
+
"function ": "\u0192 ",
|
|
16772
|
+
"return ": "\u0280 ",
|
|
16773
|
+
"const ": "\u1D04 ",
|
|
16774
|
+
"export ": "\u1D07 ",
|
|
16775
|
+
"import ": "\u026A ",
|
|
16776
|
+
"interface ": "\u026A\u0274\u1D1B ",
|
|
16777
|
+
"class ": "\u1D04\u029Fs ",
|
|
16778
|
+
"async ": "\u1D00 ",
|
|
16779
|
+
"await ": "\u1D00\u1D21 ",
|
|
16780
|
+
"undefined": "\u1D1C\u0274\u1D05",
|
|
16781
|
+
"null": "\u0274\u1D1C\u029F",
|
|
16782
|
+
"true": "\u1D1B",
|
|
16783
|
+
"false": "\uA730",
|
|
16784
|
+
"```typescript": "```ts",
|
|
16785
|
+
"```javascript": "```js",
|
|
16786
|
+
"## ": "\u2E2B ",
|
|
16787
|
+
"### ": "\u2E2C ",
|
|
16788
|
+
"#### ": "\u2E2D ",
|
|
16789
|
+
'"description"': '"desc"',
|
|
16790
|
+
'"dependencies"': '"deps"',
|
|
16791
|
+
'"devDependencies"': '"devDeps"',
|
|
16792
|
+
'"repository"': '"repo"',
|
|
16793
|
+
'"homepage"': '"home"',
|
|
16794
|
+
'"keywords"': '"keys"',
|
|
16795
|
+
'"license"': '"lic"',
|
|
16796
|
+
'"version"': '"ver"',
|
|
16797
|
+
'"required"': '"req"',
|
|
16798
|
+
'"optional"': '"opt"',
|
|
16799
|
+
'"default"': '"def"',
|
|
16800
|
+
'"example"': '"ex"',
|
|
16801
|
+
'"properties"': '"props"',
|
|
16802
|
+
'"additionalProperties"': '"addProps"',
|
|
16803
|
+
"node_modules/": "nm/",
|
|
16804
|
+
"src/": "s/",
|
|
16805
|
+
"dist/": "d/",
|
|
16806
|
+
"test/": "t/",
|
|
16807
|
+
"tests/": "t/",
|
|
16808
|
+
".typescript": ".ts",
|
|
16809
|
+
".javascript": ".js"
|
|
16810
|
+
};
|
|
16811
|
+
/**
|
|
16812
|
+
* Compress text for token-efficient context loading.
|
|
16813
|
+
* Finds repeated substrings, replaces with paragraph-sign codes, generates a legend.
|
|
16814
|
+
* Inspired by the CTON compress-for-context tool.
|
|
16815
|
+
*/
|
|
16816
|
+
compressForContext(text, options) {
|
|
16817
|
+
const level = options?.level ?? "medium";
|
|
16818
|
+
let compressed = text;
|
|
16819
|
+
const legend = {};
|
|
16820
|
+
if (level !== "light") {
|
|
16821
|
+
compressed = compressed.replace(/[ \t]+/g, " ");
|
|
16822
|
+
compressed = compressed.replace(/\n{3,}/g, "\n\n");
|
|
16823
|
+
}
|
|
16824
|
+
if (level === "aggressive") {
|
|
16825
|
+
const patternResult = this.applyCommonPatterns(compressed);
|
|
16826
|
+
compressed = patternResult.text;
|
|
16827
|
+
Object.assign(legend, patternResult.legend);
|
|
16828
|
+
}
|
|
16829
|
+
const minLength2 = level === "light" ? 8 : level === "medium" ? 6 : 5;
|
|
16830
|
+
const minOccurrences = level === "light" ? 4 : 3;
|
|
16831
|
+
const maxSubstrings = level === "light" ? 20 : level === "medium" ? 30 : 36;
|
|
16832
|
+
const substrings = this.findRepeatedSubstrings(compressed, minLength2, minOccurrences, maxSubstrings);
|
|
16833
|
+
if (substrings.length > 0) {
|
|
16834
|
+
const totalSavings = substrings.reduce((sum, s) => sum + s.savings, 0);
|
|
16835
|
+
if (totalSavings > 5) {
|
|
16836
|
+
const result = this.applySubstringCompression(compressed, substrings);
|
|
16837
|
+
Object.assign(legend, result.legend);
|
|
16838
|
+
compressed = result.compressed;
|
|
16839
|
+
}
|
|
16840
|
+
}
|
|
16841
|
+
if (Object.keys(legend).length > 0) {
|
|
16842
|
+
const legendStr = "=== Legend ===\n" + Object.entries(legend).map(([a, f]) => `${a} = ${f}`).join("\n") + "\n=============\n\n";
|
|
16843
|
+
compressed = legendStr + compressed;
|
|
16844
|
+
}
|
|
16845
|
+
const originalTokens = this.estimateStringTokens(text);
|
|
16846
|
+
const hasLegend = Object.keys(legend).length > 0;
|
|
16847
|
+
const compressedTokens = hasLegend ? this.estimateStringTokens(compressed) : originalTokens;
|
|
16848
|
+
const savedTokens = Math.max(0, originalTokens - compressedTokens);
|
|
16849
|
+
return {
|
|
16850
|
+
compressed: hasLegend ? compressed : text,
|
|
16851
|
+
legend,
|
|
16852
|
+
stats: {
|
|
16853
|
+
originalTokens,
|
|
16854
|
+
compressedTokens,
|
|
16855
|
+
savedTokens,
|
|
16856
|
+
savedPercent: originalTokens > 0 ? Math.round(savedTokens / originalTokens * 100) : 0
|
|
16857
|
+
}
|
|
16858
|
+
};
|
|
16859
|
+
}
|
|
16860
|
+
/**
|
|
16861
|
+
* Compress entities for context loading. Formats entities as compact text,
|
|
16862
|
+
* then applies compression. Sorted by importance descending.
|
|
16863
|
+
*/
|
|
16864
|
+
compressEntitiesForContext(entities, options) {
|
|
16865
|
+
const sorted = [...entities].filter((e) => e.entityType !== "profile" && e.entityType !== "diary").sort((a, b) => (b.importance ?? 0) - (a.importance ?? 0));
|
|
16866
|
+
const lines = [];
|
|
16867
|
+
let tokenCount = 0;
|
|
16868
|
+
let entityCount = 0;
|
|
16869
|
+
const maxTokens = options?.maxTokens ?? Infinity;
|
|
16870
|
+
for (const e of sorted) {
|
|
16871
|
+
const obs = e.observations?.slice(0, 3).join("; ") ?? "";
|
|
16872
|
+
const line = `[${e.entityType}] ${e.name}: ${obs}`;
|
|
16873
|
+
const lineTokens = this.estimateStringTokens(line);
|
|
16874
|
+
if (tokenCount + lineTokens > maxTokens) break;
|
|
16875
|
+
lines.push(line);
|
|
16876
|
+
tokenCount += lineTokens;
|
|
16877
|
+
entityCount++;
|
|
16878
|
+
}
|
|
16879
|
+
const raw = lines.join("\n");
|
|
16880
|
+
const result = this.compressForContext(raw, { level: options?.level });
|
|
16881
|
+
return { ...result, entityCount };
|
|
16882
|
+
}
|
|
16883
|
+
// ==================== Compression Helpers ====================
|
|
16884
|
+
/**
|
|
16885
|
+
* Find repeated substrings and calculate compression potential.
|
|
16886
|
+
* Returns substrings sorted by net savings (highest first).
|
|
16887
|
+
* @internal
|
|
16888
|
+
*/
|
|
16889
|
+
findRepeatedSubstrings(text, minLength2, minOccurrences, maxSubstrings = 36) {
|
|
16890
|
+
if (text.length < minLength2 * 2) return [];
|
|
16891
|
+
const substringCounts = /* @__PURE__ */ new Map();
|
|
16892
|
+
const MAX_MAP_SIZE = 1e4;
|
|
16893
|
+
const tokens = text.split(/(\s+|[{}()\[\]<>:;,."'`|=])/);
|
|
16894
|
+
outer: for (let n = 1; n <= 6; n++) {
|
|
16895
|
+
for (let i = 0; i <= tokens.length - n; i++) {
|
|
16896
|
+
const ngram = tokens.slice(i, i + n).join("");
|
|
16897
|
+
if (ngram.length < minLength2 || ngram.length > 50) continue;
|
|
16898
|
+
if (/^\s*$/.test(ngram)) continue;
|
|
16899
|
+
if ((ngram.match(/\s/g) || []).length > ngram.length * 0.5) continue;
|
|
16900
|
+
const opens = (ngram.match(/[{(\[<]/g) || []).length;
|
|
16901
|
+
const closes = (ngram.match(/[})\]>]/g) || []).length;
|
|
16902
|
+
if (opens !== closes) continue;
|
|
16903
|
+
substringCounts.set(ngram, (substringCounts.get(ngram) || 0) + 1);
|
|
16904
|
+
if (substringCounts.size >= MAX_MAP_SIZE) break outer;
|
|
16905
|
+
}
|
|
16906
|
+
}
|
|
16907
|
+
const pathRe = /[a-zA-Z0-9_\-./]+\/[a-zA-Z0-9_\-./]+/g;
|
|
16908
|
+
let pathMatch;
|
|
16909
|
+
while ((pathMatch = pathRe.exec(text)) !== null) {
|
|
16910
|
+
const p = pathMatch[0];
|
|
16911
|
+
if (p.length >= minLength2 && substringCounts.size < MAX_MAP_SIZE) {
|
|
16912
|
+
substringCounts.set(p, (substringCounts.get(p) || 0) + 1);
|
|
16913
|
+
}
|
|
16914
|
+
}
|
|
16915
|
+
const candidates = [];
|
|
16916
|
+
for (const [substring, _mapCount] of substringCounts.entries()) {
|
|
16917
|
+
const actualCount = text.split(substring).length - 1;
|
|
16918
|
+
if (actualCount >= minOccurrences) {
|
|
16919
|
+
const abbrevLength = 2;
|
|
16920
|
+
const legendCost = abbrevLength + substring.length + 4;
|
|
16921
|
+
const savingsPerOccurrence = substring.length - abbrevLength;
|
|
16922
|
+
const netSavings = savingsPerOccurrence * actualCount - legendCost;
|
|
16923
|
+
if (netSavings > 5) {
|
|
16924
|
+
candidates.push({ substring, count: actualCount, savings: netSavings });
|
|
16925
|
+
}
|
|
16926
|
+
}
|
|
16927
|
+
}
|
|
16928
|
+
candidates.sort((a, b) => b.savings - a.savings);
|
|
16929
|
+
const selected = [];
|
|
16930
|
+
const usedSubstrings = [];
|
|
16931
|
+
for (const candidate of candidates) {
|
|
16932
|
+
let isTooSimilar = false;
|
|
16933
|
+
const candidateTrimmed = candidate.substring.trim();
|
|
16934
|
+
if (candidateTrimmed.length < 3) continue;
|
|
16935
|
+
for (const used of usedSubstrings) {
|
|
16936
|
+
const usedTrimmed = used.trim();
|
|
16937
|
+
if (used.includes(candidate.substring) || candidate.substring.includes(used)) {
|
|
16938
|
+
isTooSimilar = true;
|
|
16939
|
+
break;
|
|
16940
|
+
}
|
|
16941
|
+
if (candidateTrimmed === usedTrimmed || candidateTrimmed.includes(usedTrimmed) || usedTrimmed.includes(candidateTrimmed)) {
|
|
16942
|
+
isTooSimilar = true;
|
|
16943
|
+
break;
|
|
16944
|
+
}
|
|
16945
|
+
const shorter = candidateTrimmed.length < usedTrimmed.length ? candidateTrimmed : usedTrimmed;
|
|
16946
|
+
const longer = candidateTrimmed.length >= usedTrimmed.length ? candidateTrimmed : usedTrimmed;
|
|
16947
|
+
if (longer.includes(shorter.slice(0, Math.floor(shorter.length * 0.7)))) {
|
|
16948
|
+
isTooSimilar = true;
|
|
16949
|
+
break;
|
|
16950
|
+
}
|
|
16951
|
+
}
|
|
16952
|
+
if (!isTooSimilar) {
|
|
16953
|
+
selected.push(candidate);
|
|
16954
|
+
usedSubstrings.push(candidate.substring);
|
|
16955
|
+
if (selected.length >= maxSubstrings) break;
|
|
16956
|
+
}
|
|
16957
|
+
}
|
|
16958
|
+
return selected;
|
|
16959
|
+
}
|
|
16960
|
+
/**
|
|
16961
|
+
* Apply substring replacements to text.
|
|
16962
|
+
* Applies replacements in savings-descending order (as returned by findRepeatedSubstrings).
|
|
16963
|
+
* @internal
|
|
16964
|
+
*/
|
|
16965
|
+
applySubstringCompression(text, substrings) {
|
|
16966
|
+
const legend = {};
|
|
16967
|
+
let compressed = text;
|
|
16968
|
+
substrings.forEach((item, index) => {
|
|
16969
|
+
const abbrev = `\xA7${index.toString(36)}`;
|
|
16970
|
+
legend[abbrev] = item.substring;
|
|
16971
|
+
compressed = compressed.split(item.substring).join(abbrev);
|
|
16972
|
+
});
|
|
16973
|
+
return { compressed, legend };
|
|
16974
|
+
}
|
|
16975
|
+
/**
|
|
16976
|
+
* Apply COMMON_PATTERNS unicode abbreviations (aggressive level only).
|
|
16977
|
+
* @internal
|
|
16978
|
+
*/
|
|
16979
|
+
applyCommonPatterns(text) {
|
|
16980
|
+
let result = text;
|
|
16981
|
+
const legend = {};
|
|
16982
|
+
for (const [pattern2, replacement] of Object.entries(_ContextWindowManager.COMMON_PATTERNS)) {
|
|
16983
|
+
try {
|
|
16984
|
+
if (!result.includes(pattern2)) continue;
|
|
16985
|
+
const count = result.split(pattern2).length - 1;
|
|
16986
|
+
const savings = (pattern2.length - replacement.length) * count;
|
|
16987
|
+
if (savings > pattern2.length + replacement.length + 5) {
|
|
16988
|
+
legend[replacement] = pattern2;
|
|
16989
|
+
result = result.split(pattern2).join(replacement);
|
|
16990
|
+
}
|
|
16991
|
+
} catch {
|
|
16992
|
+
continue;
|
|
16993
|
+
}
|
|
16994
|
+
}
|
|
16995
|
+
return { text: result, legend };
|
|
16996
|
+
}
|
|
16757
16997
|
};
|
|
16758
16998
|
}
|
|
16759
16999
|
});
|