@agentlensai/server 0.2.0 → 0.5.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/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 +250 -4
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/schema.sqlite.d.ts +881 -55
- package/dist/db/schema.sqlite.d.ts.map +1 -1
- package/dist/db/schema.sqlite.js +82 -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 +25 -13
- package/dist/db/sqlite-store.d.ts.map +1 -1
- package/dist/db/sqlite-store.js +224 -47
- 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 +25 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +99 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/alert-engine.d.ts +72 -0
- package/dist/lib/alert-engine.d.ts.map +1 -0
- package/dist/lib/alert-engine.js +318 -0
- package/dist/lib/alert-engine.js.map +1 -0
- 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 +158 -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 +155 -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 +118 -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 +145 -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 +56 -0
- package/dist/lib/context/retrieval.d.ts.map +1 -0
- package/dist/lib/context/retrieval.js +229 -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/event-bus.d.ts +48 -0
- package/dist/lib/event-bus.d.ts.map +1 -0
- package/dist/lib/event-bus.js +34 -0
- package/dist/lib/event-bus.js.map +1 -0
- package/dist/lib/retention.d.ts +1 -1
- package/dist/lib/retention.d.ts.map +1 -1
- package/dist/lib/sse.d.ts +22 -0
- package/dist/lib/sse.d.ts.map +1 -0
- package/dist/lib/sse.js +121 -0
- package/dist/lib/sse.js.map +1 -0
- 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 +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 +17 -0
- package/dist/routes/alerts.d.ts.map +1 -0
- package/dist/routes/alerts.js +141 -0
- package/dist/routes/alerts.js.map +1 -0
- package/dist/routes/analytics.d.ts +16 -0
- package/dist/routes/analytics.d.ts.map +1 -0
- package/dist/routes/analytics.js +307 -0
- package/dist/routes/analytics.js.map +1 -0
- 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/config.d.ts +5 -0
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +27 -7
- package/dist/routes/config.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 +7 -2
- package/dist/routes/events.d.ts.map +1 -1
- package/dist/routes/events.js +76 -8
- package/dist/routes/events.js.map +1 -1
- package/dist/routes/ingest.d.ts +36 -0
- package/dist/routes/ingest.d.ts.map +1 -0
- package/dist/routes/ingest.js +280 -0
- package/dist/routes/ingest.js.map +1 -0
- 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/recall.d.ts +20 -0
- package/dist/routes/recall.d.ts.map +1 -0
- package/dist/routes/recall.js +71 -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 +55 -0
- package/dist/routes/reflect.js.map +1 -0
- package/dist/routes/sessions.d.ts +1 -1
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +9 -7
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/stats.d.ts +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 +23 -0
- package/dist/routes/stream.d.ts.map +1 -0
- package/dist/routes/stream.js +94 -0
- package/dist/routes/stream.js.map +1 -0
- 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 +23 -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__/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__/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__/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__/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,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert Evaluation Engine (Story 12.2 + 12.3)
|
|
3
|
+
*
|
|
4
|
+
* Periodically evaluates alert rules against recent analytics data.
|
|
5
|
+
* When conditions are met, triggers alerts:
|
|
6
|
+
* 1. Stores in alertHistory table
|
|
7
|
+
* 2. Delivers via webhook (Story 12.3)
|
|
8
|
+
* 3. Emits on EventBus (Story 12.5)
|
|
9
|
+
* 4. Logs to console
|
|
10
|
+
*/
|
|
11
|
+
import type { IEventStore } from '@agentlensai/core';
|
|
12
|
+
import type { AlertHistory } from '@agentlensai/core';
|
|
13
|
+
/**
|
|
14
|
+
* Validate that a webhook URL is safe to deliver to.
|
|
15
|
+
* Blocks private/reserved IPs and non-HTTP(S) schemes to prevent SSRF.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isWebhookUrlAllowed(url: string): boolean;
|
|
18
|
+
export interface AlertEngineOptions {
|
|
19
|
+
/** Evaluation interval in milliseconds */
|
|
20
|
+
checkIntervalMs?: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class AlertEngine {
|
|
23
|
+
private store;
|
|
24
|
+
private timer;
|
|
25
|
+
private running;
|
|
26
|
+
private readonly checkIntervalMs;
|
|
27
|
+
constructor(store: IEventStore, options?: AlertEngineOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Start the periodic evaluation loop.
|
|
30
|
+
*/
|
|
31
|
+
start(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Stop the evaluation loop.
|
|
34
|
+
*/
|
|
35
|
+
stop(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Get a tenant-scoped store for a given rule.
|
|
38
|
+
* If the underlying store is SqliteEventStore and the rule has a tenantId,
|
|
39
|
+
* returns a TenantScopedStore. Otherwise returns the original store.
|
|
40
|
+
*/
|
|
41
|
+
private getTenantStore;
|
|
42
|
+
/**
|
|
43
|
+
* Run a single evaluation cycle (can also be called manually / from tests).
|
|
44
|
+
*
|
|
45
|
+
* Rules are grouped by tenantId so every operation — listing rules,
|
|
46
|
+
* querying analytics, deduplication, and history insertion — goes
|
|
47
|
+
* through a tenant-scoped store. This prevents any cross-tenant
|
|
48
|
+
* data leakage during alert evaluation.
|
|
49
|
+
*/
|
|
50
|
+
evaluate(): Promise<AlertHistory[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Compute the current metric value for a rule by querying analytics.
|
|
53
|
+
*/
|
|
54
|
+
private computeCurrentValue;
|
|
55
|
+
/**
|
|
56
|
+
* Check whether the condition is met.
|
|
57
|
+
*/
|
|
58
|
+
private checkCondition;
|
|
59
|
+
/**
|
|
60
|
+
* Trigger an alert: persist history, deliver webhooks, emit on EventBus.
|
|
61
|
+
*/
|
|
62
|
+
private triggerAlert;
|
|
63
|
+
/**
|
|
64
|
+
* Deliver alert to configured webhook URLs (Story 12.3).
|
|
65
|
+
*/
|
|
66
|
+
private deliverWebhooks;
|
|
67
|
+
/**
|
|
68
|
+
* Build a human-readable alert message.
|
|
69
|
+
*/
|
|
70
|
+
private buildMessage;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=alert-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alert-engine.d.ts","sourceRoot":"","sources":["../../src/lib/alert-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAa,YAAY,EAAkB,MAAM,mBAAmB,CAAC;AAUjF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA+CxD;AAED,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,WAAW;IAMpB,OAAO,CAAC,KAAK;IALf,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAG/B,KAAK,EAAE,WAAW,EAC1B,OAAO,CAAC,EAAE,kBAAkB;IAQ9B;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAQtB;;;;;;;OAOG;IACG,QAAQ,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IA6DzC;;OAEG;YACW,mBAAmB;IAwCjC;;OAEG;IACH,OAAO,CAAC,cAAc;IAqBtB;;OAEG;YACW,YAAY;IAkC1B;;OAEG;YACW,eAAe;IA4C7B;;OAEG;IACH,OAAO,CAAC,YAAY;CAmBrB"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert Evaluation Engine (Story 12.2 + 12.3)
|
|
3
|
+
*
|
|
4
|
+
* Periodically evaluates alert rules against recent analytics data.
|
|
5
|
+
* When conditions are met, triggers alerts:
|
|
6
|
+
* 1. Stores in alertHistory table
|
|
7
|
+
* 2. Delivers via webhook (Story 12.3)
|
|
8
|
+
* 3. Emits on EventBus (Story 12.5)
|
|
9
|
+
* 4. Logs to console
|
|
10
|
+
*/
|
|
11
|
+
import { ulid } from 'ulid';
|
|
12
|
+
import { SqliteEventStore } from '../db/sqlite-store.js';
|
|
13
|
+
import { TenantScopedStore } from '../db/tenant-scoped-store.js';
|
|
14
|
+
import { eventBus } from './event-bus.js';
|
|
15
|
+
/** Default evaluation interval: 60 seconds */
|
|
16
|
+
const DEFAULT_CHECK_INTERVAL_MS = 60_000;
|
|
17
|
+
// ─── SSRF Protection ────────────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Validate that a webhook URL is safe to deliver to.
|
|
20
|
+
* Blocks private/reserved IPs and non-HTTP(S) schemes to prevent SSRF.
|
|
21
|
+
*/
|
|
22
|
+
export function isWebhookUrlAllowed(url) {
|
|
23
|
+
let parsed;
|
|
24
|
+
try {
|
|
25
|
+
parsed = new URL(url);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
// Only allow http and https schemes
|
|
31
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const hostname = parsed.hostname.replace(/^\[|\]$/g, ''); // strip IPv6 brackets
|
|
35
|
+
// Allow http://localhost only for dev
|
|
36
|
+
if (parsed.protocol === 'http:' && hostname !== 'localhost' && hostname !== '127.0.0.1') {
|
|
37
|
+
// In production, require https for non-localhost
|
|
38
|
+
// For now, allow http in dev but still block private IPs below
|
|
39
|
+
}
|
|
40
|
+
// Block IPv6 loopback
|
|
41
|
+
if (hostname === '::1' || hostname === '0:0:0:0:0:0:0:1') {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// Block private/reserved IPv4 ranges
|
|
45
|
+
const PRIVATE_IP_PATTERNS = [
|
|
46
|
+
/^127\./, // loopback
|
|
47
|
+
/^10\./, // 10.0.0.0/8
|
|
48
|
+
/^172\.(1[6-9]|2\d|3[01])\./, // 172.16.0.0/12
|
|
49
|
+
/^192\.168\./, // 192.168.0.0/16
|
|
50
|
+
/^169\.254\./, // link-local
|
|
51
|
+
/^0\./, // 0.0.0.0/8
|
|
52
|
+
];
|
|
53
|
+
if (PRIVATE_IP_PATTERNS.some((p) => p.test(hostname))) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// Block if hostname resolves to a known-private DNS name
|
|
57
|
+
if (hostname === 'localhost' && parsed.protocol === 'https:') {
|
|
58
|
+
// https://localhost is suspicious but technically okay for dev
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
export class AlertEngine {
|
|
64
|
+
store;
|
|
65
|
+
timer = null;
|
|
66
|
+
running = false;
|
|
67
|
+
checkIntervalMs;
|
|
68
|
+
constructor(store, options) {
|
|
69
|
+
this.store = store;
|
|
70
|
+
this.checkIntervalMs =
|
|
71
|
+
options?.checkIntervalMs ??
|
|
72
|
+
(parseInt(process.env['ALERT_CHECK_INTERVAL_MS'] ?? '', 10) ||
|
|
73
|
+
DEFAULT_CHECK_INTERVAL_MS);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Start the periodic evaluation loop.
|
|
77
|
+
*/
|
|
78
|
+
start() {
|
|
79
|
+
if (this.timer)
|
|
80
|
+
return;
|
|
81
|
+
console.log(`[AlertEngine] Starting evaluation loop (interval: ${this.checkIntervalMs}ms)`);
|
|
82
|
+
this.timer = setInterval(() => {
|
|
83
|
+
this.evaluate().catch((err) => {
|
|
84
|
+
console.error('[AlertEngine] Evaluation error:', err);
|
|
85
|
+
});
|
|
86
|
+
}, this.checkIntervalMs);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Stop the evaluation loop.
|
|
90
|
+
*/
|
|
91
|
+
stop() {
|
|
92
|
+
if (this.timer) {
|
|
93
|
+
clearInterval(this.timer);
|
|
94
|
+
this.timer = null;
|
|
95
|
+
console.log('[AlertEngine] Stopped');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get a tenant-scoped store for a given rule.
|
|
100
|
+
* If the underlying store is SqliteEventStore and the rule has a tenantId,
|
|
101
|
+
* returns a TenantScopedStore. Otherwise returns the original store.
|
|
102
|
+
*/
|
|
103
|
+
getTenantStore(rule) {
|
|
104
|
+
const tenantId = rule.tenantId ?? 'default';
|
|
105
|
+
if (this.store instanceof SqliteEventStore) {
|
|
106
|
+
return new TenantScopedStore(this.store, tenantId);
|
|
107
|
+
}
|
|
108
|
+
return this.store;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Run a single evaluation cycle (can also be called manually / from tests).
|
|
112
|
+
*
|
|
113
|
+
* Rules are grouped by tenantId so every operation — listing rules,
|
|
114
|
+
* querying analytics, deduplication, and history insertion — goes
|
|
115
|
+
* through a tenant-scoped store. This prevents any cross-tenant
|
|
116
|
+
* data leakage during alert evaluation.
|
|
117
|
+
*/
|
|
118
|
+
async evaluate() {
|
|
119
|
+
if (this.running)
|
|
120
|
+
return [];
|
|
121
|
+
this.running = true;
|
|
122
|
+
try {
|
|
123
|
+
// Fetch all rules then group by tenant so every subsequent
|
|
124
|
+
// operation uses the correct tenant-scoped store.
|
|
125
|
+
const allRules = await this.store.listAlertRules();
|
|
126
|
+
const rulesByTenant = new Map();
|
|
127
|
+
for (const rule of allRules) {
|
|
128
|
+
const tid = rule.tenantId ?? 'default';
|
|
129
|
+
let bucket = rulesByTenant.get(tid);
|
|
130
|
+
if (!bucket) {
|
|
131
|
+
bucket = [];
|
|
132
|
+
rulesByTenant.set(tid, bucket);
|
|
133
|
+
}
|
|
134
|
+
bucket.push(rule);
|
|
135
|
+
}
|
|
136
|
+
const triggered = [];
|
|
137
|
+
for (const [tenantId, tenantRules] of rulesByTenant) {
|
|
138
|
+
// Build a single tenant-scoped store per tenant
|
|
139
|
+
const tenantStore = this.store instanceof SqliteEventStore
|
|
140
|
+
? new TenantScopedStore(this.store, tenantId)
|
|
141
|
+
: this.store;
|
|
142
|
+
const enabledRules = tenantRules.filter((r) => r.enabled);
|
|
143
|
+
for (const rule of enabledRules) {
|
|
144
|
+
try {
|
|
145
|
+
const currentValue = await this.computeCurrentValue(rule, tenantStore);
|
|
146
|
+
const shouldTrigger = this.checkCondition(rule.condition, currentValue, rule.threshold);
|
|
147
|
+
if (shouldTrigger) {
|
|
148
|
+
// M1 fix: Deduplication — skip if the rule already triggered within its window
|
|
149
|
+
const recentHistory = await tenantStore.listAlertHistory({ ruleId: rule.id, limit: 1 });
|
|
150
|
+
if (recentHistory.entries.length > 0) {
|
|
151
|
+
const lastTrigger = new Date(recentHistory.entries[0].triggeredAt).getTime();
|
|
152
|
+
const cooldownMs = rule.windowMinutes * 60_000;
|
|
153
|
+
if (Date.now() - lastTrigger < cooldownMs) {
|
|
154
|
+
continue; // Still within cooldown period
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const entry = await this.triggerAlert(rule, currentValue, tenantStore);
|
|
158
|
+
triggered.push(entry);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
console.error(`[AlertEngine] Error evaluating rule "${rule.name}" (${rule.id}):`, err);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return triggered;
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
this.running = false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Compute the current metric value for a rule by querying analytics.
|
|
174
|
+
*/
|
|
175
|
+
async computeCurrentValue(rule, tenantStore) {
|
|
176
|
+
const now = new Date();
|
|
177
|
+
const windowStart = new Date(now.getTime() - rule.windowMinutes * 60_000);
|
|
178
|
+
const from = windowStart.toISOString();
|
|
179
|
+
const to = now.toISOString();
|
|
180
|
+
const analytics = await tenantStore.getAnalytics({
|
|
181
|
+
from,
|
|
182
|
+
to,
|
|
183
|
+
agentId: rule.scope.agentId,
|
|
184
|
+
granularity: 'hour',
|
|
185
|
+
});
|
|
186
|
+
const totals = analytics.totals;
|
|
187
|
+
switch (rule.condition) {
|
|
188
|
+
case 'error_rate_exceeds': {
|
|
189
|
+
if (totals.eventCount === 0)
|
|
190
|
+
return 0;
|
|
191
|
+
return totals.errorCount / totals.eventCount;
|
|
192
|
+
}
|
|
193
|
+
case 'cost_exceeds': {
|
|
194
|
+
return totals.totalCostUsd;
|
|
195
|
+
}
|
|
196
|
+
case 'latency_exceeds': {
|
|
197
|
+
return totals.avgLatencyMs;
|
|
198
|
+
}
|
|
199
|
+
case 'event_count_exceeds': {
|
|
200
|
+
return totals.eventCount;
|
|
201
|
+
}
|
|
202
|
+
case 'no_events_for': {
|
|
203
|
+
// For no_events_for, the "value" is the number of events.
|
|
204
|
+
// Trigger if event count is 0 (or below threshold — threshold is ignored,
|
|
205
|
+
// the rule triggers when there are zero events in the window).
|
|
206
|
+
return totals.eventCount;
|
|
207
|
+
}
|
|
208
|
+
default:
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Check whether the condition is met.
|
|
214
|
+
*/
|
|
215
|
+
checkCondition(condition, currentValue, threshold) {
|
|
216
|
+
switch (condition) {
|
|
217
|
+
case 'error_rate_exceeds':
|
|
218
|
+
case 'cost_exceeds':
|
|
219
|
+
case 'latency_exceeds':
|
|
220
|
+
case 'event_count_exceeds':
|
|
221
|
+
return currentValue > threshold;
|
|
222
|
+
case 'no_events_for':
|
|
223
|
+
// Triggers when event count is 0 (no events in window)
|
|
224
|
+
return currentValue === 0;
|
|
225
|
+
default:
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Trigger an alert: persist history, deliver webhooks, emit on EventBus.
|
|
231
|
+
*/
|
|
232
|
+
async triggerAlert(rule, currentValue, tenantStore) {
|
|
233
|
+
const now = new Date().toISOString();
|
|
234
|
+
const message = this.buildMessage(rule, currentValue);
|
|
235
|
+
const entry = {
|
|
236
|
+
id: ulid(),
|
|
237
|
+
ruleId: rule.id,
|
|
238
|
+
triggeredAt: now,
|
|
239
|
+
currentValue,
|
|
240
|
+
threshold: rule.threshold,
|
|
241
|
+
message,
|
|
242
|
+
tenantId: rule.tenantId,
|
|
243
|
+
};
|
|
244
|
+
// 1. Persist to alert history (via tenant-scoped store)
|
|
245
|
+
await tenantStore.insertAlertHistory(entry);
|
|
246
|
+
// 2. Console log
|
|
247
|
+
console.log(`[AlertEngine] 🔔 Alert triggered: "${rule.name}" — ${message}`);
|
|
248
|
+
// 3. Emit on EventBus (Story 12.5)
|
|
249
|
+
eventBus.emit({
|
|
250
|
+
type: 'alert_triggered',
|
|
251
|
+
rule,
|
|
252
|
+
history: entry,
|
|
253
|
+
timestamp: now,
|
|
254
|
+
});
|
|
255
|
+
// 4. Webhook delivery (Story 12.3) — fire and forget
|
|
256
|
+
await this.deliverWebhooks(rule, entry);
|
|
257
|
+
return entry;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Deliver alert to configured webhook URLs (Story 12.3).
|
|
261
|
+
*/
|
|
262
|
+
async deliverWebhooks(rule, entry) {
|
|
263
|
+
const webhookUrls = rule.notifyChannels.filter((ch) => (ch.startsWith('http://') || ch.startsWith('https://')) && isWebhookUrlAllowed(ch));
|
|
264
|
+
if (webhookUrls.length === 0)
|
|
265
|
+
return;
|
|
266
|
+
const payload = {
|
|
267
|
+
alertRuleId: rule.id,
|
|
268
|
+
alertName: rule.name,
|
|
269
|
+
condition: rule.condition,
|
|
270
|
+
currentValue: entry.currentValue,
|
|
271
|
+
threshold: entry.threshold,
|
|
272
|
+
message: entry.message,
|
|
273
|
+
triggeredAt: entry.triggeredAt,
|
|
274
|
+
windowMinutes: rule.windowMinutes,
|
|
275
|
+
scope: rule.scope,
|
|
276
|
+
};
|
|
277
|
+
for (const url of webhookUrls) {
|
|
278
|
+
try {
|
|
279
|
+
const res = await fetch(url, {
|
|
280
|
+
method: 'POST',
|
|
281
|
+
headers: { 'Content-Type': 'application/json' },
|
|
282
|
+
body: JSON.stringify(payload),
|
|
283
|
+
signal: AbortSignal.timeout(10_000), // 10s timeout
|
|
284
|
+
});
|
|
285
|
+
if (!res.ok) {
|
|
286
|
+
console.warn(`[AlertEngine] Webhook delivery to ${url} failed: HTTP ${res.status}`);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
console.log(`[AlertEngine] Webhook delivered to ${url}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
console.warn(`[AlertEngine] Webhook delivery to ${url} error:`, err instanceof Error ? err.message : err);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Build a human-readable alert message.
|
|
299
|
+
*/
|
|
300
|
+
buildMessage(rule, currentValue) {
|
|
301
|
+
const formatted = (v) => Number.isInteger(v) ? v.toString() : v.toFixed(4);
|
|
302
|
+
switch (rule.condition) {
|
|
303
|
+
case 'error_rate_exceeds':
|
|
304
|
+
return `Error rate ${(currentValue * 100).toFixed(1)}% exceeds threshold ${(rule.threshold * 100).toFixed(1)}% in the last ${rule.windowMinutes}m`;
|
|
305
|
+
case 'cost_exceeds':
|
|
306
|
+
return `Cost $${formatted(currentValue)} exceeds threshold $${formatted(rule.threshold)} in the last ${rule.windowMinutes}m`;
|
|
307
|
+
case 'latency_exceeds':
|
|
308
|
+
return `Average latency ${formatted(currentValue)}ms exceeds threshold ${formatted(rule.threshold)}ms in the last ${rule.windowMinutes}m`;
|
|
309
|
+
case 'event_count_exceeds':
|
|
310
|
+
return `Event count ${formatted(currentValue)} exceeds threshold ${formatted(rule.threshold)} in the last ${rule.windowMinutes}m`;
|
|
311
|
+
case 'no_events_for':
|
|
312
|
+
return `No events received in the last ${rule.windowMinutes}m`;
|
|
313
|
+
default:
|
|
314
|
+
return `Alert condition met: ${rule.condition}`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=alert-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alert-engine.js","sourceRoot":"","sources":["../../src/lib/alert-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost Analysis (Story 4.3)
|
|
3
|
+
*
|
|
4
|
+
* Analyzes cost data from sessions and llm_response events to provide
|
|
5
|
+
* cost breakdowns by model, agent, and time trends.
|
|
6
|
+
*/
|
|
7
|
+
import type { IEventStore } from '@agentlensai/core';
|
|
8
|
+
import type { CostAnalysisResult } from '@agentlensai/core';
|
|
9
|
+
export interface CostAnalysisOpts {
|
|
10
|
+
agentId?: string;
|
|
11
|
+
from?: string;
|
|
12
|
+
to?: string;
|
|
13
|
+
model?: string;
|
|
14
|
+
limit?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Analyze costs across sessions and LLM calls.
|
|
18
|
+
*/
|
|
19
|
+
export declare function analyzeCosts(store: IEventStore, opts?: CostAnalysisOpts): Promise<CostAnalysisResult>;
|
|
20
|
+
//# sourceMappingURL=cost-analysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-analysis.d.ts","sourceRoot":"","sources":["../../../src/lib/analysis/cost-analysis.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EACV,kBAAkB,EAKnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,WAAW,EAClB,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CA+I7B"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost Analysis (Story 4.3)
|
|
3
|
+
*
|
|
4
|
+
* Analyzes cost data from sessions and llm_response events to provide
|
|
5
|
+
* cost breakdowns by model, agent, and time trends.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Analyze costs across sessions and LLM calls.
|
|
9
|
+
*/
|
|
10
|
+
export async function analyzeCosts(store, opts = {}) {
|
|
11
|
+
const limit = opts.limit ?? 20;
|
|
12
|
+
// Query sessions with cost data
|
|
13
|
+
const sessionResult = await store.querySessions({
|
|
14
|
+
agentId: opts.agentId,
|
|
15
|
+
from: opts.from,
|
|
16
|
+
to: opts.to,
|
|
17
|
+
limit: 500,
|
|
18
|
+
});
|
|
19
|
+
// Query llm_response events for model breakdown
|
|
20
|
+
const llmResult = await store.queryEvents({
|
|
21
|
+
agentId: opts.agentId,
|
|
22
|
+
from: opts.from,
|
|
23
|
+
to: opts.to,
|
|
24
|
+
eventType: 'llm_response',
|
|
25
|
+
limit: 500,
|
|
26
|
+
order: 'asc',
|
|
27
|
+
});
|
|
28
|
+
const sessions = sessionResult.sessions;
|
|
29
|
+
const llmEvents = llmResult.events;
|
|
30
|
+
// Filter llm events by model if specified
|
|
31
|
+
const filteredLlmEvents = opts.model
|
|
32
|
+
? llmEvents.filter((e) => {
|
|
33
|
+
const p = e.payload;
|
|
34
|
+
return p.model === opts.model;
|
|
35
|
+
})
|
|
36
|
+
: llmEvents;
|
|
37
|
+
const eventsAnalyzed = filteredLlmEvents.length;
|
|
38
|
+
const timeRange = {
|
|
39
|
+
from: sessions.length > 0
|
|
40
|
+
? sessions.reduce((min, s) => (s.startedAt < min ? s.startedAt : min), sessions[0].startedAt)
|
|
41
|
+
: (opts.from ?? ''),
|
|
42
|
+
to: sessions.length > 0
|
|
43
|
+
? sessions.reduce((max, s) => (s.startedAt > max ? s.startedAt : max), sessions[0].startedAt)
|
|
44
|
+
: (opts.to ?? ''),
|
|
45
|
+
};
|
|
46
|
+
// Summary from sessions
|
|
47
|
+
const totalCost = sessions.reduce((sum, s) => sum + s.totalCostUsd, 0);
|
|
48
|
+
const totalSessions = sessions.length;
|
|
49
|
+
const avgPerSession = totalSessions > 0 ? totalCost / totalSessions : 0;
|
|
50
|
+
// Cost by model from llm_response events
|
|
51
|
+
const modelMap = new Map();
|
|
52
|
+
for (const event of filteredLlmEvents) {
|
|
53
|
+
const p = event.payload;
|
|
54
|
+
const model = p.model ?? 'unknown';
|
|
55
|
+
const cost = p.costUsd ?? 0;
|
|
56
|
+
const entry = modelMap.get(model) ?? { totalCost: 0, callCount: 0 };
|
|
57
|
+
entry.totalCost += cost;
|
|
58
|
+
entry.callCount++;
|
|
59
|
+
modelMap.set(model, entry);
|
|
60
|
+
}
|
|
61
|
+
const byModel = Array.from(modelMap.entries())
|
|
62
|
+
.map(([model, data]) => ({
|
|
63
|
+
model,
|
|
64
|
+
totalCost: Math.round(data.totalCost * 1_000_000) / 1_000_000,
|
|
65
|
+
callCount: data.callCount,
|
|
66
|
+
avgCostPerCall: data.callCount > 0
|
|
67
|
+
? Math.round((data.totalCost / data.callCount) * 1_000_000) / 1_000_000
|
|
68
|
+
: 0,
|
|
69
|
+
}))
|
|
70
|
+
.sort((a, b) => b.totalCost - a.totalCost);
|
|
71
|
+
// Cost by agent from sessions
|
|
72
|
+
const agentMap = new Map();
|
|
73
|
+
for (const session of sessions) {
|
|
74
|
+
const entry = agentMap.get(session.agentId) ?? { totalCost: 0, sessionCount: 0 };
|
|
75
|
+
entry.totalCost += session.totalCostUsd;
|
|
76
|
+
entry.sessionCount++;
|
|
77
|
+
agentMap.set(session.agentId, entry);
|
|
78
|
+
}
|
|
79
|
+
const byAgent = Array.from(agentMap.entries())
|
|
80
|
+
.map(([agentId, data]) => ({
|
|
81
|
+
agentId,
|
|
82
|
+
totalCost: Math.round(data.totalCost * 1_000_000) / 1_000_000,
|
|
83
|
+
sessionCount: data.sessionCount,
|
|
84
|
+
avgCostPerSession: data.sessionCount > 0
|
|
85
|
+
? Math.round((data.totalCost / data.sessionCount) * 1_000_000) / 1_000_000
|
|
86
|
+
: 0,
|
|
87
|
+
}))
|
|
88
|
+
.sort((a, b) => b.totalCost - a.totalCost);
|
|
89
|
+
// Cost trend over time (daily buckets)
|
|
90
|
+
const dailyMap = new Map();
|
|
91
|
+
for (const session of sessions) {
|
|
92
|
+
const date = session.startedAt.slice(0, 10); // YYYY-MM-DD
|
|
93
|
+
const entry = dailyMap.get(date) ?? { totalCost: 0, sessionCount: 0 };
|
|
94
|
+
entry.totalCost += session.totalCostUsd;
|
|
95
|
+
entry.sessionCount++;
|
|
96
|
+
dailyMap.set(date, entry);
|
|
97
|
+
}
|
|
98
|
+
const buckets = Array.from(dailyMap.entries())
|
|
99
|
+
.map(([date, data]) => ({
|
|
100
|
+
date,
|
|
101
|
+
totalCost: Math.round(data.totalCost * 1_000_000) / 1_000_000,
|
|
102
|
+
sessionCount: data.sessionCount,
|
|
103
|
+
}))
|
|
104
|
+
.sort((a, b) => a.date.localeCompare(b.date));
|
|
105
|
+
// Trend detection: compare last 7 days vs previous 7 days
|
|
106
|
+
const direction = detectCostTrend(buckets);
|
|
107
|
+
// Top sessions by cost
|
|
108
|
+
const topSessions = [...sessions]
|
|
109
|
+
.sort((a, b) => b.totalCostUsd - a.totalCostUsd)
|
|
110
|
+
.slice(0, limit)
|
|
111
|
+
.map((s) => ({
|
|
112
|
+
sessionId: s.id,
|
|
113
|
+
agentId: s.agentId,
|
|
114
|
+
totalCost: Math.round(s.totalCostUsd * 1_000_000) / 1_000_000,
|
|
115
|
+
startedAt: s.startedAt,
|
|
116
|
+
}));
|
|
117
|
+
return {
|
|
118
|
+
summary: {
|
|
119
|
+
totalCost: Math.round(totalCost * 1_000_000) / 1_000_000,
|
|
120
|
+
avgPerSession: Math.round(avgPerSession * 1_000_000) / 1_000_000,
|
|
121
|
+
totalSessions,
|
|
122
|
+
},
|
|
123
|
+
byModel,
|
|
124
|
+
byAgent,
|
|
125
|
+
trend: {
|
|
126
|
+
direction,
|
|
127
|
+
buckets,
|
|
128
|
+
},
|
|
129
|
+
topSessions,
|
|
130
|
+
metadata: {
|
|
131
|
+
eventsAnalyzed,
|
|
132
|
+
timeRange,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Detect cost trend by comparing the most recent 7 day-buckets vs the previous 7.
|
|
138
|
+
*/
|
|
139
|
+
function detectCostTrend(buckets) {
|
|
140
|
+
if (buckets.length < 2)
|
|
141
|
+
return 'stable';
|
|
142
|
+
const recent = buckets.slice(-7);
|
|
143
|
+
const previous = buckets.slice(-14, -7);
|
|
144
|
+
if (previous.length === 0)
|
|
145
|
+
return 'stable';
|
|
146
|
+
const recentTotal = recent.reduce((s, b) => s + b.totalCost, 0);
|
|
147
|
+
const previousTotal = previous.reduce((s, b) => s + b.totalCost, 0);
|
|
148
|
+
if (previousTotal === 0) {
|
|
149
|
+
return recentTotal > 0 ? 'increasing' : 'stable';
|
|
150
|
+
}
|
|
151
|
+
const changeRatio = (recentTotal - previousTotal) / previousTotal;
|
|
152
|
+
if (changeRatio > 0.1)
|
|
153
|
+
return 'increasing';
|
|
154
|
+
if (changeRatio < -0.1)
|
|
155
|
+
return 'decreasing';
|
|
156
|
+
return 'stable';
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=cost-analysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-analysis.js","sourceRoot":"","sources":["../../../src/lib/analysis/cost-analysis.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmBH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAkB,EAClB,OAAyB,EAAE;IAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAE/B,gCAAgC;IAChC,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QAC9C,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,KAAK,EAAE,GAAG;KACX,CAAC,CAAC;IAEH,gDAAgD;IAChD,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC;QACxC,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,SAAS,EAAE,cAAc;QACzB,KAAK,EAAE,GAAG;QACV,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC;IACxC,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC;IAEnC,0CAA0C;IAC1C,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK;QAClC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACrB,MAAM,CAAC,GAAG,CAAC,CAAC,OAAkC,CAAC;YAC/C,OAAO,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC;QAChC,CAAC,CAAC;QACJ,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,cAAc,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAChD,MAAM,SAAS,GAAG;QAChB,IAAI,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC;YACvB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC;YAC9F,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,EAAE,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC;YACrB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC;YAC9F,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;KACpB,CAAC;IAEF,wBAAwB;IACxB,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC;IACtC,MAAM,aAAa,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAExE,yCAAyC;IACzC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoD,CAAC;IAC7E,KAAK,MAAM,KAAK,IAAI,iBAAiB,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,KAAK,CAAC,OAA6B,CAAC;QAC9C,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,SAAS,CAAC;QACnC,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;QAE5B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QACpE,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC;QACxB,KAAK,CAAC,SAAS,EAAE,CAAC;QAClB,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,OAAO,GAAkB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;SAC1D,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACvB,KAAK;QACL,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,SAAS;QAC7D,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,cAAc,EACZ,IAAI,CAAC,SAAS,GAAG,CAAC;YAChB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS;YACvE,CAAC,CAAC,CAAC;KACR,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAE7C,8BAA8B;IAC9B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuD,CAAC;IAChF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;QACjF,KAAK,CAAC,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;QACxC,KAAK,CAAC,YAAY,EAAE,CAAC;QACrB,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,OAAO,GAAkB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;SAC1D,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,SAAS;QAC7D,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,iBAAiB,EACf,IAAI,CAAC,YAAY,GAAG,CAAC;YACnB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS;YAC1E,CAAC,CAAC,CAAC;KACR,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAE7C,uCAAuC;IACvC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuD,CAAC;IAChF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa;QAC1D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;QACtE,KAAK,CAAC,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;QACxC,KAAK,CAAC,YAAY,EAAE,CAAC;QACrB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,OAAO,GAAsB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;SAC9D,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,IAAI;QACJ,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,SAAS;QAC7D,YAAY,EAAE,IAAI,CAAC,YAAY;KAChC,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhD,0DAA0D;IAC1D,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAE3C,uBAAuB;IACvB,MAAM,WAAW,GAAG,CAAC,GAAG,QAAQ,CAAC;SAC9B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;SAC/C,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,SAAS,EAAE,CAAC,CAAC,EAAE;QACf,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,GAAG,SAAS,CAAC,GAAG,SAAS;QAC7D,SAAS,EAAE,CAAC,CAAC,SAAS;KACvB,CAAC,CAAC,CAAC;IAEN,OAAO;QACL,OAAO,EAAE;YACP,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,SAAS;YACxD,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,SAAS,CAAC,GAAG,SAAS;YAChE,aAAa;SACd;QACD,OAAO;QACP,OAAO;QACP,KAAK,EAAE;YACL,SAAS;YACT,OAAO;SACR;QACD,WAAW;QACX,QAAQ,EAAE;YACR,cAAc;YACd,SAAS;SACV;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CACtB,OAA0B;IAE1B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAExC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAExC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE3C,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAChE,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAEpE,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC;IACnD,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,WAAW,GAAG,aAAa,CAAC,GAAG,aAAa,CAAC;IAElE,IAAI,WAAW,GAAG,GAAG;QAAE,OAAO,YAAY,CAAC;IAC3C,IAAI,WAAW,GAAG,CAAC,GAAG;QAAE,OAAO,YAAY,CAAC;IAC5C,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Pattern Analysis (Story 4.1)
|
|
3
|
+
*
|
|
4
|
+
* Analyzes tool_error events and error/critical severity events to find
|
|
5
|
+
* recurring error patterns, grouping by normalized message.
|
|
6
|
+
*/
|
|
7
|
+
import type { IEventStore } from '@agentlensai/core';
|
|
8
|
+
import type { ErrorPatternResult } from '@agentlensai/core';
|
|
9
|
+
export interface ErrorPatternOpts {
|
|
10
|
+
agentId?: string;
|
|
11
|
+
from?: string;
|
|
12
|
+
to?: string;
|
|
13
|
+
limit?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Normalize an error message for grouping:
|
|
17
|
+
* - Replace UUIDs with <UUID>
|
|
18
|
+
* - Replace file paths with <PATH>
|
|
19
|
+
* - Replace numbers with <N>
|
|
20
|
+
*/
|
|
21
|
+
export declare function normalizeErrorMessage(msg: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Analyze error patterns from the event store.
|
|
24
|
+
*/
|
|
25
|
+
export declare function analyzeErrorPatterns(store: IEventStore, opts?: ErrorPatternOpts): Promise<ErrorPatternResult>;
|
|
26
|
+
//# sourceMappingURL=error-patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-patterns.d.ts","sourceRoot":"","sources":["../../../src/lib/analysis/error-patterns.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQzD;AAgBD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,WAAW,EAClB,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CA4H7B"}
|