@traqr/memory 0.1.0

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 (156) hide show
  1. package/README.md +135 -0
  2. package/dist/index.d.ts +34 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +38 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/auth.d.ts +18 -0
  7. package/dist/lib/auth.d.ts.map +1 -0
  8. package/dist/lib/auth.js +31 -0
  9. package/dist/lib/auth.js.map +1 -0
  10. package/dist/lib/auto-derive.d.ts +35 -0
  11. package/dist/lib/auto-derive.js +261 -0
  12. package/dist/lib/auto-derive.js.map +1 -0
  13. package/dist/lib/borderline.d.ts +26 -0
  14. package/dist/lib/borderline.js +121 -0
  15. package/dist/lib/borderline.js.map +1 -0
  16. package/dist/lib/client.d.ts +28 -0
  17. package/dist/lib/client.d.ts.map +1 -0
  18. package/dist/lib/client.js +60 -0
  19. package/dist/lib/client.js.map +1 -0
  20. package/dist/lib/context.d.ts +38 -0
  21. package/dist/lib/context.d.ts.map +1 -0
  22. package/dist/lib/context.js +334 -0
  23. package/dist/lib/context.js.map +1 -0
  24. package/dist/lib/embeddings.d.ts +60 -0
  25. package/dist/lib/embeddings.d.ts.map +1 -0
  26. package/dist/lib/embeddings.js +229 -0
  27. package/dist/lib/embeddings.js.map +1 -0
  28. package/dist/lib/entity-pipeline.d.ts +23 -0
  29. package/dist/lib/entity-pipeline.js +151 -0
  30. package/dist/lib/entity-pipeline.js.map +1 -0
  31. package/dist/lib/formatting.d.ts +13 -0
  32. package/dist/lib/formatting.d.ts.map +1 -0
  33. package/dist/lib/formatting.js +60 -0
  34. package/dist/lib/formatting.js.map +1 -0
  35. package/dist/lib/learning-extractor.d.ts +144 -0
  36. package/dist/lib/learning-extractor.d.ts.map +1 -0
  37. package/dist/lib/learning-extractor.js +921 -0
  38. package/dist/lib/learning-extractor.js.map +1 -0
  39. package/dist/lib/lifecycle.d.ts +45 -0
  40. package/dist/lib/lifecycle.js +84 -0
  41. package/dist/lib/lifecycle.js.map +1 -0
  42. package/dist/lib/memory.d.ts +128 -0
  43. package/dist/lib/memory.d.ts.map +1 -0
  44. package/dist/lib/memory.js +590 -0
  45. package/dist/lib/memory.js.map +1 -0
  46. package/dist/lib/quality-gate.d.ts +32 -0
  47. package/dist/lib/quality-gate.js +158 -0
  48. package/dist/lib/quality-gate.js.map +1 -0
  49. package/dist/lib/quality-gate.test.d.ts +7 -0
  50. package/dist/lib/quality-gate.test.js +75 -0
  51. package/dist/lib/quality-gate.test.js.map +1 -0
  52. package/dist/lib/rerank.d.ts +22 -0
  53. package/dist/lib/rerank.js +61 -0
  54. package/dist/lib/rerank.js.map +1 -0
  55. package/dist/lib/retrieval.d.ts +75 -0
  56. package/dist/lib/retrieval.js +380 -0
  57. package/dist/lib/retrieval.js.map +1 -0
  58. package/dist/migrate.d.ts +17 -0
  59. package/dist/migrate.d.ts.map +1 -0
  60. package/dist/migrate.js +81 -0
  61. package/dist/migrate.js.map +1 -0
  62. package/dist/routes/analyze-codebase.d.ts +9 -0
  63. package/dist/routes/analyze-codebase.d.ts.map +1 -0
  64. package/dist/routes/analyze-codebase.js +70 -0
  65. package/dist/routes/analyze-codebase.js.map +1 -0
  66. package/dist/routes/analyze-voice.d.ts +9 -0
  67. package/dist/routes/analyze-voice.d.ts.map +1 -0
  68. package/dist/routes/analyze-voice.js +63 -0
  69. package/dist/routes/analyze-voice.js.map +1 -0
  70. package/dist/routes/assemble-context.d.ts +9 -0
  71. package/dist/routes/assemble-context.d.ts.map +1 -0
  72. package/dist/routes/assemble-context.js +68 -0
  73. package/dist/routes/assemble-context.js.map +1 -0
  74. package/dist/routes/bootstrap.d.ts +12 -0
  75. package/dist/routes/bootstrap.d.ts.map +1 -0
  76. package/dist/routes/bootstrap.js +102 -0
  77. package/dist/routes/bootstrap.js.map +1 -0
  78. package/dist/routes/browse.d.ts +11 -0
  79. package/dist/routes/browse.js +85 -0
  80. package/dist/routes/browse.js.map +1 -0
  81. package/dist/routes/capture-thought.d.ts +13 -0
  82. package/dist/routes/capture-thought.d.ts.map +1 -0
  83. package/dist/routes/capture-thought.js +178 -0
  84. package/dist/routes/capture-thought.js.map +1 -0
  85. package/dist/routes/capture.d.ts +13 -0
  86. package/dist/routes/capture.d.ts.map +1 -0
  87. package/dist/routes/capture.js +86 -0
  88. package/dist/routes/capture.js.map +1 -0
  89. package/dist/routes/cite.d.ts +9 -0
  90. package/dist/routes/cite.d.ts.map +1 -0
  91. package/dist/routes/cite.js +49 -0
  92. package/dist/routes/cite.js.map +1 -0
  93. package/dist/routes/crud.d.ts +11 -0
  94. package/dist/routes/crud.d.ts.map +1 -0
  95. package/dist/routes/crud.js +176 -0
  96. package/dist/routes/crud.js.map +1 -0
  97. package/dist/routes/dashboard.d.ts +9 -0
  98. package/dist/routes/dashboard.d.ts.map +1 -0
  99. package/dist/routes/dashboard.js +85 -0
  100. package/dist/routes/dashboard.js.map +1 -0
  101. package/dist/routes/entity-cron.d.ts +8 -0
  102. package/dist/routes/entity-cron.js +31 -0
  103. package/dist/routes/entity-cron.js.map +1 -0
  104. package/dist/routes/export.d.ts +8 -0
  105. package/dist/routes/export.d.ts.map +1 -0
  106. package/dist/routes/export.js +69 -0
  107. package/dist/routes/export.js.map +1 -0
  108. package/dist/routes/extract-pr-learnings.d.ts +12 -0
  109. package/dist/routes/extract-pr-learnings.d.ts.map +1 -0
  110. package/dist/routes/extract-pr-learnings.js +127 -0
  111. package/dist/routes/extract-pr-learnings.js.map +1 -0
  112. package/dist/routes/forget-cron.d.ts +9 -0
  113. package/dist/routes/forget-cron.js +30 -0
  114. package/dist/routes/forget-cron.js.map +1 -0
  115. package/dist/routes/learnings.d.ts +9 -0
  116. package/dist/routes/learnings.d.ts.map +1 -0
  117. package/dist/routes/learnings.js +237 -0
  118. package/dist/routes/learnings.js.map +1 -0
  119. package/dist/routes/pulse.d.ts +9 -0
  120. package/dist/routes/pulse.d.ts.map +1 -0
  121. package/dist/routes/pulse.js +133 -0
  122. package/dist/routes/pulse.js.map +1 -0
  123. package/dist/routes/search.d.ts +8 -0
  124. package/dist/routes/search.d.ts.map +1 -0
  125. package/dist/routes/search.js +107 -0
  126. package/dist/routes/search.js.map +1 -0
  127. package/dist/routes/store.d.ts +8 -0
  128. package/dist/routes/store.d.ts.map +1 -0
  129. package/dist/routes/store.js +89 -0
  130. package/dist/routes/store.js.map +1 -0
  131. package/dist/routes/sync.d.ts +12 -0
  132. package/dist/routes/sync.d.ts.map +1 -0
  133. package/dist/routes/sync.js +83 -0
  134. package/dist/routes/sync.js.map +1 -0
  135. package/dist/routes/voice-profile.d.ts +9 -0
  136. package/dist/routes/voice-profile.d.ts.map +1 -0
  137. package/dist/routes/voice-profile.js +124 -0
  138. package/dist/routes/voice-profile.js.map +1 -0
  139. package/dist/server.d.ts +37 -0
  140. package/dist/server.d.ts.map +1 -0
  141. package/dist/server.js +99 -0
  142. package/dist/server.js.map +1 -0
  143. package/dist/vectordb/index.d.ts +17 -0
  144. package/dist/vectordb/index.d.ts.map +1 -0
  145. package/dist/vectordb/index.js +39 -0
  146. package/dist/vectordb/index.js.map +1 -0
  147. package/dist/vectordb/supabase.d.ts +62 -0
  148. package/dist/vectordb/supabase.d.ts.map +1 -0
  149. package/dist/vectordb/supabase.js +711 -0
  150. package/dist/vectordb/supabase.js.map +1 -0
  151. package/dist/vectordb/types.d.ts +217 -0
  152. package/dist/vectordb/types.d.ts.map +1 -0
  153. package/dist/vectordb/types.js +28 -0
  154. package/dist/vectordb/types.js.map +1 -0
  155. package/package.json +49 -0
  156. package/setup.sql +1037 -0
@@ -0,0 +1,711 @@
1
+ /**
2
+ * Supabase VectorDB Provider
3
+ *
4
+ * Implementation of VectorDBProvider using Supabase with pgvector.
5
+ * All memory operations go through the configured Supabase instance.
6
+ */
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ import { getMemoryClient, getUserId, getProjectId, getTableName } from '../lib/client.js';
9
+ import { generateEmbedding, formatEmbeddingForPgVector } from '../lib/embeddings.js';
10
+ // Convert database row to Memory type
11
+ function rowToMemory(row) {
12
+ return {
13
+ id: row.id,
14
+ content: row.content,
15
+ summary: row.summary ?? undefined,
16
+ category: row.category,
17
+ tags: row.tags || [],
18
+ contextTags: row.context_tags || [],
19
+ sourceType: row.source_type,
20
+ sourceRef: row.source_ref ?? undefined,
21
+ sourceProject: row.source_project,
22
+ originalConfidence: row.original_confidence,
23
+ lastValidated: new Date(row.last_validated),
24
+ relatedTo: row.related_to || [],
25
+ isContradiction: row.is_contradiction,
26
+ isArchived: row.is_archived,
27
+ archiveReason: row.archive_reason ?? undefined,
28
+ archivedAt: row.archived_at ? new Date(row.archived_at) : undefined,
29
+ embeddingModel: row.embedding_model,
30
+ embeddingModelVersion: row.embedding_model_version,
31
+ createdAt: new Date(row.created_at),
32
+ updatedAt: new Date(row.updated_at),
33
+ durability: row.durability || 'permanent',
34
+ expiresAt: row.expires_at ? new Date(row.expires_at) : undefined,
35
+ domain: row.domain ?? undefined,
36
+ topic: row.topic ?? undefined,
37
+ isUniversal: row.is_universal ?? false,
38
+ agentType: row.agent_type ?? undefined,
39
+ timesReturned: row.times_returned ?? 0,
40
+ timesCited: row.times_cited ?? 0,
41
+ lastReturnedAt: row.last_returned_at ? new Date(row.last_returned_at) : undefined,
42
+ lastCitedAt: row.last_cited_at ? new Date(row.last_cited_at) : undefined,
43
+ // v2: Memory lifecycle
44
+ memoryType: row.memory_type ?? undefined,
45
+ validAt: row.valid_at ? new Date(row.valid_at) : undefined,
46
+ invalidAt: row.invalid_at ? new Date(row.invalid_at) : undefined,
47
+ isLatest: row.is_latest ?? true,
48
+ isForgotten: row.is_forgotten ?? false,
49
+ forgottenAt: row.forgotten_at ? new Date(row.forgotten_at) : undefined,
50
+ forgetAfter: row.forget_after ? new Date(row.forget_after) : undefined,
51
+ sourceTool: row.source_tool ?? undefined,
52
+ };
53
+ }
54
+ function rowToSearchResult(row) {
55
+ return {
56
+ ...rowToMemory(row),
57
+ currentConfidence: row.current_confidence,
58
+ similarity: row.similarity,
59
+ relevanceScore: row.relevance_score,
60
+ };
61
+ }
62
+ export class SupabaseVectorProvider {
63
+ async store(input, domainId) {
64
+ const client = getMemoryClient();
65
+ const projectId = domainId || getProjectId();
66
+ // Use pre-computed embedding if available (saves one OpenAI API call in triage flow)
67
+ let embeddingStr;
68
+ let embeddingModel = 'text-embedding-3-small';
69
+ let embeddingModelVersion = '1';
70
+ if (input.precomputedEmbedding) {
71
+ embeddingStr = input.precomputedEmbedding;
72
+ }
73
+ else {
74
+ const result = await generateEmbedding(input.content);
75
+ embeddingStr = formatEmbeddingForPgVector(result.embedding);
76
+ embeddingModel = result.model;
77
+ embeddingModelVersion = result.modelVersion;
78
+ }
79
+ const insertData = {
80
+ user_id: getUserId(),
81
+ project_id: projectId,
82
+ content: input.content,
83
+ summary: input.summary,
84
+ category: input.category,
85
+ tags: input.tags || [],
86
+ context_tags: input.contextTags || [],
87
+ embedding: embeddingStr,
88
+ embedding_model: embeddingModel,
89
+ embedding_model_version: embeddingModelVersion,
90
+ source_type: input.sourceType,
91
+ source_ref: input.sourceRef,
92
+ source_project: input.sourceProject || 'default',
93
+ original_confidence: input.confidence ?? 1.0,
94
+ related_to: input.relatedTo || [],
95
+ is_contradiction: input.isContradiction || false,
96
+ is_universal: input.isUniversal || false,
97
+ agent_type: input.agentType || null,
98
+ durability: input.durability || 'permanent',
99
+ expires_at: input.expiresAt ? input.expiresAt.toISOString() : null,
100
+ is_portable: true,
101
+ domain: input.domain || null,
102
+ topic: input.topic || null,
103
+ // v2: Memory lifecycle
104
+ memory_type: input.memoryType || null,
105
+ source_tool: input.sourceTool || null,
106
+ valid_at: input.validAt ? input.validAt.toISOString() : new Date().toISOString(),
107
+ forget_after: input.forgetAfter ? input.forgetAfter.toISOString() : null,
108
+ is_latest: true,
109
+ is_forgotten: false,
110
+ };
111
+ const { data, error } = await client
112
+ .from(getTableName())
113
+ .insert(insertData)
114
+ .select()
115
+ .single();
116
+ if (error) {
117
+ console.error('[VectorDB] Error storing memory:', error);
118
+ throw new Error(`Failed to store memory: ${error.message}`);
119
+ }
120
+ return rowToMemory(data);
121
+ }
122
+ async search(query, options = {}) {
123
+ const client = getMemoryClient();
124
+ const embeddingStr = options.precomputedEmbedding
125
+ ?? formatEmbeddingForPgVector((await generateEmbedding(query)).embedding);
126
+ if (options.includeUniversal || options.sourceProject || options.agentType) {
127
+ const { data, error } = await client.rpc('search_memories_cross_project', {
128
+ p_query_embedding: embeddingStr,
129
+ p_project_id: options.domainId || null,
130
+ p_source_project: options.sourceProject || null,
131
+ p_category: options.category || null,
132
+ p_tags: options.tags || null,
133
+ p_include_archived: options.includeArchived || false,
134
+ p_include_portable: options.includeUniversal ?? true,
135
+ p_agent_type: options.agentType || null,
136
+ p_limit: options.limit || 10,
137
+ p_similarity_threshold: options.similarityThreshold || 0.3,
138
+ });
139
+ if (error) {
140
+ console.error('[VectorDB] Error searching memories (cross-project):', error);
141
+ }
142
+ else {
143
+ return (data || []).map((row) => rowToSearchResult(row));
144
+ }
145
+ }
146
+ const { data, error } = await client.rpc('search_memories', {
147
+ p_query_embedding: embeddingStr,
148
+ p_project_id: options.domainId || null,
149
+ p_category: options.category || null,
150
+ p_tags: options.tags || null,
151
+ p_include_archived: options.includeArchived || false,
152
+ p_limit: options.limit || 10,
153
+ p_similarity_threshold: options.similarityThreshold || 0.3,
154
+ p_latest_only: options.latestOnly ?? true,
155
+ });
156
+ if (error) {
157
+ console.error('[VectorDB] Error searching memories:', error);
158
+ throw new Error(`Failed to search memories: ${error.message}`);
159
+ }
160
+ return (data || []).map((row) => rowToSearchResult(row));
161
+ }
162
+ async getById(id) {
163
+ const client = getMemoryClient();
164
+ const { data, error } = await client
165
+ .from(getTableName())
166
+ .select()
167
+ .eq('id', id)
168
+ .single();
169
+ if (error) {
170
+ if (error.code === 'PGRST116')
171
+ return null;
172
+ console.error('[VectorDB] Error getting memory:', error);
173
+ throw new Error(`Failed to get memory: ${error.message}`);
174
+ }
175
+ return rowToMemory(data);
176
+ }
177
+ async update(id, updates) {
178
+ const client = getMemoryClient();
179
+ const current = await this.getById(id);
180
+ if (!current) {
181
+ throw new Error(`Memory not found: ${id}`);
182
+ }
183
+ const updateData = {
184
+ updated_at: new Date().toISOString(),
185
+ };
186
+ if (updates.content !== undefined) {
187
+ updateData.content = updates.content;
188
+ const embeddingResult = await generateEmbedding(updates.content);
189
+ updateData.embedding = formatEmbeddingForPgVector(embeddingResult.embedding);
190
+ updateData.embedding_model = embeddingResult.model;
191
+ updateData.embedding_model_version = embeddingResult.modelVersion;
192
+ }
193
+ if (updates.summary !== undefined)
194
+ updateData.summary = updates.summary;
195
+ if (updates.category !== undefined)
196
+ updateData.category = updates.category;
197
+ if (updates.tags !== undefined)
198
+ updateData.tags = updates.tags;
199
+ if (updates.contextTags !== undefined)
200
+ updateData.context_tags = updates.contextTags;
201
+ if (updates.confidence !== undefined)
202
+ updateData.original_confidence = updates.confidence;
203
+ if (updates.relatedTo !== undefined)
204
+ updateData.related_to = updates.relatedTo;
205
+ if (updates.isContradiction !== undefined)
206
+ updateData.is_contradiction = updates.isContradiction;
207
+ if (updates.content && updates.content !== current.content) {
208
+ await client.from('traqr_memory_history').insert({
209
+ memory_id: id,
210
+ previous_content: current.content,
211
+ previous_confidence: current.originalConfidence,
212
+ change_reason: updates.changeReason || 'Content updated',
213
+ });
214
+ }
215
+ const { data, error } = await client
216
+ .from(getTableName())
217
+ .update(updateData)
218
+ .eq('id', id)
219
+ .select()
220
+ .single();
221
+ if (error) {
222
+ console.error('[VectorDB] Error updating memory:', error);
223
+ throw new Error(`Failed to update memory: ${error.message}`);
224
+ }
225
+ return rowToMemory(data);
226
+ }
227
+ async delete(id) {
228
+ const client = getMemoryClient();
229
+ const { error } = await client
230
+ .from(getTableName())
231
+ .delete()
232
+ .eq('id', id);
233
+ if (error) {
234
+ console.error('[VectorDB] Error deleting memory:', error);
235
+ throw new Error(`Failed to delete memory: ${error.message}`);
236
+ }
237
+ }
238
+ async validate(id) {
239
+ const client = getMemoryClient();
240
+ const { data, error } = await client
241
+ .from(getTableName())
242
+ .update({
243
+ last_validated: new Date().toISOString(),
244
+ updated_at: new Date().toISOString(),
245
+ })
246
+ .eq('id', id)
247
+ .select()
248
+ .single();
249
+ if (error) {
250
+ console.error('[VectorDB] Error validating memory:', error);
251
+ throw new Error(`Failed to validate memory: ${error.message}`);
252
+ }
253
+ return rowToMemory(data);
254
+ }
255
+ async invalidate(id) {
256
+ const client = getMemoryClient();
257
+ const { error } = await client
258
+ .from(getTableName())
259
+ .update({
260
+ invalid_at: new Date().toISOString(),
261
+ is_latest: false,
262
+ updated_at: new Date().toISOString(),
263
+ })
264
+ .eq('id', id);
265
+ if (error) {
266
+ console.warn('[VectorDB] Error invalidating memory:', error.message);
267
+ }
268
+ }
269
+ async supersede(id) {
270
+ const client = getMemoryClient();
271
+ const { error } = await client
272
+ .from(getTableName())
273
+ .update({
274
+ is_latest: false,
275
+ updated_at: new Date().toISOString(),
276
+ })
277
+ .eq('id', id);
278
+ if (error) {
279
+ console.warn('[VectorDB] Error superseding memory:', error.message);
280
+ }
281
+ }
282
+ async archive(id, reason) {
283
+ const client = getMemoryClient();
284
+ const { data, error } = await client
285
+ .from(getTableName())
286
+ .update({
287
+ is_archived: true,
288
+ archived_at: new Date().toISOString(),
289
+ archive_reason: reason || 'manual',
290
+ updated_at: new Date().toISOString(),
291
+ })
292
+ .eq('id', id)
293
+ .select()
294
+ .single();
295
+ if (error) {
296
+ console.error('[VectorDB] Error archiving memory:', error);
297
+ throw new Error(`Failed to archive memory: ${error.message}`);
298
+ }
299
+ return rowToMemory(data);
300
+ }
301
+ async unarchive(id) {
302
+ const client = getMemoryClient();
303
+ const { data, error } = await client
304
+ .from(getTableName())
305
+ .update({
306
+ is_archived: false,
307
+ archived_at: null,
308
+ archive_reason: null,
309
+ updated_at: new Date().toISOString(),
310
+ })
311
+ .eq('id', id)
312
+ .select()
313
+ .single();
314
+ if (error) {
315
+ console.error('[VectorDB] Error unarchiving memory:', error);
316
+ throw new Error(`Failed to unarchive memory: ${error.message}`);
317
+ }
318
+ return rowToMemory(data);
319
+ }
320
+ async exportAll(domainId) {
321
+ const client = getMemoryClient();
322
+ let query = client.from(getTableName()).select('*');
323
+ if (domainId) {
324
+ query = query.eq('project_id', domainId);
325
+ }
326
+ const { data, error } = await query;
327
+ if (error) {
328
+ console.error('[VectorDB] Error exporting memories:', error);
329
+ throw new Error(`Failed to export memories: ${error.message}`);
330
+ }
331
+ return (data || []).map((row) => ({
332
+ id: row.id,
333
+ content: row.content,
334
+ summary: row.summary ?? undefined,
335
+ category: row.category,
336
+ tags: row.tags || [],
337
+ contextTags: row.context_tags || [],
338
+ sourceType: row.source_type,
339
+ sourceRef: row.source_ref ?? undefined,
340
+ sourceProject: row.source_project,
341
+ originalConfidence: row.original_confidence,
342
+ lastValidated: row.last_validated,
343
+ relatedTo: row.related_to || [],
344
+ isContradiction: row.is_contradiction,
345
+ isArchived: row.is_archived,
346
+ archiveReason: row.archive_reason ?? undefined,
347
+ durability: row.durability,
348
+ expiresAt: row.expires_at ?? undefined,
349
+ embeddingModel: row.embedding_model,
350
+ embeddingModelVersion: row.embedding_model_version,
351
+ createdAt: row.created_at,
352
+ updatedAt: row.updated_at,
353
+ domainName: undefined,
354
+ userEmail: undefined,
355
+ }));
356
+ }
357
+ async importBulk(memories, domainId) {
358
+ const client = getMemoryClient();
359
+ let importedCount = 0;
360
+ const BATCH_SIZE = 10;
361
+ for (let i = 0; i < memories.length; i += BATCH_SIZE) {
362
+ const batch = memories.slice(i, i + BATCH_SIZE);
363
+ const embeddings = await Promise.all(batch.map(m => generateEmbedding(m.content)));
364
+ const insertData = batch.map((memory, idx) => ({
365
+ user_id: getUserId(),
366
+ project_id: domainId,
367
+ content: memory.content,
368
+ summary: memory.summary,
369
+ category: memory.category,
370
+ tags: memory.tags,
371
+ context_tags: memory.contextTags,
372
+ embedding: formatEmbeddingForPgVector(embeddings[idx].embedding),
373
+ embedding_model: embeddings[idx].model,
374
+ embedding_model_version: embeddings[idx].modelVersion,
375
+ source_type: memory.sourceType,
376
+ source_ref: memory.sourceRef,
377
+ source_project: memory.sourceProject,
378
+ original_confidence: memory.originalConfidence,
379
+ last_validated: memory.lastValidated,
380
+ related_to: memory.relatedTo,
381
+ is_contradiction: memory.isContradiction,
382
+ is_archived: memory.isArchived,
383
+ archive_reason: memory.archiveReason,
384
+ created_at: memory.createdAt,
385
+ updated_at: memory.updatedAt,
386
+ is_portable: true,
387
+ }));
388
+ const { error } = await client.from(getTableName()).insert(insertData);
389
+ if (error) {
390
+ console.error(`[VectorDB] Error importing batch ${i / BATCH_SIZE}:`, error);
391
+ }
392
+ else {
393
+ importedCount += batch.length;
394
+ }
395
+ }
396
+ return importedCount;
397
+ }
398
+ async createDomain(name, description, userId) {
399
+ const client = getMemoryClient();
400
+ const { data, error } = await client
401
+ .from('traqr_projects')
402
+ .insert({
403
+ user_id: userId || getUserId(),
404
+ name,
405
+ slug: name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
406
+ description,
407
+ is_active: true,
408
+ })
409
+ .select()
410
+ .single();
411
+ if (error) {
412
+ console.error('[VectorDB] Error creating domain:', error);
413
+ throw new Error(`Failed to create domain: ${error.message}`);
414
+ }
415
+ return {
416
+ id: data.id,
417
+ userId: data.user_id,
418
+ name: data.name,
419
+ description: data.description ?? undefined,
420
+ isShareable: data.is_active,
421
+ createdAt: new Date(data.created_at),
422
+ updatedAt: new Date(data.last_activity),
423
+ };
424
+ }
425
+ async getDomain(name) {
426
+ const client = getMemoryClient();
427
+ const { data, error } = await client
428
+ .from('traqr_projects')
429
+ .select()
430
+ .eq('slug', name.toLowerCase().replace(/[^a-z0-9]+/g, '-'))
431
+ .single();
432
+ if (error) {
433
+ if (error.code === 'PGRST116')
434
+ return null;
435
+ console.error('[VectorDB] Error getting domain:', error);
436
+ throw new Error(`Failed to get domain: ${error.message}`);
437
+ }
438
+ return {
439
+ id: data.id,
440
+ userId: data.user_id,
441
+ name: data.name,
442
+ description: data.description ?? undefined,
443
+ isShareable: data.is_active,
444
+ createdAt: new Date(data.created_at),
445
+ updatedAt: new Date(data.last_activity),
446
+ };
447
+ }
448
+ async getDefaultDomain() {
449
+ const client = getMemoryClient();
450
+ const { data, error } = await client
451
+ .from('traqr_projects')
452
+ .select()
453
+ .eq('id', getProjectId())
454
+ .single();
455
+ if (error) {
456
+ console.error('[VectorDB] Error getting default domain:', error);
457
+ throw new Error(`Failed to get default domain: ${error.message}`);
458
+ }
459
+ return {
460
+ id: data.id,
461
+ userId: data.user_id,
462
+ name: data.name,
463
+ description: data.description ?? undefined,
464
+ isShareable: data.is_active,
465
+ createdAt: new Date(data.created_at),
466
+ updatedAt: new Date(data.last_activity),
467
+ };
468
+ }
469
+ // ============================================================
470
+ // v2 Search Strategies (I-M4) — additive, graceful degradation
471
+ // ============================================================
472
+ async bm25Search(queryText, options) {
473
+ const client = getMemoryClient();
474
+ const { data, error } = await client.rpc('bm25_search', {
475
+ p_query_text: queryText,
476
+ p_project_id: options?.projectId || null,
477
+ p_domain: options?.domain || null,
478
+ p_category: options?.category || null,
479
+ p_limit: options?.limit || 20,
480
+ p_min_score: options?.minScore || 0.01,
481
+ });
482
+ if (error) {
483
+ console.error('[VectorDB] BM25 search error:', error);
484
+ return [];
485
+ }
486
+ return (data || []).map((row) => ({
487
+ id: row.id,
488
+ content: row.content,
489
+ summary: row.summary ?? undefined,
490
+ bm25Score: row.bm25_score,
491
+ domain: row.domain ?? undefined,
492
+ category: row.category ?? undefined,
493
+ memoryType: row.memory_type ?? undefined,
494
+ }));
495
+ }
496
+ async temporalSearch(query, dateStart, dateEnd, options) {
497
+ const client = getMemoryClient();
498
+ const embeddingStr = options?.precomputedEmbedding
499
+ ?? formatEmbeddingForPgVector((await generateEmbedding(query)).embedding);
500
+ const { data, error } = await client.rpc('temporal_search', {
501
+ p_query_embedding: embeddingStr,
502
+ p_date_start: dateStart.toISOString(),
503
+ p_date_end: dateEnd.toISOString(),
504
+ p_project_id: options?.projectId || null,
505
+ p_similarity_threshold: options?.similarityThreshold || 0.3,
506
+ p_limit: options?.limit || 20,
507
+ });
508
+ if (error) {
509
+ console.error('[VectorDB] Temporal search error:', error);
510
+ return [];
511
+ }
512
+ return (data || []).map((row) => ({
513
+ id: row.id,
514
+ content: row.content,
515
+ summary: row.summary ?? undefined,
516
+ similarity: row.similarity,
517
+ temporalProximity: row.temporal_proximity,
518
+ validAt: new Date(row.valid_at),
519
+ }));
520
+ }
521
+ async graphSearch(seedIds, options) {
522
+ const client = getMemoryClient();
523
+ const { data, error } = await client.rpc('graph_search', {
524
+ p_seed_ids: seedIds,
525
+ p_edge_types: options?.edgeTypes || ['updates', 'extends', 'derives', 'related'],
526
+ p_max_depth: options?.maxDepth || 2,
527
+ p_limit: options?.limit || 20,
528
+ });
529
+ if (error) {
530
+ console.error('[VectorDB] Graph search error:', error);
531
+ return [];
532
+ }
533
+ return (data || []).map((row) => ({
534
+ id: row.id,
535
+ content: row.content,
536
+ summary: row.summary ?? undefined,
537
+ graphScore: row.graph_score,
538
+ edgeType: row.edge_type,
539
+ depth: row.depth,
540
+ }));
541
+ }
542
+ // ============================================================
543
+ // ENTITY OPERATIONS
544
+ // ============================================================
545
+ async findEntityByName(name, entityType) {
546
+ const client = getMemoryClient();
547
+ const { data, error } = await client.from('memory_entities')
548
+ .select('*')
549
+ .eq('user_id', getUserId())
550
+ .ilike('name', name.trim())
551
+ .eq('entity_type', entityType)
552
+ .eq('is_archived', false)
553
+ .limit(1)
554
+ .maybeSingle();
555
+ if (error || !data)
556
+ return null;
557
+ return data;
558
+ }
559
+ async findEntityByNameFuzzy(name, entityType) {
560
+ const client = getMemoryClient();
561
+ const { data, error } = await client.from('memory_entities')
562
+ .select('*')
563
+ .eq('user_id', getUserId())
564
+ .ilike('name', `%${name.trim()}%`)
565
+ .eq('entity_type', entityType)
566
+ .eq('is_archived', false)
567
+ .limit(1)
568
+ .maybeSingle();
569
+ if (error || !data)
570
+ return null;
571
+ return data;
572
+ }
573
+ async findEntityByEmbedding(embeddingStr, entityType, threshold = 0.85) {
574
+ const client = getMemoryClient();
575
+ const { data, error } = await client.rpc('search_entities', {
576
+ p_query_embedding: embeddingStr,
577
+ p_user_id: getUserId(),
578
+ p_entity_type: entityType,
579
+ p_threshold: threshold,
580
+ p_limit: 1,
581
+ });
582
+ if (error || !data || data.length === 0)
583
+ return null;
584
+ return data[0];
585
+ }
586
+ async createEntity(entity) {
587
+ const client = getMemoryClient();
588
+ const { data, error } = await client.from('memory_entities')
589
+ .insert({
590
+ user_id: entity.userId || getUserId(),
591
+ name: entity.name,
592
+ entity_type: entity.entityType,
593
+ embedding: entity.embedding || null,
594
+ mentions_count: 1,
595
+ first_seen_at: new Date().toISOString(),
596
+ last_seen_at: new Date().toISOString(),
597
+ })
598
+ .select()
599
+ .single();
600
+ if (error) {
601
+ // UNIQUE constraint violation = entity already exists, find and return it
602
+ if (error.code === '23505') {
603
+ return this.findEntityByName(entity.name, entity.entityType);
604
+ }
605
+ console.warn('[VectorDB] Error creating entity:', error.message);
606
+ return null;
607
+ }
608
+ return data;
609
+ }
610
+ async incrementEntityMentions(entityId) {
611
+ const client = getMemoryClient();
612
+ // Read current count, increment, write back
613
+ const { data } = await client.from('memory_entities')
614
+ .select('mentions_count')
615
+ .eq('id', entityId)
616
+ .single();
617
+ const newCount = (data?.mentions_count || 0) + 1;
618
+ const { error } = await client.from('memory_entities')
619
+ .update({
620
+ mentions_count: newCount,
621
+ last_seen_at: new Date().toISOString(),
622
+ })
623
+ .eq('id', entityId);
624
+ if (error) {
625
+ console.warn('[VectorDB] Error incrementing entity mentions:', error.message);
626
+ }
627
+ }
628
+ async linkMemoryToEntity(memoryId, entityId, role = 'mentions') {
629
+ const client = getMemoryClient();
630
+ const { error } = await client.from('memory_entity_links')
631
+ .insert({
632
+ memory_id: memoryId,
633
+ entity_id: entityId,
634
+ role,
635
+ });
636
+ if (error && error.code !== '23505') { // ignore duplicate links
637
+ console.warn('[VectorDB] Error linking memory to entity:', error.message);
638
+ }
639
+ }
640
+ async findEntitiesByNames(names) {
641
+ if (names.length === 0)
642
+ return [];
643
+ const client = getMemoryClient();
644
+ const lowerNames = names.map((n) => n.toLowerCase().trim()).filter(Boolean);
645
+ const { data, error } = await client.from('memory_entities')
646
+ .select('id, name')
647
+ .eq('user_id', getUserId())
648
+ .eq('is_archived', false)
649
+ .in('name', lowerNames); // exact match on lowercase names
650
+ if (error || !data) {
651
+ // Fallback: try ILIKE for case-insensitive matching
652
+ const results = [];
653
+ for (const name of lowerNames.slice(0, 10)) { // limit to avoid N+1 explosion
654
+ const { data: d } = await client.from('memory_entities')
655
+ .select('id, name')
656
+ .eq('user_id', getUserId())
657
+ .eq('is_archived', false)
658
+ .ilike('name', name)
659
+ .limit(1)
660
+ .maybeSingle();
661
+ if (d)
662
+ results.push({ id: d.id, name: d.name });
663
+ }
664
+ return results;
665
+ }
666
+ return (data || []).map((d) => ({ id: d.id, name: d.name }));
667
+ }
668
+ async findOrphanedEntities() {
669
+ const client = getMemoryClient();
670
+ // Find entities with zero non-archived memory links
671
+ const { data, error } = await client.from('memory_entities')
672
+ .select('id')
673
+ .eq('user_id', getUserId())
674
+ .eq('is_archived', false);
675
+ if (error || !data)
676
+ return [];
677
+ const orphaned = [];
678
+ for (const entity of data) {
679
+ const { count } = await client.from('memory_entity_links')
680
+ .select('*', { count: 'exact', head: true })
681
+ .eq('entity_id', entity.id);
682
+ if (count === 0)
683
+ orphaned.push(entity.id);
684
+ }
685
+ return orphaned;
686
+ }
687
+ async archiveEntities(ids) {
688
+ if (ids.length === 0)
689
+ return 0;
690
+ const client = getMemoryClient();
691
+ const { error } = await client.from('memory_entities')
692
+ .update({ is_archived: true })
693
+ .in('id', ids);
694
+ if (error) {
695
+ console.warn('[VectorDB] Error archiving entities:', error.message);
696
+ return 0;
697
+ }
698
+ return ids.length;
699
+ }
700
+ async ping() {
701
+ try {
702
+ const client = getMemoryClient();
703
+ const { error } = await client.from('traqr_users').select('id').limit(1);
704
+ return !error;
705
+ }
706
+ catch {
707
+ return false;
708
+ }
709
+ }
710
+ }
711
+ //# sourceMappingURL=supabase.js.map