UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

690 lines 84.8 kB
/** * Memory Search Index - High-performance indexed search for Memory elements * * Addresses issue #984: Implement search indexing for Memory element scalability * * Features: * - Tag index for O(1) tag-based queries * - Content index using inverted index pattern * - Temporal index for date range queries * - Privacy level index for filtered access * - Incremental index updates * - Optional persistence * - Configurable thresholds */ import { MEMORY_CONSTANTS, MEMORY_SECURITY_EVENTS } from './constants.js'; import { logger } from '../../utils/logger.js'; import { UnicodeValidator } from '../../security/validators/unicodeValidator.js'; import { SecurityMonitor } from '../../security/securityMonitor.js'; /** * Inverted index for content search */ class ContentIndex { termToEntries = new Map(); entryToTerms = new Map(); config; constructor(config) { this.config = config; } /** * Add an entry to the content index */ addEntry(entryId, content) { const terms = this.extractTerms(content, { entryId }); // Store terms for this entry this.entryToTerms.set(entryId, terms); // Update inverted index for (const term of terms) { let entries = this.termToEntries.get(term); if (!entries) { entries = new Set(); this.termToEntries.set(term, entries); } entries.add(entryId); } } /** * Remove an entry from the index */ removeEntry(entryId) { const terms = this.entryToTerms.get(entryId); if (!terms) return; // Remove from inverted index for (const term of terms) { const entries = this.termToEntries.get(term); if (entries) { entries.delete(entryId); if (entries.size === 0) { this.termToEntries.delete(term); } } } // Remove entry terms this.entryToTerms.delete(entryId); } /** * Search for entries containing query terms */ search(query) { const queryTerms = this.extractTerms(query); const scores = new Map(); for (const term of queryTerms) { const entries = this.termToEntries.get(term); if (entries) { for (const entryId of entries) { scores.set(entryId, (scores.get(entryId) || 0) + 1); } } } return scores; } /** * Extract searchable terms from content * SECURITY FIX: Added audit logging for Unicode validation operations */ extractTerms(content, context) { // Normalize for security const normalized = UnicodeValidator.normalize(content); if (!normalized.isValid) { logger.warn('Invalid Unicode in content for indexing', { entryId: context?.entryId }); // SECURITY FIX: Log security event for audit trail (DMCP-SEC-006) SecurityMonitor.logSecurityEvent({ type: MEMORY_SECURITY_EVENTS.MEMORY_UNICODE_VALIDATION_FAILED, severity: 'LOW', source: 'MemorySearchIndex.extractTerms', details: `Invalid Unicode detected during term extraction${context?.entryId ? ` for entry ${context.entryId}` : ''}`, metadata: { entryId: context?.entryId, issueCount: 0 // UnicodeValidationResult doesn't expose issues } }); return new Set(); } const text = normalized.normalizedContent.toLowerCase(); const terms = new Set(); // Simple tokenization (can be improved with better NLP) const words = text.match(/\b\w+\b/g) || []; let termCount = 0; for (const word of words) { if (word.length >= (this.config.minTermLength || 2)) { terms.add(word); termCount++; if (termCount >= (this.config.maxTermsPerEntry || 100)) { break; } } } return terms; } clear() { this.termToEntries.clear(); this.entryToTerms.clear(); } get size() { return this.termToEntries.size; } } /** * Date range index using sorted array for efficient range queries */ class TemporalIndex { entries = []; addEntry(entryId, date) { const timestamp = date.getTime(); // Binary search for insertion point let left = 0; let right = this.entries.length; while (left < right) { const mid = Math.floor((left + right) / 2); if (this.entries[mid].timestamp < timestamp) { left = mid + 1; } else { right = mid; } } // Insert at correct position to maintain sort this.entries.splice(left, 0, { id: entryId, timestamp }); } removeEntry(entryId) { const index = this.entries.findIndex(e => e.id === entryId); if (index >= 0) { this.entries.splice(index, 1); } } /** * Find entries within date range */ searchRange(from, to) { const results = new Set(); const fromTime = from?.getTime() || 0; const toTime = to?.getTime() || Date.now(); // Binary search for start let start = 0; let end = this.entries.length; while (start < end) { const mid = Math.floor((start + end) / 2); if (this.entries[mid].timestamp < fromTime) { start = mid + 1; } else { end = mid; } } // Collect all entries in range for (let i = start; i < this.entries.length; i++) { const entry = this.entries[i]; if (entry.timestamp > toTime) break; results.add(entry.id); } return results; } clear() { this.entries = []; } get size() { return this.entries.length; } serialize() { return this.entries; } deserialize(data) { this.entries = data || []; } } /** * Main search index for Memory elements */ export class MemorySearchIndex { config; // Indexes tagIndex = new Map(); privacyIndex = new Map(); contentIndex = null; temporalIndex = new TemporalIndex(); // Cached entries for scoring entriesCache = new Map(); // Index state isBuilt = false; isBuilding = false; buildQueue = null; stats = { isIndexed: false, entryCount: 0, tagCount: 0, termCount: 0, memoryUsageBytes: 0 }; // Memory management maxMemoryBytes; memoryUsageBytes = 0; constructor(config = {}) { this.config = { indexThreshold: config.indexThreshold || 100, enableContentIndex: config.enableContentIndex !== false, maxTermsPerEntry: config.maxTermsPerEntry || 100, minTermLength: config.minTermLength || 2, enablePersistence: config.enablePersistence || false, maxMemoryMB: config.maxMemoryMB || 100, enableLRUEviction: config.enableLRUEviction !== false }; // Configure memory limit (default 100MB, configurable) this.maxMemoryBytes = (this.config.maxMemoryMB || 100) * 1024 * 1024; if (this.config.enableContentIndex) { this.contentIndex = new ContentIndex(this.config); } // Config available via stats methods — no need to log on every instantiation } /** * Build or rebuild the index from entries * FIX: Added race condition protection with isBuilding flag and build queue */ async buildIndex(entries) { // If already building, wait for the current build to complete if (this.isBuilding && this.buildQueue) { logger.debug('Index build already in progress, waiting...'); return this.buildQueue; } // Create build promise to handle concurrent calls this.buildQueue = this._doBuildIndex(entries); return this.buildQueue; } async _doBuildIndex(entries) { // Prevent concurrent builds if (this.isBuilding) { logger.warn('Attempted concurrent index build, skipping'); return; } this.isBuilding = true; const startTime = Date.now(); try { // Clear existing indexes this.clear(); // Check if we should index based on threshold if (entries.size < (this.config.indexThreshold || 100)) { logger.debug('Not building index - below threshold', { entryCount: entries.size, threshold: this.config.indexThreshold }); return; } // Build indexes for (const [id, entry] of entries) { this.addToIndex(id, entry); } // Calculate memory usage this.updateMemoryUsage(); // SECURITY FIX: Log security event for index build operation (DMCP-SEC-006) SecurityMonitor.logSecurityEvent({ type: MEMORY_SECURITY_EVENTS.MEMORY_CREATED, // Using CREATED for index build severity: 'LOW', source: 'MemorySearchIndex.buildIndex', details: `Memory search index built successfully with ${entries.size} entries`, metadata: { entryCount: entries.size, tagCount: this.tagIndex.size, memoryUsageBytes: this.memoryUsageBytes } }); // Update stats const buildTime = Date.now() - startTime; this.stats = { isIndexed: true, entryCount: entries.size, tagCount: this.tagIndex.size, termCount: this.contentIndex?.size || 0, lastBuilt: new Date(), buildTimeMs: buildTime, memoryUsageBytes: this.memoryUsageBytes }; this.isBuilt = true; logger.info('Memory search index built', this.stats); } catch (error) { logger.error('Failed to build memory search index', error); // Reset state on failure this.clear(); throw error; } finally { this.isBuilding = false; this.buildQueue = null; } } /** * Add a single entry to the index (incremental update) */ addEntry(entry) { if (!this.isBuilt) return; this.addToIndex(entry.id, entry); this.stats.entryCount++; logger.debug('Entry added to index', { id: entry.id }); } /** * Remove an entry from the index */ removeEntry(entryId) { if (!this.isBuilt) return; const entry = this.entriesCache.get(entryId); if (!entry) return; // Remove from tag index if (entry.metadata?.tags) { for (const tag of entry.metadata.tags) { const entries = this.tagIndex.get(tag); if (entries) { entries.delete(entryId); if (entries.size === 0) { this.tagIndex.delete(tag); } } } } // Remove from privacy index const privacyLevel = entry.metadata?.privacyLevel || entry.privacyLevel || MEMORY_CONSTANTS.DEFAULT_PRIVACY_LEVEL; const privacyEntries = this.privacyIndex.get(privacyLevel); if (privacyEntries) { privacyEntries.delete(entryId); } // Remove from content index this.contentIndex?.removeEntry(entryId); // Remove from temporal index this.temporalIndex.removeEntry(entryId); // Remove from cache this.entriesCache.delete(entryId); this.stats.entryCount--; logger.debug('Entry removed from index', { id: entryId }); } /** * Search the index with multiple criteria */ search(query, entries) { if (!this.isBuilt) { logger.debug('Index not built, falling back to linear search'); return this.linearSearch(query, entries); } // Start with all entries or filtered by privacy let candidateIds = null; // Filter by privacy level if (query.privacyLevel) { candidateIds = new Set(this.privacyIndex.get(query.privacyLevel) || []); } // Filter by tags (intersection) if (query.tags && query.tags.length > 0) { const tagResults = new Set(); for (const tag of query.tags) { const entries = this.tagIndex.get(tag.toLowerCase()); if (entries) { if (candidateIds) { // Intersection with existing candidates for (const id of entries) { if (candidateIds.has(id)) { tagResults.add(id); } } } else { // First filter entries.forEach(id => tagResults.add(id)); } } } candidateIds = tagResults; } // Filter by date range if (query.dateFrom || query.dateTo) { const dateResults = this.temporalIndex.searchRange(query.dateFrom, query.dateTo); if (candidateIds) { // Intersection const intersection = new Set(); for (const id of candidateIds) { if (dateResults.has(id)) { intersection.add(id); } } candidateIds = intersection; } else { candidateIds = dateResults; } } // Score by content if provided const contentScores = query.content && this.contentIndex ? this.contentIndex.search(query.content) : new Map(); // Build results const results = []; const searchEntries = candidateIds || new Set(entries.keys()); for (const id of searchEntries) { const entry = entries.get(id); if (!entry) continue; // Calculate score let score = 1; // Content relevance score if (contentScores.has(id)) { score += contentScores.get(id) * 2; } // Tag match bonus if (query.tags && entry.metadata?.tags) { const matchedTags = query.tags.filter(tag => entry.metadata?.tags?.includes(tag)); score += matchedTags.length; } results.push({ entry, score, matches: { tags: query.tags?.filter(tag => entry.metadata?.tags?.includes(tag)), terms: query.content ? ['indexed'] : undefined } }); } // Sort by score and apply pagination results.sort((a, b) => b.score - a.score); const offset = query.offset || 0; const limit = query.limit || 100; return results.slice(offset, offset + limit); } /** * Fallback linear search when index is not available */ linearSearch(query, entries) { const results = []; for (const [, entry] of entries) { // Check privacy level if (query.privacyLevel && entry.metadata?.privacyLevel !== query.privacyLevel) { continue; } // Check date range if (query.dateFrom && entry.metadata?.timestamp && entry.metadata.timestamp < query.dateFrom) { continue; } if (query.dateTo && entry.metadata?.timestamp && entry.metadata.timestamp > query.dateTo) { continue; } // Check tags if (query.tags && query.tags.length > 0) { const hasAllTags = query.tags.every(tag => entry.metadata?.tags?.includes(tag)); if (!hasAllTags) continue; } // Check content (simple substring match) let score = 1; if (query.content) { const normalized = UnicodeValidator.normalize(query.content); if (normalized.isValid) { const searchTerm = normalized.normalizedContent.toLowerCase(); if (entry.content.toLowerCase().includes(searchTerm)) { score += 2; } } } results.push({ entry, score, matches: { tags: query.tags?.filter(tag => entry.metadata?.tags?.includes(tag)) } }); } // Sort and paginate results.sort((a, b) => b.score - a.score); const offset = query.offset || 0; const limit = query.limit || 100; return results.slice(offset, offset + limit); } /** * Internal helper to add entry to all indexes */ addToIndex(id, entry) { // Cache entry this.entriesCache.set(id, entry); // Add to tag index if (entry.metadata?.tags) { for (const tag of entry.metadata.tags) { const normalizedTag = tag.toLowerCase(); let entries = this.tagIndex.get(normalizedTag); if (!entries) { entries = new Set(); this.tagIndex.set(normalizedTag, entries); } entries.add(id); } } // Add to privacy index const privacyLevel = entry.metadata?.privacyLevel || entry.privacyLevel || MEMORY_CONSTANTS.DEFAULT_PRIVACY_LEVEL; let privacyEntries = this.privacyIndex.get(privacyLevel); if (!privacyEntries) { privacyEntries = new Set(); this.privacyIndex.set(privacyLevel, privacyEntries); } privacyEntries.add(id); // Add to content index if (this.contentIndex) { this.contentIndex.addEntry(id, entry.content); } // Add to temporal index this.temporalIndex.addEntry(id, entry.metadata?.timestamp || entry.timestamp); } /** * Clear all indexes */ clear() { this.tagIndex.clear(); this.privacyIndex.clear(); this.contentIndex?.clear(); this.temporalIndex.clear(); this.entriesCache.clear(); this.isBuilt = false; this.stats = { isIndexed: false, entryCount: 0, tagCount: 0, termCount: 0 }; } /** * Get index statistics */ getStats() { return { ...this.stats }; } /** * Check if index is built */ get isIndexed() { return this.isBuilt; } /** * Calculate and update memory usage * FIX: Added memory monitoring for content index */ updateMemoryUsage() { let totalBytes = 0; // Estimate tag index memory for (const [tag, entries] of this.tagIndex) { totalBytes += tag.length * 2; // UTF-16 encoding totalBytes += entries.size * 32; // Estimated ID size } // Estimate content index memory if (this.contentIndex) { // Rough estimate: each term + entry IDs totalBytes += this.contentIndex.size * 100; } // Estimate temporal index memory totalBytes += this.temporalIndex.size * 48; // Date + ID // Estimate cache memory for (const entry of this.entriesCache.values()) { totalBytes += JSON.stringify(entry).length * 2; } this.memoryUsageBytes = totalBytes; // Check if we're exceeding memory limit if (totalBytes > this.maxMemoryBytes) { logger.warn('Memory search index exceeding limit', { usedMB: Math.round(totalBytes / 1024 / 1024), limitMB: Math.round(this.maxMemoryBytes / 1024 / 1024) }); // Trigger LRU eviction if enabled if (this.config.enableLRUEviction) { this.evictLRUEntries(); } } } /** * Evict least recently used entries to free memory * FIX: Added LRU eviction for memory management */ evictLRUEntries() { // Get entries sorted by access time (would need to track this) // For now, evict oldest 20% of entries const entriesToEvict = Math.floor(this.entriesCache.size * 0.2); const entriesArray = Array.from(this.entriesCache.keys()); for (let i = 0; i < entriesToEvict; i++) { const idToEvict = entriesArray[i]; this.removeEntry(idToEvict); } logger.info('Evicted LRU entries', { evictedCount: entriesToEvict, remainingCount: this.entriesCache.size }); } /** * Serialize index to JSON for persistence * FIX: Added index serialization for cold start optimization */ serialize() { if (!this.isBuilt) { throw new Error('Cannot serialize unbuilt index'); } const indexData = { version: '1.0.0', stats: this.stats, tagIndex: Array.from(this.tagIndex.entries()).map(([tag, ids]) => ({ tag, ids: Array.from(ids) })), privacyIndex: Array.from(this.privacyIndex.entries()).map(([level, ids]) => ({ level, ids: Array.from(ids) })), temporalIndex: this.temporalIndex.serialize(), // Note: Content index is not serialized due to size // It will be rebuilt on load if needed }; return JSON.stringify(indexData); } /** * Deserialize index from JSON * FIX: Added index deserialization for faster startup */ deserialize(data, entries) { try { const indexData = JSON.parse(data); // Validate version if (indexData.version !== '1.0.0') { throw new Error(`Unsupported index version: ${indexData.version}`); } // Clear existing indexes this.clear(); // Restore tag index for (const { tag, ids } of indexData.tagIndex) { this.tagIndex.set(tag, new Set(ids)); } // Restore privacy index for (const { level, ids } of indexData.privacyIndex) { this.privacyIndex.set(level, new Set(ids)); } // Restore temporal index this.temporalIndex.deserialize(indexData.temporalIndex); // Restore entries cache from provided entries for (const [id, entry] of entries) { if (indexData.tagIndex.some((item) => item.ids.includes(id))) { this.entriesCache.set(id, entry); } } // Rebuild content index if enabled (can't serialize efficiently) if (this.config.enableContentIndex) { this.contentIndex = new ContentIndex(this.config); for (const [id, entry] of this.entriesCache) { this.contentIndex.addEntry(id, entry.content); } } // Update stats this.stats = indexData.stats; this.isBuilt = true; logger.info('Memory search index deserialized', this.stats); } catch (error) { logger.error('Failed to deserialize index, rebuilding', error); // Fall back to building from scratch this.buildIndex(entries); } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWVtb3J5U2VhcmNoSW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZWxlbWVudHMvbWVtb3JpZXMvTWVtb3J5U2VhcmNoSW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUdILE9BQU8sRUFBZ0IsZ0JBQWdCLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4RixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFDakYsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBcUZwRTs7R0FFRztBQUNILE1BQU0sWUFBWTtJQUNSLGFBQWEsR0FBRyxJQUFJLEdBQUcsRUFBdUIsQ0FBQztJQUMvQyxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFDckMsTUFBTSxDQUFvQjtJQUUzQyxZQUFZLE1BQXlCO1FBQ25DLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNILFFBQVEsQ0FBQyxPQUFlLEVBQUUsT0FBZTtRQUN2QyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFFdEQsNkJBQTZCO1FBQzdCLElBQUksQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUV0Qyx3QkFBd0I7UUFDeEIsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN6QixJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ2IsT0FBTyxHQUFHLElB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