@agentlensai/server 0.3.0 → 0.6.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/db/embedding-store.d.ts +74 -0
- package/dist/db/embedding-store.d.ts.map +1 -0
- package/dist/db/embedding-store.js +177 -0
- package/dist/db/embedding-store.js.map +1 -0
- package/dist/db/health-snapshot-store.d.ts +33 -0
- package/dist/db/health-snapshot-store.d.ts.map +1 -0
- package/dist/db/health-snapshot-store.js +112 -0
- package/dist/db/health-snapshot-store.js.map +1 -0
- package/dist/db/lesson-store.d.ts +57 -0
- package/dist/db/lesson-store.d.ts.map +1 -0
- package/dist/db/lesson-store.js +217 -0
- package/dist/db/lesson-store.js.map +1 -0
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +256 -8
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/schema.sqlite.d.ts +822 -47
- package/dist/db/schema.sqlite.d.ts.map +1 -1
- package/dist/db/schema.sqlite.js +79 -4
- package/dist/db/schema.sqlite.js.map +1 -1
- package/dist/db/session-summary-store.d.ts +45 -0
- package/dist/db/session-summary-store.d.ts.map +1 -0
- package/dist/db/session-summary-store.js +112 -0
- package/dist/db/session-summary-store.js.map +1 -0
- package/dist/db/sqlite-store.d.ts +19 -12
- package/dist/db/sqlite-store.d.ts.map +1 -1
- package/dist/db/sqlite-store.js +145 -44
- package/dist/db/sqlite-store.js.map +1 -1
- package/dist/db/tenant-scoped-store.d.ts +61 -0
- package/dist/db/tenant-scoped-store.d.ts.map +1 -0
- package/dist/db/tenant-scoped-store.js +94 -0
- package/dist/db/tenant-scoped-store.js.map +1 -0
- package/dist/index.d.ts +18 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +78 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/alert-engine.d.ts +11 -0
- package/dist/lib/alert-engine.d.ts.map +1 -1
- package/dist/lib/alert-engine.js +65 -24
- package/dist/lib/alert-engine.js.map +1 -1
- package/dist/lib/analysis/cost-analysis.d.ts +20 -0
- package/dist/lib/analysis/cost-analysis.d.ts.map +1 -0
- package/dist/lib/analysis/cost-analysis.js +161 -0
- package/dist/lib/analysis/cost-analysis.js.map +1 -0
- package/dist/lib/analysis/error-patterns.d.ts +26 -0
- package/dist/lib/analysis/error-patterns.d.ts.map +1 -0
- package/dist/lib/analysis/error-patterns.js +158 -0
- package/dist/lib/analysis/error-patterns.js.map +1 -0
- package/dist/lib/analysis/index.d.ts +23 -0
- package/dist/lib/analysis/index.d.ts.map +1 -0
- package/dist/lib/analysis/index.js +144 -0
- package/dist/lib/analysis/index.js.map +1 -0
- package/dist/lib/analysis/performance-trends.d.ts +19 -0
- package/dist/lib/analysis/performance-trends.d.ts.map +1 -0
- package/dist/lib/analysis/performance-trends.js +121 -0
- package/dist/lib/analysis/performance-trends.js.map +1 -0
- package/dist/lib/analysis/tool-sequences.d.ts +19 -0
- package/dist/lib/analysis/tool-sequences.d.ts.map +1 -0
- package/dist/lib/analysis/tool-sequences.js +148 -0
- package/dist/lib/analysis/tool-sequences.js.map +1 -0
- package/dist/lib/context/index.d.ts +5 -0
- package/dist/lib/context/index.d.ts.map +1 -0
- package/dist/lib/context/index.js +5 -0
- package/dist/lib/context/index.js.map +1 -0
- package/dist/lib/context/retrieval.d.ts +60 -0
- package/dist/lib/context/retrieval.d.ts.map +1 -0
- package/dist/lib/context/retrieval.js +233 -0
- package/dist/lib/context/retrieval.js.map +1 -0
- package/dist/lib/embeddings/index.d.ts +31 -0
- package/dist/lib/embeddings/index.d.ts.map +1 -0
- package/dist/lib/embeddings/index.js +31 -0
- package/dist/lib/embeddings/index.js.map +1 -0
- package/dist/lib/embeddings/local.d.ts +15 -0
- package/dist/lib/embeddings/local.d.ts.map +1 -0
- package/dist/lib/embeddings/local.js +65 -0
- package/dist/lib/embeddings/local.js.map +1 -0
- package/dist/lib/embeddings/math.d.ts +13 -0
- package/dist/lib/embeddings/math.d.ts.map +1 -0
- package/dist/lib/embeddings/math.js +35 -0
- package/dist/lib/embeddings/math.js.map +1 -0
- package/dist/lib/embeddings/openai.d.ts +15 -0
- package/dist/lib/embeddings/openai.d.ts.map +1 -0
- package/dist/lib/embeddings/openai.js +58 -0
- package/dist/lib/embeddings/openai.js.map +1 -0
- package/dist/lib/embeddings/summarizer.d.ts +26 -0
- package/dist/lib/embeddings/summarizer.d.ts.map +1 -0
- package/dist/lib/embeddings/summarizer.js +181 -0
- package/dist/lib/embeddings/summarizer.js.map +1 -0
- package/dist/lib/embeddings/types.d.ts +17 -0
- package/dist/lib/embeddings/types.d.ts.map +1 -0
- package/dist/lib/embeddings/types.js +5 -0
- package/dist/lib/embeddings/types.js.map +1 -0
- package/dist/lib/embeddings/worker.d.ts +56 -0
- package/dist/lib/embeddings/worker.d.ts.map +1 -0
- package/dist/lib/embeddings/worker.js +109 -0
- package/dist/lib/embeddings/worker.js.map +1 -0
- package/dist/lib/health/computer.d.ts +28 -0
- package/dist/lib/health/computer.d.ts.map +1 -0
- package/dist/lib/health/computer.js +270 -0
- package/dist/lib/health/computer.js.map +1 -0
- package/dist/lib/optimization/classifier.d.ts +34 -0
- package/dist/lib/optimization/classifier.d.ts.map +1 -0
- package/dist/lib/optimization/classifier.js +108 -0
- package/dist/lib/optimization/classifier.js.map +1 -0
- package/dist/lib/optimization/engine.d.ts +24 -0
- package/dist/lib/optimization/engine.d.ts.map +1 -0
- package/dist/lib/optimization/engine.js +202 -0
- package/dist/lib/optimization/engine.js.map +1 -0
- package/dist/lib/optimization/index.d.ts +10 -0
- package/dist/lib/optimization/index.d.ts.map +1 -0
- package/dist/lib/optimization/index.js +9 -0
- package/dist/lib/optimization/index.js.map +1 -0
- package/dist/lib/sse.d.ts +1 -0
- package/dist/lib/sse.d.ts.map +1 -1
- package/dist/lib/sse.js +8 -1
- package/dist/lib/sse.js.map +1 -1
- package/dist/middleware/auth.d.ts +1 -0
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +2 -1
- package/dist/middleware/auth.js.map +1 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +6 -3
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/alerts.d.ts.map +1 -1
- package/dist/routes/alerts.js +15 -7
- package/dist/routes/alerts.js.map +1 -1
- package/dist/routes/analytics.d.ts.map +1 -1
- package/dist/routes/analytics.js +16 -2
- package/dist/routes/analytics.js.map +1 -1
- package/dist/routes/api-keys.d.ts.map +1 -1
- package/dist/routes/api-keys.js +30 -5
- package/dist/routes/api-keys.js.map +1 -1
- package/dist/routes/context.d.ts +23 -0
- package/dist/routes/context.d.ts.map +1 -0
- package/dist/routes/context.js +52 -0
- package/dist/routes/context.js.map +1 -0
- package/dist/routes/events.d.ts +6 -1
- package/dist/routes/events.d.ts.map +1 -1
- package/dist/routes/events.js +61 -6
- package/dist/routes/events.js.map +1 -1
- package/dist/routes/health.d.ts +21 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +142 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/ingest.d.ts +6 -0
- package/dist/routes/ingest.d.ts.map +1 -1
- package/dist/routes/ingest.js +23 -4
- package/dist/routes/ingest.js.map +1 -1
- package/dist/routes/lessons.d.ts +19 -0
- package/dist/routes/lessons.d.ts.map +1 -0
- package/dist/routes/lessons.js +164 -0
- package/dist/routes/lessons.js.map +1 -0
- package/dist/routes/optimize.d.ts +15 -0
- package/dist/routes/optimize.d.ts.map +1 -0
- package/dist/routes/optimize.js +55 -0
- package/dist/routes/optimize.js.map +1 -0
- package/dist/routes/recall.d.ts +22 -0
- package/dist/routes/recall.d.ts.map +1 -0
- package/dist/routes/recall.js +91 -0
- package/dist/routes/recall.js.map +1 -0
- package/dist/routes/reflect.d.ts +15 -0
- package/dist/routes/reflect.d.ts.map +1 -0
- package/dist/routes/reflect.js +54 -0
- package/dist/routes/reflect.js.map +1 -0
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +8 -6
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/stats.d.ts.map +1 -1
- package/dist/routes/stats.js +3 -1
- package/dist/routes/stats.js.map +1 -1
- package/dist/routes/stream.d.ts +9 -2
- package/dist/routes/stream.d.ts.map +1 -1
- package/dist/routes/stream.js +55 -2
- package/dist/routes/stream.js.map +1 -1
- package/dist/routes/tenant-helper.d.ts +20 -0
- package/dist/routes/tenant-helper.d.ts.map +1 -0
- package/dist/routes/tenant-helper.js +35 -0
- package/dist/routes/tenant-helper.js.map +1 -0
- package/package.json +11 -11
- package/LICENSE +0 -21
- package/dist/__tests__/agents-stats.test.d.ts +0 -5
- package/dist/__tests__/agents-stats.test.d.ts.map +0 -1
- package/dist/__tests__/agents-stats.test.js +0 -134
- package/dist/__tests__/agents-stats.test.js.map +0 -1
- package/dist/__tests__/alerts.test.d.ts +0 -5
- package/dist/__tests__/alerts.test.d.ts.map +0 -1
- package/dist/__tests__/alerts.test.js +0 -245
- package/dist/__tests__/alerts.test.js.map +0 -1
- package/dist/__tests__/analytics.test.d.ts +0 -5
- package/dist/__tests__/analytics.test.d.ts.map +0 -1
- package/dist/__tests__/analytics.test.js +0 -218
- package/dist/__tests__/analytics.test.js.map +0 -1
- package/dist/__tests__/api-keys.test.d.ts +0 -5
- package/dist/__tests__/api-keys.test.d.ts.map +0 -1
- package/dist/__tests__/api-keys.test.js +0 -118
- package/dist/__tests__/api-keys.test.js.map +0 -1
- package/dist/__tests__/auth-no-db.test.d.ts +0 -2
- package/dist/__tests__/auth-no-db.test.d.ts.map +0 -1
- package/dist/__tests__/auth-no-db.test.js +0 -43
- package/dist/__tests__/auth-no-db.test.js.map +0 -1
- package/dist/__tests__/auth.test.d.ts +0 -5
- package/dist/__tests__/auth.test.d.ts.map +0 -1
- package/dist/__tests__/auth.test.js +0 -86
- package/dist/__tests__/auth.test.js.map +0 -1
- package/dist/__tests__/config.test.d.ts +0 -2
- package/dist/__tests__/config.test.d.ts.map +0 -1
- package/dist/__tests__/config.test.js +0 -37
- package/dist/__tests__/config.test.js.map +0 -1
- package/dist/__tests__/events-ingest.test.d.ts +0 -5
- package/dist/__tests__/events-ingest.test.d.ts.map +0 -1
- package/dist/__tests__/events-ingest.test.js +0 -248
- package/dist/__tests__/events-ingest.test.js.map +0 -1
- package/dist/__tests__/events-query.test.d.ts +0 -5
- package/dist/__tests__/events-query.test.d.ts.map +0 -1
- package/dist/__tests__/events-query.test.js +0 -205
- package/dist/__tests__/events-query.test.js.map +0 -1
- package/dist/__tests__/health.test.d.ts +0 -5
- package/dist/__tests__/health.test.d.ts.map +0 -1
- package/dist/__tests__/health.test.js +0 -40
- package/dist/__tests__/health.test.js.map +0 -1
- package/dist/__tests__/ingest.test.d.ts +0 -8
- package/dist/__tests__/ingest.test.d.ts.map +0 -1
- package/dist/__tests__/ingest.test.js +0 -469
- package/dist/__tests__/ingest.test.js.map +0 -1
- package/dist/__tests__/llm-tracking.test.d.ts +0 -10
- package/dist/__tests__/llm-tracking.test.d.ts.map +0 -1
- package/dist/__tests__/llm-tracking.test.js +0 -602
- package/dist/__tests__/llm-tracking.test.js.map +0 -1
- package/dist/__tests__/sessions.test.d.ts +0 -5
- package/dist/__tests__/sessions.test.d.ts.map +0 -1
- package/dist/__tests__/sessions.test.js +0 -176
- package/dist/__tests__/sessions.test.js.map +0 -1
- package/dist/__tests__/stream.test.d.ts +0 -5
- package/dist/__tests__/stream.test.d.ts.map +0 -1
- package/dist/__tests__/stream.test.js +0 -352
- package/dist/__tests__/stream.test.js.map +0 -1
- package/dist/__tests__/test-helpers.d.ts +0 -24
- package/dist/__tests__/test-helpers.d.ts.map +0 -1
- package/dist/__tests__/test-helpers.js +0 -45
- package/dist/__tests__/test-helpers.js.map +0 -1
- package/dist/db/__tests__/init.test.d.ts +0 -2
- package/dist/db/__tests__/init.test.d.ts.map +0 -1
- package/dist/db/__tests__/init.test.js +0 -73
- package/dist/db/__tests__/init.test.js.map +0 -1
- package/dist/db/__tests__/sqlite-store-read.test.d.ts +0 -2
- package/dist/db/__tests__/sqlite-store-read.test.d.ts.map +0 -1
- package/dist/db/__tests__/sqlite-store-read.test.js +0 -749
- package/dist/db/__tests__/sqlite-store-read.test.js.map +0 -1
- package/dist/db/__tests__/sqlite-store-write.test.d.ts +0 -2
- package/dist/db/__tests__/sqlite-store-write.test.d.ts.map +0 -1
- package/dist/db/__tests__/sqlite-store-write.test.js +0 -418
- package/dist/db/__tests__/sqlite-store-write.test.js.map +0 -1
- package/dist/lib/__tests__/alert-engine.test.d.ts +0 -5
- package/dist/lib/__tests__/alert-engine.test.d.ts.map +0 -1
- package/dist/lib/__tests__/alert-engine.test.js +0 -211
- package/dist/lib/__tests__/alert-engine.test.js.map +0 -1
- package/dist/lib/__tests__/retention.test.d.ts +0 -2
- package/dist/lib/__tests__/retention.test.d.ts.map +0 -1
- package/dist/lib/__tests__/retention.test.js +0 -238
- package/dist/lib/__tests__/retention.test.js.map +0 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding Store — storage and retrieval of vector embeddings (Story 2.2)
|
|
3
|
+
*
|
|
4
|
+
* Handles CRUD operations for embeddings including:
|
|
5
|
+
* - Content-hash deduplication
|
|
6
|
+
* - In-JS cosine similarity search (SQLite doesn't support vector ops)
|
|
7
|
+
* - Tenant isolation
|
|
8
|
+
*/
|
|
9
|
+
import type { SqliteDb } from './index.js';
|
|
10
|
+
/** Options for similarity search */
|
|
11
|
+
export interface SimilaritySearchOptions {
|
|
12
|
+
/** Filter by source type ('event', 'session', 'lesson') */
|
|
13
|
+
sourceType?: string;
|
|
14
|
+
/** Filter by creation time — from (ISO 8601) */
|
|
15
|
+
from?: string;
|
|
16
|
+
/** Filter by creation time — to (ISO 8601) */
|
|
17
|
+
to?: string;
|
|
18
|
+
/** Maximum number of results (default: 10) */
|
|
19
|
+
limit?: number;
|
|
20
|
+
/** Minimum similarity score to include (default: 0.0) */
|
|
21
|
+
minScore?: number;
|
|
22
|
+
}
|
|
23
|
+
/** A single similarity search result */
|
|
24
|
+
export interface SimilarityResult {
|
|
25
|
+
id: string;
|
|
26
|
+
sourceType: string;
|
|
27
|
+
sourceId: string;
|
|
28
|
+
score: number;
|
|
29
|
+
text: string;
|
|
30
|
+
embeddingModel: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
}
|
|
33
|
+
/** Stored embedding record (without the raw vector) */
|
|
34
|
+
export interface StoredEmbedding {
|
|
35
|
+
id: string;
|
|
36
|
+
tenantId: string;
|
|
37
|
+
sourceType: string;
|
|
38
|
+
sourceId: string;
|
|
39
|
+
contentHash: string;
|
|
40
|
+
textContent: string;
|
|
41
|
+
embedding: Float32Array;
|
|
42
|
+
embeddingModel: string;
|
|
43
|
+
dimensions: number;
|
|
44
|
+
createdAt: string;
|
|
45
|
+
}
|
|
46
|
+
export declare class EmbeddingStore {
|
|
47
|
+
private readonly db;
|
|
48
|
+
constructor(db: SqliteDb);
|
|
49
|
+
/**
|
|
50
|
+
* Store an embedding with source-level deduplication.
|
|
51
|
+
* If an embedding with the same (tenant, source_type, source_id) already exists,
|
|
52
|
+
* it's updated rather than duplicated. Different sources with the same content
|
|
53
|
+
* are stored as separate rows to preserve source metadata.
|
|
54
|
+
*/
|
|
55
|
+
store(tenantId: string, sourceType: string, sourceId: string, textContent: string, embedding: Float32Array, model: string, dimensions: number): string;
|
|
56
|
+
/**
|
|
57
|
+
* Get embeddings for a specific source.
|
|
58
|
+
*/
|
|
59
|
+
getBySource(tenantId: string, sourceType: string, sourceId: string): StoredEmbedding | null;
|
|
60
|
+
/**
|
|
61
|
+
* Similarity search: load candidate embeddings filtered by tenant/sourceType/time,
|
|
62
|
+
* compute cosine similarity in JS, sort by score, filter by minScore, return top N.
|
|
63
|
+
*/
|
|
64
|
+
similaritySearch(tenantId: string, queryVector: Float32Array, opts?: SimilaritySearchOptions): SimilarityResult[];
|
|
65
|
+
/**
|
|
66
|
+
* Delete embedding(s) for a specific source.
|
|
67
|
+
*/
|
|
68
|
+
delete(tenantId: string, sourceType: string, sourceId: string): number;
|
|
69
|
+
/**
|
|
70
|
+
* Count embeddings for a tenant.
|
|
71
|
+
*/
|
|
72
|
+
count(tenantId: string): number;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=embedding-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-store.d.ts","sourceRoot":"","sources":["../../src/db/embedding-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAyB3C,oCAAoC;AACpC,MAAM,WAAW,uBAAuB;IACtC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,YAAY,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAEzC;;;;;OAKG;IACH,KAAK,CACH,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,YAAY,EACvB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,MAAM;IAqDT;;OAEG;IACH,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,eAAe,GAAG,IAAI;IA6BzB;;;OAGG;IACH,gBAAgB,CACd,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,YAAY,EACzB,IAAI,GAAE,uBAA4B,GACjC,gBAAgB,EAAE;IAoDrB;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAetE;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAShC"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding Store — storage and retrieval of vector embeddings (Story 2.2)
|
|
3
|
+
*
|
|
4
|
+
* Handles CRUD operations for embeddings including:
|
|
5
|
+
* - Content-hash deduplication
|
|
6
|
+
* - In-JS cosine similarity search (SQLite doesn't support vector ops)
|
|
7
|
+
* - Tenant isolation
|
|
8
|
+
*/
|
|
9
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
10
|
+
import { eq, and, sql } from 'drizzle-orm';
|
|
11
|
+
import { embeddings } from './schema.sqlite.js';
|
|
12
|
+
import { cosineSimilarity } from '../lib/embeddings/math.js';
|
|
13
|
+
/** Maximum candidates loaded into memory for similarity search */
|
|
14
|
+
const MAX_CANDIDATES = 10_000;
|
|
15
|
+
/** Compute SHA-256 content hash of text */
|
|
16
|
+
function contentHash(text) {
|
|
17
|
+
return createHash('sha256').update(text).digest('hex');
|
|
18
|
+
}
|
|
19
|
+
/** Serialize Float32Array to Buffer for SQLite BLOB storage */
|
|
20
|
+
function serializeEmbedding(embedding) {
|
|
21
|
+
// Slice to copy — avoids view mutation if source buffer is reused
|
|
22
|
+
return Buffer.from(embedding.buffer.slice(embedding.byteOffset, embedding.byteOffset + embedding.byteLength));
|
|
23
|
+
}
|
|
24
|
+
/** Deserialize Buffer from SQLite BLOB to Float32Array */
|
|
25
|
+
function deserializeEmbedding(blob) {
|
|
26
|
+
// Copy to fresh aligned buffer — Buffer from SQLite may have non-4-byte-aligned offset
|
|
27
|
+
const copy = new Uint8Array(blob).buffer;
|
|
28
|
+
return new Float32Array(copy);
|
|
29
|
+
}
|
|
30
|
+
export class EmbeddingStore {
|
|
31
|
+
db;
|
|
32
|
+
constructor(db) {
|
|
33
|
+
this.db = db;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Store an embedding with source-level deduplication.
|
|
37
|
+
* If an embedding with the same (tenant, source_type, source_id) already exists,
|
|
38
|
+
* it's updated rather than duplicated. Different sources with the same content
|
|
39
|
+
* are stored as separate rows to preserve source metadata.
|
|
40
|
+
*/
|
|
41
|
+
store(tenantId, sourceType, sourceId, textContent, embedding, model, dimensions) {
|
|
42
|
+
const hash = contentHash(textContent);
|
|
43
|
+
const now = new Date().toISOString();
|
|
44
|
+
// Check for existing embedding from the same source
|
|
45
|
+
const existing = this.db
|
|
46
|
+
.select({ id: embeddings.id })
|
|
47
|
+
.from(embeddings)
|
|
48
|
+
.where(and(eq(embeddings.tenantId, tenantId), eq(embeddings.sourceType, sourceType), eq(embeddings.sourceId, sourceId)))
|
|
49
|
+
.get();
|
|
50
|
+
if (existing) {
|
|
51
|
+
// Update existing — content may have changed
|
|
52
|
+
this.db
|
|
53
|
+
.update(embeddings)
|
|
54
|
+
.set({
|
|
55
|
+
contentHash: hash,
|
|
56
|
+
textContent,
|
|
57
|
+
embedding: serializeEmbedding(embedding),
|
|
58
|
+
embeddingModel: model,
|
|
59
|
+
dimensions,
|
|
60
|
+
})
|
|
61
|
+
.where(eq(embeddings.id, existing.id))
|
|
62
|
+
.run();
|
|
63
|
+
return existing.id;
|
|
64
|
+
}
|
|
65
|
+
const id = randomUUID();
|
|
66
|
+
this.db
|
|
67
|
+
.insert(embeddings)
|
|
68
|
+
.values({
|
|
69
|
+
id,
|
|
70
|
+
tenantId,
|
|
71
|
+
sourceType,
|
|
72
|
+
sourceId,
|
|
73
|
+
contentHash: hash,
|
|
74
|
+
textContent,
|
|
75
|
+
embedding: serializeEmbedding(embedding),
|
|
76
|
+
embeddingModel: model,
|
|
77
|
+
dimensions,
|
|
78
|
+
createdAt: now,
|
|
79
|
+
})
|
|
80
|
+
.run();
|
|
81
|
+
return id;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get embeddings for a specific source.
|
|
85
|
+
*/
|
|
86
|
+
getBySource(tenantId, sourceType, sourceId) {
|
|
87
|
+
const row = this.db
|
|
88
|
+
.select()
|
|
89
|
+
.from(embeddings)
|
|
90
|
+
.where(and(eq(embeddings.tenantId, tenantId), eq(embeddings.sourceType, sourceType), eq(embeddings.sourceId, sourceId)))
|
|
91
|
+
.get();
|
|
92
|
+
if (!row)
|
|
93
|
+
return null;
|
|
94
|
+
return {
|
|
95
|
+
id: row.id,
|
|
96
|
+
tenantId: row.tenantId,
|
|
97
|
+
sourceType: row.sourceType,
|
|
98
|
+
sourceId: row.sourceId,
|
|
99
|
+
contentHash: row.contentHash,
|
|
100
|
+
textContent: row.textContent,
|
|
101
|
+
embedding: deserializeEmbedding(row.embedding),
|
|
102
|
+
embeddingModel: row.embeddingModel,
|
|
103
|
+
dimensions: row.dimensions,
|
|
104
|
+
createdAt: row.createdAt,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Similarity search: load candidate embeddings filtered by tenant/sourceType/time,
|
|
109
|
+
* compute cosine similarity in JS, sort by score, filter by minScore, return top N.
|
|
110
|
+
*/
|
|
111
|
+
similaritySearch(tenantId, queryVector, opts = {}) {
|
|
112
|
+
const { sourceType, from, to, limit = 10, minScore = 0.0 } = opts;
|
|
113
|
+
// Build all conditions upfront
|
|
114
|
+
const conditions = [eq(embeddings.tenantId, tenantId)];
|
|
115
|
+
if (sourceType) {
|
|
116
|
+
conditions.push(eq(embeddings.sourceType, sourceType));
|
|
117
|
+
}
|
|
118
|
+
if (from) {
|
|
119
|
+
conditions.push(sql `${embeddings.createdAt} >= ${from}`);
|
|
120
|
+
}
|
|
121
|
+
if (to) {
|
|
122
|
+
conditions.push(sql `${embeddings.createdAt} <= ${to}`);
|
|
123
|
+
}
|
|
124
|
+
const rows = this.db
|
|
125
|
+
.select()
|
|
126
|
+
.from(embeddings)
|
|
127
|
+
.where(and(...conditions))
|
|
128
|
+
.limit(MAX_CANDIDATES)
|
|
129
|
+
.all();
|
|
130
|
+
if (rows.length >= MAX_CANDIDATES) {
|
|
131
|
+
console.warn(`[EmbeddingStore] similaritySearch hit MAX_CANDIDATES limit (${MAX_CANDIDATES}) ` +
|
|
132
|
+
`for tenant=${tenantId}. Results may be incomplete.`);
|
|
133
|
+
}
|
|
134
|
+
// Compute cosine similarity for each candidate
|
|
135
|
+
const results = [];
|
|
136
|
+
for (const row of rows) {
|
|
137
|
+
const embVector = deserializeEmbedding(row.embedding);
|
|
138
|
+
const score = cosineSimilarity(queryVector, embVector);
|
|
139
|
+
if (score >= minScore) {
|
|
140
|
+
results.push({
|
|
141
|
+
id: row.id,
|
|
142
|
+
sourceType: row.sourceType,
|
|
143
|
+
sourceId: row.sourceId,
|
|
144
|
+
score,
|
|
145
|
+
text: row.textContent,
|
|
146
|
+
embeddingModel: row.embeddingModel,
|
|
147
|
+
createdAt: row.createdAt,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Sort by score descending and take top N
|
|
152
|
+
results.sort((a, b) => b.score - a.score);
|
|
153
|
+
return results.slice(0, limit);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Delete embedding(s) for a specific source.
|
|
157
|
+
*/
|
|
158
|
+
delete(tenantId, sourceType, sourceId) {
|
|
159
|
+
const result = this.db
|
|
160
|
+
.delete(embeddings)
|
|
161
|
+
.where(and(eq(embeddings.tenantId, tenantId), eq(embeddings.sourceType, sourceType), eq(embeddings.sourceId, sourceId)))
|
|
162
|
+
.run();
|
|
163
|
+
return result.changes;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Count embeddings for a tenant.
|
|
167
|
+
*/
|
|
168
|
+
count(tenantId) {
|
|
169
|
+
const result = this.db
|
|
170
|
+
.select({ count: sql `COUNT(*)` })
|
|
171
|
+
.from(embeddings)
|
|
172
|
+
.where(eq(embeddings.tenantId, tenantId))
|
|
173
|
+
.get();
|
|
174
|
+
return result?.count ?? 0;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=embedding-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-store.js","sourceRoot":"","sources":["../../src/db/embedding-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Snapshot Store (Story 1.3)
|
|
3
|
+
*
|
|
4
|
+
* CRUD operations for the health_snapshots table with tenant isolation.
|
|
5
|
+
* Stores daily health score snapshots for historical tracking.
|
|
6
|
+
*/
|
|
7
|
+
import type { SqliteDb } from './index.js';
|
|
8
|
+
import type { HealthSnapshot } from '@agentlensai/core';
|
|
9
|
+
export declare class HealthSnapshotStore {
|
|
10
|
+
private readonly db;
|
|
11
|
+
constructor(db: SqliteDb);
|
|
12
|
+
/**
|
|
13
|
+
* Upsert a health snapshot (INSERT OR REPLACE by tenant_id + agent_id + date).
|
|
14
|
+
*/
|
|
15
|
+
save(tenantId: string, snapshot: HealthSnapshot): void;
|
|
16
|
+
/**
|
|
17
|
+
* Get a single snapshot by agent ID and date.
|
|
18
|
+
*/
|
|
19
|
+
get(tenantId: string, agentId: string, date: string): HealthSnapshot | null;
|
|
20
|
+
/**
|
|
21
|
+
* Get snapshot history for an agent, ordered by date DESC.
|
|
22
|
+
*/
|
|
23
|
+
getHistory(tenantId: string, agentId: string, days: number): HealthSnapshot[];
|
|
24
|
+
/**
|
|
25
|
+
* Get the latest snapshot for each agent in a tenant.
|
|
26
|
+
*/
|
|
27
|
+
getLatest(tenantId: string): Map<string, HealthSnapshot>;
|
|
28
|
+
/**
|
|
29
|
+
* Delete snapshots older than retention period.
|
|
30
|
+
*/
|
|
31
|
+
cleanup(tenantId: string, retentionDays: number): number;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=health-snapshot-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-snapshot-store.d.ts","sourceRoot":"","sources":["../../src/db/health-snapshot-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAiCxD,qBAAa,mBAAmB;IAClB,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAEzC;;OAEG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAmBtD;;OAEG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAW3E;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE;IAgB7E;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;IAoBxD;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM;CAazD"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Snapshot Store (Story 1.3)
|
|
3
|
+
*
|
|
4
|
+
* CRUD operations for the health_snapshots table with tenant isolation.
|
|
5
|
+
* Stores daily health score snapshots for historical tracking.
|
|
6
|
+
*/
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { sql } from 'drizzle-orm';
|
|
9
|
+
/** Parse a DB row into a HealthSnapshot */
|
|
10
|
+
function rowToSnapshot(row) {
|
|
11
|
+
return {
|
|
12
|
+
agentId: row.agent_id,
|
|
13
|
+
date: row.date,
|
|
14
|
+
overallScore: row.overall_score,
|
|
15
|
+
errorRateScore: row.error_rate_score,
|
|
16
|
+
costEfficiencyScore: row.cost_efficiency_score,
|
|
17
|
+
toolSuccessScore: row.tool_success_score,
|
|
18
|
+
latencyScore: row.latency_score,
|
|
19
|
+
completionRateScore: row.completion_rate_score,
|
|
20
|
+
sessionCount: row.session_count,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export class HealthSnapshotStore {
|
|
24
|
+
db;
|
|
25
|
+
constructor(db) {
|
|
26
|
+
this.db = db;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Upsert a health snapshot (INSERT OR REPLACE by tenant_id + agent_id + date).
|
|
30
|
+
*/
|
|
31
|
+
save(tenantId, snapshot) {
|
|
32
|
+
const id = randomUUID();
|
|
33
|
+
const now = new Date().toISOString();
|
|
34
|
+
this.db.run(sql `
|
|
35
|
+
INSERT OR REPLACE INTO health_snapshots (
|
|
36
|
+
id, tenant_id, agent_id, date,
|
|
37
|
+
overall_score, error_rate_score, cost_efficiency_score,
|
|
38
|
+
tool_success_score, latency_score, completion_rate_score,
|
|
39
|
+
session_count, created_at
|
|
40
|
+
) VALUES (
|
|
41
|
+
${id}, ${tenantId}, ${snapshot.agentId}, ${snapshot.date},
|
|
42
|
+
${snapshot.overallScore}, ${snapshot.errorRateScore}, ${snapshot.costEfficiencyScore},
|
|
43
|
+
${snapshot.toolSuccessScore}, ${snapshot.latencyScore}, ${snapshot.completionRateScore},
|
|
44
|
+
${snapshot.sessionCount}, ${now}
|
|
45
|
+
)
|
|
46
|
+
`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get a single snapshot by agent ID and date.
|
|
50
|
+
*/
|
|
51
|
+
get(tenantId, agentId, date) {
|
|
52
|
+
const row = this.db.get(sql `
|
|
53
|
+
SELECT * FROM health_snapshots
|
|
54
|
+
WHERE tenant_id = ${tenantId}
|
|
55
|
+
AND agent_id = ${agentId}
|
|
56
|
+
AND date = ${date}
|
|
57
|
+
`);
|
|
58
|
+
return row ? rowToSnapshot(row) : null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get snapshot history for an agent, ordered by date DESC.
|
|
62
|
+
*/
|
|
63
|
+
getHistory(tenantId, agentId, days) {
|
|
64
|
+
const cutoff = new Date();
|
|
65
|
+
cutoff.setDate(cutoff.getDate() - days);
|
|
66
|
+
const cutoffDate = cutoff.toISOString().slice(0, 10);
|
|
67
|
+
const rows = this.db.all(sql `
|
|
68
|
+
SELECT * FROM health_snapshots
|
|
69
|
+
WHERE tenant_id = ${tenantId}
|
|
70
|
+
AND agent_id = ${agentId}
|
|
71
|
+
AND date >= ${cutoffDate}
|
|
72
|
+
ORDER BY date DESC
|
|
73
|
+
`);
|
|
74
|
+
return rows.map(rowToSnapshot);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the latest snapshot for each agent in a tenant.
|
|
78
|
+
*/
|
|
79
|
+
getLatest(tenantId) {
|
|
80
|
+
// Use a subquery to get the max date per agent
|
|
81
|
+
const rows = this.db.all(sql `
|
|
82
|
+
SELECT hs.* FROM health_snapshots hs
|
|
83
|
+
INNER JOIN (
|
|
84
|
+
SELECT agent_id, MAX(date) as max_date
|
|
85
|
+
FROM health_snapshots
|
|
86
|
+
WHERE tenant_id = ${tenantId}
|
|
87
|
+
GROUP BY agent_id
|
|
88
|
+
) latest ON hs.agent_id = latest.agent_id AND hs.date = latest.max_date
|
|
89
|
+
WHERE hs.tenant_id = ${tenantId}
|
|
90
|
+
`);
|
|
91
|
+
const result = new Map();
|
|
92
|
+
for (const row of rows) {
|
|
93
|
+
result.set(row.agent_id, rowToSnapshot(row));
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Delete snapshots older than retention period.
|
|
99
|
+
*/
|
|
100
|
+
cleanup(tenantId, retentionDays) {
|
|
101
|
+
const cutoff = new Date();
|
|
102
|
+
cutoff.setDate(cutoff.getDate() - retentionDays);
|
|
103
|
+
const cutoffDate = cutoff.toISOString().slice(0, 10);
|
|
104
|
+
const result = this.db.run(sql `
|
|
105
|
+
DELETE FROM health_snapshots
|
|
106
|
+
WHERE tenant_id = ${tenantId}
|
|
107
|
+
AND date < ${cutoffDate}
|
|
108
|
+
`);
|
|
109
|
+
return result.changes;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=health-snapshot-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-snapshot-store.js","sourceRoot":"","sources":["../../src/db/health-snapshot-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lesson Store (Story 3.1)
|
|
3
|
+
*
|
|
4
|
+
* CRUD operations for the lessons table with tenant isolation.
|
|
5
|
+
*/
|
|
6
|
+
import type { SqliteDb } from './index.js';
|
|
7
|
+
import type { Lesson, LessonQuery, LessonImportance } from '@agentlensai/core';
|
|
8
|
+
/** Input for creating a new lesson */
|
|
9
|
+
export interface CreateLessonInput {
|
|
10
|
+
title: string;
|
|
11
|
+
content: string;
|
|
12
|
+
category?: string;
|
|
13
|
+
importance?: LessonImportance;
|
|
14
|
+
agentId?: string;
|
|
15
|
+
context?: Record<string, unknown>;
|
|
16
|
+
sourceSessionId?: string;
|
|
17
|
+
sourceEventId?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Input for updating an existing lesson */
|
|
20
|
+
export interface UpdateLessonInput {
|
|
21
|
+
title?: string;
|
|
22
|
+
content?: string;
|
|
23
|
+
category?: string;
|
|
24
|
+
importance?: LessonImportance;
|
|
25
|
+
agentId?: string;
|
|
26
|
+
context?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export declare class LessonStore {
|
|
29
|
+
private readonly db;
|
|
30
|
+
constructor(db: SqliteDb);
|
|
31
|
+
/**
|
|
32
|
+
* Create a new lesson.
|
|
33
|
+
*/
|
|
34
|
+
create(tenantId: string, input: CreateLessonInput): Lesson;
|
|
35
|
+
/**
|
|
36
|
+
* Get a lesson by ID. Increments access_count and updates last_accessed_at.
|
|
37
|
+
* Wrapped in a transaction for atomicity.
|
|
38
|
+
*/
|
|
39
|
+
get(tenantId: string, id: string): Lesson | null;
|
|
40
|
+
/**
|
|
41
|
+
* List lessons with optional filters.
|
|
42
|
+
*/
|
|
43
|
+
list(tenantId: string, query?: LessonQuery): Lesson[];
|
|
44
|
+
/**
|
|
45
|
+
* Update a lesson (partial update).
|
|
46
|
+
*/
|
|
47
|
+
update(tenantId: string, id: string, updates: UpdateLessonInput): Lesson;
|
|
48
|
+
/**
|
|
49
|
+
* Soft-delete (archive) a lesson.
|
|
50
|
+
*/
|
|
51
|
+
archive(tenantId: string, id: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Count lessons matching filters.
|
|
54
|
+
*/
|
|
55
|
+
count(tenantId: string, query?: LessonQuery): number;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=lesson-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lesson-store.d.ts","sourceRoot":"","sources":["../../src/db/lesson-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG/E,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AA+BD,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAEzC;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM;IA2B1D;;;OAGG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IA4BhD;;OAEG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,EAAE;IAyCrD;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM;IAsCxE;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAmB3C;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM;CAkCrD"}
|