@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
JavaScript
/**
* 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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