@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.
Files changed (222) hide show
  1. package/dist/db/embedding-store.d.ts +74 -0
  2. package/dist/db/embedding-store.d.ts.map +1 -0
  3. package/dist/db/embedding-store.js +177 -0
  4. package/dist/db/embedding-store.js.map +1 -0
  5. package/dist/db/lesson-store.d.ts +57 -0
  6. package/dist/db/lesson-store.d.ts.map +1 -0
  7. package/dist/db/lesson-store.js +217 -0
  8. package/dist/db/lesson-store.js.map +1 -0
  9. package/dist/db/migrate.d.ts.map +1 -1
  10. package/dist/db/migrate.js +250 -4
  11. package/dist/db/migrate.js.map +1 -1
  12. package/dist/db/schema.sqlite.d.ts +881 -55
  13. package/dist/db/schema.sqlite.d.ts.map +1 -1
  14. package/dist/db/schema.sqlite.js +82 -4
  15. package/dist/db/schema.sqlite.js.map +1 -1
  16. package/dist/db/session-summary-store.d.ts +45 -0
  17. package/dist/db/session-summary-store.d.ts.map +1 -0
  18. package/dist/db/session-summary-store.js +112 -0
  19. package/dist/db/session-summary-store.js.map +1 -0
  20. package/dist/db/sqlite-store.d.ts +25 -13
  21. package/dist/db/sqlite-store.d.ts.map +1 -1
  22. package/dist/db/sqlite-store.js +224 -47
  23. package/dist/db/sqlite-store.js.map +1 -1
  24. package/dist/db/tenant-scoped-store.d.ts +61 -0
  25. package/dist/db/tenant-scoped-store.d.ts.map +1 -0
  26. package/dist/db/tenant-scoped-store.js +94 -0
  27. package/dist/db/tenant-scoped-store.js.map +1 -0
  28. package/dist/index.d.ts +25 -4
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +99 -5
  31. package/dist/index.js.map +1 -1
  32. package/dist/lib/alert-engine.d.ts +72 -0
  33. package/dist/lib/alert-engine.d.ts.map +1 -0
  34. package/dist/lib/alert-engine.js +318 -0
  35. package/dist/lib/alert-engine.js.map +1 -0
  36. package/dist/lib/analysis/cost-analysis.d.ts +20 -0
  37. package/dist/lib/analysis/cost-analysis.d.ts.map +1 -0
  38. package/dist/lib/analysis/cost-analysis.js +158 -0
  39. package/dist/lib/analysis/cost-analysis.js.map +1 -0
  40. package/dist/lib/analysis/error-patterns.d.ts +26 -0
  41. package/dist/lib/analysis/error-patterns.d.ts.map +1 -0
  42. package/dist/lib/analysis/error-patterns.js +155 -0
  43. package/dist/lib/analysis/error-patterns.js.map +1 -0
  44. package/dist/lib/analysis/index.d.ts +23 -0
  45. package/dist/lib/analysis/index.d.ts.map +1 -0
  46. package/dist/lib/analysis/index.js +144 -0
  47. package/dist/lib/analysis/index.js.map +1 -0
  48. package/dist/lib/analysis/performance-trends.d.ts +19 -0
  49. package/dist/lib/analysis/performance-trends.d.ts.map +1 -0
  50. package/dist/lib/analysis/performance-trends.js +118 -0
  51. package/dist/lib/analysis/performance-trends.js.map +1 -0
  52. package/dist/lib/analysis/tool-sequences.d.ts +19 -0
  53. package/dist/lib/analysis/tool-sequences.d.ts.map +1 -0
  54. package/dist/lib/analysis/tool-sequences.js +145 -0
  55. package/dist/lib/analysis/tool-sequences.js.map +1 -0
  56. package/dist/lib/context/index.d.ts +5 -0
  57. package/dist/lib/context/index.d.ts.map +1 -0
  58. package/dist/lib/context/index.js +5 -0
  59. package/dist/lib/context/index.js.map +1 -0
  60. package/dist/lib/context/retrieval.d.ts +56 -0
  61. package/dist/lib/context/retrieval.d.ts.map +1 -0
  62. package/dist/lib/context/retrieval.js +229 -0
  63. package/dist/lib/context/retrieval.js.map +1 -0
  64. package/dist/lib/embeddings/index.d.ts +31 -0
  65. package/dist/lib/embeddings/index.d.ts.map +1 -0
  66. package/dist/lib/embeddings/index.js +31 -0
  67. package/dist/lib/embeddings/index.js.map +1 -0
  68. package/dist/lib/embeddings/local.d.ts +15 -0
  69. package/dist/lib/embeddings/local.d.ts.map +1 -0
  70. package/dist/lib/embeddings/local.js +65 -0
  71. package/dist/lib/embeddings/local.js.map +1 -0
  72. package/dist/lib/embeddings/math.d.ts +13 -0
  73. package/dist/lib/embeddings/math.d.ts.map +1 -0
  74. package/dist/lib/embeddings/math.js +35 -0
  75. package/dist/lib/embeddings/math.js.map +1 -0
  76. package/dist/lib/embeddings/openai.d.ts +15 -0
  77. package/dist/lib/embeddings/openai.d.ts.map +1 -0
  78. package/dist/lib/embeddings/openai.js +58 -0
  79. package/dist/lib/embeddings/openai.js.map +1 -0
  80. package/dist/lib/embeddings/summarizer.d.ts +26 -0
  81. package/dist/lib/embeddings/summarizer.d.ts.map +1 -0
  82. package/dist/lib/embeddings/summarizer.js +181 -0
  83. package/dist/lib/embeddings/summarizer.js.map +1 -0
  84. package/dist/lib/embeddings/types.d.ts +17 -0
  85. package/dist/lib/embeddings/types.d.ts.map +1 -0
  86. package/dist/lib/embeddings/types.js +5 -0
  87. package/dist/lib/embeddings/types.js.map +1 -0
  88. package/dist/lib/embeddings/worker.d.ts +56 -0
  89. package/dist/lib/embeddings/worker.d.ts.map +1 -0
  90. package/dist/lib/embeddings/worker.js +109 -0
  91. package/dist/lib/embeddings/worker.js.map +1 -0
  92. package/dist/lib/event-bus.d.ts +48 -0
  93. package/dist/lib/event-bus.d.ts.map +1 -0
  94. package/dist/lib/event-bus.js +34 -0
  95. package/dist/lib/event-bus.js.map +1 -0
  96. package/dist/lib/retention.d.ts +1 -1
  97. package/dist/lib/retention.d.ts.map +1 -1
  98. package/dist/lib/sse.d.ts +22 -0
  99. package/dist/lib/sse.d.ts.map +1 -0
  100. package/dist/lib/sse.js +121 -0
  101. package/dist/lib/sse.js.map +1 -0
  102. package/dist/middleware/auth.d.ts +1 -0
  103. package/dist/middleware/auth.d.ts.map +1 -1
  104. package/dist/middleware/auth.js +2 -1
  105. package/dist/middleware/auth.js.map +1 -1
  106. package/dist/routes/agents.d.ts +1 -1
  107. package/dist/routes/agents.d.ts.map +1 -1
  108. package/dist/routes/agents.js +6 -3
  109. package/dist/routes/agents.js.map +1 -1
  110. package/dist/routes/alerts.d.ts +17 -0
  111. package/dist/routes/alerts.d.ts.map +1 -0
  112. package/dist/routes/alerts.js +141 -0
  113. package/dist/routes/alerts.js.map +1 -0
  114. package/dist/routes/analytics.d.ts +16 -0
  115. package/dist/routes/analytics.d.ts.map +1 -0
  116. package/dist/routes/analytics.js +307 -0
  117. package/dist/routes/analytics.js.map +1 -0
  118. package/dist/routes/api-keys.d.ts.map +1 -1
  119. package/dist/routes/api-keys.js +30 -5
  120. package/dist/routes/api-keys.js.map +1 -1
  121. package/dist/routes/config.d.ts +5 -0
  122. package/dist/routes/config.d.ts.map +1 -1
  123. package/dist/routes/config.js +27 -7
  124. package/dist/routes/config.js.map +1 -1
  125. package/dist/routes/context.d.ts +23 -0
  126. package/dist/routes/context.d.ts.map +1 -0
  127. package/dist/routes/context.js +52 -0
  128. package/dist/routes/context.js.map +1 -0
  129. package/dist/routes/events.d.ts +7 -2
  130. package/dist/routes/events.d.ts.map +1 -1
  131. package/dist/routes/events.js +76 -8
  132. package/dist/routes/events.js.map +1 -1
  133. package/dist/routes/ingest.d.ts +36 -0
  134. package/dist/routes/ingest.d.ts.map +1 -0
  135. package/dist/routes/ingest.js +280 -0
  136. package/dist/routes/ingest.js.map +1 -0
  137. package/dist/routes/lessons.d.ts +19 -0
  138. package/dist/routes/lessons.d.ts.map +1 -0
  139. package/dist/routes/lessons.js +164 -0
  140. package/dist/routes/lessons.js.map +1 -0
  141. package/dist/routes/recall.d.ts +20 -0
  142. package/dist/routes/recall.d.ts.map +1 -0
  143. package/dist/routes/recall.js +71 -0
  144. package/dist/routes/recall.js.map +1 -0
  145. package/dist/routes/reflect.d.ts +15 -0
  146. package/dist/routes/reflect.d.ts.map +1 -0
  147. package/dist/routes/reflect.js +55 -0
  148. package/dist/routes/reflect.js.map +1 -0
  149. package/dist/routes/sessions.d.ts +1 -1
  150. package/dist/routes/sessions.d.ts.map +1 -1
  151. package/dist/routes/sessions.js +9 -7
  152. package/dist/routes/sessions.js.map +1 -1
  153. package/dist/routes/stats.d.ts +1 -1
  154. package/dist/routes/stats.d.ts.map +1 -1
  155. package/dist/routes/stats.js +3 -1
  156. package/dist/routes/stats.js.map +1 -1
  157. package/dist/routes/stream.d.ts +23 -0
  158. package/dist/routes/stream.d.ts.map +1 -0
  159. package/dist/routes/stream.js +94 -0
  160. package/dist/routes/stream.js.map +1 -0
  161. package/dist/routes/tenant-helper.d.ts +20 -0
  162. package/dist/routes/tenant-helper.d.ts.map +1 -0
  163. package/dist/routes/tenant-helper.js +23 -0
  164. package/dist/routes/tenant-helper.js.map +1 -0
  165. package/package.json +11 -11
  166. package/LICENSE +0 -21
  167. package/dist/__tests__/agents-stats.test.d.ts +0 -5
  168. package/dist/__tests__/agents-stats.test.d.ts.map +0 -1
  169. package/dist/__tests__/agents-stats.test.js +0 -134
  170. package/dist/__tests__/agents-stats.test.js.map +0 -1
  171. package/dist/__tests__/api-keys.test.d.ts +0 -5
  172. package/dist/__tests__/api-keys.test.d.ts.map +0 -1
  173. package/dist/__tests__/api-keys.test.js +0 -118
  174. package/dist/__tests__/api-keys.test.js.map +0 -1
  175. package/dist/__tests__/auth-no-db.test.d.ts +0 -2
  176. package/dist/__tests__/auth-no-db.test.d.ts.map +0 -1
  177. package/dist/__tests__/auth-no-db.test.js +0 -43
  178. package/dist/__tests__/auth-no-db.test.js.map +0 -1
  179. package/dist/__tests__/auth.test.d.ts +0 -5
  180. package/dist/__tests__/auth.test.d.ts.map +0 -1
  181. package/dist/__tests__/auth.test.js +0 -86
  182. package/dist/__tests__/auth.test.js.map +0 -1
  183. package/dist/__tests__/config.test.d.ts +0 -2
  184. package/dist/__tests__/config.test.d.ts.map +0 -1
  185. package/dist/__tests__/config.test.js +0 -37
  186. package/dist/__tests__/config.test.js.map +0 -1
  187. package/dist/__tests__/events-ingest.test.d.ts +0 -5
  188. package/dist/__tests__/events-ingest.test.d.ts.map +0 -1
  189. package/dist/__tests__/events-ingest.test.js +0 -248
  190. package/dist/__tests__/events-ingest.test.js.map +0 -1
  191. package/dist/__tests__/events-query.test.d.ts +0 -5
  192. package/dist/__tests__/events-query.test.d.ts.map +0 -1
  193. package/dist/__tests__/events-query.test.js +0 -205
  194. package/dist/__tests__/events-query.test.js.map +0 -1
  195. package/dist/__tests__/health.test.d.ts +0 -5
  196. package/dist/__tests__/health.test.d.ts.map +0 -1
  197. package/dist/__tests__/health.test.js +0 -40
  198. package/dist/__tests__/health.test.js.map +0 -1
  199. package/dist/__tests__/sessions.test.d.ts +0 -5
  200. package/dist/__tests__/sessions.test.d.ts.map +0 -1
  201. package/dist/__tests__/sessions.test.js +0 -176
  202. package/dist/__tests__/sessions.test.js.map +0 -1
  203. package/dist/__tests__/test-helpers.d.ts +0 -24
  204. package/dist/__tests__/test-helpers.d.ts.map +0 -1
  205. package/dist/__tests__/test-helpers.js +0 -45
  206. package/dist/__tests__/test-helpers.js.map +0 -1
  207. package/dist/db/__tests__/init.test.d.ts +0 -2
  208. package/dist/db/__tests__/init.test.d.ts.map +0 -1
  209. package/dist/db/__tests__/init.test.js +0 -73
  210. package/dist/db/__tests__/init.test.js.map +0 -1
  211. package/dist/db/__tests__/sqlite-store-read.test.d.ts +0 -2
  212. package/dist/db/__tests__/sqlite-store-read.test.d.ts.map +0 -1
  213. package/dist/db/__tests__/sqlite-store-read.test.js +0 -749
  214. package/dist/db/__tests__/sqlite-store-read.test.js.map +0 -1
  215. package/dist/db/__tests__/sqlite-store-write.test.d.ts +0 -2
  216. package/dist/db/__tests__/sqlite-store-write.test.d.ts.map +0 -1
  217. package/dist/db/__tests__/sqlite-store-write.test.js +0 -418
  218. package/dist/db/__tests__/sqlite-store-write.test.js.map +0 -1
  219. package/dist/lib/__tests__/retention.test.d.ts +0 -2
  220. package/dist/lib/__tests__/retention.test.d.ts.map +0 -1
  221. package/dist/lib/__tests__/retention.test.js +0 -238
  222. 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":"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"}
@@ -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"}