family-ai-agent 1.0.6 → 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 (147) hide show
  1. package/.letta/settings.local.json +3 -0
  2. package/dist/database/adapters/base-adapter.d.ts +81 -0
  3. package/dist/database/adapters/base-adapter.d.ts.map +1 -0
  4. package/dist/database/adapters/base-adapter.js +105 -0
  5. package/dist/database/adapters/base-adapter.js.map +1 -0
  6. package/dist/database/adapters/index.d.ts +49 -0
  7. package/dist/database/adapters/index.d.ts.map +1 -0
  8. package/dist/database/adapters/index.js +200 -0
  9. package/dist/database/adapters/index.js.map +1 -0
  10. package/dist/database/adapters/postgres-adapter.d.ts +75 -0
  11. package/dist/database/adapters/postgres-adapter.d.ts.map +1 -0
  12. package/dist/database/adapters/postgres-adapter.js +225 -0
  13. package/dist/database/adapters/postgres-adapter.js.map +1 -0
  14. package/dist/database/adapters/sqlite-adapter.d.ts +72 -0
  15. package/dist/database/adapters/sqlite-adapter.d.ts.map +1 -0
  16. package/dist/database/adapters/sqlite-adapter.js +368 -0
  17. package/dist/database/adapters/sqlite-adapter.js.map +1 -0
  18. package/dist/database/cache/cache-keys.d.ts +180 -0
  19. package/dist/database/cache/cache-keys.d.ts.map +1 -0
  20. package/dist/database/cache/cache-keys.js +107 -0
  21. package/dist/database/cache/cache-keys.js.map +1 -0
  22. package/dist/database/cache/index.d.ts +24 -0
  23. package/dist/database/cache/index.d.ts.map +1 -0
  24. package/dist/database/cache/index.js +34 -0
  25. package/dist/database/cache/index.js.map +1 -0
  26. package/dist/database/cache/query-cache.d.ts +67 -0
  27. package/dist/database/cache/query-cache.d.ts.map +1 -0
  28. package/dist/database/cache/query-cache.js +177 -0
  29. package/dist/database/cache/query-cache.js.map +1 -0
  30. package/dist/database/client.d.ts +63 -4
  31. package/dist/database/client.d.ts.map +1 -1
  32. package/dist/database/client.js +147 -59
  33. package/dist/database/client.js.map +1 -1
  34. package/dist/database/db-config.d.ts +104 -0
  35. package/dist/database/db-config.d.ts.map +1 -0
  36. package/dist/database/db-config.js +167 -0
  37. package/dist/database/db-config.js.map +1 -0
  38. package/dist/database/drizzle/index.d.ts +42 -0
  39. package/dist/database/drizzle/index.d.ts.map +1 -0
  40. package/dist/database/drizzle/index.js +48 -0
  41. package/dist/database/drizzle/index.js.map +1 -0
  42. package/dist/database/drizzle/schema/audit.d.ts +533 -0
  43. package/dist/database/drizzle/schema/audit.d.ts.map +1 -0
  44. package/dist/database/drizzle/schema/audit.js +71 -0
  45. package/dist/database/drizzle/schema/audit.js.map +1 -0
  46. package/dist/database/drizzle/schema/checkpoints.d.ts +665 -0
  47. package/dist/database/drizzle/schema/checkpoints.d.ts.map +1 -0
  48. package/dist/database/drizzle/schema/checkpoints.js +110 -0
  49. package/dist/database/drizzle/schema/checkpoints.js.map +1 -0
  50. package/dist/database/drizzle/schema/conversations.d.ts +449 -0
  51. package/dist/database/drizzle/schema/conversations.d.ts.map +1 -0
  52. package/dist/database/drizzle/schema/conversations.js +91 -0
  53. package/dist/database/drizzle/schema/conversations.js.map +1 -0
  54. package/dist/database/drizzle/schema/documents.d.ts +600 -0
  55. package/dist/database/drizzle/schema/documents.d.ts.map +1 -0
  56. package/dist/database/drizzle/schema/documents.js +100 -0
  57. package/dist/database/drizzle/schema/documents.js.map +1 -0
  58. package/dist/database/drizzle/schema/index.d.ts +3084 -0
  59. package/dist/database/drizzle/schema/index.d.ts.map +1 -0
  60. package/dist/database/drizzle/schema/index.js +46 -0
  61. package/dist/database/drizzle/schema/index.js.map +1 -0
  62. package/dist/database/drizzle/schema/memories.d.ts +435 -0
  63. package/dist/database/drizzle/schema/memories.d.ts.map +1 -0
  64. package/dist/database/drizzle/schema/memories.js +73 -0
  65. package/dist/database/drizzle/schema/memories.js.map +1 -0
  66. package/dist/database/drizzle/schema/tasks.d.ts +565 -0
  67. package/dist/database/drizzle/schema/tasks.d.ts.map +1 -0
  68. package/dist/database/drizzle/schema/tasks.js +84 -0
  69. package/dist/database/drizzle/schema/tasks.js.map +1 -0
  70. package/dist/database/health/circuit-breaker.d.ts +81 -0
  71. package/dist/database/health/circuit-breaker.d.ts.map +1 -0
  72. package/dist/database/health/circuit-breaker.js +184 -0
  73. package/dist/database/health/circuit-breaker.js.map +1 -0
  74. package/dist/database/health/health-monitor.d.ts +69 -0
  75. package/dist/database/health/health-monitor.d.ts.map +1 -0
  76. package/dist/database/health/health-monitor.js +174 -0
  77. package/dist/database/health/health-monitor.js.map +1 -0
  78. package/dist/database/health/index.d.ts +27 -0
  79. package/dist/database/health/index.d.ts.map +1 -0
  80. package/dist/database/health/index.js +23 -0
  81. package/dist/database/health/index.js.map +1 -0
  82. package/dist/database/index.d.ts +16 -0
  83. package/dist/database/index.d.ts.map +1 -0
  84. package/dist/database/index.js +41 -0
  85. package/dist/database/index.js.map +1 -0
  86. package/dist/database/migrations/index.d.ts +34 -0
  87. package/dist/database/migrations/index.d.ts.map +1 -0
  88. package/dist/database/migrations/index.js +45 -0
  89. package/dist/database/migrations/index.js.map +1 -0
  90. package/dist/database/migrations/migrator.d.ts +77 -0
  91. package/dist/database/migrations/migrator.d.ts.map +1 -0
  92. package/dist/database/migrations/migrator.js +258 -0
  93. package/dist/database/migrations/migrator.js.map +1 -0
  94. package/dist/database/migrations/versions/001_initial.d.ts +9 -0
  95. package/dist/database/migrations/versions/001_initial.d.ts.map +1 -0
  96. package/dist/database/migrations/versions/001_initial.js +183 -0
  97. package/dist/database/migrations/versions/001_initial.js.map +1 -0
  98. package/dist/database/types.d.ts +255 -0
  99. package/dist/database/types.d.ts.map +1 -0
  100. package/dist/database/types.js +8 -0
  101. package/dist/database/types.js.map +1 -0
  102. package/dist/database/vector/embedding-cache.d.ts +92 -0
  103. package/dist/database/vector/embedding-cache.d.ts.map +1 -0
  104. package/dist/database/vector/embedding-cache.js +185 -0
  105. package/dist/database/vector/embedding-cache.js.map +1 -0
  106. package/dist/database/vector/hnsw-index.d.ts +111 -0
  107. package/dist/database/vector/hnsw-index.d.ts.map +1 -0
  108. package/dist/database/vector/hnsw-index.js +337 -0
  109. package/dist/database/vector/hnsw-index.js.map +1 -0
  110. package/dist/database/vector/index.d.ts +75 -0
  111. package/dist/database/vector/index.d.ts.map +1 -0
  112. package/dist/database/vector/index.js +213 -0
  113. package/dist/database/vector/index.js.map +1 -0
  114. package/dist/database/vector/similarity.d.ts +67 -0
  115. package/dist/database/vector/similarity.d.ts.map +1 -0
  116. package/dist/database/vector/similarity.js +176 -0
  117. package/dist/database/vector/similarity.js.map +1 -0
  118. package/package.json +6 -3
  119. package/src/database/adapters/base-adapter.ts +171 -0
  120. package/src/database/adapters/index.ts +224 -0
  121. package/src/database/adapters/postgres-adapter.ts +285 -0
  122. package/src/database/adapters/sqlite-adapter.ts +420 -0
  123. package/src/database/cache/cache-keys.ts +150 -0
  124. package/src/database/cache/index.ts +44 -0
  125. package/src/database/cache/query-cache.ts +213 -0
  126. package/src/database/client.ts +166 -64
  127. package/src/database/db-config.ts +194 -0
  128. package/src/database/drizzle/index.ts +66 -0
  129. package/src/database/drizzle/schema/audit.ts +127 -0
  130. package/src/database/drizzle/schema/checkpoints.ts +164 -0
  131. package/src/database/drizzle/schema/conversations.ts +138 -0
  132. package/src/database/drizzle/schema/documents.ts +157 -0
  133. package/src/database/drizzle/schema/index.ts +139 -0
  134. package/src/database/drizzle/schema/memories.ts +127 -0
  135. package/src/database/drizzle/schema/tasks.ts +129 -0
  136. package/src/database/health/circuit-breaker.ts +214 -0
  137. package/src/database/health/health-monitor.ts +224 -0
  138. package/src/database/health/index.ts +41 -0
  139. package/src/database/index.ts +157 -0
  140. package/src/database/migrations/index.ts +52 -0
  141. package/src/database/migrations/migrator.ts +325 -0
  142. package/src/database/migrations/versions/001_initial.ts +198 -0
  143. package/src/database/types.ts +324 -0
  144. package/src/database/vector/embedding-cache.ts +234 -0
  145. package/src/database/vector/hnsw-index.ts +452 -0
  146. package/src/database/vector/index.ts +292 -0
  147. package/src/database/vector/similarity.ts +198 -0
@@ -0,0 +1,285 @@
1
+ /**
2
+ * PostgreSQL Database Adapter
3
+ *
4
+ * Adapter for PostgreSQL with connection pooling and pgvector support.
5
+ */
6
+
7
+ import pg from 'pg';
8
+ import type { QueryResultRow } from 'pg';
9
+ import { drizzle } from 'drizzle-orm/node-postgres';
10
+ import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
11
+ import { sql } from 'drizzle-orm';
12
+
13
+ import { BaseAdapter } from './base-adapter.js';
14
+ import type {
15
+ QueryResult,
16
+ TransactionClient,
17
+ PoolStats,
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 { Pool } = pg;
26
+ const logger = createLogger('PostgresAdapter');
27
+
28
+ /**
29
+ * PostgreSQL database adapter with Drizzle ORM
30
+ */
31
+ export class PostgresAdapter extends BaseAdapter {
32
+ readonly type = 'postgresql' as const;
33
+
34
+ private pool: pg.Pool | null = null;
35
+ private drizzleInstance: NodePgDatabase<typeof schema> | null = null;
36
+ private connectionString: string;
37
+ private config: DatabaseConfig;
38
+
39
+ constructor(connectionString: string, config: DatabaseConfig) {
40
+ super();
41
+ this.connectionString = connectionString;
42
+ this.config = config;
43
+ }
44
+
45
+ /**
46
+ * Get Drizzle ORM instance
47
+ */
48
+ get drizzle(): NodePgDatabase<typeof schema> {
49
+ this.ensureInitialized();
50
+ return this.drizzleInstance!;
51
+ }
52
+
53
+ /**
54
+ * Get raw pool (for advanced operations)
55
+ */
56
+ get rawPool(): pg.Pool {
57
+ this.ensureInitialized();
58
+ return this.pool!;
59
+ }
60
+
61
+ /**
62
+ * Initialize PostgreSQL connection pool
63
+ */
64
+ async initialize(): Promise<void> {
65
+ if (this._isInitialized) return;
66
+
67
+ try {
68
+ this.pool = new Pool({
69
+ connectionString: this.connectionString,
70
+ max: this.config.postgresPoolMax,
71
+ idleTimeoutMillis: this.config.postgresIdleTimeoutMs,
72
+ connectionTimeoutMillis: this.config.postgresConnectionTimeoutMs,
73
+ });
74
+
75
+ // Set up event handlers
76
+ this.pool.on('error', (err) => {
77
+ logger.error('Pool error', { error: err.message });
78
+ });
79
+
80
+ this.pool.on('connect', () => {
81
+ logger.debug('New client connected to pool');
82
+ });
83
+
84
+ // Create Drizzle instance
85
+ this.drizzleInstance = drizzle(this.pool, {
86
+ schema,
87
+ logger: process.env.NODE_ENV === 'development',
88
+ });
89
+
90
+ // Test connection and enable pgvector
91
+ await this.testConnection();
92
+ await this.enablePgVector();
93
+
94
+ this._isInitialized = true;
95
+ logger.info('PostgreSQL adapter initialized', {
96
+ host: this.config.postgresHost,
97
+ database: this.config.postgresDatabase,
98
+ });
99
+ } catch (error) {
100
+ const message = error instanceof Error ? error.message : 'Unknown error';
101
+ logger.error('Failed to initialize PostgreSQL', { error: message });
102
+ throw new MemoryError(`PostgreSQL initialization failed: ${message}`);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Test database connection
108
+ */
109
+ private async testConnection(): Promise<void> {
110
+ const client = await this.pool!.connect();
111
+ try {
112
+ await client.query('SELECT 1');
113
+ } finally {
114
+ client.release();
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Enable pgvector extension if not already enabled
120
+ */
121
+ private async enablePgVector(): Promise<void> {
122
+ try {
123
+ await this.pool!.query('CREATE EXTENSION IF NOT EXISTS vector');
124
+ logger.debug('pgvector extension enabled');
125
+ } catch (error) {
126
+ logger.warn('Could not enable pgvector extension', {
127
+ error: error instanceof Error ? error.message : 'Unknown'
128
+ });
129
+ // Don't fail - extension might already exist or user might not have permissions
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Execute a raw SQL query
135
+ */
136
+ async query<T extends QueryResultRow = Record<string, any>>(
137
+ sqlText: string,
138
+ params?: unknown[]
139
+ ): Promise<QueryResult<T>> {
140
+ this.ensureInitialized();
141
+ const start = Date.now();
142
+
143
+ try {
144
+ const result = await this.pool!.query<T>(sqlText, params);
145
+ const duration = Date.now() - start;
146
+ this.logQuery(sqlText, duration, result.rowCount);
147
+
148
+ return {
149
+ rows: result.rows,
150
+ rowCount: result.rowCount,
151
+ };
152
+ } catch (error) {
153
+ this.logError(sqlText, error);
154
+ const message = error instanceof Error ? error.message : 'Unknown error';
155
+ throw new MemoryError(`Query failed: ${message}`);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Execute a transaction
161
+ */
162
+ async transaction<T>(
163
+ callback: (client: TransactionClient) => Promise<T>
164
+ ): Promise<T> {
165
+ this.ensureInitialized();
166
+ const client = await this.pool!.connect();
167
+
168
+ try {
169
+ await client.query('BEGIN');
170
+
171
+ const transactionClient: TransactionClient = {
172
+ query: async <R extends QueryResultRow = Record<string, any>>(
173
+ sqlText: string,
174
+ params?: unknown[]
175
+ ): Promise<QueryResult<R>> => {
176
+ const result = await client.query<R>(sqlText, params);
177
+ return {
178
+ rows: result.rows,
179
+ rowCount: result.rowCount,
180
+ };
181
+ },
182
+ };
183
+
184
+ const result = await callback(transactionClient);
185
+ await client.query('COMMIT');
186
+ return result;
187
+ } catch (error) {
188
+ await client.query('ROLLBACK');
189
+ throw error;
190
+ } finally {
191
+ client.release();
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Check database health
197
+ */
198
+ async healthCheck(): Promise<boolean> {
199
+ try {
200
+ const result = await this.query<{ ok: number }>('SELECT 1 as ok');
201
+ return result.rows.length > 0 && result.rows[0]!.ok === 1;
202
+ } catch {
203
+ return false;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Get connection pool statistics
209
+ */
210
+ getPoolStats(): PoolStats {
211
+ if (!this.pool) {
212
+ return { total: 0, idle: 0, waiting: 0 };
213
+ }
214
+
215
+ return {
216
+ total: this.pool.totalCount,
217
+ idle: this.pool.idleCount,
218
+ waiting: this.pool.waitingCount,
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Close all connections
224
+ */
225
+ async close(): Promise<void> {
226
+ if (this.pool) {
227
+ await this.pool.end();
228
+ this.pool = null;
229
+ this.drizzleInstance = null;
230
+ this._isInitialized = false;
231
+ logger.info('PostgreSQL pool closed');
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Execute vector similarity search
237
+ */
238
+ async vectorSearch<T = Record<string, unknown>>(
239
+ table: string,
240
+ embeddingColumn: string,
241
+ queryEmbedding: number[],
242
+ options: {
243
+ limit?: number;
244
+ minSimilarity?: number;
245
+ filters?: Record<string, unknown>;
246
+ selectColumns?: string[];
247
+ } = {}
248
+ ): Promise<Array<T & { similarity: number }>> {
249
+ const {
250
+ limit = 5,
251
+ minSimilarity = 0.7,
252
+ filters = {},
253
+ selectColumns = ['*'],
254
+ } = options;
255
+
256
+ const embeddingStr = `[${queryEmbedding.join(',')}]`;
257
+
258
+ // Build WHERE clause
259
+ const whereConditions = [`1 - (${embeddingColumn} <=> $1::vector) >= $2`];
260
+ const params: unknown[] = [embeddingStr, minSimilarity];
261
+ let paramIndex = 3;
262
+
263
+ for (const [key, value] of Object.entries(filters)) {
264
+ whereConditions.push(`${key} = $${paramIndex}`);
265
+ params.push(value);
266
+ paramIndex++;
267
+ }
268
+
269
+ params.push(limit);
270
+
271
+ const columns = selectColumns.join(', ');
272
+ const sql = `
273
+ SELECT ${columns}, 1 - (${embeddingColumn} <=> $1::vector) as similarity
274
+ FROM ${table}
275
+ WHERE ${whereConditions.join(' AND ')}
276
+ ORDER BY similarity DESC
277
+ LIMIT $${paramIndex}
278
+ `;
279
+
280
+ const result = await this.query<T & { similarity: number }>(sql, params);
281
+ return result.rows;
282
+ }
283
+ }
284
+
285
+ export default PostgresAdapter;
@@ -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;