squish-memory 0.7.3 → 0.8.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.
- package/dist/core/memory/consolidation.d.ts +51 -0
- package/dist/core/memory/consolidation.d.ts.map +1 -0
- package/dist/core/memory/consolidation.js +312 -0
- package/dist/core/memory/consolidation.js.map +1 -0
- package/dist/core/memory/hybrid-search.d.ts +25 -0
- package/dist/core/memory/hybrid-search.d.ts.map +1 -0
- package/dist/core/memory/hybrid-search.js +336 -0
- package/dist/core/memory/hybrid-search.js.map +1 -0
- package/dist/core/memory/importance.d.ts +68 -0
- package/dist/core/memory/importance.d.ts.map +1 -0
- package/dist/core/memory/importance.js +325 -0
- package/dist/core/memory/importance.js.map +1 -0
- package/dist/core/memory/memories.d.ts +1 -1
- package/dist/core/memory/memories.d.ts.map +1 -1
- package/dist/core/memory/memories.js +26 -2
- package/dist/core/memory/memories.js.map +1 -1
- package/dist/db/bootstrap.d.ts.map +1 -1
- package/dist/db/bootstrap.js +123 -56
- package/dist/db/bootstrap.js.map +1 -1
- package/dist/drizzle/schema-sqlite.d.ts.map +1 -1
- package/dist/drizzle/schema-sqlite.js +12 -0
- package/dist/drizzle/schema-sqlite.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +195 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Search - Combines BM25 keyword search with vector semantic search
|
|
3
|
+
* Uses Reciprocal Rank Fusion (RRF) for intelligent result merging
|
|
4
|
+
*
|
|
5
|
+
* Based on research showing 40-60% improvement over pure vector search:
|
|
6
|
+
* - BM25 excels at exact keyword matches
|
|
7
|
+
* - Vector search captures semantic similarity
|
|
8
|
+
* - RRF merges both without score calibration issues
|
|
9
|
+
*/
|
|
10
|
+
import { getDb } from '../../db/index.js';
|
|
11
|
+
import { createDatabaseClient } from '../../core/database.js';
|
|
12
|
+
import { getEmbedding } from '../../core/embeddings.js';
|
|
13
|
+
import { getProjectByPath } from '../../core/projects.js';
|
|
14
|
+
import { fromSqliteTags, normalizeTags } from './serialization.js';
|
|
15
|
+
import { isDatabaseUnavailableError } from '../../core/utils.js';
|
|
16
|
+
/**
|
|
17
|
+
* Reciprocal Rank Fusion (RRF) constant
|
|
18
|
+
* Higher values = more influence from lower-ranked items
|
|
19
|
+
* 60 is the standard value used in research
|
|
20
|
+
*/
|
|
21
|
+
const RRF_K = 60;
|
|
22
|
+
/**
|
|
23
|
+
* Main hybrid search function - combines BM25 and vector search with RRF
|
|
24
|
+
*/
|
|
25
|
+
export async function hybridSearch(input, options = {}) {
|
|
26
|
+
const limit = options.limit ?? input.limit ?? 10;
|
|
27
|
+
const bm25Weight = options.bm25Weight ?? 0.5;
|
|
28
|
+
const vectorWeight = options.vectorWeight ?? 0.5;
|
|
29
|
+
// Run both searches in parallel
|
|
30
|
+
const [bm25Results, vectorResults] = await Promise.all([
|
|
31
|
+
bm25Search(input, { ...options, limit: limit * 2 }),
|
|
32
|
+
vectorSearch(input, { ...options, limit: limit * 2 }),
|
|
33
|
+
]);
|
|
34
|
+
// Apply RRF to merge results
|
|
35
|
+
return reciprocalRankFusion(bm25Results, vectorResults, { limit, bm25Weight, vectorWeight });
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* BM25 keyword search using SQLite FTS5
|
|
39
|
+
*/
|
|
40
|
+
async function bm25Search(input, options) {
|
|
41
|
+
try {
|
|
42
|
+
const db = createDatabaseClient(await getDb());
|
|
43
|
+
const sqlite = db.$client;
|
|
44
|
+
const limit = options.limit ?? 10;
|
|
45
|
+
const tags = normalizeTags(options.tags ?? input.tags);
|
|
46
|
+
// Build FTS5 query with proper escaping
|
|
47
|
+
const ftsQuery = buildFtsQuery(input.query);
|
|
48
|
+
// Build WHERE conditions
|
|
49
|
+
const conditions = ['memories_fts MATCH ?'];
|
|
50
|
+
const params = [ftsQuery];
|
|
51
|
+
if (input.type) {
|
|
52
|
+
conditions.push('m.type = ?');
|
|
53
|
+
params.push(input.type);
|
|
54
|
+
}
|
|
55
|
+
if (tags.length) {
|
|
56
|
+
conditions.push('(' + tags.map(() => 'm.tags LIKE ?').join(' OR ') + ')');
|
|
57
|
+
params.push(...tags.map((tag) => `%${tag}%`));
|
|
58
|
+
}
|
|
59
|
+
let projectId = null;
|
|
60
|
+
if (input.project) {
|
|
61
|
+
const project = await getProjectByPath(input.project);
|
|
62
|
+
if (project) {
|
|
63
|
+
projectId = project.id;
|
|
64
|
+
conditions.push('m.project_id = ?');
|
|
65
|
+
params.push(project.id);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const whereClause = conditions.join(' AND ');
|
|
69
|
+
// FTS5 search with BM25 ranking
|
|
70
|
+
// Note: SQLite doesn't allow rowid access in subquery, so we use direct join
|
|
71
|
+
const statement = sqlite.prepare(`
|
|
72
|
+
SELECT
|
|
73
|
+
m.id as id,
|
|
74
|
+
m.project_id as projectId,
|
|
75
|
+
m.type as type,
|
|
76
|
+
m.content as content,
|
|
77
|
+
m.summary as summary,
|
|
78
|
+
m.tags as tags,
|
|
79
|
+
m.metadata as metadata,
|
|
80
|
+
bm25(memories_fts) as bm25Score,
|
|
81
|
+
m.created_at as createdAt
|
|
82
|
+
FROM memories m
|
|
83
|
+
INNER JOIN memories_fts ON m.rowid = memories_fts.rowid
|
|
84
|
+
WHERE ${whereClause}
|
|
85
|
+
ORDER BY bm25(memories_fts)
|
|
86
|
+
LIMIT ?
|
|
87
|
+
`);
|
|
88
|
+
const rows = statement.all(...params, limit * 3);
|
|
89
|
+
// Return as ranked results (lower BM25 score = better rank)
|
|
90
|
+
return rows.map((row, index) => ({
|
|
91
|
+
id: row.id,
|
|
92
|
+
rank: index + 1,
|
|
93
|
+
score: row.bm25Score,
|
|
94
|
+
result: {
|
|
95
|
+
id: row.id,
|
|
96
|
+
projectId: row.projectId,
|
|
97
|
+
type: row.type,
|
|
98
|
+
content: row.content,
|
|
99
|
+
summary: row.summary ?? undefined,
|
|
100
|
+
tags: fromSqliteTags(row.tags ?? null),
|
|
101
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : null,
|
|
102
|
+
createdAt: row.createdAt ? new Date((Number(row.createdAt) || 0) * 1000).toISOString() : undefined,
|
|
103
|
+
},
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (isDatabaseUnavailableError(error)) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Vector semantic search using cosine similarity
|
|
115
|
+
*/
|
|
116
|
+
async function vectorSearch(input, options) {
|
|
117
|
+
try {
|
|
118
|
+
const db = createDatabaseClient(await getDb());
|
|
119
|
+
const sqlite = db.$client;
|
|
120
|
+
const limit = options.limit ?? 10;
|
|
121
|
+
const tags = normalizeTags(options.tags ?? input.tags);
|
|
122
|
+
// Get query embedding
|
|
123
|
+
const queryEmbedding = await getEmbedding(input.query);
|
|
124
|
+
if (!queryEmbedding) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
// Build WHERE conditions
|
|
128
|
+
const conditions = [];
|
|
129
|
+
const params = [];
|
|
130
|
+
if (input.type) {
|
|
131
|
+
conditions.push('m.type = ?');
|
|
132
|
+
params.push(input.type);
|
|
133
|
+
}
|
|
134
|
+
if (tags.length) {
|
|
135
|
+
conditions.push('(' + tags.map(() => 'm.tags LIKE ?').join(' OR ') + ')');
|
|
136
|
+
params.push(...tags.map((tag) => `%${tag}%`));
|
|
137
|
+
}
|
|
138
|
+
let projectId = null;
|
|
139
|
+
if (input.project) {
|
|
140
|
+
const project = await getProjectByPath(input.project);
|
|
141
|
+
if (project) {
|
|
142
|
+
projectId = project.id;
|
|
143
|
+
conditions.push('m.project_id = ?');
|
|
144
|
+
params.push(project.id);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const whereClause = conditions.length > 0
|
|
148
|
+
? 'WHERE ' + conditions.join(' AND ')
|
|
149
|
+
: '';
|
|
150
|
+
// Fetch candidates for vector search
|
|
151
|
+
const statement = sqlite.prepare(`
|
|
152
|
+
SELECT
|
|
153
|
+
m.id as id,
|
|
154
|
+
m.project_id as projectId,
|
|
155
|
+
m.type as type,
|
|
156
|
+
m.content as content,
|
|
157
|
+
m.summary as summary,
|
|
158
|
+
m.tags as tags,
|
|
159
|
+
m.metadata as metadata,
|
|
160
|
+
m.embedding as embedding,
|
|
161
|
+
m.embedding_json as embeddingJson,
|
|
162
|
+
m.created_at as createdAt
|
|
163
|
+
FROM memories m
|
|
164
|
+
${whereClause}
|
|
165
|
+
ORDER BY m.created_at DESC
|
|
166
|
+
LIMIT ?
|
|
167
|
+
`);
|
|
168
|
+
const rows = statement.all(...params, limit * 3);
|
|
169
|
+
// Calculate cosine similarity for each result
|
|
170
|
+
const scored = rows
|
|
171
|
+
.map((row) => {
|
|
172
|
+
const embedding = parseEmbedding(row.embedding) ?? parseEmbedding(row.embeddingJson);
|
|
173
|
+
if (!embedding)
|
|
174
|
+
return null;
|
|
175
|
+
const similarity = cosineSimilarity(queryEmbedding, embedding);
|
|
176
|
+
return {
|
|
177
|
+
id: row.id,
|
|
178
|
+
projectId: row.projectId,
|
|
179
|
+
type: row.type,
|
|
180
|
+
content: row.content,
|
|
181
|
+
summary: row.summary,
|
|
182
|
+
tags: row.tags,
|
|
183
|
+
metadata: row.metadata,
|
|
184
|
+
createdAt: row.createdAt,
|
|
185
|
+
similarity,
|
|
186
|
+
};
|
|
187
|
+
})
|
|
188
|
+
.filter((item) => item !== null);
|
|
189
|
+
// Sort by similarity (descending) and return as ranked results
|
|
190
|
+
scored.sort((a, b) => b.similarity - a.similarity);
|
|
191
|
+
return scored.slice(0, limit * 2).map((item, index) => ({
|
|
192
|
+
id: item.id,
|
|
193
|
+
rank: index + 1,
|
|
194
|
+
score: item.similarity,
|
|
195
|
+
result: {
|
|
196
|
+
id: item.id,
|
|
197
|
+
projectId: item.projectId,
|
|
198
|
+
type: item.type,
|
|
199
|
+
content: item.content,
|
|
200
|
+
summary: item.summary ?? undefined,
|
|
201
|
+
tags: fromSqliteTags(item.tags ?? null),
|
|
202
|
+
metadata: item.metadata ? JSON.parse(item.metadata) : null,
|
|
203
|
+
createdAt: item.createdAt ? new Date((Number(item.createdAt) || 0) * 1000).toISOString() : undefined,
|
|
204
|
+
similarity: item.similarity,
|
|
205
|
+
},
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
if (isDatabaseUnavailableError(error)) {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Reciprocal Rank Fusion (RRF) - merges ranked lists without score calibration
|
|
217
|
+
*
|
|
218
|
+
* RRF Formula: score(item) = sum(weight_i / (k + rank_i))
|
|
219
|
+
* Where k is a constant (typically 60) that prevents high ranks from dominating
|
|
220
|
+
*
|
|
221
|
+
* Benefits:
|
|
222
|
+
* - No need to calibrate different scoring systems (BM25 vs cosine similarity)
|
|
223
|
+
* - Handles items that appear in only one list
|
|
224
|
+
* - Proven to outperform weighted score fusion in most cases
|
|
225
|
+
*/
|
|
226
|
+
function reciprocalRankFusion(bm25Results, vectorResults, options) {
|
|
227
|
+
const { limit, bm25Weight, vectorWeight } = options;
|
|
228
|
+
const scores = new Map();
|
|
229
|
+
// Process BM25 results
|
|
230
|
+
for (const item of bm25Results) {
|
|
231
|
+
const rrfScore = (bm25Weight * 2) / (RRF_K + item.rank);
|
|
232
|
+
const existing = scores.get(item.id);
|
|
233
|
+
if (existing) {
|
|
234
|
+
existing.score += rrfScore;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
scores.set(item.id, { score: rrfScore, result: item.result });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Process vector results
|
|
241
|
+
for (const item of vectorResults) {
|
|
242
|
+
const rrfScore = (vectorWeight * 2) / (RRF_K + item.rank);
|
|
243
|
+
const existing = scores.get(item.id);
|
|
244
|
+
if (existing) {
|
|
245
|
+
existing.score += rrfScore;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
scores.set(item.id, { score: rrfScore, result: item.result });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Sort by RRF score (descending) and return top results
|
|
252
|
+
return Array.from(scores.values())
|
|
253
|
+
.sort((a, b) => b.score - a.score)
|
|
254
|
+
.slice(0, limit)
|
|
255
|
+
.map((item) => ({
|
|
256
|
+
...item.result,
|
|
257
|
+
similarity: item.result.similarity ?? 0,
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Build FTS5 query string from user input
|
|
262
|
+
* Handles phrase searches, AND/OR operators, and special characters
|
|
263
|
+
*/
|
|
264
|
+
function buildFtsQuery(query) {
|
|
265
|
+
// Remove special characters that could break FTS5 syntax
|
|
266
|
+
let cleaned = query.replace(/[^\w\s"'-]/g, ' ');
|
|
267
|
+
// If query contains quotes, preserve as phrase search
|
|
268
|
+
if (cleaned.includes('"')) {
|
|
269
|
+
return cleaned;
|
|
270
|
+
}
|
|
271
|
+
// Split into terms and join with AND for better precision
|
|
272
|
+
const terms = cleaned.trim().split(/\s+/).filter((t) => t.length > 0);
|
|
273
|
+
if (terms.length === 0) {
|
|
274
|
+
return '""'; // Empty query
|
|
275
|
+
}
|
|
276
|
+
// For multi-word queries, use NEAR operator for proximity search
|
|
277
|
+
if (terms.length > 1) {
|
|
278
|
+
return terms.join(' ');
|
|
279
|
+
}
|
|
280
|
+
return terms[0];
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Parse embedding from SQLite storage
|
|
284
|
+
*/
|
|
285
|
+
function parseEmbedding(embeddingData) {
|
|
286
|
+
if (!embeddingData)
|
|
287
|
+
return null;
|
|
288
|
+
if (Array.isArray(embeddingData))
|
|
289
|
+
return embeddingData;
|
|
290
|
+
if (embeddingData instanceof Uint8Array || Buffer.isBuffer(embeddingData)) {
|
|
291
|
+
try {
|
|
292
|
+
const json = JSON.parse(embeddingData.toString());
|
|
293
|
+
if (Array.isArray(json))
|
|
294
|
+
return json;
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
try {
|
|
298
|
+
const floatArray = new Float32Array(embeddingData.buffer || embeddingData);
|
|
299
|
+
return Array.from(floatArray);
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (typeof embeddingData === 'string') {
|
|
307
|
+
try {
|
|
308
|
+
const parsed = JSON.parse(embeddingData);
|
|
309
|
+
if (Array.isArray(parsed))
|
|
310
|
+
return parsed;
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Calculate cosine similarity between two vectors
|
|
320
|
+
*/
|
|
321
|
+
function cosineSimilarity(a, b) {
|
|
322
|
+
if (a.length !== b.length)
|
|
323
|
+
return 0;
|
|
324
|
+
let dotProduct = 0;
|
|
325
|
+
let normA = 0;
|
|
326
|
+
let normB = 0;
|
|
327
|
+
for (let i = 0; i < a.length; i++) {
|
|
328
|
+
dotProduct += a[i] * b[i];
|
|
329
|
+
normA += a[i] * a[i];
|
|
330
|
+
normB += b[i] * b[i];
|
|
331
|
+
}
|
|
332
|
+
if (normA === 0 || normB === 0)
|
|
333
|
+
return 0;
|
|
334
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=hybrid-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hybrid-search.js","sourceRoot":"","sources":["../../../core/memory/hybrid-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Importance Scoring System
|
|
3
|
+
*
|
|
4
|
+
* Calculates and manages memory importance scores (0-100) with temporal decay.
|
|
5
|
+
* Based on research from Mnemosyne architecture and ReMe (Memory Replay).
|
|
6
|
+
*
|
|
7
|
+
* Scoring factors:
|
|
8
|
+
* - Base score: 50 (neutral)
|
|
9
|
+
* - Recency boost: decays over time (exponential decay)
|
|
10
|
+
* - Access frequency: more frequently accessed = higher importance
|
|
11
|
+
* - Type weighting: decisions > facts > preferences > context > observations
|
|
12
|
+
* - User flags: pinned/protected memories get maximum importance
|
|
13
|
+
*/
|
|
14
|
+
import type { Memory } from '../../drizzle/schema.js';
|
|
15
|
+
export interface ImportanceScore {
|
|
16
|
+
score: number;
|
|
17
|
+
components: {
|
|
18
|
+
base: number;
|
|
19
|
+
recency: number;
|
|
20
|
+
accessFrequency: number;
|
|
21
|
+
typeWeight: number;
|
|
22
|
+
userFlags: number;
|
|
23
|
+
};
|
|
24
|
+
explanation: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Calculate importance score for a memory
|
|
28
|
+
*
|
|
29
|
+
* Formula: base + recency + accessFrequency + typeWeight + userFlags
|
|
30
|
+
* All values are clamped to 0-100 range
|
|
31
|
+
*/
|
|
32
|
+
export declare function calculateImportance(memory: Partial<Memory>): ImportanceScore;
|
|
33
|
+
/**
|
|
34
|
+
* Update importance score for a memory
|
|
35
|
+
* Used when memory is accessed or modified
|
|
36
|
+
*/
|
|
37
|
+
export declare function updateImportanceScore(memoryId: string, incrementAccess?: boolean): Promise<number>;
|
|
38
|
+
/**
|
|
39
|
+
* Decay importance scores for old memories
|
|
40
|
+
* Should be run periodically (e.g., daily) to reduce scores of stale memories
|
|
41
|
+
*
|
|
42
|
+
* This function applies exponential decay to all memories that haven't been
|
|
43
|
+
* recalculated recently, keeping the system's importance scores current.
|
|
44
|
+
*/
|
|
45
|
+
export declare function decayImportanceScores(projectId?: string): Promise<number>;
|
|
46
|
+
/**
|
|
47
|
+
* Get low-importance memories that are candidates for consolidation
|
|
48
|
+
* These are old, rarely accessed memories with low importance scores
|
|
49
|
+
*/
|
|
50
|
+
export declare function getLowImportanceMemories(projectId: string, options?: {
|
|
51
|
+
minAge?: number;
|
|
52
|
+
maxImportance?: number;
|
|
53
|
+
limit?: number;
|
|
54
|
+
}): Promise<any[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Set importance score manually (for user override)
|
|
57
|
+
*/
|
|
58
|
+
export declare function setImportanceScore(memoryId: string, score: number): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Pin a memory to prevent decay and consolidation
|
|
61
|
+
*/
|
|
62
|
+
export declare function pinMemory(memoryId: string, pinned?: boolean): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Calculate cosine similarity between two vectors
|
|
65
|
+
* Re-exported for use in consolidation
|
|
66
|
+
*/
|
|
67
|
+
export declare function cosineSimilarity(vecA: number[], vecB: number[]): number;
|
|
68
|
+
//# sourceMappingURL=importance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"importance.d.ts","sourceRoot":"","sources":["../../../core/memory/importance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAMtD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;CACrB;AAcD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,eAAe,CA2B5E;AAgHD;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,eAAe,GAAE,OAAe,GAC/B,OAAO,CAAC,MAAM,CAAC,CAsCjB;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgD/E;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,GACL,OAAO,CAAC,GAAG,EAAE,CAAC,CA0ChB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,OAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAavF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAuBvE"}
|