@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.
- package/dist/__tests__/alerts.test.d.ts +5 -0
- package/dist/__tests__/alerts.test.d.ts.map +1 -0
- package/dist/__tests__/alerts.test.js +245 -0
- package/dist/__tests__/alerts.test.js.map +1 -0
- package/dist/__tests__/analytics.test.d.ts +5 -0
- package/dist/__tests__/analytics.test.d.ts.map +1 -0
- package/dist/__tests__/analytics.test.js +218 -0
- package/dist/__tests__/analytics.test.js.map +1 -0
- package/dist/__tests__/ingest.test.d.ts +8 -0
- package/dist/__tests__/ingest.test.d.ts.map +1 -0
- package/dist/__tests__/ingest.test.js +469 -0
- package/dist/__tests__/ingest.test.js.map +1 -0
- package/dist/__tests__/llm-tracking.test.d.ts +10 -0
- package/dist/__tests__/llm-tracking.test.d.ts.map +1 -0
- package/dist/__tests__/llm-tracking.test.js +602 -0
- package/dist/__tests__/llm-tracking.test.js.map +1 -0
- package/dist/__tests__/stream.test.d.ts +5 -0
- package/dist/__tests__/stream.test.d.ts.map +1 -0
- package/dist/__tests__/stream.test.js +352 -0
- package/dist/__tests__/stream.test.js.map +1 -0
- package/dist/db/__tests__/sqlite-store-read.test.js +1 -1
- package/dist/db/__tests__/sqlite-store-read.test.js.map +1 -1
- package/dist/db/__tests__/sqlite-store-write.test.js +1 -1
- package/dist/db/__tests__/sqlite-store-write.test.js.map +1 -1
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +17 -0
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/schema.sqlite.d.ts +52 -1
- package/dist/db/schema.sqlite.d.ts.map +1 -1
- package/dist/db/schema.sqlite.js +3 -0
- package/dist/db/schema.sqlite.js.map +1 -1
- package/dist/db/sqlite-store.d.ts +12 -2
- package/dist/db/sqlite-store.d.ts.map +1 -1
- package/dist/db/sqlite-store.js +96 -5
- package/dist/db/sqlite-store.js.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/__tests__/alert-engine.test.d.ts +5 -0
- package/dist/lib/__tests__/alert-engine.test.d.ts.map +1 -0
- package/dist/lib/__tests__/alert-engine.test.js +211 -0
- package/dist/lib/__tests__/alert-engine.test.js.map +1 -0
- package/dist/lib/__tests__/retention.test.js +1 -1
- package/dist/lib/__tests__/retention.test.js.map +1 -1
- package/dist/lib/alert-engine.d.ts +61 -0
- package/dist/lib/alert-engine.d.ts.map +1 -0
- package/dist/lib/alert-engine.js +280 -0
- package/dist/lib/alert-engine.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 +21 -0
- package/dist/lib/sse.d.ts.map +1 -0
- package/dist/lib/sse.js +114 -0
- package/dist/lib/sse.js.map +1 -0
- package/dist/routes/agents.d.ts +1 -1
- package/dist/routes/agents.d.ts.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 +133 -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 +293 -0
- package/dist/routes/analytics.js.map +1 -0
- 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/events.d.ts +1 -1
- package/dist/routes/events.d.ts.map +1 -1
- package/dist/routes/events.js +17 -4
- package/dist/routes/events.js.map +1 -1
- package/dist/routes/ingest.d.ts +30 -0
- package/dist/routes/ingest.d.ts.map +1 -0
- package/dist/routes/ingest.js +261 -0
- package/dist/routes/ingest.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 +1 -1
- 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/stream.d.ts +16 -0
- package/dist/routes/stream.d.ts.map +1 -0
- package/dist/routes/stream.js +41 -0
- package/dist/routes/stream.js.map +1 -0
- 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"}
|
package/dist/lib/retention.d.ts
CHANGED
|
@@ -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 '@
|
|
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,
|
|
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"}
|
package/dist/lib/sse.js
ADDED
|
@@ -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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"}
|
package/dist/routes/agents.d.ts
CHANGED
|
@@ -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 '@
|
|
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,
|
|
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"}
|