family-ai-agent 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/.letta/settings.local.json +3 -0
  2. package/dist/cli/index.js +6 -4
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/database/adapters/base-adapter.d.ts +81 -0
  5. package/dist/database/adapters/base-adapter.d.ts.map +1 -0
  6. package/dist/database/adapters/base-adapter.js +105 -0
  7. package/dist/database/adapters/base-adapter.js.map +1 -0
  8. package/dist/database/adapters/index.d.ts +49 -0
  9. package/dist/database/adapters/index.d.ts.map +1 -0
  10. package/dist/database/adapters/index.js +200 -0
  11. package/dist/database/adapters/index.js.map +1 -0
  12. package/dist/database/adapters/postgres-adapter.d.ts +75 -0
  13. package/dist/database/adapters/postgres-adapter.d.ts.map +1 -0
  14. package/dist/database/adapters/postgres-adapter.js +225 -0
  15. package/dist/database/adapters/postgres-adapter.js.map +1 -0
  16. package/dist/database/adapters/sqlite-adapter.d.ts +72 -0
  17. package/dist/database/adapters/sqlite-adapter.d.ts.map +1 -0
  18. package/dist/database/adapters/sqlite-adapter.js +368 -0
  19. package/dist/database/adapters/sqlite-adapter.js.map +1 -0
  20. package/dist/database/cache/cache-keys.d.ts +180 -0
  21. package/dist/database/cache/cache-keys.d.ts.map +1 -0
  22. package/dist/database/cache/cache-keys.js +107 -0
  23. package/dist/database/cache/cache-keys.js.map +1 -0
  24. package/dist/database/cache/index.d.ts +24 -0
  25. package/dist/database/cache/index.d.ts.map +1 -0
  26. package/dist/database/cache/index.js +34 -0
  27. package/dist/database/cache/index.js.map +1 -0
  28. package/dist/database/cache/query-cache.d.ts +67 -0
  29. package/dist/database/cache/query-cache.d.ts.map +1 -0
  30. package/dist/database/cache/query-cache.js +177 -0
  31. package/dist/database/cache/query-cache.js.map +1 -0
  32. package/dist/database/client.d.ts +63 -4
  33. package/dist/database/client.d.ts.map +1 -1
  34. package/dist/database/client.js +147 -59
  35. package/dist/database/client.js.map +1 -1
  36. package/dist/database/db-config.d.ts +104 -0
  37. package/dist/database/db-config.d.ts.map +1 -0
  38. package/dist/database/db-config.js +167 -0
  39. package/dist/database/db-config.js.map +1 -0
  40. package/dist/database/drizzle/index.d.ts +42 -0
  41. package/dist/database/drizzle/index.d.ts.map +1 -0
  42. package/dist/database/drizzle/index.js +48 -0
  43. package/dist/database/drizzle/index.js.map +1 -0
  44. package/dist/database/drizzle/schema/audit.d.ts +533 -0
  45. package/dist/database/drizzle/schema/audit.d.ts.map +1 -0
  46. package/dist/database/drizzle/schema/audit.js +71 -0
  47. package/dist/database/drizzle/schema/audit.js.map +1 -0
  48. package/dist/database/drizzle/schema/checkpoints.d.ts +665 -0
  49. package/dist/database/drizzle/schema/checkpoints.d.ts.map +1 -0
  50. package/dist/database/drizzle/schema/checkpoints.js +110 -0
  51. package/dist/database/drizzle/schema/checkpoints.js.map +1 -0
  52. package/dist/database/drizzle/schema/conversations.d.ts +449 -0
  53. package/dist/database/drizzle/schema/conversations.d.ts.map +1 -0
  54. package/dist/database/drizzle/schema/conversations.js +91 -0
  55. package/dist/database/drizzle/schema/conversations.js.map +1 -0
  56. package/dist/database/drizzle/schema/documents.d.ts +600 -0
  57. package/dist/database/drizzle/schema/documents.d.ts.map +1 -0
  58. package/dist/database/drizzle/schema/documents.js +100 -0
  59. package/dist/database/drizzle/schema/documents.js.map +1 -0
  60. package/dist/database/drizzle/schema/index.d.ts +3084 -0
  61. package/dist/database/drizzle/schema/index.d.ts.map +1 -0
  62. package/dist/database/drizzle/schema/index.js +46 -0
  63. package/dist/database/drizzle/schema/index.js.map +1 -0
  64. package/dist/database/drizzle/schema/memories.d.ts +435 -0
  65. package/dist/database/drizzle/schema/memories.d.ts.map +1 -0
  66. package/dist/database/drizzle/schema/memories.js +73 -0
  67. package/dist/database/drizzle/schema/memories.js.map +1 -0
  68. package/dist/database/drizzle/schema/tasks.d.ts +565 -0
  69. package/dist/database/drizzle/schema/tasks.d.ts.map +1 -0
  70. package/dist/database/drizzle/schema/tasks.js +84 -0
  71. package/dist/database/drizzle/schema/tasks.js.map +1 -0
  72. package/dist/database/health/circuit-breaker.d.ts +81 -0
  73. package/dist/database/health/circuit-breaker.d.ts.map +1 -0
  74. package/dist/database/health/circuit-breaker.js +184 -0
  75. package/dist/database/health/circuit-breaker.js.map +1 -0
  76. package/dist/database/health/health-monitor.d.ts +69 -0
  77. package/dist/database/health/health-monitor.d.ts.map +1 -0
  78. package/dist/database/health/health-monitor.js +174 -0
  79. package/dist/database/health/health-monitor.js.map +1 -0
  80. package/dist/database/health/index.d.ts +27 -0
  81. package/dist/database/health/index.d.ts.map +1 -0
  82. package/dist/database/health/index.js +23 -0
  83. package/dist/database/health/index.js.map +1 -0
  84. package/dist/database/index.d.ts +16 -0
  85. package/dist/database/index.d.ts.map +1 -0
  86. package/dist/database/index.js +41 -0
  87. package/dist/database/index.js.map +1 -0
  88. package/dist/database/migrations/index.d.ts +34 -0
  89. package/dist/database/migrations/index.d.ts.map +1 -0
  90. package/dist/database/migrations/index.js +45 -0
  91. package/dist/database/migrations/index.js.map +1 -0
  92. package/dist/database/migrations/migrator.d.ts +77 -0
  93. package/dist/database/migrations/migrator.d.ts.map +1 -0
  94. package/dist/database/migrations/migrator.js +258 -0
  95. package/dist/database/migrations/migrator.js.map +1 -0
  96. package/dist/database/migrations/versions/001_initial.d.ts +9 -0
  97. package/dist/database/migrations/versions/001_initial.d.ts.map +1 -0
  98. package/dist/database/migrations/versions/001_initial.js +183 -0
  99. package/dist/database/migrations/versions/001_initial.js.map +1 -0
  100. package/dist/database/types.d.ts +255 -0
  101. package/dist/database/types.d.ts.map +1 -0
  102. package/dist/database/types.js +8 -0
  103. package/dist/database/types.js.map +1 -0
  104. package/dist/database/vector/embedding-cache.d.ts +92 -0
  105. package/dist/database/vector/embedding-cache.d.ts.map +1 -0
  106. package/dist/database/vector/embedding-cache.js +185 -0
  107. package/dist/database/vector/embedding-cache.js.map +1 -0
  108. package/dist/database/vector/hnsw-index.d.ts +111 -0
  109. package/dist/database/vector/hnsw-index.d.ts.map +1 -0
  110. package/dist/database/vector/hnsw-index.js +337 -0
  111. package/dist/database/vector/hnsw-index.js.map +1 -0
  112. package/dist/database/vector/index.d.ts +75 -0
  113. package/dist/database/vector/index.d.ts.map +1 -0
  114. package/dist/database/vector/index.js +213 -0
  115. package/dist/database/vector/index.js.map +1 -0
  116. package/dist/database/vector/similarity.d.ts +67 -0
  117. package/dist/database/vector/similarity.d.ts.map +1 -0
  118. package/dist/database/vector/similarity.js +176 -0
  119. package/dist/database/vector/similarity.js.map +1 -0
  120. package/dist/index.d.ts +1 -1
  121. package/dist/index.js +1 -1
  122. package/package.json +6 -3
  123. package/src/cli/index.ts +5 -5
  124. package/src/database/adapters/base-adapter.ts +171 -0
  125. package/src/database/adapters/index.ts +224 -0
  126. package/src/database/adapters/postgres-adapter.ts +285 -0
  127. package/src/database/adapters/sqlite-adapter.ts +420 -0
  128. package/src/database/cache/cache-keys.ts +150 -0
  129. package/src/database/cache/index.ts +44 -0
  130. package/src/database/cache/query-cache.ts +213 -0
  131. package/src/database/client.ts +166 -64
  132. package/src/database/db-config.ts +194 -0
  133. package/src/database/drizzle/index.ts +66 -0
  134. package/src/database/drizzle/schema/audit.ts +127 -0
  135. package/src/database/drizzle/schema/checkpoints.ts +164 -0
  136. package/src/database/drizzle/schema/conversations.ts +138 -0
  137. package/src/database/drizzle/schema/documents.ts +157 -0
  138. package/src/database/drizzle/schema/index.ts +139 -0
  139. package/src/database/drizzle/schema/memories.ts +127 -0
  140. package/src/database/drizzle/schema/tasks.ts +129 -0
  141. package/src/database/health/circuit-breaker.ts +214 -0
  142. package/src/database/health/health-monitor.ts +224 -0
  143. package/src/database/health/index.ts +41 -0
  144. package/src/database/index.ts +157 -0
  145. package/src/database/migrations/index.ts +52 -0
  146. package/src/database/migrations/migrator.ts +325 -0
  147. package/src/database/migrations/versions/001_initial.ts +198 -0
  148. package/src/database/types.ts +324 -0
  149. package/src/database/vector/embedding-cache.ts +234 -0
  150. package/src/database/vector/hnsw-index.ts +452 -0
  151. package/src/database/vector/index.ts +292 -0
  152. package/src/database/vector/similarity.ts +198 -0
  153. package/src/index.ts +1 -1
@@ -0,0 +1,420 @@
1
+ /**
2
+ * SQLite Database Adapter
3
+ *
4
+ * Adapter for SQLite using better-sqlite3 for embedded database support.
5
+ * Provides zero-config database that works out of the box.
6
+ */
7
+
8
+ import Database from 'better-sqlite3';
9
+ import { drizzle } from 'drizzle-orm/better-sqlite3';
10
+ import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
11
+ import { existsSync, mkdirSync } from 'fs';
12
+ import { dirname } from 'path';
13
+
14
+ import { BaseAdapter } from './base-adapter.js';
15
+ import type {
16
+ QueryResult,
17
+ TransactionClient,
18
+ DatabaseConfig,
19
+ } from '../types.js';
20
+ import { createLogger } from '../../utils/logger.js';
21
+ import { MemoryError } from '../../utils/errors.js';
22
+
23
+ import * as schema from '../drizzle/schema/index.js';
24
+
25
+ const logger = createLogger('SQLiteAdapter');
26
+
27
+ /**
28
+ * SQLite database adapter with Drizzle ORM
29
+ */
30
+ export class SQLiteAdapter extends BaseAdapter {
31
+ readonly type = 'sqlite' as const;
32
+
33
+ private db: Database.Database | null = null;
34
+ private drizzleInstance: BetterSQLite3Database<typeof schema> | null = null;
35
+ private dbPath: string;
36
+ private config: DatabaseConfig;
37
+
38
+ constructor(dbPath: string, config: DatabaseConfig) {
39
+ super();
40
+ this.dbPath = dbPath;
41
+ this.config = config;
42
+ }
43
+
44
+ /**
45
+ * Get Drizzle ORM instance
46
+ */
47
+ get drizzle(): BetterSQLite3Database<typeof schema> {
48
+ this.ensureInitialized();
49
+ return this.drizzleInstance!;
50
+ }
51
+
52
+ /**
53
+ * Get raw database (for advanced operations)
54
+ */
55
+ get rawDb(): Database.Database {
56
+ this.ensureInitialized();
57
+ return this.db!;
58
+ }
59
+
60
+ /**
61
+ * Initialize SQLite database
62
+ */
63
+ async initialize(): Promise<void> {
64
+ if (this._isInitialized) return;
65
+
66
+ try {
67
+ // Ensure directory exists
68
+ const dir = dirname(this.dbPath);
69
+ if (!existsSync(dir)) {
70
+ mkdirSync(dir, { recursive: true });
71
+ logger.debug('Created database directory', { path: dir });
72
+ }
73
+
74
+ // Open database
75
+ this.db = new Database(this.dbPath);
76
+
77
+ // Configure for better performance
78
+ this.db.pragma('journal_mode = WAL');
79
+ this.db.pragma('synchronous = NORMAL');
80
+ this.db.pragma('cache_size = -64000'); // 64MB cache
81
+ this.db.pragma('foreign_keys = ON');
82
+ this.db.pragma('temp_store = MEMORY');
83
+
84
+ // Create Drizzle instance
85
+ this.drizzleInstance = drizzle(this.db, {
86
+ schema,
87
+ logger: process.env.NODE_ENV === 'development',
88
+ });
89
+
90
+ // Initialize schema
91
+ await this.initializeSchema();
92
+
93
+ this._isInitialized = true;
94
+ logger.info('SQLite adapter initialized', { path: this.dbPath });
95
+ } catch (error) {
96
+ const message = error instanceof Error ? error.message : 'Unknown error';
97
+ logger.error('Failed to initialize SQLite', { error: message });
98
+ throw new MemoryError(`SQLite initialization failed: ${message}`);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Initialize database schema
104
+ */
105
+ private async initializeSchema(): Promise<void> {
106
+ const statements = [
107
+ // Conversations
108
+ `CREATE TABLE IF NOT EXISTS conversations (
109
+ id TEXT PRIMARY KEY,
110
+ thread_id TEXT NOT NULL,
111
+ user_id TEXT,
112
+ created_at INTEGER NOT NULL,
113
+ updated_at INTEGER NOT NULL
114
+ )`,
115
+ `CREATE INDEX IF NOT EXISTS idx_conversations_thread ON conversations(thread_id)`,
116
+ `CREATE INDEX IF NOT EXISTS idx_conversations_user ON conversations(user_id)`,
117
+
118
+ // Messages
119
+ `CREATE TABLE IF NOT EXISTS messages (
120
+ id TEXT PRIMARY KEY,
121
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
122
+ role TEXT NOT NULL,
123
+ content TEXT NOT NULL,
124
+ metadata TEXT NOT NULL DEFAULT '{}',
125
+ created_at INTEGER NOT NULL
126
+ )`,
127
+ `CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id)`,
128
+
129
+ // Long-term memories
130
+ `CREATE TABLE IF NOT EXISTS long_term_memories (
131
+ id TEXT PRIMARY KEY,
132
+ user_id TEXT,
133
+ memory_type TEXT NOT NULL,
134
+ content TEXT NOT NULL,
135
+ embedding TEXT NOT NULL,
136
+ importance REAL NOT NULL DEFAULT 0.5,
137
+ access_count INTEGER NOT NULL DEFAULT 0,
138
+ last_accessed INTEGER,
139
+ metadata TEXT NOT NULL DEFAULT '{}',
140
+ created_at INTEGER NOT NULL,
141
+ updated_at INTEGER NOT NULL
142
+ )`,
143
+ `CREATE INDEX IF NOT EXISTS idx_memories_user ON long_term_memories(user_id)`,
144
+ `CREATE INDEX IF NOT EXISTS idx_memories_type ON long_term_memories(memory_type)`,
145
+
146
+ // Documents
147
+ `CREATE TABLE IF NOT EXISTS documents (
148
+ id TEXT PRIMARY KEY,
149
+ user_id TEXT,
150
+ filename TEXT NOT NULL,
151
+ file_type TEXT,
152
+ file_size INTEGER,
153
+ content TEXT,
154
+ metadata TEXT NOT NULL DEFAULT '{}',
155
+ created_at INTEGER NOT NULL
156
+ )`,
157
+ `CREATE INDEX IF NOT EXISTS idx_documents_user ON documents(user_id)`,
158
+
159
+ // Document chunks
160
+ `CREATE TABLE IF NOT EXISTS document_chunks (
161
+ id TEXT PRIMARY KEY,
162
+ document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
163
+ chunk_index INTEGER NOT NULL,
164
+ content TEXT NOT NULL,
165
+ embedding TEXT NOT NULL,
166
+ metadata TEXT NOT NULL DEFAULT '{}',
167
+ created_at INTEGER NOT NULL
168
+ )`,
169
+ `CREATE INDEX IF NOT EXISTS idx_chunks_document ON document_chunks(document_id)`,
170
+
171
+ // Checkpoints
172
+ `CREATE TABLE IF NOT EXISTS checkpoints (
173
+ thread_id TEXT NOT NULL,
174
+ checkpoint_ns TEXT NOT NULL DEFAULT '',
175
+ checkpoint_id TEXT NOT NULL,
176
+ parent_checkpoint_id TEXT,
177
+ type TEXT,
178
+ checkpoint TEXT NOT NULL,
179
+ metadata TEXT NOT NULL DEFAULT '{}',
180
+ created_at INTEGER NOT NULL,
181
+ PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
182
+ )`,
183
+
184
+ // Checkpoint writes
185
+ `CREATE TABLE IF NOT EXISTS checkpoint_writes (
186
+ thread_id TEXT NOT NULL,
187
+ checkpoint_ns TEXT NOT NULL DEFAULT '',
188
+ checkpoint_id TEXT NOT NULL,
189
+ task_id TEXT NOT NULL,
190
+ idx INTEGER NOT NULL,
191
+ channel TEXT NOT NULL,
192
+ type TEXT,
193
+ value TEXT,
194
+ created_at INTEGER NOT NULL,
195
+ PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
196
+ )`,
197
+
198
+ // Audit logs
199
+ `CREATE TABLE IF NOT EXISTS audit_logs (
200
+ id TEXT PRIMARY KEY,
201
+ user_id TEXT,
202
+ agent_id TEXT,
203
+ action_type TEXT NOT NULL,
204
+ action_details TEXT NOT NULL DEFAULT '{}',
205
+ input_hash TEXT,
206
+ output_hash TEXT,
207
+ status TEXT NOT NULL DEFAULT 'success',
208
+ error_message TEXT,
209
+ execution_time_ms INTEGER,
210
+ ip_address TEXT,
211
+ user_agent TEXT,
212
+ success INTEGER NOT NULL DEFAULT 1,
213
+ created_at INTEGER NOT NULL
214
+ )`,
215
+ `CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_logs(user_id)`,
216
+ `CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_logs(action_type)`,
217
+ `CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_logs(created_at)`,
218
+
219
+ // Tasks
220
+ `CREATE TABLE IF NOT EXISTS tasks (
221
+ id TEXT PRIMARY KEY,
222
+ user_id TEXT,
223
+ task_type TEXT NOT NULL,
224
+ priority INTEGER NOT NULL DEFAULT 0,
225
+ status TEXT NOT NULL DEFAULT 'pending',
226
+ payload TEXT NOT NULL DEFAULT '{}',
227
+ result TEXT,
228
+ error_message TEXT,
229
+ scheduled_at INTEGER,
230
+ started_at INTEGER,
231
+ completed_at INTEGER,
232
+ retry_count INTEGER NOT NULL DEFAULT 0,
233
+ max_retries INTEGER NOT NULL DEFAULT 3,
234
+ created_at INTEGER NOT NULL,
235
+ updated_at INTEGER NOT NULL
236
+ )`,
237
+ `CREATE INDEX IF NOT EXISTS idx_tasks_user ON tasks(user_id)`,
238
+ `CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)`,
239
+
240
+ // Schema version tracking
241
+ `CREATE TABLE IF NOT EXISTS schema_versions (
242
+ version INTEGER PRIMARY KEY,
243
+ name TEXT NOT NULL,
244
+ applied_at INTEGER NOT NULL,
245
+ checksum TEXT
246
+ )`,
247
+ ];
248
+
249
+ for (const statement of statements) {
250
+ try {
251
+ this.db!.exec(statement);
252
+ } catch (error) {
253
+ // Ignore errors for existing objects
254
+ const message = error instanceof Error ? error.message : '';
255
+ if (!message.includes('already exists')) {
256
+ throw error;
257
+ }
258
+ }
259
+ }
260
+
261
+ logger.debug('SQLite schema initialized');
262
+ }
263
+
264
+ /**
265
+ * Execute a raw SQL query
266
+ */
267
+ async query<T extends Record<string, any> = Record<string, any>>(
268
+ sqlText: string,
269
+ params?: unknown[]
270
+ ): Promise<QueryResult<T>> {
271
+ this.ensureInitialized();
272
+ const start = Date.now();
273
+
274
+ try {
275
+ // Convert PostgreSQL placeholders to SQLite
276
+ const sqliteSql = this.convertToSqlite(sqlText);
277
+ const sqliteParams = params ?? [];
278
+
279
+ const isSelect = this.isSelectQuery(sqlText);
280
+ const hasReturning = this.hasReturning(sqlText);
281
+
282
+ let rows: T[] = [];
283
+ let rowCount: number | null = 0;
284
+
285
+ if (isSelect) {
286
+ const stmt = this.db!.prepare(sqliteSql);
287
+ rows = stmt.all(...sqliteParams) as T[];
288
+ rowCount = rows.length;
289
+ } else if (hasReturning) {
290
+ // Handle INSERT/UPDATE/DELETE with RETURNING
291
+ const returning = this.extractReturning(sqlText);
292
+ const tableName = this.extractTableName(sqlText);
293
+
294
+ // Remove RETURNING clause for SQLite
295
+ const sqlWithoutReturning = sqliteSql.replace(/\s+RETURNING\s+.+$/i, '');
296
+ const stmt = this.db!.prepare(sqlWithoutReturning);
297
+ const info = stmt.run(...sqliteParams);
298
+ rowCount = info.changes;
299
+
300
+ if (returning && tableName && info.lastInsertRowid) {
301
+ // Fetch the inserted/updated row
302
+ const selectStmt = this.db!.prepare(
303
+ `SELECT ${returning} FROM ${tableName} WHERE rowid = ?`
304
+ );
305
+ const row = selectStmt.get(info.lastInsertRowid);
306
+ if (row) {
307
+ rows = [row as T];
308
+ }
309
+ }
310
+ } else {
311
+ const stmt = this.db!.prepare(sqliteSql);
312
+ const info = stmt.run(...sqliteParams);
313
+ rowCount = info.changes;
314
+ }
315
+
316
+ const duration = Date.now() - start;
317
+ this.logQuery(sqlText, duration, rowCount);
318
+
319
+ return { rows, rowCount };
320
+ } catch (error) {
321
+ this.logError(sqlText, error);
322
+ const message = error instanceof Error ? error.message : 'Unknown error';
323
+ throw new MemoryError(`Query failed: ${message}`);
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Execute a transaction
329
+ */
330
+ async transaction<T>(
331
+ callback: (client: TransactionClient) => Promise<T>
332
+ ): Promise<T> {
333
+ this.ensureInitialized();
334
+
335
+ const transactionClient: TransactionClient = {
336
+ query: async <R extends Record<string, any> = Record<string, any>>(
337
+ sqlText: string,
338
+ params?: unknown[]
339
+ ): Promise<QueryResult<R>> => {
340
+ return this.query<R>(sqlText, params);
341
+ },
342
+ };
343
+
344
+ try {
345
+ this.db!.exec('BEGIN');
346
+ const result = await callback(transactionClient);
347
+ this.db!.exec('COMMIT');
348
+ return result;
349
+ } catch (error) {
350
+ this.db!.exec('ROLLBACK');
351
+ throw error;
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Check database health
357
+ */
358
+ async healthCheck(): Promise<boolean> {
359
+ try {
360
+ const result = await this.query<{ ok: number }>('SELECT 1 as ok');
361
+ return result.rows.length > 0 && result.rows[0]!.ok === 1;
362
+ } catch {
363
+ return false;
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Close database connection
369
+ */
370
+ async close(): Promise<void> {
371
+ if (this.db) {
372
+ this.db.close();
373
+ this.db = null;
374
+ this.drizzleInstance = null;
375
+ this._isInitialized = false;
376
+ logger.info('SQLite database closed');
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Get database file path
382
+ */
383
+ getPath(): string {
384
+ return this.dbPath;
385
+ }
386
+
387
+ /**
388
+ * Get database file size in bytes
389
+ */
390
+ getFileSize(): number {
391
+ if (!this.db) return 0;
392
+ try {
393
+ const stats = this.db.pragma('page_count') as Array<{ page_count: number }>;
394
+ const pageSize = this.db.pragma('page_size') as Array<{ page_size: number }>;
395
+ return (stats[0]?.page_count ?? 0) * (pageSize[0]?.page_size ?? 4096);
396
+ } catch {
397
+ return 0;
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Vacuum database to reclaim space
403
+ */
404
+ vacuum(): void {
405
+ this.ensureInitialized();
406
+ this.db!.exec('VACUUM');
407
+ logger.debug('Database vacuumed');
408
+ }
409
+
410
+ /**
411
+ * Checkpoint WAL to main database
412
+ */
413
+ checkpoint(): void {
414
+ this.ensureInitialized();
415
+ this.db!.pragma('wal_checkpoint(TRUNCATE)');
416
+ logger.debug('WAL checkpoint completed');
417
+ }
418
+ }
419
+
420
+ export default SQLiteAdapter;
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Cache Key Generation
3
+ *
4
+ * Utilities for generating consistent cache keys.
5
+ */
6
+
7
+ /**
8
+ * Generate a cache key from operation, table, and parameters
9
+ */
10
+ export function generateCacheKey(
11
+ operation: string,
12
+ table: string,
13
+ params: Record<string, unknown>
14
+ ): string {
15
+ const sortedParams = Object.keys(params)
16
+ .sort()
17
+ .map((key) => `${key}:${JSON.stringify(params[key])}`)
18
+ .join('|');
19
+
20
+ return `${operation}:${table}:${sortedParams}`;
21
+ }
22
+
23
+ /**
24
+ * Cache key generators for common operations
25
+ */
26
+ export const cacheKeys = {
27
+ /**
28
+ * Get conversation by thread ID
29
+ */
30
+ conversation: (threadId: string): string =>
31
+ generateCacheKey('get', 'conversations', { threadId }),
32
+
33
+ /**
34
+ * Get conversation by ID
35
+ */
36
+ conversationById: (id: string): string =>
37
+ generateCacheKey('get', 'conversations', { id }),
38
+
39
+ /**
40
+ * List messages for a conversation
41
+ */
42
+ messages: (conversationId: string, limit: number, offset: number): string =>
43
+ generateCacheKey('list', 'messages', { conversationId, limit, offset }),
44
+
45
+ /**
46
+ * Get document by ID
47
+ */
48
+ document: (id: string): string =>
49
+ generateCacheKey('get', 'documents', { id }),
50
+
51
+ /**
52
+ * List documents for a user
53
+ */
54
+ userDocuments: (userId: string, limit: number, offset: number): string =>
55
+ generateCacheKey('list', 'documents', { userId, limit, offset }),
56
+
57
+ /**
58
+ * Get memory by ID
59
+ */
60
+ memory: (id: string): string =>
61
+ generateCacheKey('get', 'long_term_memories', { id }),
62
+
63
+ /**
64
+ * List recent memories for a user
65
+ */
66
+ userMemories: (userId: string, limit: number): string =>
67
+ generateCacheKey('list', 'long_term_memories', { userId, limit }),
68
+
69
+ /**
70
+ * Vector search results
71
+ */
72
+ vectorSearch: (
73
+ embeddingHash: string,
74
+ options: Record<string, unknown>
75
+ ): string =>
76
+ generateCacheKey('search', 'embeddings', { hash: embeddingHash, ...options }),
77
+
78
+ /**
79
+ * Task by ID
80
+ */
81
+ task: (id: string): string =>
82
+ generateCacheKey('get', 'tasks', { id }),
83
+
84
+ /**
85
+ * List tasks for a user
86
+ */
87
+ userTasks: (userId: string, status: string, limit: number): string =>
88
+ generateCacheKey('list', 'tasks', { userId, status, limit }),
89
+ };
90
+
91
+ /**
92
+ * Invalidation patterns for write operations
93
+ */
94
+ export const invalidationPatterns = {
95
+ /**
96
+ * Invalidate all conversation-related cache
97
+ */
98
+ conversation: (conversationId: string): RegExp =>
99
+ new RegExp(`conversations.*${conversationId}`),
100
+
101
+ /**
102
+ * Invalidate all message-related cache for a conversation
103
+ */
104
+ messages: (conversationId: string): RegExp =>
105
+ new RegExp(`messages.*${conversationId}`),
106
+
107
+ /**
108
+ * Invalidate all document-related cache
109
+ */
110
+ document: (documentId: string): RegExp =>
111
+ new RegExp(`documents.*${documentId}`),
112
+
113
+ /**
114
+ * Invalidate user's document list
115
+ */
116
+ userDocuments: (userId: string): string => `list:documents:userId:"${userId}"`,
117
+
118
+ /**
119
+ * Invalidate all memory-related cache
120
+ */
121
+ memory: (memoryId: string): RegExp =>
122
+ new RegExp(`long_term_memories.*${memoryId}`),
123
+
124
+ /**
125
+ * Invalidate user's memory list
126
+ */
127
+ userMemories: (userId: string): string =>
128
+ `list:long_term_memories:userId:"${userId}"`,
129
+
130
+ /**
131
+ * Invalidate all vector search results
132
+ */
133
+ vectorSearchAll: (): string => 'search:embeddings',
134
+
135
+ /**
136
+ * Invalidate all task-related cache
137
+ */
138
+ task: (taskId: string): RegExp => new RegExp(`tasks.*${taskId}`),
139
+
140
+ /**
141
+ * Invalidate user's task list
142
+ */
143
+ userTasks: (userId: string): string => `list:tasks:userId:"${userId}"`,
144
+ };
145
+
146
+ export default {
147
+ generateCacheKey,
148
+ cacheKeys,
149
+ invalidationPatterns,
150
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Cache Layer Exports
3
+ *
4
+ * Central export for query caching functionality.
5
+ */
6
+
7
+ export { QueryCache } from './query-cache.js';
8
+ export {
9
+ generateCacheKey,
10
+ cacheKeys,
11
+ invalidationPatterns,
12
+ } from './cache-keys.js';
13
+
14
+ import { QueryCache } from './query-cache.js';
15
+ import type { QueryCacheOptions } from '../types.js';
16
+
17
+ // Singleton cache instance
18
+ let cacheInstance: QueryCache | null = null;
19
+
20
+ /**
21
+ * Get the global query cache instance
22
+ */
23
+ export function getQueryCache(options?: Partial<QueryCacheOptions>): QueryCache {
24
+ if (!cacheInstance) {
25
+ cacheInstance = new QueryCache(options);
26
+ }
27
+ return cacheInstance;
28
+ }
29
+
30
+ /**
31
+ * Reset the global cache instance
32
+ */
33
+ export function resetQueryCache(): void {
34
+ if (cacheInstance) {
35
+ cacheInstance.clear();
36
+ cacheInstance = null;
37
+ }
38
+ }
39
+
40
+ export default {
41
+ QueryCache,
42
+ getQueryCache,
43
+ resetQueryCache,
44
+ };