@agentlensai/server 0.2.0 → 0.3.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 (92) hide show
  1. package/dist/__tests__/alerts.test.d.ts +5 -0
  2. package/dist/__tests__/alerts.test.d.ts.map +1 -0
  3. package/dist/__tests__/alerts.test.js +245 -0
  4. package/dist/__tests__/alerts.test.js.map +1 -0
  5. package/dist/__tests__/analytics.test.d.ts +5 -0
  6. package/dist/__tests__/analytics.test.d.ts.map +1 -0
  7. package/dist/__tests__/analytics.test.js +218 -0
  8. package/dist/__tests__/analytics.test.js.map +1 -0
  9. package/dist/__tests__/ingest.test.d.ts +8 -0
  10. package/dist/__tests__/ingest.test.d.ts.map +1 -0
  11. package/dist/__tests__/ingest.test.js +469 -0
  12. package/dist/__tests__/ingest.test.js.map +1 -0
  13. package/dist/__tests__/llm-tracking.test.d.ts +10 -0
  14. package/dist/__tests__/llm-tracking.test.d.ts.map +1 -0
  15. package/dist/__tests__/llm-tracking.test.js +602 -0
  16. package/dist/__tests__/llm-tracking.test.js.map +1 -0
  17. package/dist/__tests__/stream.test.d.ts +5 -0
  18. package/dist/__tests__/stream.test.d.ts.map +1 -0
  19. package/dist/__tests__/stream.test.js +352 -0
  20. package/dist/__tests__/stream.test.js.map +1 -0
  21. package/dist/db/__tests__/sqlite-store-read.test.js +1 -1
  22. package/dist/db/__tests__/sqlite-store-read.test.js.map +1 -1
  23. package/dist/db/__tests__/sqlite-store-write.test.js +1 -1
  24. package/dist/db/__tests__/sqlite-store-write.test.js.map +1 -1
  25. package/dist/db/migrate.d.ts.map +1 -1
  26. package/dist/db/migrate.js +17 -0
  27. package/dist/db/migrate.js.map +1 -1
  28. package/dist/db/schema.sqlite.d.ts +52 -1
  29. package/dist/db/schema.sqlite.d.ts.map +1 -1
  30. package/dist/db/schema.sqlite.js +3 -0
  31. package/dist/db/schema.sqlite.js.map +1 -1
  32. package/dist/db/sqlite-store.d.ts +12 -2
  33. package/dist/db/sqlite-store.d.ts.map +1 -1
  34. package/dist/db/sqlite-store.js +96 -5
  35. package/dist/db/sqlite-store.js.map +1 -1
  36. package/dist/index.d.ts +9 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +35 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/lib/__tests__/alert-engine.test.d.ts +5 -0
  41. package/dist/lib/__tests__/alert-engine.test.d.ts.map +1 -0
  42. package/dist/lib/__tests__/alert-engine.test.js +211 -0
  43. package/dist/lib/__tests__/alert-engine.test.js.map +1 -0
  44. package/dist/lib/__tests__/retention.test.js +1 -1
  45. package/dist/lib/__tests__/retention.test.js.map +1 -1
  46. package/dist/lib/alert-engine.d.ts +61 -0
  47. package/dist/lib/alert-engine.d.ts.map +1 -0
  48. package/dist/lib/alert-engine.js +280 -0
  49. package/dist/lib/alert-engine.js.map +1 -0
  50. package/dist/lib/event-bus.d.ts +48 -0
  51. package/dist/lib/event-bus.d.ts.map +1 -0
  52. package/dist/lib/event-bus.js +34 -0
  53. package/dist/lib/event-bus.js.map +1 -0
  54. package/dist/lib/retention.d.ts +1 -1
  55. package/dist/lib/retention.d.ts.map +1 -1
  56. package/dist/lib/sse.d.ts +21 -0
  57. package/dist/lib/sse.d.ts.map +1 -0
  58. package/dist/lib/sse.js +114 -0
  59. package/dist/lib/sse.js.map +1 -0
  60. package/dist/routes/agents.d.ts +1 -1
  61. package/dist/routes/agents.d.ts.map +1 -1
  62. package/dist/routes/alerts.d.ts +17 -0
  63. package/dist/routes/alerts.d.ts.map +1 -0
  64. package/dist/routes/alerts.js +133 -0
  65. package/dist/routes/alerts.js.map +1 -0
  66. package/dist/routes/analytics.d.ts +16 -0
  67. package/dist/routes/analytics.d.ts.map +1 -0
  68. package/dist/routes/analytics.js +293 -0
  69. package/dist/routes/analytics.js.map +1 -0
  70. package/dist/routes/config.d.ts +5 -0
  71. package/dist/routes/config.d.ts.map +1 -1
  72. package/dist/routes/config.js +27 -7
  73. package/dist/routes/config.js.map +1 -1
  74. package/dist/routes/events.d.ts +1 -1
  75. package/dist/routes/events.d.ts.map +1 -1
  76. package/dist/routes/events.js +17 -4
  77. package/dist/routes/events.js.map +1 -1
  78. package/dist/routes/ingest.d.ts +30 -0
  79. package/dist/routes/ingest.d.ts.map +1 -0
  80. package/dist/routes/ingest.js +261 -0
  81. package/dist/routes/ingest.js.map +1 -0
  82. package/dist/routes/sessions.d.ts +1 -1
  83. package/dist/routes/sessions.d.ts.map +1 -1
  84. package/dist/routes/sessions.js +1 -1
  85. package/dist/routes/sessions.js.map +1 -1
  86. package/dist/routes/stats.d.ts +1 -1
  87. package/dist/routes/stats.d.ts.map +1 -1
  88. package/dist/routes/stream.d.ts +16 -0
  89. package/dist/routes/stream.d.ts.map +1 -0
  90. package/dist/routes/stream.js +41 -0
  91. package/dist/routes/stream.js.map +1 -0
  92. package/package.json +2 -2
@@ -0,0 +1,280 @@
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 { eventBus } from './event-bus.js';
13
+ /** Default evaluation interval: 60 seconds */
14
+ const DEFAULT_CHECK_INTERVAL_MS = 60_000;
15
+ // ─── SSRF Protection ────────────────────────────────────────────────
16
+ /**
17
+ * Validate that a webhook URL is safe to deliver to.
18
+ * Blocks private/reserved IPs and non-HTTP(S) schemes to prevent SSRF.
19
+ */
20
+ export function isWebhookUrlAllowed(url) {
21
+ let parsed;
22
+ try {
23
+ parsed = new URL(url);
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ // Only allow http and https schemes
29
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
30
+ return false;
31
+ }
32
+ const hostname = parsed.hostname.replace(/^\[|\]$/g, ''); // strip IPv6 brackets
33
+ // Allow http://localhost only for dev
34
+ if (parsed.protocol === 'http:' && hostname !== 'localhost' && hostname !== '127.0.0.1') {
35
+ // In production, require https for non-localhost
36
+ // For now, allow http in dev but still block private IPs below
37
+ }
38
+ // Block IPv6 loopback
39
+ if (hostname === '::1' || hostname === '0:0:0:0:0:0:0:1') {
40
+ return false;
41
+ }
42
+ // Block private/reserved IPv4 ranges
43
+ const PRIVATE_IP_PATTERNS = [
44
+ /^127\./, // loopback
45
+ /^10\./, // 10.0.0.0/8
46
+ /^172\.(1[6-9]|2\d|3[01])\./, // 172.16.0.0/12
47
+ /^192\.168\./, // 192.168.0.0/16
48
+ /^169\.254\./, // link-local
49
+ /^0\./, // 0.0.0.0/8
50
+ ];
51
+ if (PRIVATE_IP_PATTERNS.some((p) => p.test(hostname))) {
52
+ return false;
53
+ }
54
+ // Block if hostname resolves to a known-private DNS name
55
+ if (hostname === 'localhost' && parsed.protocol === 'https:') {
56
+ // https://localhost is suspicious but technically okay for dev
57
+ return true;
58
+ }
59
+ return true;
60
+ }
61
+ export class AlertEngine {
62
+ store;
63
+ timer = null;
64
+ running = false;
65
+ checkIntervalMs;
66
+ constructor(store, options) {
67
+ this.store = store;
68
+ this.checkIntervalMs =
69
+ options?.checkIntervalMs ??
70
+ (parseInt(process.env['ALERT_CHECK_INTERVAL_MS'] ?? '', 10) ||
71
+ DEFAULT_CHECK_INTERVAL_MS);
72
+ }
73
+ /**
74
+ * Start the periodic evaluation loop.
75
+ */
76
+ start() {
77
+ if (this.timer)
78
+ return;
79
+ console.log(`[AlertEngine] Starting evaluation loop (interval: ${this.checkIntervalMs}ms)`);
80
+ this.timer = setInterval(() => {
81
+ this.evaluate().catch((err) => {
82
+ console.error('[AlertEngine] Evaluation error:', err);
83
+ });
84
+ }, this.checkIntervalMs);
85
+ }
86
+ /**
87
+ * Stop the evaluation loop.
88
+ */
89
+ stop() {
90
+ if (this.timer) {
91
+ clearInterval(this.timer);
92
+ this.timer = null;
93
+ console.log('[AlertEngine] Stopped');
94
+ }
95
+ }
96
+ /**
97
+ * Run a single evaluation cycle (can also be called manually / from tests).
98
+ */
99
+ async evaluate() {
100
+ if (this.running)
101
+ return [];
102
+ this.running = true;
103
+ try {
104
+ const rules = await this.store.listAlertRules();
105
+ const enabledRules = rules.filter((r) => r.enabled);
106
+ const triggered = [];
107
+ for (const rule of enabledRules) {
108
+ try {
109
+ const currentValue = await this.computeCurrentValue(rule);
110
+ const shouldTrigger = this.checkCondition(rule.condition, currentValue, rule.threshold);
111
+ if (shouldTrigger) {
112
+ // M1 fix: Deduplication — skip if the rule already triggered within its window
113
+ const recentHistory = await this.store.listAlertHistory({ ruleId: rule.id, limit: 1 });
114
+ if (recentHistory.entries.length > 0) {
115
+ const lastTrigger = new Date(recentHistory.entries[0].triggeredAt).getTime();
116
+ const cooldownMs = rule.windowMinutes * 60_000;
117
+ if (Date.now() - lastTrigger < cooldownMs) {
118
+ continue; // Still within cooldown period
119
+ }
120
+ }
121
+ const entry = await this.triggerAlert(rule, currentValue);
122
+ triggered.push(entry);
123
+ }
124
+ }
125
+ catch (err) {
126
+ console.error(`[AlertEngine] Error evaluating rule "${rule.name}" (${rule.id}):`, err);
127
+ }
128
+ }
129
+ return triggered;
130
+ }
131
+ finally {
132
+ this.running = false;
133
+ }
134
+ }
135
+ /**
136
+ * Compute the current metric value for a rule by querying analytics.
137
+ */
138
+ async computeCurrentValue(rule) {
139
+ const now = new Date();
140
+ const windowStart = new Date(now.getTime() - rule.windowMinutes * 60_000);
141
+ const from = windowStart.toISOString();
142
+ const to = now.toISOString();
143
+ const analytics = await this.store.getAnalytics({
144
+ from,
145
+ to,
146
+ agentId: rule.scope.agentId,
147
+ granularity: 'hour',
148
+ });
149
+ const totals = analytics.totals;
150
+ switch (rule.condition) {
151
+ case 'error_rate_exceeds': {
152
+ if (totals.eventCount === 0)
153
+ return 0;
154
+ return totals.errorCount / totals.eventCount;
155
+ }
156
+ case 'cost_exceeds': {
157
+ return totals.totalCostUsd;
158
+ }
159
+ case 'latency_exceeds': {
160
+ return totals.avgLatencyMs;
161
+ }
162
+ case 'event_count_exceeds': {
163
+ return totals.eventCount;
164
+ }
165
+ case 'no_events_for': {
166
+ // For no_events_for, the "value" is the number of events.
167
+ // Trigger if event count is 0 (or below threshold — threshold is ignored,
168
+ // the rule triggers when there are zero events in the window).
169
+ return totals.eventCount;
170
+ }
171
+ default:
172
+ return 0;
173
+ }
174
+ }
175
+ /**
176
+ * Check whether the condition is met.
177
+ */
178
+ checkCondition(condition, currentValue, threshold) {
179
+ switch (condition) {
180
+ case 'error_rate_exceeds':
181
+ case 'cost_exceeds':
182
+ case 'latency_exceeds':
183
+ case 'event_count_exceeds':
184
+ return currentValue > threshold;
185
+ case 'no_events_for':
186
+ // Triggers when event count is 0 (no events in window)
187
+ return currentValue === 0;
188
+ default:
189
+ return false;
190
+ }
191
+ }
192
+ /**
193
+ * Trigger an alert: persist history, deliver webhooks, emit on EventBus.
194
+ */
195
+ async triggerAlert(rule, currentValue) {
196
+ const now = new Date().toISOString();
197
+ const message = this.buildMessage(rule, currentValue);
198
+ const entry = {
199
+ id: ulid(),
200
+ ruleId: rule.id,
201
+ triggeredAt: now,
202
+ currentValue,
203
+ threshold: rule.threshold,
204
+ message,
205
+ };
206
+ // 1. Persist to alert history
207
+ await this.store.insertAlertHistory(entry);
208
+ // 2. Console log
209
+ console.log(`[AlertEngine] 🔔 Alert triggered: "${rule.name}" — ${message}`);
210
+ // 3. Emit on EventBus (Story 12.5)
211
+ eventBus.emit({
212
+ type: 'alert_triggered',
213
+ rule,
214
+ history: entry,
215
+ timestamp: now,
216
+ });
217
+ // 4. Webhook delivery (Story 12.3) — fire and forget
218
+ await this.deliverWebhooks(rule, entry);
219
+ return entry;
220
+ }
221
+ /**
222
+ * Deliver alert to configured webhook URLs (Story 12.3).
223
+ */
224
+ async deliverWebhooks(rule, entry) {
225
+ const webhookUrls = rule.notifyChannels.filter((ch) => (ch.startsWith('http://') || ch.startsWith('https://')) && isWebhookUrlAllowed(ch));
226
+ if (webhookUrls.length === 0)
227
+ return;
228
+ const payload = {
229
+ alertRuleId: rule.id,
230
+ alertName: rule.name,
231
+ condition: rule.condition,
232
+ currentValue: entry.currentValue,
233
+ threshold: entry.threshold,
234
+ message: entry.message,
235
+ triggeredAt: entry.triggeredAt,
236
+ windowMinutes: rule.windowMinutes,
237
+ scope: rule.scope,
238
+ };
239
+ for (const url of webhookUrls) {
240
+ try {
241
+ const res = await fetch(url, {
242
+ method: 'POST',
243
+ headers: { 'Content-Type': 'application/json' },
244
+ body: JSON.stringify(payload),
245
+ signal: AbortSignal.timeout(10_000), // 10s timeout
246
+ });
247
+ if (!res.ok) {
248
+ console.warn(`[AlertEngine] Webhook delivery to ${url} failed: HTTP ${res.status}`);
249
+ }
250
+ else {
251
+ console.log(`[AlertEngine] Webhook delivered to ${url}`);
252
+ }
253
+ }
254
+ catch (err) {
255
+ console.warn(`[AlertEngine] Webhook delivery to ${url} error:`, err instanceof Error ? err.message : err);
256
+ }
257
+ }
258
+ }
259
+ /**
260
+ * Build a human-readable alert message.
261
+ */
262
+ buildMessage(rule, currentValue) {
263
+ const formatted = (v) => Number.isInteger(v) ? v.toString() : v.toFixed(4);
264
+ switch (rule.condition) {
265
+ case 'error_rate_exceeds':
266
+ return `Error rate ${(currentValue * 100).toFixed(1)}% exceeds threshold ${(rule.threshold * 100).toFixed(1)}% in the last ${rule.windowMinutes}m`;
267
+ case 'cost_exceeds':
268
+ return `Cost $${formatted(currentValue)} exceeds threshold $${formatted(rule.threshold)} in the last ${rule.windowMinutes}m`;
269
+ case 'latency_exceeds':
270
+ return `Average latency ${formatted(currentValue)}ms exceeds threshold ${formatted(rule.threshold)}ms in the last ${rule.windowMinutes}m`;
271
+ case 'event_count_exceeds':
272
+ return `Event count ${formatted(currentValue)} exceeds threshold ${formatted(rule.threshold)} in the last ${rule.windowMinutes}m`;
273
+ case 'no_events_for':
274
+ return `No events received in the last ${rule.windowMinutes}m`;
275
+ default:
276
+ return `Alert condition met: ${rule.condition}`;
277
+ }
278
+ }
279
+ }
280
+ //# 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,48 @@
1
+ /**
2
+ * In-process EventBus for real-time notifications (Story 12.5, Epic 14 — Arch §11.1)
3
+ *
4
+ * Typed EventEmitter for decoupled communication between
5
+ * server components. Consumed by SSE endpoint (Epic 14) and Alert Engine (Epic 12).
6
+ */
7
+ import type { AgentLensEvent, AlertRule, AlertHistory, Session } from '@agentlensai/core';
8
+ export interface AlertTriggeredEvent {
9
+ type: 'alert_triggered';
10
+ rule: AlertRule;
11
+ history: AlertHistory;
12
+ timestamp: string;
13
+ }
14
+ export interface AlertResolvedEvent {
15
+ type: 'alert_resolved';
16
+ ruleId: string;
17
+ historyId: string;
18
+ timestamp: string;
19
+ }
20
+ /** New event ingested (Epic 14 — Story 14.1) */
21
+ export interface EventIngestedEvent {
22
+ type: 'event_ingested';
23
+ event: AgentLensEvent;
24
+ timestamp: string;
25
+ }
26
+ /** Session updated (Epic 14 — Story 14.1) */
27
+ export interface SessionUpdatedEvent {
28
+ type: 'session_updated';
29
+ session: Session;
30
+ timestamp: string;
31
+ }
32
+ export type BusEvent = AlertTriggeredEvent | AlertResolvedEvent | EventIngestedEvent | SessionUpdatedEvent;
33
+ /**
34
+ * Typed event bus for internal server communication.
35
+ * Wraps Node.js EventEmitter with type safety.
36
+ */
37
+ declare class EventBus {
38
+ private emitter;
39
+ constructor();
40
+ emit(event: BusEvent): void;
41
+ on(type: BusEvent['type'] | '*', listener: (event: BusEvent) => void): void;
42
+ off(type: BusEvent['type'] | '*', listener: (event: BusEvent) => void): void;
43
+ removeAllListeners(): void;
44
+ }
45
+ /** Singleton event bus instance */
46
+ export declare const eventBus: EventBus;
47
+ export {};
48
+ //# sourceMappingURL=event-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/lib/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAI1F,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,QAAQ,GAChB,mBAAmB,GACnB,kBAAkB,GAClB,kBAAkB,GAClB,mBAAmB,CAAC;AAExB;;;GAGG;AACH,cAAM,QAAQ;IACZ,OAAO,CAAC,OAAO,CAAsB;;IAOrC,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAK3B,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAI3E,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAI5E,kBAAkB,IAAI,IAAI;CAG3B;AAED,mCAAmC;AACnC,eAAO,MAAM,QAAQ,UAAiB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * In-process EventBus for real-time notifications (Story 12.5, Epic 14 — Arch §11.1)
3
+ *
4
+ * Typed EventEmitter for decoupled communication between
5
+ * server components. Consumed by SSE endpoint (Epic 14) and Alert Engine (Epic 12).
6
+ */
7
+ import { EventEmitter } from 'node:events';
8
+ /**
9
+ * Typed event bus for internal server communication.
10
+ * Wraps Node.js EventEmitter with type safety.
11
+ */
12
+ class EventBus {
13
+ emitter = new EventEmitter();
14
+ constructor() {
15
+ // Allow many SSE clients
16
+ this.emitter.setMaxListeners(1000);
17
+ }
18
+ emit(event) {
19
+ this.emitter.emit(event.type, event);
20
+ this.emitter.emit('*', event); // wildcard for "all events"
21
+ }
22
+ on(type, listener) {
23
+ this.emitter.on(type, listener);
24
+ }
25
+ off(type, listener) {
26
+ this.emitter.off(type, listener);
27
+ }
28
+ removeAllListeners() {
29
+ this.emitter.removeAllListeners();
30
+ }
31
+ }
32
+ /** Singleton event bus instance */
33
+ export const eventBus = new EventBus();
34
+ //# sourceMappingURL=event-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../src/lib/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAuC3C;;;GAGG;AACH,MAAM,QAAQ;IACJ,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IAErC;QACE,yBAAyB;QACzB,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,KAAe;QAClB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,4BAA4B;IAC7D,CAAC;IAED,EAAE,CAAC,IAA4B,EAAE,QAAmC;QAClE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,IAA4B,EAAE,QAAmC;QACnE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,kBAAkB;QAChB,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IACpC,CAAC;CACF;AAED,mCAAmC;AACnC,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC"}
@@ -7,7 +7,7 @@
7
7
  * This is the ONE exception to the "no DELETE on events" rule —
8
8
  * retention cleanup is the audited, authorized path for event deletion.
9
9
  */
10
- import type { IEventStore } from '@agentlens/core';
10
+ import type { IEventStore } from '@agentlensai/core';
11
11
  export interface RetentionPolicy {
12
12
  /** Keep events for this many days (0 = keep forever) */
13
13
  retentionDays: number;
@@ -1 +1 @@
1
- {"version":3,"file":"retention.d.ts","sourceRoot":"","sources":["../../src/lib/retention.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;CACvB;AAID;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CASpD;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,WAAW,EAClB,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAoBrD"}
1
+ {"version":3,"file":"retention.d.ts","sourceRoot":"","sources":["../../src/lib/retention.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;CACvB;AAID;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CASpD;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,WAAW,EAClB,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAoBrD"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * SSE Connection Manager (Story 14.1, Arch §11.3)
3
+ *
4
+ * Creates a ReadableStream that:
5
+ * - Subscribes to EventBus with optional filters
6
+ * - Sends heartbeat every 30s
7
+ * - Cleans up on client disconnect
8
+ */
9
+ export interface SSEFilters {
10
+ sessionId?: string;
11
+ agentId?: string;
12
+ eventTypes?: string[];
13
+ }
14
+ /**
15
+ * Create a ReadableStream for SSE that subscribes to the EventBus.
16
+ *
17
+ * @param filters — optional filters for sessionId, agentId, eventTypes
18
+ * @param signal — AbortSignal from the request for disconnect cleanup
19
+ */
20
+ export declare function createSSEStream(filters: SSEFilters, signal: AbortSignal): ReadableStream<Uint8Array>;
21
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/lib/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AA4BD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,WAAW,GAClB,cAAc,CAAC,UAAU,CAAC,CA6E5B"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * SSE Connection Manager (Story 14.1, Arch §11.3)
3
+ *
4
+ * Creates a ReadableStream that:
5
+ * - Subscribes to EventBus with optional filters
6
+ * - Sends heartbeat every 30s
7
+ * - Cleans up on client disconnect
8
+ */
9
+ import { eventBus } from './event-bus.js';
10
+ /** Default heartbeat interval in ms */
11
+ const HEARTBEAT_INTERVAL_MS = 30_000;
12
+ /**
13
+ * Format an SSE message (event: type\ndata: json\n\n)
14
+ */
15
+ function formatSSE(eventName, data) {
16
+ return `event: ${eventName}\ndata: ${JSON.stringify(data)}\n\n`;
17
+ }
18
+ /**
19
+ * Check if an ingested event matches the SSE filters.
20
+ */
21
+ function matchesFilters(event, filters) {
22
+ if (filters.sessionId && event.sessionId !== filters.sessionId)
23
+ return false;
24
+ if (filters.agentId && event.agentId !== filters.agentId)
25
+ return false;
26
+ if (filters.eventTypes &&
27
+ filters.eventTypes.length > 0 &&
28
+ !filters.eventTypes.includes(event.eventType)) {
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ /**
34
+ * Create a ReadableStream for SSE that subscribes to the EventBus.
35
+ *
36
+ * @param filters — optional filters for sessionId, agentId, eventTypes
37
+ * @param signal — AbortSignal from the request for disconnect cleanup
38
+ */
39
+ export function createSSEStream(filters, signal) {
40
+ const encoder = new TextEncoder();
41
+ return new ReadableStream({
42
+ start(controller) {
43
+ const send = (eventName, data) => {
44
+ try {
45
+ controller.enqueue(encoder.encode(formatSSE(eventName, data)));
46
+ }
47
+ catch {
48
+ // Controller may be closed if client disconnected
49
+ }
50
+ };
51
+ // Send initial heartbeat so client knows connection is alive
52
+ send('heartbeat', { time: new Date().toISOString() });
53
+ // Heartbeat every 30s to keep connection alive
54
+ const heartbeatTimer = setInterval(() => {
55
+ send('heartbeat', { time: new Date().toISOString() });
56
+ }, HEARTBEAT_INTERVAL_MS);
57
+ // Handler for all bus events
58
+ const handler = (busEvent) => {
59
+ switch (busEvent.type) {
60
+ case 'event_ingested': {
61
+ const ev = busEvent.event;
62
+ if (matchesFilters(ev, filters)) {
63
+ send('event', ev);
64
+ }
65
+ break;
66
+ }
67
+ case 'session_updated': {
68
+ const session = busEvent.session;
69
+ // Session updates are sent if:
70
+ // - No sessionId filter, or matching sessionId
71
+ // - No agentId filter, or matching agentId
72
+ if (filters.sessionId && session.id !== filters.sessionId)
73
+ break;
74
+ if (filters.agentId && session.agentId !== filters.agentId)
75
+ break;
76
+ send('session_update', session);
77
+ break;
78
+ }
79
+ case 'alert_triggered': {
80
+ const alert = busEvent;
81
+ // Alerts are sent to all connections (no filter)
82
+ send('alert', {
83
+ ruleId: alert.rule.id,
84
+ name: alert.rule.name,
85
+ currentValue: alert.history.currentValue,
86
+ threshold: alert.history.threshold,
87
+ message: alert.history.message,
88
+ triggeredAt: alert.history.triggeredAt,
89
+ });
90
+ break;
91
+ }
92
+ // alert_resolved is informational, not critical for SSE
93
+ default:
94
+ break;
95
+ }
96
+ };
97
+ // Subscribe to all bus events
98
+ eventBus.on('*', handler);
99
+ // Cleanup on client disconnect
100
+ const cleanup = () => {
101
+ clearInterval(heartbeatTimer);
102
+ eventBus.off('*', handler);
103
+ try {
104
+ controller.close();
105
+ }
106
+ catch {
107
+ // Already closed
108
+ }
109
+ };
110
+ signal.addEventListener('abort', cleanup, { once: true });
111
+ },
112
+ });
113
+ }
114
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/lib/sse.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  * GET /api/agents/:id — single agent
6
6
  */
7
7
  import { Hono } from 'hono';
8
- import type { IEventStore } from '@agentlens/core';
8
+ import type { IEventStore } from '@agentlensai/core';
9
9
  import type { AuthVariables } from '../middleware/auth.js';
10
10
  export declare function agentsRoutes(store: IEventStore): Hono<{
11
11
  Variables: AuthVariables;
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CAiChD"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CAiChD"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Alert Endpoints (Story 12.1)
3
+ *
4
+ * POST /api/alerts/rules — create alert rule
5
+ * GET /api/alerts/rules — list alert rules
6
+ * GET /api/alerts/rules/:id — get single alert rule
7
+ * PUT /api/alerts/rules/:id — update alert rule
8
+ * DELETE /api/alerts/rules/:id — delete alert rule
9
+ * GET /api/alerts/history — list alert history
10
+ */
11
+ import { Hono } from 'hono';
12
+ import type { IEventStore } from '@agentlensai/core';
13
+ import type { AuthVariables } from '../middleware/auth.js';
14
+ export declare function alertsRoutes(store: IEventStore): Hono<{
15
+ Variables: AuthVariables;
16
+ }, import("hono/types").BlankSchema, "/">;
17
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/routes/alerts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CA+IhD"}