File size: 2,792 Bytes
0f84d64 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 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;
},
};
|