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;
  },
};