@agentlensai/server 0.2.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/dist/db/embedding-store.d.ts +74 -0
  2. package/dist/db/embedding-store.d.ts.map +1 -0
  3. package/dist/db/embedding-store.js +177 -0
  4. package/dist/db/embedding-store.js.map +1 -0
  5. package/dist/db/lesson-store.d.ts +57 -0
  6. package/dist/db/lesson-store.d.ts.map +1 -0
  7. package/dist/db/lesson-store.js +217 -0
  8. package/dist/db/lesson-store.js.map +1 -0
  9. package/dist/db/migrate.d.ts.map +1 -1
  10. package/dist/db/migrate.js +250 -4
  11. package/dist/db/migrate.js.map +1 -1
  12. package/dist/db/schema.sqlite.d.ts +881 -55
  13. package/dist/db/schema.sqlite.d.ts.map +1 -1
  14. package/dist/db/schema.sqlite.js +82 -4
  15. package/dist/db/schema.sqlite.js.map +1 -1
  16. package/dist/db/session-summary-store.d.ts +45 -0
  17. package/dist/db/session-summary-store.d.ts.map +1 -0
  18. package/dist/db/session-summary-store.js +112 -0
  19. package/dist/db/session-summary-store.js.map +1 -0
  20. package/dist/db/sqlite-store.d.ts +25 -13
  21. package/dist/db/sqlite-store.d.ts.map +1 -1
  22. package/dist/db/sqlite-store.js +224 -47
  23. package/dist/db/sqlite-store.js.map +1 -1
  24. package/dist/db/tenant-scoped-store.d.ts +61 -0
  25. package/dist/db/tenant-scoped-store.d.ts.map +1 -0
  26. package/dist/db/tenant-scoped-store.js +94 -0
  27. package/dist/db/tenant-scoped-store.js.map +1 -0
  28. package/dist/index.d.ts +25 -4
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +99 -5
  31. package/dist/index.js.map +1 -1
  32. package/dist/lib/alert-engine.d.ts +72 -0
  33. package/dist/lib/alert-engine.d.ts.map +1 -0
  34. package/dist/lib/alert-engine.js +318 -0
  35. package/dist/lib/alert-engine.js.map +1 -0
  36. package/dist/lib/analysis/cost-analysis.d.ts +20 -0
  37. package/dist/lib/analysis/cost-analysis.d.ts.map +1 -0
  38. package/dist/lib/analysis/cost-analysis.js +158 -0
  39. package/dist/lib/analysis/cost-analysis.js.map +1 -0
  40. package/dist/lib/analysis/error-patterns.d.ts +26 -0
  41. package/dist/lib/analysis/error-patterns.d.ts.map +1 -0
  42. package/dist/lib/analysis/error-patterns.js +155 -0
  43. package/dist/lib/analysis/error-patterns.js.map +1 -0
  44. package/dist/lib/analysis/index.d.ts +23 -0
  45. package/dist/lib/analysis/index.d.ts.map +1 -0
  46. package/dist/lib/analysis/index.js +144 -0
  47. package/dist/lib/analysis/index.js.map +1 -0
  48. package/dist/lib/analysis/performance-trends.d.ts +19 -0
  49. package/dist/lib/analysis/performance-trends.d.ts.map +1 -0
  50. package/dist/lib/analysis/performance-trends.js +118 -0
  51. package/dist/lib/analysis/performance-trends.js.map +1 -0
  52. package/dist/lib/analysis/tool-sequences.d.ts +19 -0
  53. package/dist/lib/analysis/tool-sequences.d.ts.map +1 -0
  54. package/dist/lib/analysis/tool-sequences.js +145 -0
  55. package/dist/lib/analysis/tool-sequences.js.map +1 -0
  56. package/dist/lib/context/index.d.ts +5 -0
  57. package/dist/lib/context/index.d.ts.map +1 -0
  58. package/dist/lib/context/index.js +5 -0
  59. package/dist/lib/context/index.js.map +1 -0
  60. package/dist/lib/context/retrieval.d.ts +56 -0
  61. package/dist/lib/context/retrieval.d.ts.map +1 -0
  62. package/dist/lib/context/retrieval.js +229 -0
  63. package/dist/lib/context/retrieval.js.map +1 -0
  64. package/dist/lib/embeddings/index.d.ts +31 -0
  65. package/dist/lib/embeddings/index.d.ts.map +1 -0
  66. package/dist/lib/embeddings/index.js +31 -0
  67. package/dist/lib/embeddings/index.js.map +1 -0
  68. package/dist/lib/embeddings/local.d.ts +15 -0
  69. package/dist/lib/embeddings/local.d.ts.map +1 -0
  70. package/dist/lib/embeddings/local.js +65 -0
  71. package/dist/lib/embeddings/local.js.map +1 -0
  72. package/dist/lib/embeddings/math.d.ts +13 -0
  73. package/dist/lib/embeddings/math.d.ts.map +1 -0
  74. package/dist/lib/embeddings/math.js +35 -0
  75. package/dist/lib/embeddings/math.js.map +1 -0
  76. package/dist/lib/embeddings/openai.d.ts +15 -0
  77. package/dist/lib/embeddings/openai.d.ts.map +1 -0
  78. package/dist/lib/embeddings/openai.js +58 -0
  79. package/dist/lib/embeddings/openai.js.map +1 -0
  80. package/dist/lib/embeddings/summarizer.d.ts +26 -0
  81. package/dist/lib/embeddings/summarizer.d.ts.map +1 -0
  82. package/dist/lib/embeddings/summarizer.js +181 -0
  83. package/dist/lib/embeddings/summarizer.js.map +1 -0
  84. package/dist/lib/embeddings/types.d.ts +17 -0
  85. package/dist/lib/embeddings/types.d.ts.map +1 -0
  86. package/dist/lib/embeddings/types.js +5 -0
  87. package/dist/lib/embeddings/types.js.map +1 -0
  88. package/dist/lib/embeddings/worker.d.ts +56 -0
  89. package/dist/lib/embeddings/worker.d.ts.map +1 -0
  90. package/dist/lib/embeddings/worker.js +109 -0
  91. package/dist/lib/embeddings/worker.js.map +1 -0
  92. package/dist/lib/event-bus.d.ts +48 -0
  93. package/dist/lib/event-bus.d.ts.map +1 -0
  94. package/dist/lib/event-bus.js +34 -0
  95. package/dist/lib/event-bus.js.map +1 -0
  96. package/dist/lib/retention.d.ts +1 -1
  97. package/dist/lib/retention.d.ts.map +1 -1
  98. package/dist/lib/sse.d.ts +22 -0
  99. package/dist/lib/sse.d.ts.map +1 -0
  100. package/dist/lib/sse.js +121 -0
  101. package/dist/lib/sse.js.map +1 -0
  102. package/dist/middleware/auth.d.ts +1 -0
  103. package/dist/middleware/auth.d.ts.map +1 -1
  104. package/dist/middleware/auth.js +2 -1
  105. package/dist/middleware/auth.js.map +1 -1
  106. package/dist/routes/agents.d.ts +1 -1
  107. package/dist/routes/agents.d.ts.map +1 -1
  108. package/dist/routes/agents.js +6 -3
  109. package/dist/routes/agents.js.map +1 -1
  110. package/dist/routes/alerts.d.ts +17 -0
  111. package/dist/routes/alerts.d.ts.map +1 -0
  112. package/dist/routes/alerts.js +141 -0
  113. package/dist/routes/alerts.js.map +1 -0
  114. package/dist/routes/analytics.d.ts +16 -0
  115. package/dist/routes/analytics.d.ts.map +1 -0
  116. package/dist/routes/analytics.js +307 -0
  117. package/dist/routes/analytics.js.map +1 -0
  118. package/dist/routes/api-keys.d.ts.map +1 -1
  119. package/dist/routes/api-keys.js +30 -5
  120. package/dist/routes/api-keys.js.map +1 -1
  121. package/dist/routes/config.d.ts +5 -0
  122. package/dist/routes/config.d.ts.map +1 -1
  123. package/dist/routes/config.js +27 -7
  124. package/dist/routes/config.js.map +1 -1
  125. package/dist/routes/context.d.ts +23 -0
  126. package/dist/routes/context.d.ts.map +1 -0
  127. package/dist/routes/context.js +52 -0
  128. package/dist/routes/context.js.map +1 -0
  129. package/dist/routes/events.d.ts +7 -2
  130. package/dist/routes/events.d.ts.map +1 -1
  131. package/dist/routes/events.js +76 -8
  132. package/dist/routes/events.js.map +1 -1
  133. package/dist/routes/ingest.d.ts +36 -0
  134. package/dist/routes/ingest.d.ts.map +1 -0
  135. package/dist/routes/ingest.js +280 -0
  136. package/dist/routes/ingest.js.map +1 -0
  137. package/dist/routes/lessons.d.ts +19 -0
  138. package/dist/routes/lessons.d.ts.map +1 -0
  139. package/dist/routes/lessons.js +164 -0
  140. package/dist/routes/lessons.js.map +1 -0
  141. package/dist/routes/recall.d.ts +20 -0
  142. package/dist/routes/recall.d.ts.map +1 -0
  143. package/dist/routes/recall.js +71 -0
  144. package/dist/routes/recall.js.map +1 -0
  145. package/dist/routes/reflect.d.ts +15 -0
  146. package/dist/routes/reflect.d.ts.map +1 -0
  147. package/dist/routes/reflect.js +55 -0
  148. package/dist/routes/reflect.js.map +1 -0
  149. package/dist/routes/sessions.d.ts +1 -1
  150. package/dist/routes/sessions.d.ts.map +1 -1
  151. package/dist/routes/sessions.js +9 -7
  152. package/dist/routes/sessions.js.map +1 -1
  153. package/dist/routes/stats.d.ts +1 -1
  154. package/dist/routes/stats.d.ts.map +1 -1
  155. package/dist/routes/stats.js +3 -1
  156. package/dist/routes/stats.js.map +1 -1
  157. package/dist/routes/stream.d.ts +23 -0
  158. package/dist/routes/stream.d.ts.map +1 -0
  159. package/dist/routes/stream.js +94 -0
  160. package/dist/routes/stream.js.map +1 -0
  161. package/dist/routes/tenant-helper.d.ts +20 -0
  162. package/dist/routes/tenant-helper.d.ts.map +1 -0
  163. package/dist/routes/tenant-helper.js +23 -0
  164. package/dist/routes/tenant-helper.js.map +1 -0
  165. package/package.json +11 -11
  166. package/LICENSE +0 -21
  167. package/dist/__tests__/agents-stats.test.d.ts +0 -5
  168. package/dist/__tests__/agents-stats.test.d.ts.map +0 -1
  169. package/dist/__tests__/agents-stats.test.js +0 -134
  170. package/dist/__tests__/agents-stats.test.js.map +0 -1
  171. package/dist/__tests__/api-keys.test.d.ts +0 -5
  172. package/dist/__tests__/api-keys.test.d.ts.map +0 -1
  173. package/dist/__tests__/api-keys.test.js +0 -118
  174. package/dist/__tests__/api-keys.test.js.map +0 -1
  175. package/dist/__tests__/auth-no-db.test.d.ts +0 -2
  176. package/dist/__tests__/auth-no-db.test.d.ts.map +0 -1
  177. package/dist/__tests__/auth-no-db.test.js +0 -43
  178. package/dist/__tests__/auth-no-db.test.js.map +0 -1
  179. package/dist/__tests__/auth.test.d.ts +0 -5
  180. package/dist/__tests__/auth.test.d.ts.map +0 -1
  181. package/dist/__tests__/auth.test.js +0 -86
  182. package/dist/__tests__/auth.test.js.map +0 -1
  183. package/dist/__tests__/config.test.d.ts +0 -2
  184. package/dist/__tests__/config.test.d.ts.map +0 -1
  185. package/dist/__tests__/config.test.js +0 -37
  186. package/dist/__tests__/config.test.js.map +0 -1
  187. package/dist/__tests__/events-ingest.test.d.ts +0 -5
  188. package/dist/__tests__/events-ingest.test.d.ts.map +0 -1
  189. package/dist/__tests__/events-ingest.test.js +0 -248
  190. package/dist/__tests__/events-ingest.test.js.map +0 -1
  191. package/dist/__tests__/events-query.test.d.ts +0 -5
  192. package/dist/__tests__/events-query.test.d.ts.map +0 -1
  193. package/dist/__tests__/events-query.test.js +0 -205
  194. package/dist/__tests__/events-query.test.js.map +0 -1
  195. package/dist/__tests__/health.test.d.ts +0 -5
  196. package/dist/__tests__/health.test.d.ts.map +0 -1
  197. package/dist/__tests__/health.test.js +0 -40
  198. package/dist/__tests__/health.test.js.map +0 -1
  199. package/dist/__tests__/sessions.test.d.ts +0 -5
  200. package/dist/__tests__/sessions.test.d.ts.map +0 -1
  201. package/dist/__tests__/sessions.test.js +0 -176
  202. package/dist/__tests__/sessions.test.js.map +0 -1
  203. package/dist/__tests__/test-helpers.d.ts +0 -24
  204. package/dist/__tests__/test-helpers.d.ts.map +0 -1
  205. package/dist/__tests__/test-helpers.js +0 -45
  206. package/dist/__tests__/test-helpers.js.map +0 -1
  207. package/dist/db/__tests__/init.test.d.ts +0 -2
  208. package/dist/db/__tests__/init.test.d.ts.map +0 -1
  209. package/dist/db/__tests__/init.test.js +0 -73
  210. package/dist/db/__tests__/init.test.js.map +0 -1
  211. package/dist/db/__tests__/sqlite-store-read.test.d.ts +0 -2
  212. package/dist/db/__tests__/sqlite-store-read.test.d.ts.map +0 -1
  213. package/dist/db/__tests__/sqlite-store-read.test.js +0 -749
  214. package/dist/db/__tests__/sqlite-store-read.test.js.map +0 -1
  215. package/dist/db/__tests__/sqlite-store-write.test.d.ts +0 -2
  216. package/dist/db/__tests__/sqlite-store-write.test.d.ts.map +0 -1
  217. package/dist/db/__tests__/sqlite-store-write.test.js +0 -418
  218. package/dist/db/__tests__/sqlite-store-write.test.js.map +0 -1
  219. package/dist/lib/__tests__/retention.test.d.ts +0 -2
  220. package/dist/lib/__tests__/retention.test.d.ts.map +0 -1
  221. package/dist/lib/__tests__/retention.test.js +0 -238
  222. package/dist/lib/__tests__/retention.test.js.map +0 -1
@@ -0,0 +1,307 @@
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
+ import { getTenantStore } from './tenant-helper.js';
13
+ export function analyticsRoutes(store, db) {
14
+ const app = new Hono();
15
+ // GET /api/analytics — bucketed metrics over time
16
+ app.get('/', async (c) => {
17
+ const tenantStore = getTenantStore(store, c);
18
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
19
+ const to = c.req.query('to') ?? new Date().toISOString();
20
+ const granularity = (c.req.query('granularity') ?? 'hour');
21
+ const agentId = c.req.query('agentId');
22
+ if (!['hour', 'day', 'week'].includes(granularity)) {
23
+ return c.json({ error: 'Invalid granularity. Use: hour, day, week', status: 400 }, 400);
24
+ }
25
+ const result = await tenantStore.getAnalytics({ from, to, agentId, granularity });
26
+ return c.json(result);
27
+ });
28
+ // GET /api/analytics/costs — cost breakdown by agent and time
29
+ app.get('/costs', async (c) => {
30
+ const tenantId = c.get('apiKey')?.tenantId ?? 'default';
31
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
32
+ const to = c.req.query('to') ?? new Date().toISOString();
33
+ const granularity = (c.req.query('granularity') ?? 'day');
34
+ const formatStr = granularity === 'hour'
35
+ ? '%Y-%m-%dT%H:00:00Z'
36
+ : granularity === 'day'
37
+ ? '%Y-%m-%dT00:00:00Z'
38
+ : '%Y-%W';
39
+ // Cost by agent
40
+ const byAgent = db
41
+ .all(sql `
42
+ SELECT
43
+ agent_id as agentId,
44
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as totalCostUsd,
45
+ COALESCE(SUM(json_extract(payload, '$.inputTokens')), 0) as totalInputTokens,
46
+ COALESCE(SUM(json_extract(payload, '$.outputTokens')), 0) as totalOutputTokens,
47
+ COALESCE(SUM(json_extract(payload, '$.totalTokens')), 0) as totalTokens,
48
+ COUNT(*) as eventCount
49
+ FROM events
50
+ WHERE event_type = 'cost_tracked'
51
+ AND timestamp >= ${from}
52
+ AND timestamp <= ${to}
53
+ AND tenant_id = ${tenantId}
54
+ GROUP BY agent_id
55
+ ORDER BY totalCostUsd DESC
56
+ `);
57
+ // Cost over time — broken down by agent for stacked chart
58
+ const overTimeByAgent = db
59
+ .all(sql `
60
+ SELECT
61
+ strftime(${formatStr}, timestamp) as bucket,
62
+ agent_id as agentId,
63
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as totalCostUsd,
64
+ COUNT(*) as eventCount
65
+ FROM events
66
+ WHERE event_type = 'cost_tracked'
67
+ AND timestamp >= ${from}
68
+ AND timestamp <= ${to}
69
+ AND tenant_id = ${tenantId}
70
+ GROUP BY bucket, agent_id
71
+ ORDER BY bucket ASC
72
+ `);
73
+ // Pivot: group by bucket, with per-agent cost fields
74
+ const bucketMap = new Map();
75
+ for (const row of overTimeByAgent) {
76
+ const existing = bucketMap.get(row.bucket) ?? {
77
+ bucket: row.bucket,
78
+ totalCostUsd: 0,
79
+ eventCount: 0,
80
+ byAgent: {},
81
+ };
82
+ existing.totalCostUsd += Number(row.totalCostUsd);
83
+ existing.eventCount += Number(row.eventCount);
84
+ existing.byAgent[row.agentId] = Number(row.totalCostUsd);
85
+ bucketMap.set(row.bucket, existing);
86
+ }
87
+ const overTime = Array.from(bucketMap.values());
88
+ // Total cost
89
+ const total = db.get(sql `
90
+ SELECT
91
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as totalCostUsd,
92
+ COALESCE(SUM(json_extract(payload, '$.inputTokens')), 0) as totalInputTokens,
93
+ COALESCE(SUM(json_extract(payload, '$.outputTokens')), 0) as totalOutputTokens,
94
+ COALESCE(SUM(json_extract(payload, '$.totalTokens')), 0) as totalTokens
95
+ FROM events
96
+ WHERE event_type = 'cost_tracked'
97
+ AND timestamp >= ${from}
98
+ AND timestamp <= ${to}
99
+ AND tenant_id = ${tenantId}
100
+ `);
101
+ return c.json({
102
+ byAgent: byAgent.map((r) => ({
103
+ agentId: r.agentId,
104
+ totalCostUsd: Number(r.totalCostUsd),
105
+ totalInputTokens: Number(r.totalInputTokens),
106
+ totalOutputTokens: Number(r.totalOutputTokens),
107
+ totalTokens: Number(r.totalTokens),
108
+ eventCount: Number(r.eventCount),
109
+ })),
110
+ overTime: overTime.map((r) => ({
111
+ bucket: r.bucket,
112
+ totalCostUsd: Number(r.totalCostUsd),
113
+ eventCount: Number(r.eventCount),
114
+ byAgent: r.byAgent,
115
+ })),
116
+ totals: {
117
+ totalCostUsd: Number(total?.totalCostUsd ?? 0),
118
+ totalInputTokens: Number(total?.totalInputTokens ?? 0),
119
+ totalOutputTokens: Number(total?.totalOutputTokens ?? 0),
120
+ totalTokens: Number(total?.totalTokens ?? 0),
121
+ },
122
+ });
123
+ });
124
+ // GET /api/analytics/agents — per-agent metrics
125
+ app.get('/agents', async (c) => {
126
+ const tenantId = c.get('apiKey')?.tenantId ?? 'default';
127
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
128
+ const to = c.req.query('to') ?? new Date().toISOString();
129
+ const rows = db.all(sql `
130
+ SELECT
131
+ s.agent_id as agentId,
132
+ COUNT(DISTINCT s.id) as sessionCount,
133
+ COALESCE(SUM(s.event_count), 0) as totalEvents,
134
+ COALESCE(SUM(s.error_count), 0) as totalErrors,
135
+ COALESCE(SUM(s.total_cost_usd), 0) as totalCostUsd,
136
+ COALESCE(AVG(
137
+ CASE
138
+ WHEN s.ended_at IS NOT NULL
139
+ THEN (julianday(s.ended_at) - julianday(s.started_at)) * 86400000
140
+ ELSE NULL
141
+ END
142
+ ), 0) as avgDurationMs
143
+ FROM sessions s
144
+ WHERE s.started_at >= ${from}
145
+ AND s.started_at <= ${to}
146
+ AND s.tenant_id = ${tenantId}
147
+ GROUP BY s.agent_id
148
+ ORDER BY sessionCount DESC
149
+ `);
150
+ return c.json({
151
+ agents: rows.map((r) => ({
152
+ agentId: r.agentId,
153
+ sessionCount: Number(r.sessionCount),
154
+ totalEvents: Number(r.totalEvents),
155
+ totalErrors: Number(r.totalErrors),
156
+ errorRate: Number(r.totalEvents) > 0
157
+ ? Number(r.totalErrors) / Number(r.totalEvents)
158
+ : 0,
159
+ totalCostUsd: Number(r.totalCostUsd),
160
+ avgDurationMs: Number(r.avgDurationMs),
161
+ })),
162
+ });
163
+ });
164
+ // GET /api/analytics/llm — LLM call analytics
165
+ app.get('/llm', async (c) => {
166
+ const tenantId = c.get('apiKey')?.tenantId ?? 'default';
167
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
168
+ const to = c.req.query('to') ?? new Date().toISOString();
169
+ const granularity = (c.req.query('granularity') ?? 'hour');
170
+ const agentId = c.req.query('agentId');
171
+ const model = c.req.query('model');
172
+ const provider = c.req.query('provider');
173
+ if (!['hour', 'day', 'week'].includes(granularity)) {
174
+ return c.json({ error: 'Invalid granularity. Use: hour, day, week', status: 400 }, 400);
175
+ }
176
+ const formatStr = granularity === 'hour'
177
+ ? '%Y-%m-%dT%H:00:00Z'
178
+ : granularity === 'day'
179
+ ? '%Y-%m-%dT00:00:00Z'
180
+ : '%Y-%W';
181
+ // Summary totals
182
+ const summary = db.get(sql `
183
+ SELECT
184
+ COUNT(*) as totalCalls,
185
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as totalCostUsd,
186
+ COALESCE(SUM(json_extract(payload, '$.usage.inputTokens')), 0) as totalInputTokens,
187
+ COALESCE(SUM(json_extract(payload, '$.usage.outputTokens')), 0) as totalOutputTokens,
188
+ COALESCE(AVG(json_extract(payload, '$.latencyMs')), 0) as avgLatencyMs
189
+ FROM events
190
+ WHERE event_type = 'llm_response'
191
+ AND timestamp >= ${from}
192
+ AND timestamp <= ${to}
193
+ AND tenant_id = ${tenantId}
194
+ ${agentId ? sql `AND agent_id = ${agentId}` : sql ``}
195
+ ${model ? sql `AND json_extract(payload, '$.model') = ${model}` : sql ``}
196
+ ${provider ? sql `AND json_extract(payload, '$.provider') = ${provider}` : sql ``}
197
+ `);
198
+ const totalCalls = Number(summary?.totalCalls ?? 0);
199
+ const totalCostUsd = Number(summary?.totalCostUsd ?? 0);
200
+ // By model breakdown
201
+ const byModel = db.all(sql `
202
+ SELECT
203
+ json_extract(payload, '$.provider') as provider,
204
+ json_extract(payload, '$.model') as model,
205
+ COUNT(*) as calls,
206
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as costUsd,
207
+ COALESCE(SUM(json_extract(payload, '$.usage.inputTokens')), 0) as inputTokens,
208
+ COALESCE(SUM(json_extract(payload, '$.usage.outputTokens')), 0) as outputTokens,
209
+ COALESCE(AVG(json_extract(payload, '$.latencyMs')), 0) as avgLatencyMs
210
+ FROM events
211
+ WHERE event_type = 'llm_response'
212
+ AND timestamp >= ${from}
213
+ AND timestamp <= ${to}
214
+ AND tenant_id = ${tenantId}
215
+ ${agentId ? sql `AND agent_id = ${agentId}` : sql ``}
216
+ ${model ? sql `AND json_extract(payload, '$.model') = ${model}` : sql ``}
217
+ ${provider ? sql `AND json_extract(payload, '$.provider') = ${provider}` : sql ``}
218
+ GROUP BY provider, model
219
+ ORDER BY costUsd DESC
220
+ `);
221
+ // By time bucket
222
+ const byTime = db.all(sql `
223
+ SELECT
224
+ strftime(${formatStr}, timestamp) as bucket,
225
+ COUNT(*) as calls,
226
+ COALESCE(SUM(json_extract(payload, '$.costUsd')), 0) as costUsd,
227
+ COALESCE(SUM(json_extract(payload, '$.usage.inputTokens')), 0) as inputTokens,
228
+ COALESCE(SUM(json_extract(payload, '$.usage.outputTokens')), 0) as outputTokens,
229
+ COALESCE(AVG(json_extract(payload, '$.latencyMs')), 0) as avgLatencyMs
230
+ FROM events
231
+ WHERE event_type = 'llm_response'
232
+ AND timestamp >= ${from}
233
+ AND timestamp <= ${to}
234
+ AND tenant_id = ${tenantId}
235
+ ${agentId ? sql `AND agent_id = ${agentId}` : sql ``}
236
+ ${model ? sql `AND json_extract(payload, '$.model') = ${model}` : sql ``}
237
+ ${provider ? sql `AND json_extract(payload, '$.provider') = ${provider}` : sql ``}
238
+ GROUP BY bucket
239
+ ORDER BY bucket ASC
240
+ `);
241
+ return c.json({
242
+ summary: {
243
+ totalCalls,
244
+ totalCostUsd,
245
+ totalInputTokens: Number(summary?.totalInputTokens ?? 0),
246
+ totalOutputTokens: Number(summary?.totalOutputTokens ?? 0),
247
+ avgLatencyMs: Number(summary?.avgLatencyMs ?? 0),
248
+ avgCostPerCall: totalCalls > 0 ? totalCostUsd / totalCalls : 0,
249
+ },
250
+ byModel: byModel.map((r) => ({
251
+ provider: r.provider,
252
+ model: r.model,
253
+ calls: Number(r.calls),
254
+ costUsd: Number(r.costUsd),
255
+ inputTokens: Number(r.inputTokens),
256
+ outputTokens: Number(r.outputTokens),
257
+ avgLatencyMs: Number(r.avgLatencyMs),
258
+ })),
259
+ byTime: byTime.map((r) => ({
260
+ bucket: r.bucket,
261
+ calls: Number(r.calls),
262
+ costUsd: Number(r.costUsd),
263
+ inputTokens: Number(r.inputTokens),
264
+ outputTokens: Number(r.outputTokens),
265
+ avgLatencyMs: Number(r.avgLatencyMs),
266
+ })),
267
+ });
268
+ });
269
+ // GET /api/analytics/tools — tool usage statistics
270
+ app.get('/tools', async (c) => {
271
+ const tenantId = c.get('apiKey')?.tenantId ?? 'default';
272
+ const from = c.req.query('from') ?? new Date(Date.now() - 86400_000).toISOString();
273
+ const to = c.req.query('to') ?? new Date().toISOString();
274
+ const rows = db.all(sql `
275
+ SELECT
276
+ json_extract(payload, '$.toolName') as toolName,
277
+ COUNT(*) as callCount,
278
+ SUM(CASE WHEN event_type = 'tool_error' THEN 1 ELSE 0 END) as errorCount,
279
+ COALESCE(AVG(
280
+ CASE WHEN event_type IN ('tool_response', 'tool_error')
281
+ THEN json_extract(payload, '$.durationMs')
282
+ ELSE NULL END
283
+ ), 0) as avgDurationMs
284
+ FROM events
285
+ WHERE event_type IN ('tool_call', 'tool_response', 'tool_error')
286
+ AND timestamp >= ${from}
287
+ AND timestamp <= ${to}
288
+ AND tenant_id = ${tenantId}
289
+ AND json_extract(payload, '$.toolName') IS NOT NULL
290
+ GROUP BY toolName
291
+ ORDER by callCount DESC
292
+ `);
293
+ return c.json({
294
+ tools: rows.map((r) => ({
295
+ toolName: r.toolName,
296
+ callCount: Number(r.callCount),
297
+ errorCount: Number(r.errorCount),
298
+ errorRate: Number(r.callCount) > 0
299
+ ? Number(r.errorCount) / Number(r.callCount)
300
+ : 0,
301
+ avgDurationMs: Number(r.avgDurationMs),
302
+ })),
303
+ });
304
+ });
305
+ return app;
306
+ }
307
+ //# 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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../../src/routes/api-keys.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEvE,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ;eACN,aAAa;0CAyEhD"}
1
+ {"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../../src/routes/api-keys.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEvE,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ;eACN,aAAa;0CAsGhD"}
@@ -9,7 +9,7 @@ import { Hono } from 'hono';
9
9
  import { randomBytes } from 'node:crypto';
10
10
  import { ulid } from 'ulid';
11
11
  import { apiKeys } from '../db/schema.sqlite.js';
12
- import { eq } from 'drizzle-orm';
12
+ import { eq, and } from 'drizzle-orm';
13
13
  import { hashApiKey } from '../middleware/auth.js';
14
14
  export function apiKeysRoutes(db) {
15
15
  const app = new Hono();
@@ -18,6 +18,15 @@ export function apiKeysRoutes(db) {
18
18
  const body = await c.req.json().catch(() => ({}));
19
19
  const name = body.name;
20
20
  const scopes = body.scopes;
21
+ const requestedTenantId = body.tenantId;
22
+ // Enforce tenant isolation: non-dev callers can only create keys for their own tenant
23
+ const callerKey = c.get('apiKey');
24
+ const callerTenantId = callerKey?.tenantId ?? 'default';
25
+ const isDevMode = callerKey?.id === 'dev';
26
+ // In dev mode, allow specifying tenantId; otherwise force caller's tenant
27
+ const resolvedTenantId = isDevMode
28
+ ? (requestedTenantId ?? 'default')
29
+ : callerTenantId;
21
30
  const id = ulid();
22
31
  const rawKey = `als_${randomBytes(32).toString('hex')}`;
23
32
  const keyHash = hashApiKey(rawKey);
@@ -29,6 +38,7 @@ export function apiKeysRoutes(db) {
29
38
  name: name ?? 'Unnamed Key',
30
39
  scopes: JSON.stringify(scopes ?? ['*']),
31
40
  createdAt: now,
41
+ tenantId: resolvedTenantId,
32
42
  })
33
43
  .run();
34
44
  return c.json({
@@ -36,12 +46,19 @@ export function apiKeysRoutes(db) {
36
46
  key: rawKey,
37
47
  name: name ?? 'Unnamed Key',
38
48
  scopes: scopes ?? ['*'],
49
+ tenantId: resolvedTenantId,
39
50
  createdAt: new Date(now * 1000).toISOString(),
40
51
  }, 201);
41
52
  });
42
- // GET /api/keys — list all keys
53
+ // GET /api/keys — list keys for caller's tenant only
43
54
  app.get('/', (c) => {
44
- const rows = db.select().from(apiKeys).all();
55
+ const callerKey = c.get('apiKey');
56
+ const callerTenantId = callerKey?.tenantId ?? 'default';
57
+ const rows = db
58
+ .select()
59
+ .from(apiKeys)
60
+ .where(eq(apiKeys.tenantId, callerTenantId))
61
+ .all();
45
62
  const keys = rows.map((row) => ({
46
63
  id: row.id,
47
64
  name: row.name,
@@ -53,17 +70,25 @@ export function apiKeysRoutes(db) {
53
70
  return [];
54
71
  }
55
72
  })(),
73
+ tenantId: row.tenantId,
56
74
  createdAt: new Date(row.createdAt * 1000).toISOString(),
57
75
  lastUsedAt: row.lastUsedAt ? new Date(row.lastUsedAt * 1000).toISOString() : null,
58
76
  revokedAt: row.revokedAt ? new Date(row.revokedAt * 1000).toISOString() : null,
59
77
  }));
60
78
  return c.json({ keys });
61
79
  });
62
- // DELETE /api/keys/:id — revoke a key
80
+ // DELETE /api/keys/:id — revoke a key (tenant-scoped)
63
81
  app.delete('/:id', (c) => {
64
82
  const id = c.req.param('id');
65
83
  const now = Math.floor(Date.now() / 1000);
66
- const existing = db.select().from(apiKeys).where(eq(apiKeys.id, id)).get();
84
+ const callerKey = c.get('apiKey');
85
+ const callerTenantId = callerKey?.tenantId ?? 'default';
86
+ // Look up key scoped to caller's tenant
87
+ const existing = db
88
+ .select()
89
+ .from(apiKeys)
90
+ .where(and(eq(apiKeys.id, id), eq(apiKeys.tenantId, callerTenantId)))
91
+ .get();
67
92
  if (!existing) {
68
93
  return c.json({ error: 'API key not found', status: 404 }, 404);
69
94
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/routes/api-keys.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/routes/api-keys.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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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Context REST Endpoint (Story 5.4)
3
+ *
4
+ * GET /api/context?topic=...&userId=...&agentId=...&from=...&to=...&limit=...
5
+ *
6
+ * Returns cross-session context combining session summaries, lessons,
7
+ * and key events, ranked by relevance.
8
+ */
9
+ import { Hono } from 'hono';
10
+ import type { IEventStore } from '@agentlensai/core';
11
+ import type { AuthVariables } from '../middleware/auth.js';
12
+ import type { EmbeddingService } from '../lib/embeddings/index.js';
13
+ import type { EmbeddingStore } from '../db/embedding-store.js';
14
+ import type { SqliteDb } from '../db/index.js';
15
+ export interface ContextRouteDeps {
16
+ db: SqliteDb;
17
+ embeddingService: EmbeddingService | null;
18
+ embeddingStore: EmbeddingStore | null;
19
+ }
20
+ export declare function contextRoutes(store: IEventStore, deps: ContextRouteDeps): Hono<{
21
+ Variables: AuthVariables;
22
+ }, import("hono/types").BlankSchema, "/">;
23
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/routes/context.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,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM/C,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,QAAQ,CAAC;IACb,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,gBAAgB;eACpC,aAAa;0CAgDhD"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Context REST Endpoint (Story 5.4)
3
+ *
4
+ * GET /api/context?topic=...&userId=...&agentId=...&from=...&to=...&limit=...
5
+ *
6
+ * Returns cross-session context combining session summaries, lessons,
7
+ * and key events, ranked by relevance.
8
+ */
9
+ import { Hono } from 'hono';
10
+ import { SessionSummaryStore } from '../db/session-summary-store.js';
11
+ import { LessonStore } from '../db/lesson-store.js';
12
+ import { ContextRetriever } from '../lib/context/retrieval.js';
13
+ import { getTenantStore } from './tenant-helper.js';
14
+ export function contextRoutes(store, deps) {
15
+ const app = new Hono();
16
+ const sessionSummaryStore = new SessionSummaryStore(deps.db);
17
+ const lessonStore = new LessonStore(deps.db);
18
+ // GET /api/context
19
+ app.get('/', async (c) => {
20
+ const topic = c.req.query('topic');
21
+ if (!topic) {
22
+ return c.json({ error: 'Missing required query parameter: topic', status: 400 }, 400);
23
+ }
24
+ const apiKeyInfo = c.get('apiKey');
25
+ const tenantId = apiKeyInfo?.tenantId ?? 'default';
26
+ const tenantStore = getTenantStore(store, c);
27
+ const userId = c.req.query('userId') || undefined;
28
+ const agentId = c.req.query('agentId') || undefined;
29
+ const from = c.req.query('from') || undefined;
30
+ const to = c.req.query('to') || undefined;
31
+ const limitStr = c.req.query('limit');
32
+ const limit = limitStr ? Math.max(1, Math.min(parseInt(limitStr, 10) || 10, 100)) : 10;
33
+ try {
34
+ const retriever = new ContextRetriever(deps.embeddingStore, deps.embeddingService, sessionSummaryStore, lessonStore, tenantStore);
35
+ const result = await retriever.retrieve(tenantId, {
36
+ topic,
37
+ userId,
38
+ agentId,
39
+ from,
40
+ to,
41
+ limit,
42
+ });
43
+ return c.json(result);
44
+ }
45
+ catch (error) {
46
+ console.error('[context] Retrieval failed:', error instanceof Error ? error.message : error);
47
+ return c.json({ error: 'Context retrieval failed', status: 500 }, 500);
48
+ }
49
+ });
50
+ return app;
51
+ }
52
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/routes/context.ts"],"names":[],"mappings":"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"}