stellar-memory 0.8.4 → 1.0.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 (139) hide show
  1. package/dist/api/routes/memories.js +5 -4
  2. package/dist/api/routes/memories.js.map +1 -1
  3. package/dist/api/websocket.d.ts +0 -6
  4. package/dist/api/websocket.js +1 -1
  5. package/dist/api/websocket.js.map +1 -1
  6. package/dist/engine/analytics.js +4 -33
  7. package/dist/engine/analytics.js.map +1 -1
  8. package/dist/engine/conflict.js +2 -4
  9. package/dist/engine/conflict.js.map +1 -1
  10. package/dist/engine/consolidation.js +3 -21
  11. package/dist/engine/consolidation.js.map +1 -1
  12. package/dist/engine/constellation.js +6 -14
  13. package/dist/engine/constellation.js.map +1 -1
  14. package/dist/engine/corona.js +3 -1
  15. package/dist/engine/corona.js.map +1 -1
  16. package/dist/engine/multiproject.js +6 -73
  17. package/dist/engine/multiproject.js.map +1 -1
  18. package/dist/engine/observation.d.ts +1 -42
  19. package/dist/engine/observation.js +4 -23
  20. package/dist/engine/observation.js.map +1 -1
  21. package/dist/engine/orbit.js +50 -11
  22. package/dist/engine/orbit.js.map +1 -1
  23. package/dist/engine/planet.d.ts +3 -0
  24. package/dist/engine/planet.js +16 -11
  25. package/dist/engine/planet.js.map +1 -1
  26. package/dist/engine/procedural.d.ts +0 -13
  27. package/dist/engine/procedural.js +2 -47
  28. package/dist/engine/procedural.js.map +1 -1
  29. package/dist/engine/services/commit-service.d.ts +29 -0
  30. package/dist/engine/services/commit-service.js +35 -0
  31. package/dist/engine/services/commit-service.js.map +1 -0
  32. package/dist/engine/services/index.d.ts +4 -0
  33. package/dist/engine/services/index.js +4 -0
  34. package/dist/engine/services/index.js.map +1 -0
  35. package/dist/engine/services/memory-service.d.ts +48 -0
  36. package/dist/engine/services/memory-service.js +85 -0
  37. package/dist/engine/services/memory-service.js.map +1 -0
  38. package/dist/engine/services/recall-service.d.ts +31 -0
  39. package/dist/engine/services/recall-service.js +35 -0
  40. package/dist/engine/services/recall-service.js.map +1 -0
  41. package/dist/engine/sun.d.ts +9 -5
  42. package/dist/engine/sun.js +111 -12
  43. package/dist/engine/sun.js.map +1 -1
  44. package/dist/engine/temporal.js +8 -67
  45. package/dist/engine/temporal.js.map +1 -1
  46. package/dist/engine/types.d.ts +0 -15
  47. package/dist/engine/types.js +0 -7
  48. package/dist/engine/types.js.map +1 -1
  49. package/dist/mcp/server.js +88 -123
  50. package/dist/mcp/server.js.map +1 -1
  51. package/dist/mcp/tools/analytics-handlers.d.ts +20 -0
  52. package/dist/mcp/tools/analytics-handlers.js +292 -0
  53. package/dist/mcp/tools/analytics-handlers.js.map +1 -0
  54. package/dist/mcp/tools/graph-handlers.d.ts +20 -0
  55. package/dist/mcp/tools/graph-handlers.js +126 -0
  56. package/dist/mcp/tools/graph-handlers.js.map +1 -0
  57. package/dist/mcp/tools/memory-handlers.d.ts +27 -0
  58. package/dist/mcp/tools/memory-handlers.js +177 -0
  59. package/dist/mcp/tools/memory-handlers.js.map +1 -0
  60. package/dist/mcp/tools/memory-tools.d.ts +19 -117
  61. package/dist/mcp/tools/memory-tools.js +19 -1049
  62. package/dist/mcp/tools/memory-tools.js.map +1 -1
  63. package/dist/mcp/tools/observation-handlers.d.ts +15 -0
  64. package/dist/mcp/tools/observation-handlers.js +74 -0
  65. package/dist/mcp/tools/observation-handlers.js.map +1 -0
  66. package/dist/mcp/tools/shared.d.ts +36 -0
  67. package/dist/mcp/tools/shared.js +71 -0
  68. package/dist/mcp/tools/shared.js.map +1 -0
  69. package/dist/mcp/tools/sun-handler.d.ts +12 -0
  70. package/dist/mcp/tools/sun-handler.js +35 -0
  71. package/dist/mcp/tools/sun-handler.js.map +1 -0
  72. package/dist/mcp/tools/system-handlers.d.ts +27 -0
  73. package/dist/mcp/tools/system-handlers.js +259 -0
  74. package/dist/mcp/tools/system-handlers.js.map +1 -0
  75. package/dist/mcp/tools/temporal-handlers.d.ts +15 -0
  76. package/dist/mcp/tools/temporal-handlers.js +82 -0
  77. package/dist/mcp/tools/temporal-handlers.js.map +1 -0
  78. package/dist/scanner/local/filesystem.d.ts +0 -2
  79. package/dist/scanner/local/filesystem.js +1 -5
  80. package/dist/scanner/local/filesystem.js.map +1 -1
  81. package/dist/scanner/local/git.d.ts +0 -5
  82. package/dist/scanner/local/git.js +1 -1
  83. package/dist/scanner/local/git.js.map +1 -1
  84. package/dist/scanner/metadata-scanner.d.ts +3 -14
  85. package/dist/scanner/metadata-scanner.js +10 -10
  86. package/dist/scanner/metadata-scanner.js.map +1 -1
  87. package/dist/service/scheduler.js +32 -13
  88. package/dist/service/scheduler.js.map +1 -1
  89. package/dist/storage/database.js +81 -2
  90. package/dist/storage/database.js.map +1 -1
  91. package/dist/storage/queries/analytics-queries.d.ts +59 -0
  92. package/dist/storage/queries/analytics-queries.js +265 -0
  93. package/dist/storage/queries/analytics-queries.js.map +1 -0
  94. package/dist/storage/queries/conflict-queries.d.ts +16 -0
  95. package/dist/storage/queries/conflict-queries.js +56 -0
  96. package/dist/storage/queries/conflict-queries.js.map +1 -0
  97. package/dist/storage/queries/constellation-queries.d.ts +18 -0
  98. package/dist/storage/queries/constellation-queries.js +101 -0
  99. package/dist/storage/queries/constellation-queries.js.map +1 -0
  100. package/dist/storage/queries/datasource-queries.d.ts +9 -0
  101. package/dist/storage/queries/datasource-queries.js +53 -0
  102. package/dist/storage/queries/datasource-queries.js.map +1 -0
  103. package/dist/storage/queries/memory-queries.d.ts +53 -0
  104. package/dist/storage/queries/memory-queries.js +415 -0
  105. package/dist/storage/queries/memory-queries.js.map +1 -0
  106. package/dist/storage/queries/observation-queries.d.ts +7 -0
  107. package/dist/storage/queries/observation-queries.js +23 -0
  108. package/dist/storage/queries/observation-queries.js.map +1 -0
  109. package/dist/storage/queries/orbit-queries.d.ts +7 -0
  110. package/dist/storage/queries/orbit-queries.js +23 -0
  111. package/dist/storage/queries/orbit-queries.js.map +1 -0
  112. package/dist/storage/queries/shared.d.ts +106 -0
  113. package/dist/storage/queries/shared.js +120 -0
  114. package/dist/storage/queries/shared.js.map +1 -0
  115. package/dist/storage/queries/sun-queries.d.ts +9 -0
  116. package/dist/storage/queries/sun-queries.js +43 -0
  117. package/dist/storage/queries/sun-queries.js.map +1 -0
  118. package/dist/storage/queries/temporal-queries.d.ts +32 -0
  119. package/dist/storage/queries/temporal-queries.js +112 -0
  120. package/dist/storage/queries/temporal-queries.js.map +1 -0
  121. package/dist/storage/queries.d.ts +15 -84
  122. package/dist/storage/queries.js +15 -896
  123. package/dist/storage/queries.js.map +1 -1
  124. package/dist/storage/vec.d.ts +5 -1
  125. package/dist/storage/vec.js +48 -24
  126. package/dist/storage/vec.js.map +1 -1
  127. package/dist/utils/config.d.ts +0 -1
  128. package/dist/utils/config.js +1 -1
  129. package/dist/utils/config.js.map +1 -1
  130. package/dist/utils/logger.d.ts +0 -2
  131. package/dist/utils/logger.js +0 -2
  132. package/dist/utils/logger.js.map +1 -1
  133. package/dist/utils/stopwords.d.ts +6 -0
  134. package/dist/utils/stopwords.js +22 -0
  135. package/dist/utils/stopwords.js.map +1 -0
  136. package/dist/utils/tokenizer.d.ts +2 -1
  137. package/dist/utils/tokenizer.js +7 -5
  138. package/dist/utils/tokenizer.js.map +1 -1
  139. package/package.json +78 -78
@@ -1,899 +1,18 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { getDatabase } from './database.js';
3
- import { ORBIT_ZONES } from '../engine/types.js';
4
- import { filterActiveMemories } from '../engine/validity.js';
5
- import { createLogger } from '../utils/logger.js';
6
- const log = createLogger('queries');
7
- // ---------------------------------------------------------------------------
8
- // Deserializers ??parse JSON fields coming out of SQLite
9
- // ---------------------------------------------------------------------------
10
- // Cast helpers ??node:sqlite returns Record<string, SQLOutputValue> from .get()/.all().
11
- // We cast through unknown because we know the schema guarantees the shape.
12
- function asRawMemory(row) {
13
- return row;
14
- }
15
- function asRawSunState(row) {
16
- return row;
17
- }
18
- function deserializeMemory(row) {
19
- return {
20
- ...row,
21
- type: row.type,
22
- tags: parseJsonArray(row.tags),
23
- metadata: parseJsonObject(row.metadata),
24
- source: row.source ?? 'manual',
25
- source_path: row.source_path ?? null,
26
- source_hash: row.source_hash ?? null,
27
- content_hash: row.content_hash ?? null,
28
- valid_from: row.valid_from ?? undefined,
29
- valid_until: row.valid_until ?? undefined,
30
- superseded_by: row.superseded_by ?? undefined,
31
- consolidated_into: row.consolidated_into ?? undefined,
32
- quality_score: row.quality_score ?? undefined,
33
- is_universal: row.is_universal ? Boolean(row.is_universal) : undefined,
34
- };
35
- }
36
- function deserializeConstellationEdge(row) {
37
- return {
38
- ...row,
39
- relation: row.relation,
40
- metadata: parseJsonObject(row.metadata),
41
- };
42
- }
43
- function deserializeConflict(row) {
44
- return {
45
- ...row,
46
- severity: row.severity,
47
- status: row.status,
48
- resolution: row.resolution ?? undefined,
49
- resolved_at: row.resolved_at ?? undefined,
50
- };
51
- }
52
- function deserializeObservation(row) {
53
- return {
54
- ...row,
55
- extracted_memories: parseJsonArray(row.extracted_memories),
56
- source: row.source,
57
- };
58
- }
59
- function asRawDataSource(row) {
60
- return row;
61
- }
62
- function deserializeDataSource(row) {
63
- return {
64
- ...row,
65
- type: row.type,
66
- status: row.status,
67
- config: parseJsonObject(row.config),
68
- };
69
- }
70
- function deserializeSunState(row) {
71
- return {
72
- ...row,
73
- recent_decisions: parseJsonArray(row.recent_decisions),
74
- next_steps: parseJsonArray(row.next_steps),
75
- active_errors: parseJsonArray(row.active_errors),
76
- };
77
- }
78
- function parseJsonArray(value) {
79
- if (typeof value !== 'string' || value === '')
80
- return [];
81
- try {
82
- const parsed = JSON.parse(value);
83
- return Array.isArray(parsed) ? parsed : [];
84
- }
85
- catch {
86
- log.warn('JSON array parse failed', { raw: String(value).slice(0, 100) });
87
- return [];
88
- }
89
- }
90
- function parseJsonObject(value) {
91
- if (typeof value !== 'string' || value === '')
92
- return {};
93
- try {
94
- const parsed = JSON.parse(value);
95
- return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
96
- ? parsed
97
- : {};
98
- }
99
- catch {
100
- log.warn('JSON object parse failed', { raw: String(value).slice(0, 100) });
101
- return {};
102
- }
103
- }
104
- // ---------------------------------------------------------------------------
105
- // Memory CRUD
106
- // ---------------------------------------------------------------------------
107
- export function insertMemory(memory) {
108
- const db = getDatabase();
109
- const now = new Date().toISOString();
110
- const id = memory.id ?? randomUUID();
111
- const project = memory.project ?? 'default';
112
- const content = memory.content ?? '';
113
- const summary = memory.summary ?? '';
114
- const type = memory.type ?? 'observation';
115
- const tags = JSON.stringify(memory.tags ?? []);
116
- const distance = memory.distance ?? 5.0;
117
- const importance = memory.importance ?? 0.5;
118
- const velocity = memory.velocity ?? 0.0;
119
- const impact = memory.impact ?? 0.5;
120
- const access_count = memory.access_count ?? 0;
121
- const last_accessed_at = memory.last_accessed_at ?? null;
122
- const metadata = JSON.stringify(memory.metadata ?? {});
123
- const source = memory.source ?? 'manual';
124
- const source_path = memory.source_path ?? null;
125
- const source_hash = memory.source_hash ?? null;
126
- const content_hash = memory.content_hash ?? null;
127
- const created_at = memory.created_at ?? now;
128
- const updated_at = memory.updated_at ?? now;
129
- const deleted_at = memory.deleted_at ?? null;
130
- const valid_from = memory.valid_from ?? null;
131
- const valid_until = memory.valid_until ?? null;
132
- const superseded_by = memory.superseded_by ?? null;
133
- const consolidated_into = memory.consolidated_into ?? null;
134
- const quality_score = memory.quality_score ?? null;
135
- const is_universal = memory.is_universal ? 1 : 0;
136
- db.prepare(`
137
- INSERT INTO memories (
138
- id, project, content, summary, type, tags,
139
- distance, importance, velocity, impact,
140
- access_count, last_accessed_at, metadata,
141
- source, source_path, source_hash, content_hash,
142
- created_at, updated_at, deleted_at,
143
- valid_from, valid_until, superseded_by,
144
- consolidated_into, quality_score, is_universal
145
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
146
- `).run(id, project, content, summary, type, tags, distance, importance, velocity, impact, access_count, last_accessed_at, metadata, source, source_path, source_hash, content_hash, created_at, updated_at, deleted_at, valid_from, valid_until, superseded_by, consolidated_into, quality_score, is_universal);
147
- // Return the fully resolved Memory object (no second DB hit needed)
148
- return {
149
- id, project, content, summary,
150
- type: type,
151
- tags: memory.tags ?? [],
152
- distance, importance, velocity, impact,
153
- access_count, last_accessed_at, metadata: memory.metadata ?? {},
154
- source, source_path, source_hash, content_hash,
155
- created_at, updated_at, deleted_at,
156
- valid_from: valid_from ?? undefined,
157
- valid_until: valid_until ?? undefined,
158
- superseded_by: superseded_by ?? undefined,
159
- consolidated_into: consolidated_into ?? undefined,
160
- quality_score: quality_score ?? undefined,
161
- is_universal: Boolean(is_universal),
162
- };
163
- }
164
- export function getMemoryById(id) {
165
- const db = getDatabase();
166
- const row = db.prepare(`
167
- SELECT * FROM memories WHERE id = ?
168
- `).get(id);
169
- return row ? deserializeMemory(asRawMemory(row)) : null;
170
- }
171
- export function getMemoryByIds(ids) {
172
- if (ids.length === 0)
173
- return [];
174
- const db = getDatabase();
175
- const placeholders = ids.map(() => '?').join(', ');
176
- const rows = db.prepare(`
177
- SELECT * FROM memories
178
- WHERE id IN (${placeholders})
179
- AND deleted_at IS NULL
180
- `).all(...ids);
181
- return filterActiveMemories(rows.map((r) => deserializeMemory(asRawMemory(r))));
182
- }
183
- export function getMemoriesByProject(project, includeDeleted = false) {
184
- const db = getDatabase();
185
- const sql = includeDeleted
186
- ? `SELECT * FROM memories WHERE project = ? ORDER BY distance ASC`
187
- : `SELECT * FROM memories WHERE project = ? AND deleted_at IS NULL ORDER BY distance ASC`;
188
- const rows = db.prepare(sql).all(project);
189
- return filterActiveMemories(rows.map((r) => deserializeMemory(asRawMemory(r))));
190
- }
191
1
  /**
192
- * Get memories created within the last `hoursAgo` hours for a project.
193
- * Used by auto-commit on shutdown to summarize the current session.
2
+ * storage/queries.ts Barrel re-export for backward compatibility.
3
+ *
4
+ * The entire codebase imports from '../storage/queries.js'.
5
+ * This file re-exports everything from the domain files under queries/
6
+ * so no existing import paths need to change.
194
7
  */
195
- export function getRecentMemories(project, hoursAgo = 3) {
196
- const db = getDatabase();
197
- const cutoff = new Date(Date.now() - hoursAgo * 60 * 60 * 1000).toISOString();
198
- const rows = db.prepare(`
199
- SELECT * FROM memories
200
- WHERE project = ?
201
- AND deleted_at IS NULL
202
- AND created_at > ?
203
- ORDER BY created_at DESC
204
- `).all(project, cutoff);
205
- return filterActiveMemories(rows.map((r) => deserializeMemory(asRawMemory(r))));
206
- }
207
- export function getMemoriesInZone(project, zone) {
208
- const db = getDatabase();
209
- const { min, max } = ORBIT_ZONES[zone];
210
- const rows = db.prepare(`
211
- SELECT * FROM memories
212
- WHERE project = ?
213
- AND deleted_at IS NULL
214
- AND distance >= ?
215
- AND distance < ?
216
- ORDER BY distance ASC
217
- `).all(project, min, max);
218
- return filterActiveMemories(rows.map((r) => deserializeMemory(asRawMemory(r))));
219
- }
220
- export function updateMemoryOrbit(id, distance, importance, velocity) {
221
- const db = getDatabase();
222
- const now = new Date().toISOString();
223
- db.prepare(`
224
- UPDATE memories
225
- SET distance = ?, importance = ?, velocity = ?, updated_at = ?
226
- WHERE id = ?
227
- `).run(distance, importance, velocity, now, id);
228
- }
229
- export function updateMemoryAccess(id) {
230
- const db = getDatabase();
231
- const now = new Date().toISOString();
232
- db.prepare(`
233
- UPDATE memories
234
- SET access_count = access_count + 1,
235
- last_accessed_at = ?,
236
- updated_at = ?
237
- WHERE id = ?
238
- `).run(now, now, id);
239
- }
240
- export function softDeleteMemory(id) {
241
- const db = getDatabase();
242
- const now = new Date().toISOString();
243
- db.prepare(`
244
- UPDATE memories
245
- SET deleted_at = ?, updated_at = ?
246
- WHERE id = ?
247
- `).run(now, now, id);
248
- }
249
- // ---------------------------------------------------------------------------
250
- // Full-text search (FTS5)
251
- // ---------------------------------------------------------------------------
252
- /**
253
- * Escape a user-supplied string for use in an FTS5 MATCH clause.
254
- * Wraps the entire query in double-quotes and escapes internal double-quotes
255
- * so it is treated as a literal phrase rather than FTS5 query syntax.
256
- */
257
- function escapeFtsQuery(query) {
258
- // Split into individual words, quote each one to escape FTS5 operators,
259
- // then join with spaces (implicit AND). This avoids phrase-matching issues
260
- // while still preventing FTS5 syntax errors from special characters.
261
- const words = query.trim().split(/\s+/).filter(w => w.length > 0);
262
- if (words.length === 0)
263
- return '""';
264
- return words.map(w => '"' + w.replace(/"/g, '""') + '"').join(' ');
265
- }
266
- export function searchMemories(project, query, limit = 20) {
267
- const db = getDatabase();
268
- const escapedQuery = escapeFtsQuery(query);
269
- // FTS5 MATCH uses its own query syntax; we join on rowid to get the full row
270
- const rows = db.prepare(`
271
- SELECT m.*
272
- FROM memories m
273
- JOIN memories_fts fts ON m.rowid = fts.rowid
274
- WHERE memories_fts MATCH ?
275
- AND m.project = ?
276
- AND m.deleted_at IS NULL
277
- AND m.superseded_by IS NULL
278
- AND (m.valid_from IS NULL OR m.valid_from <= datetime('now'))
279
- AND (m.valid_until IS NULL OR m.valid_until > datetime('now'))
280
- ORDER BY rank
281
- LIMIT ?
282
- `).all(escapedQuery, project, limit);
283
- return filterActiveMemories(rows.map((r) => deserializeMemory(asRawMemory(r))));
284
- }
285
- // ---------------------------------------------------------------------------
286
- // Distance-ranged FTS5 search (used by tiered recall pipeline)
287
- // ---------------------------------------------------------------------------
288
- export function searchMemoriesInRange(project, query, minDistance, maxDistance, limit) {
289
- const db = getDatabase();
290
- const escapedQuery = escapeFtsQuery(query);
291
- const rows = db.prepare(`
292
- SELECT m.*
293
- FROM memories m
294
- JOIN memories_fts fts ON m.rowid = fts.rowid
295
- WHERE memories_fts MATCH ?
296
- AND m.project = ?
297
- AND m.deleted_at IS NULL
298
- AND m.superseded_by IS NULL
299
- AND (m.valid_from IS NULL OR m.valid_from <= datetime('now'))
300
- AND (m.valid_until IS NULL OR m.valid_until > datetime('now'))
301
- AND m.distance >= ?
302
- AND m.distance < ?
303
- ORDER BY rank
304
- LIMIT ?
305
- `).all(escapedQuery, project, minDistance, maxDistance, limit);
306
- return filterActiveMemories(rows.map((r) => deserializeMemory(asRawMemory(r))));
307
- }
308
- // ---------------------------------------------------------------------------
309
- // Nearest memories (by orbital distance ??closest to the "sun" first)
310
- // ---------------------------------------------------------------------------
311
- export function getNearestMemories(project, limit) {
312
- const db = getDatabase();
313
- const rows = db.prepare(`
314
- SELECT * FROM memories
315
- WHERE project = ? AND deleted_at IS NULL
316
- ORDER BY distance ASC
317
- LIMIT ?
318
- `).all(project, limit);
319
- return rows.map((r) => deserializeMemory(asRawMemory(r)));
320
- }
321
- // ---------------------------------------------------------------------------
322
- // Sun state
323
- // ---------------------------------------------------------------------------
324
- export function getSunState(project) {
325
- const db = getDatabase();
326
- const row = db.prepare(`
327
- SELECT * FROM sun_state WHERE project = ?
328
- `).get(project);
329
- return row ? deserializeSunState(asRawSunState(row)) : null;
330
- }
331
- export function upsertSunState(state) {
332
- const db = getDatabase();
333
- const now = new Date().toISOString();
334
- // Fetch existing row so we can merge rather than blindly overwrite fields
335
- const existing = getSunState(state.project);
336
- const content = state.content ?? existing?.content ?? '';
337
- const current_work = state.current_work ?? existing?.current_work ?? '';
338
- const recent_decisions = JSON.stringify(state.recent_decisions ?? existing?.recent_decisions ?? []);
339
- const next_steps = JSON.stringify(state.next_steps ?? existing?.next_steps ?? []);
340
- const active_errors = JSON.stringify(state.active_errors ?? existing?.active_errors ?? []);
341
- const project_context = state.project_context ?? existing?.project_context ?? '';
342
- const token_count = state.token_count ?? existing?.token_count ?? 0;
343
- const last_commit_at = state.last_commit_at ?? existing?.last_commit_at ?? null;
344
- db.prepare(`
345
- INSERT INTO sun_state (
346
- project, content, current_work,
347
- recent_decisions, next_steps, active_errors,
348
- project_context, token_count, last_commit_at, updated_at
349
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
350
- ON CONFLICT(project) DO UPDATE SET
351
- content = excluded.content,
352
- current_work = excluded.current_work,
353
- recent_decisions = excluded.recent_decisions,
354
- next_steps = excluded.next_steps,
355
- active_errors = excluded.active_errors,
356
- project_context = excluded.project_context,
357
- token_count = excluded.token_count,
358
- last_commit_at = excluded.last_commit_at,
359
- updated_at = excluded.updated_at
360
- `).run(state.project, content, current_work, recent_decisions, next_steps, active_errors, project_context, token_count, last_commit_at, now);
361
- }
362
- // ---------------------------------------------------------------------------
363
- // Orbit log
364
- // ---------------------------------------------------------------------------
365
- export function insertOrbitLog(change) {
366
- const db = getDatabase();
367
- const now = new Date().toISOString();
368
- db.prepare(`
369
- INSERT INTO orbit_log (
370
- memory_id, project,
371
- old_distance, new_distance,
372
- old_importance, new_importance,
373
- trigger, created_at
374
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
375
- `).run(change.memory_id, change.project, change.old_distance, change.new_distance, change.old_importance, change.new_importance, change.trigger, now);
376
- }
377
- export function cleanupOrbitLog(retentionDays = 90) {
378
- const db = getDatabase();
379
- const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000).toISOString();
380
- const result = db.prepare('DELETE FROM orbit_log WHERE created_at < ?').run(cutoff);
381
- return Number(result.changes);
382
- }
383
- // ---------------------------------------------------------------------------
384
- // Source-path deduplication
385
- // ---------------------------------------------------------------------------
386
- /**
387
- * Check whether a memory already exists for the given source_path + source_hash.
388
- * Returns true if an identical (path, hash) pair is already stored and not deleted.
389
- */
390
- export function memoryExistsForSource(sourcePath, sourceHash) {
391
- const db = getDatabase();
392
- const row = db.prepare(`
393
- SELECT id FROM memories
394
- WHERE source_path = ? AND source_hash = ? AND deleted_at IS NULL
395
- LIMIT 1
396
- `).get(sourcePath, sourceHash);
397
- return row !== undefined;
398
- }
399
- /**
400
- * Find a memory by source_path (regardless of hash). Used to update stale entries.
401
- */
402
- export function getMemoryBySourcePath(sourcePath) {
403
- const db = getDatabase();
404
- const row = db.prepare(`
405
- SELECT * FROM memories
406
- WHERE source_path = ? AND deleted_at IS NULL
407
- LIMIT 1
408
- `).get(sourcePath);
409
- return row ? deserializeMemory(asRawMemory(row)) : null;
410
- }
411
- /**
412
- * Find a non-deleted memory in the given project that has the same content hash.
413
- * Used by createMemory() for content-level deduplication.
414
- */
415
- export function getMemoryByContentHash(project, contentHash) {
416
- const db = getDatabase();
417
- const row = db.prepare(`
418
- SELECT * FROM memories
419
- WHERE project = ? AND content_hash = ? AND deleted_at IS NULL
420
- LIMIT 1
421
- `).get(project, contentHash);
422
- return row ? deserializeMemory(asRawMemory(row)) : null;
423
- }
424
- // ---------------------------------------------------------------------------
425
- // Data sources CRUD
426
- // ---------------------------------------------------------------------------
427
- export function insertDataSource(ds) {
428
- const db = getDatabase();
429
- const now = new Date().toISOString();
430
- db.prepare(`
431
- INSERT INTO data_sources (id, path, type, status, last_scanned_at, file_count, total_size, config, created_at, updated_at)
432
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
433
- `).run(ds.id, ds.path, ds.type, ds.status, ds.last_scanned_at ?? null, ds.file_count ?? 0, ds.total_size ?? 0, JSON.stringify(ds.config ?? {}), now, now);
434
- return { ...ds, config: ds.config ?? {}, created_at: now, updated_at: now };
435
- }
436
- export function updateDataSource(id, patch) {
437
- const db = getDatabase();
438
- const now = new Date().toISOString();
439
- const sets = ['updated_at = ?'];
440
- const values = [now];
441
- if (patch.status !== undefined) {
442
- sets.push('status = ?');
443
- values.push(patch.status);
444
- }
445
- if (patch.last_scanned_at !== undefined) {
446
- sets.push('last_scanned_at = ?');
447
- values.push(patch.last_scanned_at);
448
- }
449
- if (patch.file_count !== undefined) {
450
- sets.push('file_count = ?');
451
- values.push(patch.file_count);
452
- }
453
- if (patch.total_size !== undefined) {
454
- sets.push('total_size = ?');
455
- values.push(patch.total_size);
456
- }
457
- if (patch.config !== undefined) {
458
- sets.push('config = ?');
459
- values.push(JSON.stringify(patch.config));
460
- }
461
- values.push(id);
462
- db.prepare(`UPDATE data_sources SET ${sets.join(', ')} WHERE id = ?`).run(...values);
463
- }
464
- export function getAllDataSources() {
465
- const db = getDatabase();
466
- const rows = db.prepare(`SELECT * FROM data_sources ORDER BY created_at DESC`).all();
467
- return rows.map((r) => deserializeDataSource(asRawDataSource(r)));
468
- }
469
- export function getDataSourceByPath(path) {
470
- const db = getDatabase();
471
- const row = db.prepare(`SELECT * FROM data_sources WHERE path = ? LIMIT 1`).get(path);
472
- return row ? deserializeDataSource(asRawDataSource(row)) : null;
473
- }
474
- // ---------------------------------------------------------------------------
475
- // Constellation queries (Knowledge Graph)
476
- // ---------------------------------------------------------------------------
477
- export function createEdge(edge) {
478
- const db = getDatabase();
479
- db.prepare(`
480
- INSERT INTO constellation_edges (id, source_id, target_id, relation, weight, project, metadata, created_at)
481
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
482
- ON CONFLICT(source_id, target_id, relation) DO UPDATE SET
483
- weight = excluded.weight,
484
- metadata = excluded.metadata
485
- `).run(edge.id, edge.source_id, edge.target_id, edge.relation, edge.weight, edge.project, JSON.stringify(edge.metadata ?? {}), edge.created_at);
486
- }
487
- export function getEdges(memoryId, project) {
488
- const db = getDatabase();
489
- const rows = db.prepare(`
490
- SELECT * FROM constellation_edges
491
- WHERE (source_id = ? OR target_id = ?) AND project = ?
492
- ORDER BY weight DESC
493
- `).all(memoryId, memoryId, project);
494
- return rows.map((r) => deserializeConstellationEdge(r));
495
- }
496
- export function getConstellation(memoryId, project, depth = 1) {
497
- const db = getDatabase();
498
- const visitedNodeIds = new Set([memoryId]);
499
- const allEdges = [];
500
- let frontier = [memoryId];
501
- for (let d = 0; d < depth; d++) {
502
- if (frontier.length === 0)
503
- break;
504
- const placeholders = frontier.map(() => '?').join(', ');
505
- const edgeRows = db.prepare(`
506
- SELECT * FROM constellation_edges
507
- WHERE (source_id IN (${placeholders}) OR target_id IN (${placeholders}))
508
- AND project = ?
509
- `).all(...frontier, ...frontier, project);
510
- for (const r of edgeRows) {
511
- const edge = deserializeConstellationEdge(r);
512
- allEdges.push(edge);
513
- visitedNodeIds.add(edge.source_id);
514
- visitedNodeIds.add(edge.target_id);
515
- }
516
- frontier = [...visitedNodeIds].filter((id) => !frontier.includes(id) && id !== memoryId);
517
- }
518
- const nodes = getMemoryByIds([...visitedNodeIds]);
519
- return { nodes, edges: allEdges };
520
- }
521
- /**
522
- * Get all constellation neighbors for a batch of memory IDs.
523
- * Returns a map from memory ID ??set of neighbor IDs.
524
- */
525
- export function getEdgesForBatch(memoryIds, project) {
526
- if (memoryIds.length === 0)
527
- return new Map();
528
- const db = getDatabase();
529
- const placeholders = memoryIds.map(() => '?').join(', ');
530
- const rows = db.prepare(`
531
- SELECT source_id, target_id FROM constellation_edges
532
- WHERE (source_id IN (${placeholders}) OR target_id IN (${placeholders}))
533
- AND project = ?
534
- `).all(...memoryIds, ...memoryIds, project);
535
- const idSet = new Set(memoryIds);
536
- const result = new Map();
537
- for (const r of rows) {
538
- const row = r;
539
- // For each memory in our batch, record its neighbor
540
- if (idSet.has(row.source_id)) {
541
- const neighbors = result.get(row.source_id) ?? new Set();
542
- neighbors.add(row.target_id);
543
- result.set(row.source_id, neighbors);
544
- }
545
- if (idSet.has(row.target_id)) {
546
- const neighbors = result.get(row.target_id) ?? new Set();
547
- neighbors.add(row.source_id);
548
- result.set(row.target_id, neighbors);
549
- }
550
- }
551
- return result;
552
- }
553
- export function deleteEdge(id) {
554
- const db = getDatabase();
555
- db.prepare(`DELETE FROM constellation_edges WHERE id = ?`).run(id);
556
- }
557
- export function getEdgeCountForMemory(memoryId) {
558
- const db = getDatabase();
559
- const row = db.prepare(`
560
- SELECT COUNT(*) as count FROM constellation_edges
561
- WHERE source_id = ? OR target_id = ?
562
- `).get(memoryId, memoryId);
563
- return row?.count ?? 0;
564
- }
565
- // ---------------------------------------------------------------------------
566
- // Conflict queries
567
- // ---------------------------------------------------------------------------
568
- export function createConflict(conflict) {
569
- const db = getDatabase();
570
- db.prepare(`
571
- INSERT INTO memory_conflicts (
572
- id, memory_id, conflicting_memory_id, severity,
573
- description, status, resolution, project, created_at, resolved_at
574
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
575
- `).run(conflict.id, conflict.memory_id, conflict.conflicting_memory_id, conflict.severity, conflict.description, conflict.status, conflict.resolution ?? null, conflict.project, conflict.created_at, conflict.resolved_at ?? null);
576
- }
577
- export function getConflicts(project, status) {
578
- const db = getDatabase();
579
- const rows = status
580
- ? db.prepare(`
581
- SELECT * FROM memory_conflicts
582
- WHERE project = ? AND status = ?
583
- ORDER BY created_at DESC
584
- `).all(project, status)
585
- : db.prepare(`
586
- SELECT * FROM memory_conflicts
587
- WHERE project = ?
588
- ORDER BY created_at DESC
589
- `).all(project);
590
- return rows.map((r) => deserializeConflict(r));
591
- }
592
- export function getConflictsForMemory(memoryId) {
593
- const db = getDatabase();
594
- const rows = db.prepare(`
595
- SELECT * FROM memory_conflicts
596
- WHERE memory_id = ? OR conflicting_memory_id = ?
597
- ORDER BY created_at DESC
598
- `).all(memoryId, memoryId);
599
- return rows.map((r) => deserializeConflict(r));
600
- }
601
- export function resolveConflict(id, resolution) {
602
- const db = getDatabase();
603
- const now = new Date().toISOString();
604
- db.prepare(`
605
- UPDATE memory_conflicts
606
- SET status = 'resolved', resolution = ?, resolved_at = ?
607
- WHERE id = ?
608
- `).run(resolution, now, id);
609
- }
610
- // ---------------------------------------------------------------------------
611
- // Observation queries
612
- // ---------------------------------------------------------------------------
613
- export function createObservation(entry) {
614
- const db = getDatabase();
615
- db.prepare(`
616
- INSERT INTO observation_log (id, content, extracted_memories, source, project, created_at)
617
- VALUES (?, ?, ?, ?, ?, ?)
618
- `).run(entry.id, entry.content, JSON.stringify(entry.extracted_memories), entry.source, entry.project, entry.created_at);
619
- }
620
- export function getObservations(project, limit = 20) {
621
- const db = getDatabase();
622
- const rows = db.prepare(`
623
- SELECT * FROM observation_log
624
- WHERE project = ?
625
- ORDER BY created_at DESC
626
- LIMIT ?
627
- `).all(project, limit);
628
- return rows.map((r) => deserializeObservation(r));
629
- }
630
- // ---------------------------------------------------------------------------
631
- // Temporal queries
632
- // ---------------------------------------------------------------------------
633
- export function getMemoriesAtTime(project, timestamp) {
634
- const db = getDatabase();
635
- const rows = db.prepare(`
636
- SELECT * FROM memories
637
- WHERE project = ?
638
- AND deleted_at IS NULL
639
- AND (valid_from IS NULL OR valid_from <= ?)
640
- AND (valid_until IS NULL OR valid_until > ?)
641
- ORDER BY distance ASC
642
- `).all(project, timestamp, timestamp);
643
- return rows.map((r) => deserializeMemory(asRawMemory(r)));
644
- }
645
- export function supersedMemory(memoryId, newMemoryId) {
646
- const db = getDatabase();
647
- const now = new Date().toISOString();
648
- db.prepare(`
649
- UPDATE memories
650
- SET superseded_by = ?, valid_until = ?, updated_at = ?
651
- WHERE id = ?
652
- `).run(newMemoryId, now, now, memoryId);
653
- }
654
- export function getSupersessionChain(memoryId) {
655
- const db = getDatabase();
656
- const chain = [];
657
- let currentId = memoryId;
658
- while (currentId) {
659
- const row = db.prepare(`SELECT * FROM memories WHERE id = ?`).get(currentId);
660
- if (!row)
661
- break;
662
- const mem = deserializeMemory(asRawMemory(row));
663
- chain.push(mem);
664
- currentId = mem.superseded_by ?? null;
665
- }
666
- return chain;
667
- }
668
- // ---------------------------------------------------------------------------
669
- // Consolidation queries
670
- // ---------------------------------------------------------------------------
671
- export function consolidateMemories(sourceIds, targetId) {
672
- if (sourceIds.length === 0)
673
- return;
674
- const db = getDatabase();
675
- const now = new Date().toISOString();
676
- const placeholders = sourceIds.map(() => '?').join(', ');
677
- db.prepare(`
678
- UPDATE memories
679
- SET consolidated_into = ?, updated_at = ?
680
- WHERE id IN (${placeholders})
681
- `).run(targetId, now, ...sourceIds);
682
- }
683
- export function getConsolidationHistory(memoryId) {
684
- const db = getDatabase();
685
- const rows = db.prepare(`
686
- SELECT * FROM memories
687
- WHERE consolidated_into = ?
688
- ORDER BY created_at ASC
689
- `).all(memoryId);
690
- return rows.map((r) => deserializeMemory(asRawMemory(r)));
691
- }
692
- // ---------------------------------------------------------------------------
693
- // Multi-project queries
694
- // ---------------------------------------------------------------------------
695
- export function getUniversalMemories(limit = 50) {
696
- const db = getDatabase();
697
- const rows = db.prepare(`
698
- SELECT * FROM memories
699
- WHERE is_universal = 1 AND deleted_at IS NULL
700
- ORDER BY importance DESC
701
- LIMIT ?
702
- `).all(limit);
703
- return rows.map((r) => deserializeMemory(asRawMemory(r)));
704
- }
705
- export function setUniversal(memoryId, isUniversal) {
706
- const db = getDatabase();
707
- const now = new Date().toISOString();
708
- db.prepare(`
709
- UPDATE memories SET is_universal = ?, updated_at = ? WHERE id = ?
710
- `).run(isUniversal ? 1 : 0, now, memoryId);
711
- }
712
- export function listProjects() {
713
- const db = getDatabase();
714
- const rows = db.prepare(`
715
- SELECT project, COUNT(*) as count
716
- FROM memories
717
- WHERE deleted_at IS NULL
718
- GROUP BY project
719
- ORDER BY count DESC
720
- `).all();
721
- return rows.map((r) => {
722
- const row = r;
723
- return { project: row.project, count: row.count };
724
- });
725
- }
726
- // ---------------------------------------------------------------------------
727
- // Quality queries
728
- // ---------------------------------------------------------------------------
729
- export function updateMemoryContent(id, content) {
730
- const db = getDatabase();
731
- const now = new Date().toISOString();
732
- db.prepare(`
733
- UPDATE memories SET content = ?, updated_at = ? WHERE id = ?
734
- `).run(content, now, id);
735
- }
736
- export function updateQualityScore(memoryId, score) {
737
- const db = getDatabase();
738
- const now = new Date().toISOString();
739
- db.prepare(`
740
- UPDATE memories SET quality_score = ?, updated_at = ? WHERE id = ?
741
- `).run(score, now, memoryId);
742
- }
743
- export function getMemoriesByQuality(project, minScore = 0.0, maxScore = 1.0) {
744
- const db = getDatabase();
745
- const rows = db.prepare(`
746
- SELECT * FROM memories
747
- WHERE project = ?
748
- AND deleted_at IS NULL
749
- AND quality_score >= ?
750
- AND quality_score <= ?
751
- ORDER BY quality_score DESC
752
- `).all(project, minScore, maxScore);
753
- return rows.map((r) => deserializeMemory(asRawMemory(r)));
754
- }
755
- // ---------------------------------------------------------------------------
756
- // Analytics queries
757
- // ---------------------------------------------------------------------------
758
- export function getTopTags(project, limit = 20) {
759
- const db = getDatabase();
760
- // Tags are stored as JSON arrays ??we use the memories table and parse in JS
761
- const rows = db.prepare(`
762
- SELECT tags FROM memories
763
- WHERE project = ? AND deleted_at IS NULL
764
- `).all(project);
765
- const tagCounts = new Map();
766
- for (const r of rows) {
767
- const row = r;
768
- const tags = parseJsonArray(row.tags);
769
- for (const tag of tags) {
770
- tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
771
- }
772
- }
773
- return [...tagCounts.entries()]
774
- .map(([tag, count]) => ({ tag, count }))
775
- .sort((a, b) => b.count - a.count)
776
- .slice(0, limit);
777
- }
778
- export function getActivityTimeline(project, days = 30) {
779
- const db = getDatabase();
780
- const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
781
- const createdRows = db.prepare(`
782
- SELECT date(created_at) as date, COUNT(*) as count
783
- FROM memories
784
- WHERE project = ? AND date(created_at) >= ?
785
- GROUP BY date(created_at)
786
- `).all(project, cutoff);
787
- const accessedRows = db.prepare(`
788
- SELECT date(last_accessed_at) as date, COUNT(*) as count
789
- FROM memories
790
- WHERE project = ?
791
- AND last_accessed_at IS NOT NULL
792
- AND date(last_accessed_at) >= ?
793
- GROUP BY date(last_accessed_at)
794
- `).all(project, cutoff);
795
- const timeline = new Map();
796
- for (const r of createdRows) {
797
- const row = r;
798
- const entry = timeline.get(row.date) ?? { created: 0, accessed: 0 };
799
- entry.created = row.count;
800
- timeline.set(row.date, entry);
801
- }
802
- for (const r of accessedRows) {
803
- const row = r;
804
- const entry = timeline.get(row.date) ?? { created: 0, accessed: 0 };
805
- entry.accessed = row.count;
806
- timeline.set(row.date, entry);
807
- }
808
- return [...timeline.entries()]
809
- .map(([date, counts]) => ({ date, ...counts }))
810
- .sort((a, b) => a.date.localeCompare(b.date));
811
- }
812
- export function getRecallSuccessRate(project) {
813
- const db = getDatabase();
814
- const result = db.prepare(`
815
- SELECT
816
- COUNT(*) as total,
817
- SUM(CASE WHEN access_count > 0 THEN 1 ELSE 0 END) as accessed
818
- FROM memories
819
- WHERE project = ? AND deleted_at IS NULL
820
- `).get(project);
821
- const row = result;
822
- if (!row || row.total === 0)
823
- return 0;
824
- return row.accessed / row.total;
825
- }
826
- export function getAnalytics(project) {
827
- const db = getDatabase();
828
- // Aggregate stats
829
- const statsRow = db.prepare(`
830
- SELECT
831
- COUNT(*) as total_memories,
832
- AVG(CASE WHEN quality_score IS NOT NULL THEN quality_score ELSE 0.5 END) as avg_quality,
833
- AVG(importance) as avg_importance,
834
- SUM(CASE WHEN consolidated_into IS NOT NULL THEN 1 ELSE 0 END) as consolidation_count
835
- FROM memories
836
- WHERE project = ? AND deleted_at IS NULL
837
- `).get(project);
838
- const stats = (statsRow ?? {});
839
- // Zone distribution
840
- const zoneRows = db.prepare(`
841
- SELECT
842
- CASE
843
- WHEN distance < 1.0 THEN 'core'
844
- WHEN distance < 5.0 THEN 'near'
845
- WHEN distance < 15.0 THEN 'active'
846
- WHEN distance < 40.0 THEN 'archive'
847
- WHEN distance < 70.0 THEN 'fading'
848
- ELSE 'forgotten'
849
- END as zone,
850
- COUNT(*) as count
851
- FROM memories
852
- WHERE project = ? AND deleted_at IS NULL
853
- GROUP BY zone
854
- `).all(project);
855
- const zone_distribution = {};
856
- for (const r of zoneRows) {
857
- const row = r;
858
- zone_distribution[row.zone] = row.count;
859
- }
860
- // Type distribution
861
- const typeRows = db.prepare(`
862
- SELECT type, COUNT(*) as count
863
- FROM memories
864
- WHERE project = ? AND deleted_at IS NULL
865
- GROUP BY type
866
- `).all(project);
867
- const type_distribution = {};
868
- for (const r of typeRows) {
869
- const row = r;
870
- type_distribution[row.type] = row.count;
871
- }
872
- // Conflict count
873
- const conflictRow = db.prepare(`
874
- SELECT COUNT(*) as count FROM memory_conflicts
875
- WHERE project = ? AND status = 'open'
876
- `).get(project);
877
- const conflict_count = (conflictRow?.count) ?? 0;
878
- // Activity timeline (last 30 days)
879
- const timelineRows = getActivityTimeline(project, 30);
880
- const activity_timeline = timelineRows.map((row) => ({
881
- date: row.date,
882
- created: row.created,
883
- accessed: row.accessed,
884
- forgotten: 0, // soft-delete count per day ??simplified to 0 here
885
- }));
886
- return {
887
- total_memories: stats.total_memories ?? 0,
888
- zone_distribution,
889
- type_distribution,
890
- avg_quality: stats.avg_quality ?? 0.5,
891
- avg_importance: stats.avg_importance ?? 0.5,
892
- recall_success_rate: getRecallSuccessRate(project),
893
- consolidation_count: stats.consolidation_count ?? 0,
894
- conflict_count,
895
- top_tags: getTopTags(project),
896
- activity_timeline,
897
- };
898
- }
8
+ export * from './queries/shared.js';
9
+ export * from './queries/memory-queries.js';
10
+ export * from './queries/sun-queries.js';
11
+ export * from './queries/orbit-queries.js';
12
+ export * from './queries/constellation-queries.js';
13
+ export * from './queries/conflict-queries.js';
14
+ export * from './queries/temporal-queries.js';
15
+ export * from './queries/analytics-queries.js';
16
+ export * from './queries/observation-queries.js';
17
+ export * from './queries/datasource-queries.js';
899
18
  //# sourceMappingURL=queries.js.map