import type { MemoryId } from '../../types/ids/index.js' import type { CreateMemoryParams, MemoryContent, MemoryIndexEntry, MemoryRecord, MemorySearchParams, MemorySearchResult, MemoryStore, UpdateMemoryParams, } from '../../types/memory/index.js' import { assertMemoryStatus } from '../../types/memory/index.js' import { generateMemoryId } from '../../utils/id.js' import { InMemoryMemoryIndex, searchMemoryEntries } from './index.js' import { MemoryNameConflictError, assertOptionalMemoryFields, nameHolder, withOptionalFields, } from './naming.js' export class InMemoryMemoryStore implements MemoryStore { private content = new Map() private index = new InMemoryMemoryIndex() async create( params: CreateMemoryParams, ): Promise<{ entry: MemoryIndexEntry; content: MemoryContent }> { assertOptionalMemoryFields(params) if (params.name !== undefined) { const holder = nameHolder(this.index.allEntries(), params.name) if (holder) throw new MemoryNameConflictError(params.name, holder.id) } const id = generateMemoryId() const now = Date.now() const entry: MemoryIndexEntry = withOptionalFields( { id, title: params.title, summary: params.summary, tags: params.tags ? [...params.tags] : [], status: 'active', createdAt: now, updatedAt: now, }, params, ) const memoryContent: MemoryContent = { id, content: params.content, format: params.format ?? 'text', metadata: params.metadata ? { ...params.metadata } : undefined, } this.index.set(entry) this.content.set(id, memoryContent) return { entry, content: memoryContent } } async get(id: MemoryId): Promise { return this.content.get(id) } async getRecord(id: MemoryId): Promise { const entry = this.index.getEntry(id) const content = this.content.get(id) return entry && content ? structuredClone({ entry, content }) : undefined } async update(id: MemoryId, updates: UpdateMemoryParams): Promise { if (updates.status !== undefined) assertMemoryStatus(updates.status) assertOptionalMemoryFields(updates) const existing = this.index.getEntry(id) if (!existing) return undefined if (updates.name !== undefined) { const holder = nameHolder(this.index.allEntries(), updates.name, id) if (holder) throw new MemoryNameConflictError(updates.name, holder.id) } const now = Date.now() const updated: MemoryIndexEntry = withOptionalFields( { ...existing, title: updates.title ?? existing.title, summary: updates.summary ?? existing.summary, tags: updates.tags ? [...updates.tags] : existing.tags, status: updates.status ?? existing.status, updatedAt: now, }, updates, ) this.index.set(updated) if ( updates.content !== undefined || updates.format !== undefined || updates.metadata !== undefined ) { const existingContent = this.content.get(id) if (existingContent) { const updatedContent: MemoryContent = { ...existingContent, content: updates.content ?? existingContent.content, format: updates.format ?? existingContent.format, metadata: updates.metadata !== undefined ? { ...updates.metadata } : existingContent.metadata, } this.content.set(id, updatedContent) } } return updated } async delete(id: MemoryId): Promise { const existed = this.index.remove(id) this.content.delete(id) return existed } async list(params?: MemorySearchParams): Promise { return searchMemoryEntries( this.index.allEntries(), params ?? {}, (id) => this.content.get(id)?.content ?? '', ) } getIndex(): InMemoryMemoryIndex { return this.index } }