Spaces:
Running
Running
| import crypto from 'crypto'; | |
| import path from 'path'; | |
| import { loadEncryptedJson, saveEncryptedJson } from './cryptoUtils.js'; | |
| const DATA_ROOT = '/data/memories'; | |
| const INDEX_FILE = path.join(DATA_ROOT, 'index.json'); | |
| const MAX_MEMORY_LENGTH = 220; | |
| const state = { | |
| loaded: false, | |
| index: { | |
| memories: {}, | |
| }, | |
| }; | |
| function ensureOwner(owner) { | |
| if (!owner?.type || !owner?.id) throw new Error('Invalid memory owner'); | |
| return owner; | |
| } | |
| function nowIso() { | |
| return new Date().toISOString(); | |
| } | |
| function sanitizeText(text) { | |
| return String(text || '').replace(/\s+/g, ' ').trim().slice(0, MAX_MEMORY_LENGTH); | |
| } | |
| async function ensureLoaded() { | |
| if (state.loaded) return; | |
| const stored = await loadEncryptedJson(INDEX_FILE); | |
| state.index = { | |
| memories: stored?.memories || {}, | |
| }; | |
| state.loaded = true; | |
| } | |
| async function saveIndex() { | |
| await saveEncryptedJson(INDEX_FILE, state.index); | |
| } | |
| function matchesOwner(memory, owner) { | |
| return memory.ownerType === owner.type && memory.ownerId === owner.id; | |
| } | |
| function sanitize(memory) { | |
| return { | |
| id: memory.id, | |
| content: memory.content, | |
| source: memory.source || 'assistant', | |
| sessionId: memory.sessionId || null, | |
| createdAt: memory.createdAt, | |
| updatedAt: memory.updatedAt, | |
| }; | |
| } | |
| export const memoryStore = { | |
| async list(owner) { | |
| ensureOwner(owner); | |
| await ensureLoaded(); | |
| return Object.values(state.index.memories) | |
| .filter((memory) => matchesOwner(memory, owner)) | |
| .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()) | |
| .map(sanitize); | |
| }, | |
| async create(owner, { content, sessionId = null, source = 'assistant' }) { | |
| ensureOwner(owner); | |
| await ensureLoaded(); | |
| const normalized = sanitizeText(content); | |
| if (!normalized) return null; | |
| const memory = { | |
| id: crypto.randomUUID(), | |
| ownerType: owner.type, | |
| ownerId: owner.id, | |
| content: normalized, | |
| sessionId, | |
| source, | |
| createdAt: nowIso(), | |
| updatedAt: nowIso(), | |
| }; | |
| state.index.memories[memory.id] = memory; | |
| await saveIndex(); | |
| return sanitize(memory); | |
| }, | |
| async update(owner, id, content) { | |
| ensureOwner(owner); | |
| await ensureLoaded(); | |
| const memory = state.index.memories[id]; | |
| if (!memory || !matchesOwner(memory, owner)) return null; | |
| const normalized = sanitizeText(content); | |
| if (!normalized) return null; | |
| memory.content = normalized; | |
| memory.updatedAt = nowIso(); | |
| await saveIndex(); | |
| return sanitize(memory); | |
| }, | |
| async delete(owner, id) { | |
| ensureOwner(owner); | |
| await ensureLoaded(); | |
| const memory = state.index.memories[id]; | |
| if (!memory || !matchesOwner(memory, owner)) return false; | |
| delete state.index.memories[id]; | |
| await saveIndex(); | |
| return true; | |
| }, | |
| }; | |