@softerist/heuristic-mcp 3.0.12 → 3.0.14

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/lib/cache.js CHANGED
@@ -356,13 +356,18 @@ export class EmbeddingsCache {
356
356
  }
357
357
  }
358
358
 
359
- async ensureLoaded() {
359
+ async ensureLoaded({ preferDisk = false } = {}) {
360
360
  if (!this.config.enableCache) return;
361
361
  if (!this._clearedAfterIndex) return;
362
362
  if (this._loadPromise) return this._loadPromise;
363
363
 
364
364
  this._loadPromise = (async () => {
365
- await this.load();
365
+ if (preferDisk && this.config.verbose) {
366
+ console.info('[Cache] ensureLoaded: forcing disk vector mode for incremental low-RAM reload');
367
+ }
368
+ await this.load({
369
+ forceVectorLoadMode: preferDisk ? 'disk' : undefined,
370
+ });
366
371
  this._clearedAfterIndex = false;
367
372
  })().finally(() => {
368
373
  this._loadPromise = null;
@@ -507,7 +512,7 @@ export class EmbeddingsCache {
507
512
 
508
513
  // -------------------- Load --------------------
509
514
 
510
- async load() {
515
+ async load({ forceVectorLoadMode } = {}) {
511
516
  if (!this.config.enableCache) return;
512
517
 
513
518
  try {
@@ -637,16 +642,25 @@ export class EmbeddingsCache {
637
642
  }
638
643
  }
639
644
 
645
+ const configuredVectorLoadMode =
646
+ typeof this.config.vectorStoreLoadMode === 'string'
647
+ ? this.config.vectorStoreLoadMode.toLowerCase()
648
+ : 'memory';
649
+ const effectiveVectorLoadMode =
650
+ forceVectorLoadMode === 'disk' || forceVectorLoadMode === 'memory'
651
+ ? forceVectorLoadMode
652
+ : configuredVectorLoadMode;
653
+
640
654
  if (useBinary) {
641
655
  try {
642
656
  this.binaryStore = await BinaryVectorStore.load(this.config.cacheDirectory, {
643
657
  contentCacheEntries: this.config.contentCacheEntries,
644
658
  vectorCacheEntries: this.config.vectorCacheEntries,
645
- vectorLoadMode: this.config.vectorStoreLoadMode,
659
+ vectorLoadMode: effectiveVectorLoadMode,
646
660
  });
647
661
  cacheData = await this.binaryStore.toChunkViews({
648
662
  includeContent: this.config.vectorStoreContentMode === 'inline',
649
- includeVector: this.config.vectorStoreLoadMode !== 'disk',
663
+ includeVector: effectiveVectorLoadMode !== 'disk',
650
664
  });
651
665
  } catch (err) {
652
666
  this.binaryStore = null;
@@ -661,7 +675,7 @@ export class EmbeddingsCache {
661
675
  if (this.sqliteStore) {
662
676
  cacheData = this.sqliteStore.toChunkViews({
663
677
  includeContent: this.config.vectorStoreContentMode === 'inline',
664
- includeVector: this.config.vectorStoreLoadMode !== 'disk',
678
+ includeVector: effectiveVectorLoadMode !== 'disk',
665
679
  });
666
680
  } else {
667
681
  // SQLite file missing, need reindex
@@ -684,7 +698,13 @@ export class EmbeddingsCache {
684
698
  const allowedExtensions = new Set(
685
699
  (this.config.fileExtensions || []).map((ext) => `.${ext}`)
686
700
  );
701
+ const allowedFileNames = new Set(this.config.fileNames || []);
687
702
  const applyExtensionFilter = !this.binaryStore;
703
+ const shouldKeepFile = (filePath) => {
704
+ const ext = path.extname(filePath);
705
+ if (allowedExtensions.has(ext)) return true;
706
+ return allowedFileNames.has(path.basename(filePath));
707
+ };
688
708
 
689
709
  const rawHashes = hasHashData ? new Map(Object.entries(hashData)) : new Map();
690
710
  this.vectorStore = [];
@@ -693,8 +713,7 @@ export class EmbeddingsCache {
693
713
  // Single-pass filter + normalization
694
714
  for (const chunk of cacheData) {
695
715
  if (applyExtensionFilter) {
696
- const ext = path.extname(chunk.file);
697
- if (!allowedExtensions.has(ext)) continue;
716
+ if (!shouldKeepFile(chunk.file)) continue;
698
717
  }
699
718
  normalizeChunkVector(chunk);
700
719
  this.vectorStore.push(chunk);
@@ -707,7 +726,7 @@ export class EmbeddingsCache {
707
726
  if (hasHashData) {
708
727
  // Only keep hashes for allowed extensions
709
728
  for (const [file, entry] of rawHashes) {
710
- if (!applyExtensionFilter || allowedExtensions.has(path.extname(file))) {
729
+ if (!applyExtensionFilter || shouldKeepFile(file)) {
711
730
  const normalized = normalizeFileHashEntry(entry);
712
731
  if (normalized) {
713
732
  this.fileHashes.set(file, normalized);
@@ -814,24 +833,22 @@ export class EmbeddingsCache {
814
833
  const hashFile = path.join(this.config.cacheDirectory, 'file-hashes.json');
815
834
  const metaFile = path.join(this.config.cacheDirectory, CACHE_META_FILE);
816
835
 
817
- // Snapshot to avoid race conditions during async write
836
+ // Snapshot to avoid race conditions during async write.
837
+ // Keep this shallow for binary/sqlite to prevent multi-GB vector materialization.
838
+ const snapshotStore = Array.isArray(this.vectorStore) ? [...this.vectorStore] : [];
839
+ const supportsBackendVectorResolve =
840
+ this.config.vectorStoreFormat === 'binary' || this.config.vectorStoreFormat === 'sqlite';
841
+ const hasMissingVectors = snapshotStore.some(
842
+ (chunk) => chunk && (chunk.vector === undefined || chunk.vector === null)
843
+ );
818
844
  const useDiskVectors =
819
- this.config.vectorStoreLoadMode === 'disk' &&
820
- (this.config.vectorStoreFormat === 'binary' || this.config.vectorStoreFormat === 'sqlite');
821
- const snapshotStore = Array.isArray(this.vectorStore)
822
- ? useDiskVectors
823
- ? this.vectorStore.map((chunk, index) => {
824
- if (!chunk) return null;
825
- const vector = this.getChunkVector(chunk, index);
826
- if (!vector) {
827
- throw new Error(
828
- `Missing vector data for cache write in disk mode at index ${index}`
829
- );
830
- }
831
- return { ...chunk, vector };
832
- })
833
- : [...this.vectorStore]
834
- : [];
845
+ supportsBackendVectorResolve &&
846
+ (this.config.vectorStoreLoadMode === 'disk' || hasMissingVectors);
847
+ if (hasMissingVectors && !useDiskVectors) {
848
+ throw new Error(
849
+ 'Missing vector data for cache write and backend vector resolution is unavailable'
850
+ );
851
+ }
835
852
 
836
853
  this.cacheMeta = {
837
854
  version: CACHE_META_VERSION,
@@ -856,7 +873,10 @@ export class EmbeddingsCache {
856
873
  snapshotStore,
857
874
  {
858
875
  contentCacheEntries: this.config.contentCacheEntries,
876
+ vectorCacheEntries: this.config.vectorCacheEntries,
877
+ vectorLoadMode: useDiskVectors ? 'disk' : this.config.vectorStoreLoadMode,
859
878
  getContent: (chunk, index) => this.getChunkContent(chunk, index),
879
+ getVector: useDiskVectors ? (chunk, index) => this.getChunkVector(chunk, index) : null,
860
880
  preRename: async () => {
861
881
  if (this.activeReads > 0) {
862
882
  await this.waitForReadersWithTimeout(
@@ -890,6 +910,7 @@ export class EmbeddingsCache {
890
910
  snapshotStore,
891
911
  {
892
912
  getContent: (chunk, index) => this.getChunkContent(chunk, index),
913
+ getVector: useDiskVectors ? (chunk, index) => this.getChunkVector(chunk, index) : null,
893
914
  preRename: async () => {
894
915
  if (this.activeReads > 0) {
895
916
  await this.waitForReadersWithTimeout(
@@ -1582,6 +1603,14 @@ export class EmbeddingsCache {
1582
1603
  }
1583
1604
  }
1584
1605
  this.binaryStore = null;
1606
+ if (this.sqliteStore) {
1607
+ try {
1608
+ this.sqliteStore.close();
1609
+ } catch {
1610
+ // ignore close errors
1611
+ }
1612
+ }
1613
+ this.sqliteStore = null;
1585
1614
  this.fileHashes = new Map();
1586
1615
  this.invalidateAnnIndex();
1587
1616
  await this.clearCallGraphData();