@agentlensai/server 0.5.0 → 0.7.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 (99) hide show
  1. package/dist/db/benchmark-store.d.ts +74 -0
  2. package/dist/db/benchmark-store.d.ts.map +1 -0
  3. package/dist/db/benchmark-store.js +268 -0
  4. package/dist/db/benchmark-store.js.map +1 -0
  5. package/dist/db/health-snapshot-store.d.ts +33 -0
  6. package/dist/db/health-snapshot-store.d.ts.map +1 -0
  7. package/dist/db/health-snapshot-store.js +112 -0
  8. package/dist/db/health-snapshot-store.js.map +1 -0
  9. package/dist/db/migrate.d.ts.map +1 -1
  10. package/dist/db/migrate.js +67 -0
  11. package/dist/db/migrate.js.map +1 -1
  12. package/dist/db/sqlite-store.d.ts +5 -0
  13. package/dist/db/sqlite-store.d.ts.map +1 -1
  14. package/dist/db/sqlite-store.js +15 -0
  15. package/dist/db/sqlite-store.js.map +1 -1
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +25 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/alert-engine.d.ts.map +1 -1
  21. package/dist/lib/alert-engine.js +5 -2
  22. package/dist/lib/alert-engine.js.map +1 -1
  23. package/dist/lib/analysis/cost-analysis.d.ts.map +1 -1
  24. package/dist/lib/analysis/cost-analysis.js +5 -2
  25. package/dist/lib/analysis/cost-analysis.js.map +1 -1
  26. package/dist/lib/analysis/error-patterns.d.ts.map +1 -1
  27. package/dist/lib/analysis/error-patterns.js +5 -2
  28. package/dist/lib/analysis/error-patterns.js.map +1 -1
  29. package/dist/lib/analysis/performance-trends.d.ts.map +1 -1
  30. package/dist/lib/analysis/performance-trends.js +4 -1
  31. package/dist/lib/analysis/performance-trends.js.map +1 -1
  32. package/dist/lib/analysis/tool-sequences.d.ts.map +1 -1
  33. package/dist/lib/analysis/tool-sequences.js +5 -2
  34. package/dist/lib/analysis/tool-sequences.js.map +1 -1
  35. package/dist/lib/benchmark/engine.d.ts +24 -0
  36. package/dist/lib/benchmark/engine.d.ts.map +1 -0
  37. package/dist/lib/benchmark/engine.js +159 -0
  38. package/dist/lib/benchmark/engine.js.map +1 -0
  39. package/dist/lib/benchmark/metric-aggregator.d.ts +38 -0
  40. package/dist/lib/benchmark/metric-aggregator.d.ts.map +1 -0
  41. package/dist/lib/benchmark/metric-aggregator.js +159 -0
  42. package/dist/lib/benchmark/metric-aggregator.js.map +1 -0
  43. package/dist/lib/benchmark/statistical.d.ts +51 -0
  44. package/dist/lib/benchmark/statistical.d.ts.map +1 -0
  45. package/dist/lib/benchmark/statistical.js +381 -0
  46. package/dist/lib/benchmark/statistical.js.map +1 -0
  47. package/dist/lib/context/retrieval.d.ts +4 -0
  48. package/dist/lib/context/retrieval.d.ts.map +1 -1
  49. package/dist/lib/context/retrieval.js +4 -0
  50. package/dist/lib/context/retrieval.js.map +1 -1
  51. package/dist/lib/embeddings/local.js +2 -2
  52. package/dist/lib/embeddings/local.js.map +1 -1
  53. package/dist/lib/health/computer.d.ts +28 -0
  54. package/dist/lib/health/computer.d.ts.map +1 -0
  55. package/dist/lib/health/computer.js +270 -0
  56. package/dist/lib/health/computer.js.map +1 -0
  57. package/dist/lib/optimization/classifier.d.ts +34 -0
  58. package/dist/lib/optimization/classifier.d.ts.map +1 -0
  59. package/dist/lib/optimization/classifier.js +108 -0
  60. package/dist/lib/optimization/classifier.js.map +1 -0
  61. package/dist/lib/optimization/engine.d.ts +24 -0
  62. package/dist/lib/optimization/engine.d.ts.map +1 -0
  63. package/dist/lib/optimization/engine.js +202 -0
  64. package/dist/lib/optimization/engine.js.map +1 -0
  65. package/dist/lib/optimization/index.d.ts +10 -0
  66. package/dist/lib/optimization/index.d.ts.map +1 -0
  67. package/dist/lib/optimization/index.js +9 -0
  68. package/dist/lib/optimization/index.js.map +1 -0
  69. package/dist/lib/replay/builder.d.ts +28 -0
  70. package/dist/lib/replay/builder.d.ts.map +1 -0
  71. package/dist/lib/replay/builder.js +482 -0
  72. package/dist/lib/replay/builder.js.map +1 -0
  73. package/dist/routes/benchmarks.d.ts +18 -0
  74. package/dist/routes/benchmarks.d.ts.map +1 -0
  75. package/dist/routes/benchmarks.js +312 -0
  76. package/dist/routes/benchmarks.js.map +1 -0
  77. package/dist/routes/health.d.ts +21 -0
  78. package/dist/routes/health.d.ts.map +1 -0
  79. package/dist/routes/health.js +142 -0
  80. package/dist/routes/health.js.map +1 -0
  81. package/dist/routes/optimize.d.ts +15 -0
  82. package/dist/routes/optimize.d.ts.map +1 -0
  83. package/dist/routes/optimize.js +55 -0
  84. package/dist/routes/optimize.js.map +1 -0
  85. package/dist/routes/recall.d.ts +2 -0
  86. package/dist/routes/recall.d.ts.map +1 -1
  87. package/dist/routes/recall.js +21 -1
  88. package/dist/routes/recall.js.map +1 -1
  89. package/dist/routes/reflect.d.ts.map +1 -1
  90. package/dist/routes/reflect.js +0 -1
  91. package/dist/routes/reflect.js.map +1 -1
  92. package/dist/routes/replay.d.ts +28 -0
  93. package/dist/routes/replay.d.ts.map +1 -0
  94. package/dist/routes/replay.js +140 -0
  95. package/dist/routes/replay.js.map +1 -0
  96. package/dist/routes/tenant-helper.d.ts.map +1 -1
  97. package/dist/routes/tenant-helper.js +12 -0
  98. package/dist/routes/tenant-helper.js.map +1 -1
  99. package/package.json +2 -2
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Benchmark REST Endpoints (Story 3.5)
3
+ *
4
+ * POST /api/benchmarks — Create benchmark
5
+ * GET /api/benchmarks — List benchmarks
6
+ * GET /api/benchmarks/:id — Get benchmark detail
7
+ * PUT /api/benchmarks/:id/status — Transition status
8
+ * GET /api/benchmarks/:id/results — Get comparison results
9
+ * DELETE /api/benchmarks/:id — Delete draft/cancelled
10
+ */
11
+ import { Hono } from 'hono';
12
+ import { BENCHMARK_METRICS } from '@agentlensai/core';
13
+ import { getTenantStore } from './tenant-helper.js';
14
+ import { BenchmarkStore, } from '../db/benchmark-store.js';
15
+ import { BenchmarkEngine } from '../lib/benchmark/engine.js';
16
+ // ─── Validation Helpers ────────────────────────────────────
17
+ const VALID_METRICS = new Set(BENCHMARK_METRICS);
18
+ const VALID_STATUSES = new Set(['draft', 'running', 'completed', 'cancelled']);
19
+ function validateCreateInput(body) {
20
+ if (!body || typeof body !== 'object') {
21
+ return { error: 'Request body is required' };
22
+ }
23
+ // Name required
24
+ if (!body.name || typeof body.name !== 'string' || body.name.trim() === '') {
25
+ return { error: 'name is required and must be a non-empty string' };
26
+ }
27
+ // Variants: 2-10 with name+tag
28
+ if (!Array.isArray(body.variants)) {
29
+ return { error: 'variants must be an array' };
30
+ }
31
+ if (body.variants.length < 2 || body.variants.length > 10) {
32
+ return { error: 'Must have between 2 and 10 variants' };
33
+ }
34
+ for (let i = 0; i < body.variants.length; i++) {
35
+ const v = body.variants[i];
36
+ if (!v || typeof v !== 'object') {
37
+ return { error: `variants[${i}] must be an object` };
38
+ }
39
+ if (!v.name || typeof v.name !== 'string' || v.name.trim() === '') {
40
+ return { error: `variants[${i}].name is required` };
41
+ }
42
+ if (!v.tag || typeof v.tag !== 'string' || v.tag.trim() === '') {
43
+ return { error: `variants[${i}].tag is required` };
44
+ }
45
+ }
46
+ // Metrics: optional array, but if provided must be valid
47
+ let metrics = BENCHMARK_METRICS.filter(m => m !== 'health_score'); // default: all supported
48
+ if (body.metrics !== undefined) {
49
+ if (!Array.isArray(body.metrics) || body.metrics.length === 0) {
50
+ return { error: 'metrics must be a non-empty array' };
51
+ }
52
+ for (const m of body.metrics) {
53
+ if (!VALID_METRICS.has(m)) {
54
+ return { error: `Invalid metric: ${m}` };
55
+ }
56
+ if (m === 'health_score') {
57
+ return { error: `Metric "health_score" is not yet supported for benchmarks. It requires pre-computed health snapshots.` };
58
+ }
59
+ }
60
+ metrics = body.metrics;
61
+ }
62
+ // minSessionsPerVariant: optional, must be ≥ 1
63
+ let minSessions;
64
+ if (body.minSessionsPerVariant !== undefined) {
65
+ const val = Number(body.minSessionsPerVariant);
66
+ if (!Number.isInteger(val) || val < 1) {
67
+ return { error: 'minSessionsPerVariant must be an integer ≥ 1' };
68
+ }
69
+ minSessions = val;
70
+ }
71
+ const input = {
72
+ name: body.name.trim(),
73
+ description: body.description ?? undefined,
74
+ agentId: body.agentId ?? undefined,
75
+ metrics,
76
+ minSessionsPerVariant: minSessions,
77
+ timeRange: body.timeRange,
78
+ variants: body.variants.map((v) => ({
79
+ name: v.name.trim(),
80
+ description: v.description ?? undefined,
81
+ tag: v.tag.trim(),
82
+ agentId: v.agentId ?? undefined,
83
+ })),
84
+ };
85
+ return { input };
86
+ }
87
+ // ─── Route Factory ─────────────────────────────────────────
88
+ export function benchmarkRoutes(store, db) {
89
+ const app = new Hono();
90
+ const engine = new BenchmarkEngine();
91
+ function getBenchmarkStore(c) {
92
+ if (!db)
93
+ return null;
94
+ return new BenchmarkStore(db);
95
+ }
96
+ function getTenantId(c) {
97
+ const apiKey = c.get('apiKey');
98
+ return apiKey?.tenantId ?? 'default';
99
+ }
100
+ // ─── POST / — Create benchmark ─────────────────────────
101
+ app.post('/', async (c) => {
102
+ const benchmarkStore = getBenchmarkStore(c);
103
+ if (!benchmarkStore) {
104
+ return c.json({ error: 'Database not available', status: 500 }, 500);
105
+ }
106
+ const tenantId = getTenantId(c);
107
+ const body = await c.req.json().catch(() => null);
108
+ const { error, input } = validateCreateInput(body);
109
+ if (error || !input) {
110
+ return c.json({ error, status: 400 }, 400);
111
+ }
112
+ try {
113
+ const benchmark = benchmarkStore.create(tenantId, input);
114
+ return c.json(benchmark, 201);
115
+ }
116
+ catch (err) {
117
+ const message = err instanceof Error ? err.message : 'Failed to create benchmark';
118
+ return c.json({ error: message, status: 400 }, 400);
119
+ }
120
+ });
121
+ // ─── GET / — List benchmarks ────────────────────────────
122
+ app.get('/', async (c) => {
123
+ const benchmarkStore = getBenchmarkStore(c);
124
+ if (!benchmarkStore) {
125
+ return c.json({ error: 'Database not available', status: 500 }, 500);
126
+ }
127
+ const tenantId = getTenantId(c);
128
+ // Parse query params
129
+ const statusRaw = c.req.query('status');
130
+ const agentId = c.req.query('agentId') || undefined;
131
+ const limitStr = c.req.query('limit');
132
+ const offsetStr = c.req.query('offset');
133
+ // Validate status
134
+ let status;
135
+ if (statusRaw) {
136
+ if (!VALID_STATUSES.has(statusRaw)) {
137
+ return c.json({ error: `Invalid status: ${statusRaw}`, status: 400 }, 400);
138
+ }
139
+ status = statusRaw;
140
+ }
141
+ // Validate limit (1-100, default 20)
142
+ let limit = 20;
143
+ if (limitStr !== undefined && limitStr !== '') {
144
+ const val = parseInt(limitStr, 10);
145
+ if (isNaN(val) || val < 1 || val > 100) {
146
+ return c.json({ error: 'limit must be an integer between 1 and 100', status: 400 }, 400);
147
+ }
148
+ limit = val;
149
+ }
150
+ // Validate offset
151
+ let offset = 0;
152
+ if (offsetStr !== undefined && offsetStr !== '') {
153
+ const val = parseInt(offsetStr, 10);
154
+ if (isNaN(val) || val < 0) {
155
+ return c.json({ error: 'offset must be a non-negative integer', status: 400 }, 400);
156
+ }
157
+ offset = val;
158
+ }
159
+ const filters = { status, agentId, limit, offset };
160
+ const { benchmarks, total } = benchmarkStore.list(tenantId, filters);
161
+ return c.json({
162
+ benchmarks,
163
+ total,
164
+ hasMore: offset + benchmarks.length < total,
165
+ });
166
+ });
167
+ // ─── GET /:id — Get benchmark detail ────────────────────
168
+ app.get('/:id', async (c) => {
169
+ const benchmarkStore = getBenchmarkStore(c);
170
+ if (!benchmarkStore) {
171
+ return c.json({ error: 'Database not available', status: 500 }, 500);
172
+ }
173
+ const tenantId = getTenantId(c);
174
+ const id = c.req.param('id');
175
+ const benchmark = benchmarkStore.getById(tenantId, id);
176
+ if (!benchmark) {
177
+ return c.json({ error: 'Benchmark not found', status: 404 }, 404);
178
+ }
179
+ // Enrich variants with session counts
180
+ const tenantStore = getTenantStore(store, c);
181
+ const variantsWithCounts = await Promise.all(benchmark.variants.map(async (v) => {
182
+ try {
183
+ const { total } = await tenantStore.querySessions({
184
+ tenantId: v.tenantId,
185
+ agentId: v.agentId,
186
+ tags: [v.tag],
187
+ from: benchmark.timeRange?.from,
188
+ to: benchmark.timeRange?.to,
189
+ limit: 1, // Minimal fetch — we only need the count
190
+ });
191
+ return { ...v, sessionCount: total };
192
+ }
193
+ catch {
194
+ return { ...v, sessionCount: 0 };
195
+ }
196
+ }));
197
+ return c.json({
198
+ ...benchmark,
199
+ variants: variantsWithCounts,
200
+ });
201
+ });
202
+ // ─── PUT /:id/status — Transition status ────────────────
203
+ app.put('/:id/status', async (c) => {
204
+ const benchmarkStore = getBenchmarkStore(c);
205
+ if (!benchmarkStore) {
206
+ return c.json({ error: 'Database not available', status: 500 }, 500);
207
+ }
208
+ const tenantId = getTenantId(c);
209
+ const id = c.req.param('id');
210
+ const body = await c.req.json().catch(() => null);
211
+ if (!body || !body.status) {
212
+ return c.json({ error: 'status is required', status: 400 }, 400);
213
+ }
214
+ if (!VALID_STATUSES.has(body.status)) {
215
+ return c.json({ error: `Invalid status: ${body.status}`, status: 400 }, 400);
216
+ }
217
+ const newStatus = body.status;
218
+ // Get current benchmark
219
+ const current = benchmarkStore.getById(tenantId, id);
220
+ if (!current) {
221
+ return c.json({ error: 'Benchmark not found', status: 404 }, 404);
222
+ }
223
+ // When transitioning to "running": validate ≥1 session per variant
224
+ if (newStatus === 'running') {
225
+ const tenantStore = getTenantStore(store, c);
226
+ for (const v of current.variants) {
227
+ const { sessions } = await tenantStore.querySessions({
228
+ tenantId: v.tenantId,
229
+ agentId: v.agentId,
230
+ tags: [v.tag],
231
+ from: current.timeRange?.from,
232
+ to: current.timeRange?.to,
233
+ limit: 1,
234
+ });
235
+ if (sessions.length === 0) {
236
+ return c.json({
237
+ error: `Variant "${v.name}" has no sessions. Each variant must have at least 1 session to start.`,
238
+ status: 409,
239
+ }, 409);
240
+ }
241
+ }
242
+ }
243
+ try {
244
+ const updated = benchmarkStore.updateStatus(tenantId, id, newStatus);
245
+ // When transitioning to "completed": compute and cache results
246
+ if (newStatus === 'completed') {
247
+ const tenantStore = getTenantStore(store, c);
248
+ await engine.computeResults(updated, tenantStore, benchmarkStore);
249
+ }
250
+ return c.json(updated);
251
+ }
252
+ catch (err) {
253
+ const message = err instanceof Error ? err.message : 'Failed to update status';
254
+ if (message.includes('Invalid status transition')) {
255
+ return c.json({ error: message, status: 409 }, 409);
256
+ }
257
+ return c.json({ error: message, status: 400 }, 400);
258
+ }
259
+ });
260
+ // ─── GET /:id/results — Get comparison results ──────────
261
+ app.get('/:id/results', async (c) => {
262
+ const benchmarkStore = getBenchmarkStore(c);
263
+ if (!benchmarkStore) {
264
+ return c.json({ error: 'Database not available', status: 500 }, 500);
265
+ }
266
+ const tenantId = getTenantId(c);
267
+ const id = c.req.param('id');
268
+ const benchmark = benchmarkStore.getById(tenantId, id);
269
+ if (!benchmark) {
270
+ return c.json({ error: 'Benchmark not found', status: 404 }, 404);
271
+ }
272
+ // Draft benchmarks can't have results
273
+ if (benchmark.status === 'draft') {
274
+ return c.json({ error: 'Cannot get results for a draft benchmark. Start it first.', status: 400 }, 400);
275
+ }
276
+ const tenantStore = getTenantStore(store, c);
277
+ const results = await engine.computeResults(benchmark, tenantStore, benchmarkStore);
278
+ // Strip distributions unless requested
279
+ const includeDistributions = c.req.query('includeDistributions') === 'true';
280
+ if (!includeDistributions) {
281
+ for (const v of results.variants) {
282
+ for (const key of Object.keys(v.metrics)) {
283
+ const stats = v.metrics[key];
284
+ if (stats) {
285
+ delete stats.values;
286
+ }
287
+ }
288
+ }
289
+ }
290
+ return c.json(results);
291
+ });
292
+ // ─── DELETE /:id — Delete benchmark ─────────────────────
293
+ app.delete('/:id', async (c) => {
294
+ const benchmarkStore = getBenchmarkStore(c);
295
+ if (!benchmarkStore) {
296
+ return c.json({ error: 'Database not available', status: 500 }, 500);
297
+ }
298
+ const tenantId = getTenantId(c);
299
+ const id = c.req.param('id');
300
+ const benchmark = benchmarkStore.getById(tenantId, id);
301
+ if (!benchmark) {
302
+ return c.json({ error: 'Benchmark not found', status: 404 }, 404);
303
+ }
304
+ if (benchmark.status === 'running' || benchmark.status === 'completed') {
305
+ return c.json({ error: `Cannot delete a ${benchmark.status} benchmark`, status: 409 }, 409);
306
+ }
307
+ benchmarkStore.delete(tenantId, id);
308
+ return c.body(null, 204);
309
+ });
310
+ return app;
311
+ }
312
+ //# sourceMappingURL=benchmarks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"benchmarks.js","sourceRoot":"","sources":["../../src/routes/benchmarks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Health REST Endpoints (Story 1.4)
3
+ *
4
+ * GET /api/agents/:id/health?window=7 — Single agent health score
5
+ * GET /api/health/overview?window=7 — All agents overview
6
+ * GET /api/health/history?agentId=X&days=30 — Snapshot history
7
+ * GET /api/config/health-weights — Current health weights
8
+ * PUT /api/config/health-weights — Update weights (501 placeholder)
9
+ */
10
+ import { Hono } from 'hono';
11
+ import type { IEventStore } from '@agentlensai/core';
12
+ import type { AuthVariables } from '../middleware/auth.js';
13
+ import type { SqliteDb } from '../db/index.js';
14
+ /**
15
+ * Register all health-related routes directly on the provided Hono app.
16
+ * Call this from index.ts passing the main app.
17
+ */
18
+ export declare function registerHealthRoutes(app: Hono<{
19
+ Variables: AuthVariables;
20
+ }>, store: IEventStore, db?: SqliteDb): void;
21
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAsB/C;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,IAAI,CAAC;IAAE,SAAS,EAAE,aAAa,CAAA;CAAE,CAAC,EACvC,KAAK,EAAE,WAAW,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,IAAI,CA4IN"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Health REST Endpoints (Story 1.4)
3
+ *
4
+ * GET /api/agents/:id/health?window=7 — Single agent health score
5
+ * GET /api/health/overview?window=7 — All agents overview
6
+ * GET /api/health/history?agentId=X&days=30 — Snapshot history
7
+ * GET /api/config/health-weights — Current health weights
8
+ * PUT /api/config/health-weights — Update weights (501 placeholder)
9
+ */
10
+ import { Hono } from 'hono';
11
+ import { DEFAULT_HEALTH_WEIGHTS, HealthWeightsSchema as WeightsSchema } from '@agentlensai/core';
12
+ import { getTenantStore } from './tenant-helper.js';
13
+ import { HealthComputer } from '../lib/health/computer.js';
14
+ import { HealthSnapshotStore } from '../db/health-snapshot-store.js';
15
+ /** Parse and validate `window` query param (integer 1-90, default 7) */
16
+ function parseWindow(raw) {
17
+ if (raw === undefined || raw === '')
18
+ return 7;
19
+ const val = parseInt(raw, 10);
20
+ if (isNaN(val) || val < 1 || val > 90) {
21
+ return { error: 'window must be an integer between 1 and 90' };
22
+ }
23
+ return val;
24
+ }
25
+ /** Parse and validate `days` query param (integer 1-365, default 30) */
26
+ function parseDays(raw) {
27
+ if (raw === undefined || raw === '')
28
+ return 30;
29
+ const val = parseInt(raw, 10);
30
+ if (isNaN(val) || val < 1 || val > 365) {
31
+ return { error: 'days must be an integer between 1 and 365' };
32
+ }
33
+ return val;
34
+ }
35
+ /**
36
+ * Register all health-related routes directly on the provided Hono app.
37
+ * Call this from index.ts passing the main app.
38
+ */
39
+ export function registerHealthRoutes(app, store, db) {
40
+ const snapshotStore = db ? new HealthSnapshotStore(db) : null;
41
+ // ─── GET /api/agents/:id/health ───────────────────────
42
+ app.get('/api/agents/:id/health', async (c) => {
43
+ const tenantStore = getTenantStore(store, c);
44
+ const agentId = c.req.param('id');
45
+ const window = parseWindow(c.req.query('window'));
46
+ if (typeof window === 'object') {
47
+ return c.json({ error: window.error, status: 400 }, 400);
48
+ }
49
+ try {
50
+ const computer = new HealthComputer(DEFAULT_HEALTH_WEIGHTS);
51
+ const score = await computer.compute(tenantStore, agentId, window);
52
+ if (!score) {
53
+ return c.json({ error: 'No sessions found for agent in window' }, 404);
54
+ }
55
+ // Lazy snapshot: save if first query of the day
56
+ if (snapshotStore) {
57
+ const apiKeyInfo = c.get('apiKey');
58
+ const tenantId = apiKeyInfo?.tenantId ?? 'default';
59
+ const today = new Date().toISOString().slice(0, 10);
60
+ const existing = snapshotStore.get(tenantId, agentId, today);
61
+ if (!existing) {
62
+ const dim = (name) => score.dimensions.find((d) => d.name === name)?.score ?? 0;
63
+ snapshotStore.save(tenantId, {
64
+ agentId,
65
+ date: today,
66
+ overallScore: score.overallScore,
67
+ errorRateScore: dim('error_rate'),
68
+ costEfficiencyScore: dim('cost_efficiency'),
69
+ toolSuccessScore: dim('tool_success'),
70
+ latencyScore: dim('latency'),
71
+ completionRateScore: dim('completion_rate'),
72
+ sessionCount: score.sessionCount,
73
+ });
74
+ }
75
+ }
76
+ return c.json(score);
77
+ }
78
+ catch (error) {
79
+ const message = error instanceof Error ? error.message : 'Internal error';
80
+ return c.json({ error: message, status: 500 }, 500);
81
+ }
82
+ });
83
+ // ─── GET /api/health/overview ─────────────────────────
84
+ app.get('/api/health/overview', async (c) => {
85
+ const tenantStore = getTenantStore(store, c);
86
+ const window = parseWindow(c.req.query('window'));
87
+ if (typeof window === 'object') {
88
+ return c.json({ error: window.error, status: 400 }, 400);
89
+ }
90
+ try {
91
+ const computer = new HealthComputer(DEFAULT_HEALTH_WEIGHTS);
92
+ const agents = await computer.computeOverview(tenantStore, window);
93
+ return c.json({
94
+ agents,
95
+ computedAt: new Date().toISOString(),
96
+ });
97
+ }
98
+ catch (error) {
99
+ const message = error instanceof Error ? error.message : 'Internal error';
100
+ return c.json({ error: message, status: 500 }, 500);
101
+ }
102
+ });
103
+ // ─── GET /api/health/history ──────────────────────────
104
+ app.get('/api/health/history', async (c) => {
105
+ const agentId = c.req.query('agentId');
106
+ if (!agentId) {
107
+ return c.json({ error: 'Missing required query parameter: agentId', status: 400 }, 400);
108
+ }
109
+ const days = parseDays(c.req.query('days'));
110
+ if (typeof days === 'object') {
111
+ return c.json({ error: days.error, status: 400 }, 400);
112
+ }
113
+ if (!snapshotStore) {
114
+ return c.json({ error: 'Snapshot store not available', status: 500 }, 500);
115
+ }
116
+ const apiKeyInfo = c.get('apiKey');
117
+ const tenantId = apiKeyInfo?.tenantId ?? 'default';
118
+ const snapshots = snapshotStore.getHistory(tenantId, agentId, days);
119
+ return c.json({ snapshots, agentId, days });
120
+ });
121
+ // ─── GET /api/config/health-weights ───────────────────
122
+ app.get('/api/config/health-weights', async (c) => {
123
+ return c.json(DEFAULT_HEALTH_WEIGHTS);
124
+ });
125
+ // ─── PUT /api/config/health-weights ───────────────────
126
+ app.put('/api/config/health-weights', async (c) => {
127
+ const rawBody = await c.req.json().catch(() => null);
128
+ if (!rawBody) {
129
+ return c.json({ error: 'Invalid JSON body', status: 400 }, 400);
130
+ }
131
+ // Validate with schema
132
+ const result = WeightsSchema.safeParse(rawBody);
133
+ if (!result.success) {
134
+ return c.json({
135
+ error: result.error.issues.map((i) => i.message).join('; '),
136
+ status: 400,
137
+ }, 400);
138
+ }
139
+ return c.json({ error: 'Weight customization coming in a future release', status: 501 }, 501);
140
+ });
141
+ }
142
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,IAAI,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEjG,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAGrE,wEAAwE;AACxE,SAAS,WAAW,CAAC,GAAuB;IAC1C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE,EAAE,CAAC;QACtC,OAAO,EAAE,KAAK,EAAE,4CAA4C,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,wEAAwE;AACxE,SAAS,SAAS,CAAC,GAAuB;IACxC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAC/C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;QACvC,OAAO,EAAE,KAAK,EAAE,2CAA2C,EAAE,CAAC;IAChE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAuC,EACvC,KAAkB,EAClB,EAAa;IAEb,MAAM,aAAa,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE9D,yDAAyD;IAEzD,GAAG,CAAC,GAAG,CAAC,wBAAwB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC5C,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,sBAAsB,CAAC,CAAC;YAC5D,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YAEnE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,uCAAuC,EAAE,EAClD,GAAG,CACJ,CAAC;YACJ,CAAC;YAED,gDAAgD;YAChD,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACnC,MAAM,QAAQ,GAAG,UAAU,EAAE,QAAQ,IAAI,SAAS,CAAC;gBACnD,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACpD,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;gBAC7D,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAC3B,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;oBAC5D,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE;wBAC3B,OAAO;wBACP,IAAI,EAAE,KAAK;wBACX,YAAY,EAAE,KAAK,CAAC,YAAY;wBAChC,cAAc,EAAE,GAAG,CAAC,YAAY,CAAC;wBACjC,mBAAmB,EAAE,GAAG,CAAC,iBAAiB,CAAC;wBAC3C,gBAAgB,EAAE,GAAG,CAAC,cAAc,CAAC;wBACrC,YAAY,EAAE,GAAG,CAAC,SAAS,CAAC;wBAC5B,mBAAmB,EAAE,GAAG,CAAC,iBAAiB,CAAC;wBAC3C,YAAY,EAAE,KAAK,CAAC,YAAY;qBACjC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC1E,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,yDAAyD;IAEzD,GAAG,CAAC,GAAG,CAAC,sBAAsB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1C,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAE7C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,sBAAsB,CAAC,CAAC;YAC5D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YAEnE,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,MAAM;gBACN,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACrC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC1E,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,yDAAyD;IAEzD,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACzC,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,2CAA2C,EAAE,MAAM,EAAE,GAAG,EAAE,EACnE,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,8BAA8B,EAAE,MAAM,EAAE,GAAG,EAAE,EACtD,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,UAAU,EAAE,QAAQ,IAAI,SAAS,CAAC;QAEnD,MAAM,SAAS,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACpE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,yDAAyD;IAEzD,GAAG,CAAC,GAAG,CAAC,4BAA4B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAChD,OAAO,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,yDAAyD;IAEzD,GAAG,CAAC,GAAG,CAAC,4BAA4B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAClE,CAAC;QAED,uBAAuB;QACvB,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,CAAC,IAAI,CACX;gBACE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC3D,MAAM,EAAE,GAAG;aACZ,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,iDAAiD,EAAE,MAAM,EAAE,GAAG,EAAE,EACzE,GAAG,CACJ,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Optimize REST Endpoint (Story 2.4)
3
+ *
4
+ * GET /api/optimize/recommendations?agentId=X&period=7&limit=10
5
+ *
6
+ * Returns cost optimization recommendations based on LLM call patterns.
7
+ * Requires auth (tenant-scoped).
8
+ */
9
+ import { Hono } from 'hono';
10
+ import type { IEventStore } from '@agentlensai/core';
11
+ import type { AuthVariables } from '../middleware/auth.js';
12
+ export declare function optimizeRoutes(store: IEventStore): Hono<{
13
+ Variables: AuthVariables;
14
+ }, import("hono/types").BlankSchema, "/">;
15
+ //# sourceMappingURL=optimize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimize.d.ts","sourceRoot":"","sources":["../../src/routes/optimize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;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;AAI3D,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW;eACb,aAAa;0CAqDhD"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Optimize REST Endpoint (Story 2.4)
3
+ *
4
+ * GET /api/optimize/recommendations?agentId=X&period=7&limit=10
5
+ *
6
+ * Returns cost optimization recommendations based on LLM call patterns.
7
+ * Requires auth (tenant-scoped).
8
+ */
9
+ import { Hono } from 'hono';
10
+ import { getTenantStore } from './tenant-helper.js';
11
+ import { OptimizationEngine } from '../lib/optimization/index.js';
12
+ export function optimizeRoutes(store) {
13
+ const app = new Hono();
14
+ // GET /api/optimize/recommendations
15
+ app.get('/recommendations', async (c) => {
16
+ const tenantStore = getTenantStore(store, c);
17
+ // Parse and validate query params
18
+ const agentId = c.req.query('agentId') || undefined;
19
+ const periodStr = c.req.query('period');
20
+ const limitStr = c.req.query('limit');
21
+ // period: integer 1-90, default 7
22
+ let period = 7;
23
+ if (periodStr !== undefined && periodStr !== '') {
24
+ const parsed = parseInt(periodStr, 10);
25
+ if (isNaN(parsed) || parsed < 1 || parsed > 90) {
26
+ return c.json({ error: 'Invalid period: must be an integer between 1 and 90', status: 400 }, 400);
27
+ }
28
+ period = parsed;
29
+ }
30
+ // limit: integer 1-50, default 10
31
+ let limit = 10;
32
+ if (limitStr !== undefined && limitStr !== '') {
33
+ const parsed = parseInt(limitStr, 10);
34
+ if (isNaN(parsed) || parsed < 1 || parsed > 50) {
35
+ return c.json({ error: 'Invalid limit: must be an integer between 1 and 50', status: 400 }, 400);
36
+ }
37
+ limit = parsed;
38
+ }
39
+ try {
40
+ const engine = new OptimizationEngine();
41
+ const result = await engine.getRecommendations(tenantStore, {
42
+ agentId,
43
+ period,
44
+ limit,
45
+ });
46
+ return c.json(result);
47
+ }
48
+ catch (error) {
49
+ const message = error instanceof Error ? error.message : 'Internal error';
50
+ return c.json({ error: message, status: 500 }, 500);
51
+ }
52
+ });
53
+ return app;
54
+ }
55
+ //# sourceMappingURL=optimize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimize.js","sourceRoot":"","sources":["../../src/routes/optimize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAgC,CAAC;IAErD,oCAAoC;IACpC,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACtC,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAE7C,kCAAkC;QAClC,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC;QACpD,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAEtC,kCAAkC;QAClC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACvC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;gBAC/C,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,qDAAqD,EAAE,MAAM,EAAE,GAAG,EAAE,EAC7E,GAAG,CACJ,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,MAAM,CAAC;QAClB,CAAC;QAED,kCAAkC;QAClC,IAAI,KAAK,GAAG,EAAE,CAAC;QACf,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;YAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACtC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;gBAC/C,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,oDAAoD,EAAE,MAAM,EAAE,GAAG,EAAE,EAC5E,GAAG,CACJ,CAAC;YACJ,CAAC;YACD,KAAK,GAAG,MAAM,CAAC;QACjB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,WAAW,EAAE;gBAC1D,OAAO;gBACP,MAAM;gBACN,KAAK;aACN,CAAC,CAAC;YAEH,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC1E,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -7,12 +7,14 @@
7
7
  * and returns enriched results.
8
8
  */
9
9
  import { Hono } from 'hono';
10
+ import type { IEventStore } from '@agentlensai/core';
10
11
  import type { AuthVariables } from '../middleware/auth.js';
11
12
  import type { EmbeddingService } from '../lib/embeddings/index.js';
12
13
  import type { EmbeddingStore } from '../db/embedding-store.js';
13
14
  export interface RecallRouteDeps {
14
15
  embeddingService: EmbeddingService | null;
15
16
  embeddingStore: EmbeddingStore | null;
17
+ eventStore?: IEventStore;
16
18
  }
17
19
  export declare function recallRoutes(deps: RecallRouteDeps): Hono<{
18
20
  Variables: AuthVariables;
@@ -1 +1 @@
1
- {"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/routes/recall.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,0BAA0B,CAAC;AAExF,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe;eACd,aAAa;0CAgFhD"}
1
+ {"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/routes/recall.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;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;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,0BAA0B,CAAC;AAExF,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe;eACd,aAAa;0CAsGhD"}