@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,133 @@
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 { ulid } from 'ulid';
13
+ import { createAlertRuleSchema, updateAlertRuleSchema } from '@agentlensai/core';
14
+ import { NotFoundError } from '../db/errors.js';
15
+ export function alertsRoutes(store) {
16
+ const app = new Hono();
17
+ // POST /api/alerts/rules — create alert rule
18
+ app.post('/rules', async (c) => {
19
+ const rawBody = await c.req.json().catch(() => null);
20
+ if (!rawBody) {
21
+ return c.json({ error: 'Invalid JSON body', status: 400 }, 400);
22
+ }
23
+ const parseResult = createAlertRuleSchema.safeParse(rawBody);
24
+ if (!parseResult.success) {
25
+ return c.json({
26
+ error: 'Validation failed',
27
+ status: 400,
28
+ details: parseResult.error.issues.map((issue) => ({
29
+ path: issue.path.join('.'),
30
+ message: issue.message,
31
+ })),
32
+ }, 400);
33
+ }
34
+ const input = parseResult.data;
35
+ const now = new Date().toISOString();
36
+ const rule = {
37
+ id: ulid(),
38
+ name: input.name,
39
+ enabled: input.enabled,
40
+ condition: input.condition,
41
+ threshold: input.threshold,
42
+ windowMinutes: input.windowMinutes,
43
+ scope: input.scope,
44
+ notifyChannels: input.notifyChannels,
45
+ createdAt: now,
46
+ updatedAt: now,
47
+ };
48
+ await store.createAlertRule(rule);
49
+ return c.json(rule, 201);
50
+ });
51
+ // GET /api/alerts/rules — list all alert rules
52
+ app.get('/rules', async (c) => {
53
+ const rules = await store.listAlertRules();
54
+ return c.json({ rules });
55
+ });
56
+ // GET /api/alerts/rules/:id — get single alert rule
57
+ app.get('/rules/:id', async (c) => {
58
+ const id = c.req.param('id');
59
+ const rule = await store.getAlertRule(id);
60
+ if (!rule) {
61
+ return c.json({ error: 'Alert rule not found', status: 404 }, 404);
62
+ }
63
+ return c.json(rule);
64
+ });
65
+ // PUT /api/alerts/rules/:id — update alert rule
66
+ app.put('/rules/:id', async (c) => {
67
+ const id = c.req.param('id');
68
+ const rawBody = await c.req.json().catch(() => null);
69
+ if (!rawBody) {
70
+ return c.json({ error: 'Invalid JSON body', status: 400 }, 400);
71
+ }
72
+ const parseResult = updateAlertRuleSchema.safeParse(rawBody);
73
+ if (!parseResult.success) {
74
+ return c.json({
75
+ error: 'Validation failed',
76
+ status: 400,
77
+ details: parseResult.error.issues.map((issue) => ({
78
+ path: issue.path.join('.'),
79
+ message: issue.message,
80
+ })),
81
+ }, 400);
82
+ }
83
+ const updates = parseResult.data;
84
+ try {
85
+ await store.updateAlertRule(id, {
86
+ ...updates,
87
+ updatedAt: new Date().toISOString(),
88
+ });
89
+ }
90
+ catch (err) {
91
+ if (err instanceof NotFoundError) {
92
+ return c.json({ error: 'Alert rule not found', status: 404 }, 404);
93
+ }
94
+ throw err;
95
+ }
96
+ const updated = await store.getAlertRule(id);
97
+ return c.json(updated);
98
+ });
99
+ // DELETE /api/alerts/rules/:id — delete alert rule
100
+ app.delete('/rules/:id', async (c) => {
101
+ const id = c.req.param('id');
102
+ try {
103
+ await store.deleteAlertRule(id);
104
+ }
105
+ catch (err) {
106
+ if (err instanceof NotFoundError) {
107
+ return c.json({ error: 'Alert rule not found', status: 404 }, 404);
108
+ }
109
+ throw err;
110
+ }
111
+ return c.json({ id, deleted: true });
112
+ });
113
+ // GET /api/alerts/history — list alert history
114
+ app.get('/history', async (c) => {
115
+ const ruleId = c.req.query('ruleId');
116
+ const limitStr = c.req.query('limit');
117
+ const offsetStr = c.req.query('offset');
118
+ const limit = limitStr ? Math.max(1, Math.min(parseInt(limitStr, 10) || 50, 500)) : 50;
119
+ const offset = offsetStr ? Math.max(0, parseInt(offsetStr, 10) || 0) : 0;
120
+ const result = await store.listAlertHistory({
121
+ ruleId: ruleId ?? undefined,
122
+ limit,
123
+ offset,
124
+ });
125
+ return c.json({
126
+ entries: result.entries,
127
+ total: result.total,
128
+ hasMore: offset + result.entries.length < result.total,
129
+ });
130
+ });
131
+ return app;
132
+ }
133
+ //# sourceMappingURL=alerts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/routes/alerts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Analytics Endpoints (Story 11.2)
3
+ *
4
+ * GET /api/analytics — bucketed metrics over time
5
+ * GET /api/analytics/costs — cost breakdown by agent and time period
6
+ * GET /api/analytics/agents — per-agent metrics (session count, error rate, avg duration, total cost)
7
+ * GET /api/analytics/tools — tool usage statistics (frequency, avg duration, error rate per tool)
8
+ */
9
+ import { Hono } from 'hono';
10
+ import type { IEventStore } from '@agentlensai/core';
11
+ import type { AuthVariables } from '../middleware/auth.js';
12
+ import type { SqliteDb } from '../db/index.js';
13
+ export declare function analyticsRoutes(store: IEventStore, db: SqliteDb): Hono<{
14
+ Variables: AuthVariables;
15
+ }, import("hono/types").BlankSchema, "/">;
16
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ;eAC5B,aAAa;0CAuXhD"}
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Analytics Endpoints (Story 11.2)
3
+ *
4
+ * GET /api/analytics — bucketed metrics over time
5
+ * GET /api/analytics/costs — cost breakdown by agent and time period
6
+ * GET /api/analytics/agents — per-agent metrics (session count, error rate, avg duration, total cost)
7
+ * GET /api/analytics/tools — tool usage statistics (frequency, avg duration, error rate per tool)
8
+ */
9
+ import { Hono } from 'hono';
10
+ import { sql, gte, lte, eq, and } from 'drizzle-orm';
11
+ import { events, sessions } from '../db/schema.sqlite.js';
12
+ export function analyticsRoutes(store, db) {
13
+ const app = new Hono();
14
+ // GET /api/analytics — bucketed metrics over time
15
+ app.get('/', async (c) => {
16
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
17
+ const to = c.req.query('to') ?? new Date().toISOString();
18
+ const granularity = (c.req.query('granularity') ?? 'hour');
19
+ const agentId = c.req.query('agentId');
20
+ if (!['hour', 'day', 'week'].includes(granularity)) {
21
+ return c.json({ error: 'Invalid granularity. Use: hour, day, week', status: 400 }, 400);
22
+ }
23
+ const result = await store.getAnalytics({ from, to, agentId, granularity });
24
+ return c.json(result);
25
+ });
26
+ // GET /api/analytics/costs — cost breakdown by agent and time
27
+ app.get('/costs', async (c) => {
28
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
29
+ const to = c.req.query('to') ?? new Date().toISOString();
30
+ const granularity = (c.req.query('granularity') ?? 'day');
31
+ const formatStr = granularity === 'hour'
32
+ ? '%Y-%m-%dT%H:00:00Z'
33
+ : granularity === 'day'
34
+ ? '%Y-%m-%dT00:00:00Z'
35
+ : '%Y-%W';
36
+ // Cost by agent
37
+ const byAgent = db
38
+ .all(sql `
39
+ SELECT
40
+ agent_id as agentId,
41
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as totalCostUsd,
42
+ COALESCE(SUM(json_extract(payload, '$.inputTokens')), 0) as totalInputTokens,
43
+ COALESCE(SUM(json_extract(payload, '$.outputTokens')), 0) as totalOutputTokens,
44
+ COALESCE(SUM(json_extract(payload, '$.totalTokens')), 0) as totalTokens,
45
+ COUNT(*) as eventCount
46
+ FROM events
47
+ WHERE event_type = 'cost_tracked'
48
+ AND timestamp >= ${from}
49
+ AND timestamp <= ${to}
50
+ GROUP BY agent_id
51
+ ORDER BY totalCostUsd DESC
52
+ `);
53
+ // Cost over time — broken down by agent for stacked chart
54
+ const overTimeByAgent = db
55
+ .all(sql `
56
+ SELECT
57
+ strftime(${formatStr}, timestamp) as bucket,
58
+ agent_id as agentId,
59
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as totalCostUsd,
60
+ COUNT(*) as eventCount
61
+ FROM events
62
+ WHERE event_type = 'cost_tracked'
63
+ AND timestamp >= ${from}
64
+ AND timestamp <= ${to}
65
+ GROUP BY bucket, agent_id
66
+ ORDER BY bucket ASC
67
+ `);
68
+ // Pivot: group by bucket, with per-agent cost fields
69
+ const bucketMap = new Map();
70
+ for (const row of overTimeByAgent) {
71
+ const existing = bucketMap.get(row.bucket) ?? {
72
+ bucket: row.bucket,
73
+ totalCostUsd: 0,
74
+ eventCount: 0,
75
+ byAgent: {},
76
+ };
77
+ existing.totalCostUsd += Number(row.totalCostUsd);
78
+ existing.eventCount += Number(row.eventCount);
79
+ existing.byAgent[row.agentId] = Number(row.totalCostUsd);
80
+ bucketMap.set(row.bucket, existing);
81
+ }
82
+ const overTime = Array.from(bucketMap.values());
83
+ // Total cost
84
+ const total = db.get(sql `
85
+ SELECT
86
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as totalCostUsd,
87
+ COALESCE(SUM(json_extract(payload, '$.inputTokens')), 0) as totalInputTokens,
88
+ COALESCE(SUM(json_extract(payload, '$.outputTokens')), 0) as totalOutputTokens,
89
+ COALESCE(SUM(json_extract(payload, '$.totalTokens')), 0) as totalTokens
90
+ FROM events
91
+ WHERE event_type = 'cost_tracked'
92
+ AND timestamp >= ${from}
93
+ AND timestamp <= ${to}
94
+ `);
95
+ return c.json({
96
+ byAgent: byAgent.map((r) => ({
97
+ agentId: r.agentId,
98
+ totalCostUsd: Number(r.totalCostUsd),
99
+ totalInputTokens: Number(r.totalInputTokens),
100
+ totalOutputTokens: Number(r.totalOutputTokens),
101
+ totalTokens: Number(r.totalTokens),
102
+ eventCount: Number(r.eventCount),
103
+ })),
104
+ overTime: overTime.map((r) => ({
105
+ bucket: r.bucket,
106
+ totalCostUsd: Number(r.totalCostUsd),
107
+ eventCount: Number(r.eventCount),
108
+ byAgent: r.byAgent,
109
+ })),
110
+ totals: {
111
+ totalCostUsd: Number(total?.totalCostUsd ?? 0),
112
+ totalInputTokens: Number(total?.totalInputTokens ?? 0),
113
+ totalOutputTokens: Number(total?.totalOutputTokens ?? 0),
114
+ totalTokens: Number(total?.totalTokens ?? 0),
115
+ },
116
+ });
117
+ });
118
+ // GET /api/analytics/agents — per-agent metrics
119
+ app.get('/agents', async (c) => {
120
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
121
+ const to = c.req.query('to') ?? new Date().toISOString();
122
+ const rows = db.all(sql `
123
+ SELECT
124
+ s.agent_id as agentId,
125
+ COUNT(DISTINCT s.id) as sessionCount,
126
+ COALESCE(SUM(s.event_count), 0) as totalEvents,
127
+ COALESCE(SUM(s.error_count), 0) as totalErrors,
128
+ COALESCE(SUM(s.total_cost_usd), 0) as totalCostUsd,
129
+ COALESCE(AVG(
130
+ CASE
131
+ WHEN s.ended_at IS NOT NULL
132
+ THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400000
133
+ ELSE NULL
134
+ END
135
+ ), 0) as avgDurationMs
136
+ FROM sessions s
137
+ WHERE s.started_at >= ${from}
138
+ AND s.started_at <= ${to}
139
+ GROUP BY s.agent_id
140
+ ORDER BY sessionCount DESC
141
+ `);
142
+ return c.json({
143
+ agents: rows.map((r) => ({
144
+ agentId: r.agentId,
145
+ sessionCount: Number(r.sessionCount),
146
+ totalEvents: Number(r.totalEvents),
147
+ totalErrors: Number(r.totalErrors),
148
+ errorRate: Number(r.totalEvents) > 0
149
+ ? Number(r.totalErrors) / Number(r.totalEvents)
150
+ : 0,
151
+ totalCostUsd: Number(r.totalCostUsd),
152
+ avgDurationMs: Number(r.avgDurationMs),
153
+ })),
154
+ });
155
+ });
156
+ // GET /api/analytics/llm — LLM call analytics
157
+ app.get('/llm', async (c) => {
158
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
159
+ const to = c.req.query('to') ?? new Date().toISOString();
160
+ const granularity = (c.req.query('granularity') ?? 'hour');
161
+ const agentId = c.req.query('agentId');
162
+ const model = c.req.query('model');
163
+ const provider = c.req.query('provider');
164
+ if (!['hour', 'day', 'week'].includes(granularity)) {
165
+ return c.json({ error: 'Invalid granularity. Use: hour, day, week', status: 400 }, 400);
166
+ }
167
+ const formatStr = granularity === 'hour'
168
+ ? '%Y-%m-%dT%H:00:00Z'
169
+ : granularity === 'day'
170
+ ? '%Y-%m-%dT00:00:00Z'
171
+ : '%Y-%W';
172
+ // Summary totals
173
+ const summary = db.get(sql `
174
+ SELECT
175
+ COUNT(*) as totalCalls,
176
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as totalCostUsd,
177
+ COALESCE(SUM(json_extract(payload, '$.usage.inputTokens')), 0) as totalInputTokens,
178
+ COALESCE(SUM(json_extract(payload, '$.usage.outputTokens')), 0) as totalOutputTokens,
179
+ COALESCE(AVG(json_extract(payload, '$.latencyMs')), 0) as avgLatencyMs
180
+ FROM events
181
+ WHERE event_type = 'llm_response'
182
+ AND timestamp >= ${from}
183
+ AND timestamp <= ${to}
184
+ ${agentId ? sql `AND agent_id = ${agentId}` : sql ``}
185
+ ${model ? sql `AND json_extract(payload, '$.model') = ${model}` : sql ``}
186
+ ${provider ? sql `AND json_extract(payload, '$.provider') = ${provider}` : sql ``}
187
+ `);
188
+ const totalCalls = Number(summary?.totalCalls ?? 0);
189
+ const totalCostUsd = Number(summary?.totalCostUsd ?? 0);
190
+ // By model breakdown
191
+ const byModel = db.all(sql `
192
+ SELECT
193
+ json_extract(payload, '$.provider') as provider,
194
+ json_extract(payload, '$.model') as model,
195
+ COUNT(*) as calls,
196
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as costUsd,
197
+ COALESCE(SUM(json_extract(payload, '$.usage.inputTokens')), 0) as inputTokens,
198
+ COALESCE(SUM(json_extract(payload, '$.usage.outputTokens')), 0) as outputTokens,
199
+ COALESCE(AVG(json_extract(payload, '$.latencyMs')), 0) as avgLatencyMs
200
+ FROM events
201
+ WHERE event_type = 'llm_response'
202
+ AND timestamp >= ${from}
203
+ AND timestamp <= ${to}
204
+ ${agentId ? sql `AND agent_id = ${agentId}` : sql ``}
205
+ ${model ? sql `AND json_extract(payload, '$.model') = ${model}` : sql ``}
206
+ ${provider ? sql `AND json_extract(payload, '$.provider') = ${provider}` : sql ``}
207
+ GROUP BY provider, model
208
+ ORDER BY costUsd DESC
209
+ `);
210
+ // By time bucket
211
+ const byTime = db.all(sql `
212
+ SELECT
213
+ strftime(${formatStr}, timestamp) as bucket,
214
+ COUNT(*) as calls,
215
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as costUsd,
216
+ COALESCE(SUM(json_extract(payload, '$.usage.inputTokens')), 0) as inputTokens,
217
+ COALESCE(SUM(json_extract(payload, '$.usage.outputTokens')), 0) as outputTokens,
218
+ COALESCE(AVG(json_extract(payload, '$.latencyMs')), 0) as avgLatencyMs
219
+ FROM events
220
+ WHERE event_type = 'llm_response'
221
+ AND timestamp >= ${from}
222
+ AND timestamp <= ${to}
223
+ ${agentId ? sql `AND agent_id = ${agentId}` : sql ``}
224
+ ${model ? sql `AND json_extract(payload, '$.model') = ${model}` : sql ``}
225
+ ${provider ? sql `AND json_extract(payload, '$.provider') = ${provider}` : sql ``}
226
+ GROUP BY bucket
227
+ ORDER BY bucket ASC
228
+ `);
229
+ return c.json({
230
+ summary: {
231
+ totalCalls,
232
+ totalCostUsd,
233
+ totalInputTokens: Number(summary?.totalInputTokens ?? 0),
234
+ totalOutputTokens: Number(summary?.totalOutputTokens ?? 0),
235
+ avgLatencyMs: Number(summary?.avgLatencyMs ?? 0),
236
+ avgCostPerCall: totalCalls > 0 ? totalCostUsd / totalCalls : 0,
237
+ },
238
+ byModel: byModel.map((r) => ({
239
+ provider: r.provider,
240
+ model: r.model,
241
+ calls: Number(r.calls),
242
+ costUsd: Number(r.costUsd),
243
+ inputTokens: Number(r.inputTokens),
244
+ outputTokens: Number(r.outputTokens),
245
+ avgLatencyMs: Number(r.avgLatencyMs),
246
+ })),
247
+ byTime: byTime.map((r) => ({
248
+ bucket: r.bucket,
249
+ calls: Number(r.calls),
250
+ costUsd: Number(r.costUsd),
251
+ inputTokens: Number(r.inputTokens),
252
+ outputTokens: Number(r.outputTokens),
253
+ avgLatencyMs: Number(r.avgLatencyMs),
254
+ })),
255
+ });
256
+ });
257
+ // GET /api/analytics/tools — tool usage statistics
258
+ app.get('/tools', async (c) => {
259
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
260
+ const to = c.req.query('to') ?? new Date().toISOString();
261
+ const rows = db.all(sql `
262
+ SELECT
263
+ json_extract(payload, '$.toolName') as toolName,
264
+ COUNT(*) as callCount,
265
+ SUM(CASE WHEN event_type = 'tool_error' THEN 1 ELSE 0 END) as errorCount,
266
+ COALESCE(AVG(
267
+ CASE WHEN event_type IN ('tool_response', 'tool_error')
268
+ THEN json_extract(payload, '$.durationMs')
269
+ ELSE NULL END
270
+ ), 0) as avgDurationMs
271
+ FROM events
272
+ WHERE event_type IN ('tool_call', 'tool_response', 'tool_error')
273
+ AND timestamp >= ${from}
274
+ AND timestamp <= ${to}
275
+ AND json_extract(payload, '$.toolName') IS NOT NULL
276
+ GROUP BY toolName
277
+ ORDER BY callCount DESC
278
+ `);
279
+ return c.json({
280
+ tools: rows.map((r) => ({
281
+ toolName: r.toolName,
282
+ callCount: Number(r.callCount),
283
+ errorCount: Number(r.errorCount),
284
+ errorRate: Number(r.callCount) > 0
285
+ ? Number(r.errorCount) / Number(r.callCount)
286
+ : 0,
287
+ avgDurationMs: Number(r.avgDurationMs),
288
+ })),
289
+ });
290
+ });
291
+ return app;
292
+ }
293
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,10 @@ import { Hono } from 'hono';
11
11
  import { z } from 'zod';
12
12
  import type { AuthVariables } from '../middleware/auth.js';
13
13
  import type { SqliteDb } from '../db/index.js';
14
+ /**
15
+ * Compare a plaintext secret against a stored hash using timing-safe comparison.
16
+ */
17
+ export declare function verifySecretHash(plaintext: string, storedHash: string): boolean;
14
18
  /** Schema for config update request */
15
19
  declare const configUpdateSchema: z.ZodObject<{
16
20
  retentionDays: z.ZodOptional<z.ZodNumber>;
@@ -32,6 +36,7 @@ declare const configUpdateSchema: z.ZodObject<{
32
36
  formBridgeSecret?: string | undefined;
33
37
  }>;
34
38
  export type ConfigValues = z.infer<typeof configUpdateSchema>;
39
+ export declare function getConfigValue(db: SqliteDb, key: string): string | null;
35
40
  export declare function configRoutes(db: SqliteDb): Hono<{
36
41
  Variables: AuthVariables;
37
42
  }, import("hono/types").BlankSchema, "/">;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,uCAAuC;AACvC,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;EAMtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAoC9D,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ;eACL,aAAa;0CAuDhD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAW/C;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAM/E;AAED,uCAAuC;AACvC,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;EAMtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAS9D,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvE;AAsBD,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ;eACL,aAAa;0CAwDhD"}
@@ -10,7 +10,26 @@
10
10
  import { Hono } from 'hono';
11
11
  import { sql } from 'drizzle-orm';
12
12
  import { z } from 'zod';
13
+ import { createHash, timingSafeEqual } from 'node:crypto';
13
14
  import { getConfig } from '../config.js';
15
+ /**
16
+ * Hash a secret with SHA-256 for storage.
17
+ * Used for webhook secrets so plaintext is never persisted.
18
+ */
19
+ function hashSecret(secret) {
20
+ return createHash('sha256').update(secret).digest('hex');
21
+ }
22
+ /**
23
+ * Compare a plaintext secret against a stored hash using timing-safe comparison.
24
+ */
25
+ export function verifySecretHash(plaintext, storedHash) {
26
+ const computedHash = hashSecret(plaintext);
27
+ const a = Buffer.from(computedHash, 'utf8');
28
+ const b = Buffer.from(storedHash, 'utf8');
29
+ if (a.length !== b.length)
30
+ return false;
31
+ return timingSafeEqual(a, b);
32
+ }
14
33
  /** Schema for config update request */
15
34
  const configUpdateSchema = z.object({
16
35
  retentionDays: z.number().int().min(0).max(3650).optional(),
@@ -25,7 +44,7 @@ const configUpdateSchema = z.object({
25
44
  function ensureConfigTable(db) {
26
45
  db.run(sql `CREATE TABLE IF NOT EXISTS config_kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)`);
27
46
  }
28
- function getConfigValue(db, key) {
47
+ export function getConfigValue(db, key) {
29
48
  const row = db.get(sql `SELECT value FROM config_kv WHERE key = ${key}`);
30
49
  return row?.value ?? null;
31
50
  }
@@ -48,16 +67,15 @@ function getAllConfig(db) {
48
67
  export function configRoutes(db) {
49
68
  const app = new Hono();
50
69
  ensureConfigTable(db);
51
- // GET /api/config — current config
70
+ // GET /api/config — current config (secrets are never returned)
52
71
  app.get('/', (c) => {
53
72
  const config = getAllConfig(db);
54
- // Mask secrets in response
55
73
  return c.json({
56
74
  retentionDays: config.retentionDays,
57
75
  agentGateUrl: config.agentGateUrl,
58
- agentGateSecret: config.agentGateSecret ? '••••••••' : '',
76
+ agentGateSecretSet: !!config.agentGateSecret,
59
77
  formBridgeUrl: config.formBridgeUrl,
60
- formBridgeSecret: config.formBridgeSecret ? '••••••••' : '',
78
+ formBridgeSecretSet: !!config.formBridgeSecret,
61
79
  });
62
80
  });
63
81
  // PUT /api/config — update config values
@@ -82,13 +100,15 @@ export function configRoutes(db) {
82
100
  setConfigValue(db, 'agentGateUrl', updates.agentGateUrl);
83
101
  }
84
102
  if (updates.agentGateSecret !== undefined) {
85
- setConfigValue(db, 'agentGateSecret', updates.agentGateSecret);
103
+ // Store hash of secret, never plaintext
104
+ setConfigValue(db, 'agentGateSecret', hashSecret(updates.agentGateSecret));
86
105
  }
87
106
  if (updates.formBridgeUrl !== undefined) {
88
107
  setConfigValue(db, 'formBridgeUrl', updates.formBridgeUrl);
89
108
  }
90
109
  if (updates.formBridgeSecret !== undefined) {
91
- setConfigValue(db, 'formBridgeSecret', updates.formBridgeSecret);
110
+ // Store hash of secret, never plaintext
111
+ setConfigValue(db, 'formBridgeSecret', hashSecret(updates.formBridgeSecret));
92
112
  }
93
113
  return c.json({ ok: true });
94
114
  });
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  * GET /api/events/:id — get single event
7
7
  */
8
8
  import { Hono } from 'hono';
9
- import type { IEventStore } from '@agentlens/core';
9
+ import type { IEventStore } from '@agentlensai/core';
10
10
  import type { AuthVariables } from '../middleware/auth.js';
11
11
  export declare function eventsRoutes(store: IEventStore): Hono<{
12
12
  Variables: AuthVariables;
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/routes/events.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAO3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CAyKhD"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/routes/events.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAQ3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CAuLhD"}
@@ -8,7 +8,8 @@
8
8
  import { Hono } from 'hono';
9
9
  import { ulid } from 'ulid';
10
10
  import { z } from 'zod';
11
- import { ingestEventSchema, computeEventHash, truncatePayload, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, } from '@agentlens/core';
11
+ import { ingestEventSchema, computeEventHash, truncatePayload, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, } from '@agentlensai/core';
12
+ import { eventBus } from '../lib/event-bus.js';
12
13
  /** Schema for the batch ingestion request body */
13
14
  const ingestBatchSchema = z.object({
14
15
  events: z.array(ingestEventSchema).min(1).max(1000),
@@ -43,9 +44,8 @@ export function eventsRoutes(store) {
43
44
  const allProcessed = [];
44
45
  // Phase 1: Build all events (validate and compute hashes) without writing
45
46
  for (const [sessionId, sessionEvents] of bySession) {
46
- // Get the last event hash for this session to chain from
47
- const timeline = await store.getSessionTimeline(sessionId);
48
- let prevHash = timeline.length > 0 ? timeline[timeline.length - 1].hash : null;
47
+ // Get the last event hash for this session to chain from (optimized)
48
+ let prevHash = await store.getLastEventHash(sessionId);
49
49
  for (const input of sessionEvents) {
50
50
  const id = ulid();
51
51
  const timestamp = input.timestamp ?? new Date().toISOString();
@@ -99,6 +99,19 @@ export function eventsRoutes(store) {
99
99
  const message = error instanceof Error ? error.message : 'Unknown error';
100
100
  return c.json({ error: `Batch insert failed: ${message}`, status: 500 }, 500);
101
101
  }
102
+ // Emit events to EventBus for SSE fan-out (async, non-blocking)
103
+ const now = new Date().toISOString();
104
+ for (const event of allProcessed) {
105
+ eventBus.emit({ type: 'event_ingested', event, timestamp: now });
106
+ }
107
+ // Emit session updates for affected sessions
108
+ const affectedSessionIds = new Set(allProcessed.map((e) => e.sessionId));
109
+ for (const sessionId of affectedSessionIds) {
110
+ const session = await store.getSession(sessionId);
111
+ if (session) {
112
+ eventBus.emit({ type: 'session_updated', session, timestamp: now });
113
+ }
114
+ }
102
115
  return c.json({
103
116
  ingested: allProcessed.length,
104
117
  events: allProcessed.map((e) => ({ id: e.id, hash: e.hash })),