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,139 @@
1
+ /**
2
+ * Schema Exports
3
+ *
4
+ * Central export for all Drizzle ORM schemas.
5
+ */
6
+
7
+ // Conversations
8
+ export {
9
+ conversationsPg,
10
+ messagesPg,
11
+ conversationsSqlite,
12
+ messagesSqlite,
13
+ type ConversationPg,
14
+ type NewConversationPg,
15
+ type MessagePg,
16
+ type NewMessagePg,
17
+ type ConversationSqlite,
18
+ type NewConversationSqlite,
19
+ type MessageSqlite,
20
+ type NewMessageSqlite,
21
+ type Conversation,
22
+ type NewConversation,
23
+ type Message,
24
+ type NewMessage,
25
+ } from './conversations.js';
26
+
27
+ // Long-term memories
28
+ export {
29
+ longTermMemoriesPg,
30
+ longTermMemoriesSqlite,
31
+ type LongTermMemoryPg,
32
+ type NewLongTermMemoryPg,
33
+ type LongTermMemorySqlite,
34
+ type NewLongTermMemorySqlite,
35
+ type LongTermMemory,
36
+ type NewLongTermMemory,
37
+ type MemoryType,
38
+ type ParsedMemory,
39
+ } from './memories.js';
40
+
41
+ // Documents
42
+ export {
43
+ documentsPg,
44
+ documentChunksPg,
45
+ documentsSqlite,
46
+ documentChunksSqlite,
47
+ type DocumentPg,
48
+ type NewDocumentPg,
49
+ type DocumentChunkPg,
50
+ type NewDocumentChunkPg,
51
+ type DocumentSqlite,
52
+ type NewDocumentSqlite,
53
+ type DocumentChunkSqlite,
54
+ type NewDocumentChunkSqlite,
55
+ type Document,
56
+ type NewDocument,
57
+ type DocumentChunk,
58
+ type NewDocumentChunk,
59
+ type ParsedDocumentChunk,
60
+ } from './documents.js';
61
+
62
+ // Checkpoints
63
+ export {
64
+ checkpointsPg,
65
+ checkpointWritesPg,
66
+ checkpointsSqlite,
67
+ checkpointWritesSqlite,
68
+ type CheckpointPg,
69
+ type NewCheckpointPg,
70
+ type CheckpointWritePg,
71
+ type NewCheckpointWritePg,
72
+ type CheckpointSqlite,
73
+ type NewCheckpointSqlite,
74
+ type CheckpointWriteSqlite,
75
+ type NewCheckpointWriteSqlite,
76
+ type Checkpoint,
77
+ type NewCheckpoint,
78
+ type CheckpointWrite,
79
+ type NewCheckpointWrite,
80
+ } from './checkpoints.js';
81
+
82
+ // Audit logs
83
+ export {
84
+ auditLogsPg,
85
+ auditLogsSqlite,
86
+ type AuditLogPg,
87
+ type NewAuditLogPg,
88
+ type AuditLogSqlite,
89
+ type NewAuditLogSqlite,
90
+ type AuditLog,
91
+ type NewAuditLog,
92
+ type AuditActionType,
93
+ type AuditStatus,
94
+ } from './audit.js';
95
+
96
+ // Tasks
97
+ export {
98
+ tasksPg,
99
+ tasksSqlite,
100
+ type TaskPg,
101
+ type NewTaskPg,
102
+ type TaskSqlite,
103
+ type NewTaskSqlite,
104
+ type Task,
105
+ type NewTask,
106
+ type TaskStatus,
107
+ TaskPriority,
108
+ type TaskPriorityLevel,
109
+ } from './tasks.js';
110
+
111
+ /**
112
+ * All PostgreSQL tables
113
+ */
114
+ export const pgSchema = {
115
+ conversations: () => import('./conversations.js').then((m) => m.conversationsPg),
116
+ messages: () => import('./conversations.js').then((m) => m.messagesPg),
117
+ longTermMemories: () => import('./memories.js').then((m) => m.longTermMemoriesPg),
118
+ documents: () => import('./documents.js').then((m) => m.documentsPg),
119
+ documentChunks: () => import('./documents.js').then((m) => m.documentChunksPg),
120
+ checkpoints: () => import('./checkpoints.js').then((m) => m.checkpointsPg),
121
+ checkpointWrites: () => import('./checkpoints.js').then((m) => m.checkpointWritesPg),
122
+ auditLogs: () => import('./audit.js').then((m) => m.auditLogsPg),
123
+ tasks: () => import('./tasks.js').then((m) => m.tasksPg),
124
+ };
125
+
126
+ /**
127
+ * All SQLite tables
128
+ */
129
+ export const sqliteSchema = {
130
+ conversations: () => import('./conversations.js').then((m) => m.conversationsSqlite),
131
+ messages: () => import('./conversations.js').then((m) => m.messagesSqlite),
132
+ longTermMemories: () => import('./memories.js').then((m) => m.longTermMemoriesSqlite),
133
+ documents: () => import('./documents.js').then((m) => m.documentsSqlite),
134
+ documentChunks: () => import('./documents.js').then((m) => m.documentChunksSqlite),
135
+ checkpoints: () => import('./checkpoints.js').then((m) => m.checkpointsSqlite),
136
+ checkpointWrites: () => import('./checkpoints.js').then((m) => m.checkpointWritesSqlite),
137
+ auditLogs: () => import('./audit.js').then((m) => m.auditLogsSqlite),
138
+ tasks: () => import('./tasks.js').then((m) => m.tasksSqlite),
139
+ };
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Long-Term Memories Schema
3
+ *
4
+ * Drizzle ORM schema for vector-embedded memories.
5
+ * PostgreSQL uses pgvector, SQLite stores embeddings as JSON.
6
+ */
7
+
8
+ import {
9
+ pgTable,
10
+ uuid,
11
+ text,
12
+ real,
13
+ integer,
14
+ timestamp,
15
+ jsonb,
16
+ index,
17
+ } from 'drizzle-orm/pg-core';
18
+ import {
19
+ sqliteTable,
20
+ text as sqliteText,
21
+ real as sqliteReal,
22
+ integer as sqliteInteger,
23
+ } from 'drizzle-orm/sqlite-core';
24
+ import { sql } from 'drizzle-orm';
25
+
26
+ // =============================================================================
27
+ // PostgreSQL Schema (with pgvector)
28
+ // =============================================================================
29
+
30
+ /**
31
+ * Long-term memories table (PostgreSQL)
32
+ * Uses pgvector extension for embedding storage and similarity search
33
+ */
34
+ export const longTermMemoriesPg = pgTable(
35
+ 'long_term_memories',
36
+ {
37
+ id: uuid('id')
38
+ .primaryKey()
39
+ .default(sql`gen_random_uuid()`),
40
+ userId: text('user_id'),
41
+ memoryType: text('memory_type').notNull(), // 'semantic' | 'episodic' | 'procedural'
42
+ content: text('content').notNull(),
43
+ // pgvector type - stored as vector(1536)
44
+ // Note: Drizzle doesn't have native pgvector support, so we use customType
45
+ embedding: text('embedding').notNull(), // Will store as '[0.1, 0.2, ...]' format
46
+ importance: real('importance').default(0.5).notNull(),
47
+ accessCount: integer('access_count').default(0).notNull(),
48
+ lastAccessed: timestamp('last_accessed', { withTimezone: true }),
49
+ metadata: jsonb('metadata').default({}).notNull(),
50
+ createdAt: timestamp('created_at', { withTimezone: true })
51
+ .defaultNow()
52
+ .notNull(),
53
+ updatedAt: timestamp('updated_at', { withTimezone: true })
54
+ .defaultNow()
55
+ .notNull(),
56
+ },
57
+ (table) => ({
58
+ userIdIdx: index('memories_user_id_idx').on(table.userId),
59
+ memoryTypeIdx: index('memories_memory_type_idx').on(table.memoryType),
60
+ importanceIdx: index('memories_importance_idx').on(table.importance),
61
+ createdAtIdx: index('memories_created_at_idx').on(table.createdAt),
62
+ })
63
+ );
64
+
65
+ // =============================================================================
66
+ // SQLite Schema
67
+ // =============================================================================
68
+
69
+ /**
70
+ * Long-term memories table (SQLite)
71
+ * Embeddings stored as JSON string, similarity search done in-memory
72
+ */
73
+ export const longTermMemoriesSqlite = sqliteTable('long_term_memories', {
74
+ id: sqliteText('id')
75
+ .primaryKey()
76
+ .$defaultFn(() => crypto.randomUUID()),
77
+ userId: sqliteText('user_id'),
78
+ memoryType: sqliteText('memory_type').notNull(),
79
+ content: sqliteText('content').notNull(),
80
+ // Embedding stored as JSON array string: "[0.1, 0.2, ...]"
81
+ embedding: sqliteText('embedding').notNull(),
82
+ importance: sqliteReal('importance').default(0.5).notNull(),
83
+ accessCount: sqliteInteger('access_count').default(0).notNull(),
84
+ lastAccessed: sqliteInteger('last_accessed', { mode: 'timestamp' }),
85
+ metadata: sqliteText('metadata', { mode: 'json' })
86
+ .notNull()
87
+ .$type<Record<string, unknown>>()
88
+ .default({}),
89
+ createdAt: sqliteInteger('created_at', { mode: 'timestamp' })
90
+ .notNull()
91
+ .$defaultFn(() => new Date()),
92
+ updatedAt: sqliteInteger('updated_at', { mode: 'timestamp' })
93
+ .notNull()
94
+ .$defaultFn(() => new Date()),
95
+ });
96
+
97
+ // =============================================================================
98
+ // TypeScript Types
99
+ // =============================================================================
100
+
101
+ export type LongTermMemoryPg = typeof longTermMemoriesPg.$inferSelect;
102
+ export type NewLongTermMemoryPg = typeof longTermMemoriesPg.$inferInsert;
103
+
104
+ export type LongTermMemorySqlite = typeof longTermMemoriesSqlite.$inferSelect;
105
+ export type NewLongTermMemorySqlite = typeof longTermMemoriesSqlite.$inferInsert;
106
+
107
+ // Unified types
108
+ export type LongTermMemory = LongTermMemoryPg | LongTermMemorySqlite;
109
+ export type NewLongTermMemory = NewLongTermMemoryPg | NewLongTermMemorySqlite;
110
+
111
+ // Memory type enum
112
+ export type MemoryType = 'semantic' | 'episodic' | 'procedural';
113
+
114
+ // Memory with parsed embedding
115
+ export interface ParsedMemory {
116
+ id: string;
117
+ userId: string | null;
118
+ memoryType: MemoryType;
119
+ content: string;
120
+ embedding: number[];
121
+ importance: number;
122
+ accessCount: number;
123
+ lastAccessed: Date | null;
124
+ metadata: Record<string, unknown>;
125
+ createdAt: Date;
126
+ updatedAt: Date;
127
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Tasks Schema
3
+ *
4
+ * Drizzle ORM schema for automation task queue.
5
+ * Supports scheduled and recurring tasks.
6
+ */
7
+
8
+ import {
9
+ pgTable,
10
+ uuid,
11
+ text,
12
+ integer,
13
+ timestamp,
14
+ jsonb,
15
+ index,
16
+ } from 'drizzle-orm/pg-core';
17
+ import {
18
+ sqliteTable,
19
+ text as sqliteText,
20
+ integer as sqliteInteger,
21
+ } from 'drizzle-orm/sqlite-core';
22
+ import { sql } from 'drizzle-orm';
23
+
24
+ // =============================================================================
25
+ // PostgreSQL Schema
26
+ // =============================================================================
27
+
28
+ /**
29
+ * Tasks table (PostgreSQL)
30
+ */
31
+ export const tasksPg = pgTable(
32
+ 'tasks',
33
+ {
34
+ id: uuid('id')
35
+ .primaryKey()
36
+ .default(sql`gen_random_uuid()`),
37
+ userId: text('user_id'),
38
+ taskType: text('task_type').notNull(),
39
+ priority: integer('priority').default(0).notNull(),
40
+ status: text('status').default('pending').notNull(), // 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'
41
+ payload: jsonb('payload').default({}).notNull(),
42
+ result: jsonb('result'),
43
+ errorMessage: text('error_message'),
44
+ scheduledAt: timestamp('scheduled_at', { withTimezone: true }),
45
+ startedAt: timestamp('started_at', { withTimezone: true }),
46
+ completedAt: timestamp('completed_at', { withTimezone: true }),
47
+ retryCount: integer('retry_count').default(0).notNull(),
48
+ maxRetries: integer('max_retries').default(3).notNull(),
49
+ createdAt: timestamp('created_at', { withTimezone: true })
50
+ .defaultNow()
51
+ .notNull(),
52
+ updatedAt: timestamp('updated_at', { withTimezone: true })
53
+ .defaultNow()
54
+ .notNull(),
55
+ },
56
+ (table) => ({
57
+ userIdIdx: index('tasks_user_id_idx').on(table.userId),
58
+ statusIdx: index('tasks_status_idx').on(table.status),
59
+ priorityIdx: index('tasks_priority_idx').on(table.priority),
60
+ scheduledAtIdx: index('tasks_scheduled_at_idx').on(table.scheduledAt),
61
+ taskTypeIdx: index('tasks_task_type_idx').on(table.taskType),
62
+ })
63
+ );
64
+
65
+ // =============================================================================
66
+ // SQLite Schema
67
+ // =============================================================================
68
+
69
+ /**
70
+ * Tasks table (SQLite)
71
+ */
72
+ export const tasksSqlite = sqliteTable('tasks', {
73
+ id: sqliteText('id')
74
+ .primaryKey()
75
+ .$defaultFn(() => crypto.randomUUID()),
76
+ userId: sqliteText('user_id'),
77
+ taskType: sqliteText('task_type').notNull(),
78
+ priority: sqliteInteger('priority').default(0).notNull(),
79
+ status: sqliteText('status').default('pending').notNull(),
80
+ payload: sqliteText('payload', { mode: 'json' })
81
+ .notNull()
82
+ .$type<Record<string, unknown>>()
83
+ .default({}),
84
+ result: sqliteText('result', { mode: 'json' }).$type<Record<string, unknown>>(),
85
+ errorMessage: sqliteText('error_message'),
86
+ scheduledAt: sqliteInteger('scheduled_at', { mode: 'timestamp' }),
87
+ startedAt: sqliteInteger('started_at', { mode: 'timestamp' }),
88
+ completedAt: sqliteInteger('completed_at', { mode: 'timestamp' }),
89
+ retryCount: sqliteInteger('retry_count').default(0).notNull(),
90
+ maxRetries: sqliteInteger('max_retries').default(3).notNull(),
91
+ createdAt: sqliteInteger('created_at', { mode: 'timestamp' })
92
+ .notNull()
93
+ .$defaultFn(() => new Date()),
94
+ updatedAt: sqliteInteger('updated_at', { mode: 'timestamp' })
95
+ .notNull()
96
+ .$defaultFn(() => new Date()),
97
+ });
98
+
99
+ // =============================================================================
100
+ // TypeScript Types
101
+ // =============================================================================
102
+
103
+ export type TaskPg = typeof tasksPg.$inferSelect;
104
+ export type NewTaskPg = typeof tasksPg.$inferInsert;
105
+
106
+ export type TaskSqlite = typeof tasksSqlite.$inferSelect;
107
+ export type NewTaskSqlite = typeof tasksSqlite.$inferInsert;
108
+
109
+ // Unified types
110
+ export type Task = TaskPg | TaskSqlite;
111
+ export type NewTask = NewTaskPg | NewTaskSqlite;
112
+
113
+ // Task status enum
114
+ export type TaskStatus =
115
+ | 'pending'
116
+ | 'running'
117
+ | 'completed'
118
+ | 'failed'
119
+ | 'cancelled';
120
+
121
+ // Task priority levels
122
+ export const TaskPriority = {
123
+ LOW: 0,
124
+ NORMAL: 5,
125
+ HIGH: 10,
126
+ URGENT: 20,
127
+ } as const;
128
+
129
+ export type TaskPriorityLevel = (typeof TaskPriority)[keyof typeof TaskPriority];
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Circuit Breaker
3
+ *
4
+ * Prevents cascade failures by temporarily blocking calls to failing services.
5
+ * Implements the circuit breaker pattern for database resilience.
6
+ */
7
+
8
+ import type { CircuitBreakerState, CircuitBreakerOptions } from '../types.js';
9
+ import { createLogger } from '../../utils/logger.js';
10
+
11
+ const logger = createLogger('CircuitBreaker');
12
+
13
+ /**
14
+ * Error thrown when circuit is open
15
+ */
16
+ export class CircuitOpenError extends Error {
17
+ constructor(message: string = 'Circuit breaker is open') {
18
+ super(message);
19
+ this.name = 'CircuitOpenError';
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Default circuit breaker options
25
+ */
26
+ const DEFAULT_OPTIONS: CircuitBreakerOptions = {
27
+ failureThreshold: 5,
28
+ resetTimeoutMs: 60000,
29
+ halfOpenMaxAttempts: 3,
30
+ };
31
+
32
+ /**
33
+ * Circuit breaker for database operations
34
+ */
35
+ export class CircuitBreaker {
36
+ private state: CircuitBreakerState = 'closed';
37
+ private failureCount: number = 0;
38
+ private successCount: number = 0;
39
+ private lastFailureTime: number = 0;
40
+ private options: CircuitBreakerOptions;
41
+ private stateChangeListeners: Array<(state: CircuitBreakerState) => void> = [];
42
+
43
+ constructor(options: Partial<CircuitBreakerOptions> = {}) {
44
+ this.options = { ...DEFAULT_OPTIONS, ...options };
45
+ }
46
+
47
+ /**
48
+ * Execute a function with circuit breaker protection
49
+ */
50
+ async execute<T>(fn: () => Promise<T>): Promise<T> {
51
+ // Check if circuit should transition from open to half-open
52
+ if (this.state === 'open') {
53
+ const timeSinceLastFailure = Date.now() - this.lastFailureTime;
54
+ if (timeSinceLastFailure >= this.options.resetTimeoutMs) {
55
+ this.transitionTo('half-open');
56
+ } else {
57
+ throw new CircuitOpenError(
58
+ `Circuit breaker is open. Retry in ${Math.ceil(
59
+ (this.options.resetTimeoutMs - timeSinceLastFailure) / 1000
60
+ )} seconds.`
61
+ );
62
+ }
63
+ }
64
+
65
+ try {
66
+ const result = await fn();
67
+ this.onSuccess();
68
+ return result;
69
+ } catch (error) {
70
+ this.onFailure();
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Handle successful execution
77
+ */
78
+ private onSuccess(): void {
79
+ this.failureCount = 0;
80
+
81
+ if (this.state === 'half-open') {
82
+ this.successCount++;
83
+ if (this.successCount >= this.options.halfOpenMaxAttempts) {
84
+ this.transitionTo('closed');
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Handle failed execution
91
+ */
92
+ private onFailure(): void {
93
+ this.failureCount++;
94
+ this.lastFailureTime = Date.now();
95
+ this.successCount = 0;
96
+
97
+ if (this.state === 'half-open') {
98
+ // Immediately open on failure in half-open state
99
+ this.transitionTo('open');
100
+ } else if (this.failureCount >= this.options.failureThreshold) {
101
+ this.transitionTo('open');
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Transition to a new state
107
+ */
108
+ private transitionTo(newState: CircuitBreakerState): void {
109
+ if (this.state === newState) return;
110
+
111
+ const oldState = this.state;
112
+ this.state = newState;
113
+
114
+ if (newState === 'closed') {
115
+ this.failureCount = 0;
116
+ this.successCount = 0;
117
+ } else if (newState === 'half-open') {
118
+ this.successCount = 0;
119
+ }
120
+
121
+ logger.info('Circuit breaker state changed', {
122
+ from: oldState,
123
+ to: newState,
124
+ });
125
+
126
+ // Notify listeners
127
+ for (const listener of this.stateChangeListeners) {
128
+ try {
129
+ listener(newState);
130
+ } catch (error) {
131
+ logger.error('State change listener error', { error });
132
+ }
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Register a state change listener
138
+ */
139
+ onStateChange(listener: (state: CircuitBreakerState) => void): () => void {
140
+ this.stateChangeListeners.push(listener);
141
+ return () => {
142
+ const index = this.stateChangeListeners.indexOf(listener);
143
+ if (index >= 0) {
144
+ this.stateChangeListeners.splice(index, 1);
145
+ }
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Get current state
151
+ */
152
+ getState(): CircuitBreakerState {
153
+ return this.state;
154
+ }
155
+
156
+ /**
157
+ * Check if circuit is open
158
+ */
159
+ isOpen(): boolean {
160
+ return this.state === 'open';
161
+ }
162
+
163
+ /**
164
+ * Check if circuit is half-open
165
+ */
166
+ isHalfOpen(): boolean {
167
+ return this.state === 'half-open';
168
+ }
169
+
170
+ /**
171
+ * Check if circuit is closed
172
+ */
173
+ isClosed(): boolean {
174
+ return this.state === 'closed';
175
+ }
176
+
177
+ /**
178
+ * Force circuit to open state
179
+ */
180
+ trip(): void {
181
+ this.lastFailureTime = Date.now();
182
+ this.transitionTo('open');
183
+ }
184
+
185
+ /**
186
+ * Force circuit to closed state
187
+ */
188
+ reset(): void {
189
+ this.transitionTo('closed');
190
+ }
191
+
192
+ /**
193
+ * Get circuit breaker statistics
194
+ */
195
+ getStats(): {
196
+ state: CircuitBreakerState;
197
+ failureCount: number;
198
+ successCount: number;
199
+ lastFailureTime: number | null;
200
+ timeSinceLastFailure: number | null;
201
+ } {
202
+ return {
203
+ state: this.state,
204
+ failureCount: this.failureCount,
205
+ successCount: this.successCount,
206
+ lastFailureTime: this.lastFailureTime || null,
207
+ timeSinceLastFailure: this.lastFailureTime
208
+ ? Date.now() - this.lastFailureTime
209
+ : null,
210
+ };
211
+ }
212
+ }
213
+
214
+ export default CircuitBreaker;