brains-mcp 1.61.2

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 (350) hide show
  1. package/README.md +95 -0
  2. package/dist/access/projectShares.d.ts +48 -0
  3. package/dist/access/projectShares.d.ts.map +1 -0
  4. package/dist/access/projectShares.js +203 -0
  5. package/dist/access/projectShares.js.map +1 -0
  6. package/dist/access/sharedInstructionsNote.d.ts +37 -0
  7. package/dist/access/sharedInstructionsNote.d.ts.map +1 -0
  8. package/dist/access/sharedInstructionsNote.js +76 -0
  9. package/dist/access/sharedInstructionsNote.js.map +1 -0
  10. package/dist/admin/seedUsers.d.ts +23 -0
  11. package/dist/admin/seedUsers.d.ts.map +1 -0
  12. package/dist/admin/seedUsers.js +168 -0
  13. package/dist/admin/seedUsers.js.map +1 -0
  14. package/dist/api/openapi.d.ts +2 -0
  15. package/dist/api/openapi.d.ts.map +1 -0
  16. package/dist/api/openapi.js +942 -0
  17. package/dist/api/openapi.js.map +1 -0
  18. package/dist/auth/clientStore.d.ts +43 -0
  19. package/dist/auth/clientStore.d.ts.map +1 -0
  20. package/dist/auth/clientStore.js +168 -0
  21. package/dist/auth/clientStore.js.map +1 -0
  22. package/dist/auth/deleteAccount.d.ts +34 -0
  23. package/dist/auth/deleteAccount.d.ts.map +1 -0
  24. package/dist/auth/deleteAccount.js +203 -0
  25. package/dist/auth/deleteAccount.js.map +1 -0
  26. package/dist/auth/oauth.d.ts +118 -0
  27. package/dist/auth/oauth.d.ts.map +1 -0
  28. package/dist/auth/oauth.js +491 -0
  29. package/dist/auth/oauth.js.map +1 -0
  30. package/dist/auth/patStore.d.ts +87 -0
  31. package/dist/auth/patStore.d.ts.map +1 -0
  32. package/dist/auth/patStore.js +308 -0
  33. package/dist/auth/patStore.js.map +1 -0
  34. package/dist/auth/supabase.d.ts +15 -0
  35. package/dist/auth/supabase.d.ts.map +1 -0
  36. package/dist/auth/supabase.js +66 -0
  37. package/dist/auth/supabase.js.map +1 -0
  38. package/dist/auth/tokenStore.d.ts +73 -0
  39. package/dist/auth/tokenStore.d.ts.map +1 -0
  40. package/dist/auth/tokenStore.js +261 -0
  41. package/dist/auth/tokenStore.js.map +1 -0
  42. package/dist/backup/drive.d.ts +24 -0
  43. package/dist/backup/drive.d.ts.map +1 -0
  44. package/dist/backup/drive.js +70 -0
  45. package/dist/backup/drive.js.map +1 -0
  46. package/dist/billing/tierStore.d.ts +59 -0
  47. package/dist/billing/tierStore.d.ts.map +1 -0
  48. package/dist/billing/tierStore.js +152 -0
  49. package/dist/billing/tierStore.js.map +1 -0
  50. package/dist/digest/activityDigest.d.ts +32 -0
  51. package/dist/digest/activityDigest.d.ts.map +1 -0
  52. package/dist/digest/activityDigest.js +299 -0
  53. package/dist/digest/activityDigest.js.map +1 -0
  54. package/dist/digest/digestCron.d.ts +40 -0
  55. package/dist/digest/digestCron.d.ts.map +1 -0
  56. package/dist/digest/digestCron.js +146 -0
  57. package/dist/digest/digestCron.js.map +1 -0
  58. package/dist/digest/digestPreferences.d.ts +25 -0
  59. package/dist/digest/digestPreferences.d.ts.map +1 -0
  60. package/dist/digest/digestPreferences.js +79 -0
  61. package/dist/digest/digestPreferences.js.map +1 -0
  62. package/dist/digest/emailSender.d.ts +20 -0
  63. package/dist/digest/emailSender.d.ts.map +1 -0
  64. package/dist/digest/emailSender.js +35 -0
  65. package/dist/digest/emailSender.js.map +1 -0
  66. package/dist/drive/client.d.ts +43 -0
  67. package/dist/drive/client.d.ts.map +1 -0
  68. package/dist/drive/client.js +282 -0
  69. package/dist/drive/client.js.map +1 -0
  70. package/dist/drive/errors.d.ts +27 -0
  71. package/dist/drive/errors.d.ts.map +1 -0
  72. package/dist/drive/errors.js +51 -0
  73. package/dist/drive/errors.js.map +1 -0
  74. package/dist/drive/fileCache.d.ts +22 -0
  75. package/dist/drive/fileCache.d.ts.map +1 -0
  76. package/dist/drive/fileCache.js +57 -0
  77. package/dist/drive/fileCache.js.map +1 -0
  78. package/dist/governance/accessControl.d.ts +87 -0
  79. package/dist/governance/accessControl.d.ts.map +1 -0
  80. package/dist/governance/accessControl.js +179 -0
  81. package/dist/governance/accessControl.js.map +1 -0
  82. package/dist/html/components.d.ts +8 -0
  83. package/dist/html/components.d.ts.map +1 -0
  84. package/dist/html/components.js +19 -0
  85. package/dist/html/components.js.map +1 -0
  86. package/dist/html/layout.d.ts +7 -0
  87. package/dist/html/layout.d.ts.map +1 -0
  88. package/dist/html/layout.js +24 -0
  89. package/dist/html/layout.js.map +1 -0
  90. package/dist/html/metricsUi.d.ts +8 -0
  91. package/dist/html/metricsUi.d.ts.map +1 -0
  92. package/dist/html/metricsUi.js +271 -0
  93. package/dist/html/metricsUi.js.map +1 -0
  94. package/dist/httpServer.d.ts +19 -0
  95. package/dist/httpServer.d.ts.map +1 -0
  96. package/dist/httpServer.js +3913 -0
  97. package/dist/httpServer.js.map +1 -0
  98. package/dist/index.d.ts +3 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +40 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/jobs/asyncJobRegistry.d.ts +30 -0
  103. package/dist/jobs/asyncJobRegistry.d.ts.map +1 -0
  104. package/dist/jobs/asyncJobRegistry.js +70 -0
  105. package/dist/jobs/asyncJobRegistry.js.map +1 -0
  106. package/dist/observability/metricsQuery.d.ts +41 -0
  107. package/dist/observability/metricsQuery.d.ts.map +1 -0
  108. package/dist/observability/metricsQuery.js +106 -0
  109. package/dist/observability/metricsQuery.js.map +1 -0
  110. package/dist/observability/relevanceSignal.d.ts +26 -0
  111. package/dist/observability/relevanceSignal.d.ts.map +1 -0
  112. package/dist/observability/relevanceSignal.js +95 -0
  113. package/dist/observability/relevanceSignal.js.map +1 -0
  114. package/dist/observability/requestEvents.d.ts +31 -0
  115. package/dist/observability/requestEvents.d.ts.map +1 -0
  116. package/dist/observability/requestEvents.js +54 -0
  117. package/dist/observability/requestEvents.js.map +1 -0
  118. package/dist/observability/slowQueryAlert.d.ts +25 -0
  119. package/dist/observability/slowQueryAlert.d.ts.map +1 -0
  120. package/dist/observability/slowQueryAlert.js +67 -0
  121. package/dist/observability/slowQueryAlert.js.map +1 -0
  122. package/dist/observability/zeroResultTriage.d.ts +22 -0
  123. package/dist/observability/zeroResultTriage.d.ts.map +1 -0
  124. package/dist/observability/zeroResultTriage.js +116 -0
  125. package/dist/observability/zeroResultTriage.js.map +1 -0
  126. package/dist/search/embeddings.d.ts +31 -0
  127. package/dist/search/embeddings.d.ts.map +1 -0
  128. package/dist/search/embeddings.js +91 -0
  129. package/dist/search/embeddings.js.map +1 -0
  130. package/dist/search/fileWatcher.d.ts +28 -0
  131. package/dist/search/fileWatcher.d.ts.map +1 -0
  132. package/dist/search/fileWatcher.js +103 -0
  133. package/dist/search/fileWatcher.js.map +1 -0
  134. package/dist/search/hotQueryCache.d.ts +31 -0
  135. package/dist/search/hotQueryCache.d.ts.map +1 -0
  136. package/dist/search/hotQueryCache.js +103 -0
  137. package/dist/search/hotQueryCache.js.map +1 -0
  138. package/dist/search/postgresIndex.d.ts +439 -0
  139. package/dist/search/postgresIndex.d.ts.map +1 -0
  140. package/dist/search/postgresIndex.js +1610 -0
  141. package/dist/search/postgresIndex.js.map +1 -0
  142. package/dist/search/sqliteIndex.d.ts +66 -0
  143. package/dist/search/sqliteIndex.d.ts.map +1 -0
  144. package/dist/search/sqliteIndex.js +342 -0
  145. package/dist/search/sqliteIndex.js.map +1 -0
  146. package/dist/server.d.ts +43 -0
  147. package/dist/server.d.ts.map +1 -0
  148. package/dist/server.js +532 -0
  149. package/dist/server.js.map +1 -0
  150. package/dist/session/bundle.d.ts +53 -0
  151. package/dist/session/bundle.d.ts.map +1 -0
  152. package/dist/session/bundle.js +97 -0
  153. package/dist/session/bundle.js.map +1 -0
  154. package/dist/storage/client.d.ts +157 -0
  155. package/dist/storage/client.d.ts.map +1 -0
  156. package/dist/storage/client.js +825 -0
  157. package/dist/storage/client.js.map +1 -0
  158. package/dist/tools/addWebhookPattern.d.ts +15 -0
  159. package/dist/tools/addWebhookPattern.d.ts.map +1 -0
  160. package/dist/tools/addWebhookPattern.js +15 -0
  161. package/dist/tools/addWebhookPattern.js.map +1 -0
  162. package/dist/tools/appendLog.d.ts +18 -0
  163. package/dist/tools/appendLog.d.ts.map +1 -0
  164. package/dist/tools/appendLog.js +18 -0
  165. package/dist/tools/appendLog.js.map +1 -0
  166. package/dist/tools/auditIndex.d.ts +15 -0
  167. package/dist/tools/auditIndex.d.ts.map +1 -0
  168. package/dist/tools/auditIndex.js +165 -0
  169. package/dist/tools/auditIndex.js.map +1 -0
  170. package/dist/tools/batchDeletePages.d.ts +18 -0
  171. package/dist/tools/batchDeletePages.d.ts.map +1 -0
  172. package/dist/tools/batchDeletePages.js +143 -0
  173. package/dist/tools/batchDeletePages.js.map +1 -0
  174. package/dist/tools/coerce.d.ts +3 -0
  175. package/dist/tools/coerce.d.ts.map +1 -0
  176. package/dist/tools/coerce.js +12 -0
  177. package/dist/tools/coerce.js.map +1 -0
  178. package/dist/tools/createPage.d.ts +24 -0
  179. package/dist/tools/createPage.d.ts.map +1 -0
  180. package/dist/tools/createPage.js +99 -0
  181. package/dist/tools/createPage.js.map +1 -0
  182. package/dist/tools/createPages.d.ts +51 -0
  183. package/dist/tools/createPages.d.ts.map +1 -0
  184. package/dist/tools/createPages.js +91 -0
  185. package/dist/tools/createPages.js.map +1 -0
  186. package/dist/tools/deletePage.d.ts +19 -0
  187. package/dist/tools/deletePage.d.ts.map +1 -0
  188. package/dist/tools/deletePage.js +127 -0
  189. package/dist/tools/deletePage.js.map +1 -0
  190. package/dist/tools/exportWiki.d.ts +48 -0
  191. package/dist/tools/exportWiki.d.ts.map +1 -0
  192. package/dist/tools/exportWiki.js +233 -0
  193. package/dist/tools/exportWiki.js.map +1 -0
  194. package/dist/tools/extractLinks.d.ts +14 -0
  195. package/dist/tools/extractLinks.d.ts.map +1 -0
  196. package/dist/tools/extractLinks.js +25 -0
  197. package/dist/tools/extractLinks.js.map +1 -0
  198. package/dist/tools/generateClaudeInstructions.d.ts +27 -0
  199. package/dist/tools/generateClaudeInstructions.d.ts.map +1 -0
  200. package/dist/tools/generateClaudeInstructions.js +263 -0
  201. package/dist/tools/generateClaudeInstructions.js.map +1 -0
  202. package/dist/tools/generateGptInstructions.d.ts +27 -0
  203. package/dist/tools/generateGptInstructions.d.ts.map +1 -0
  204. package/dist/tools/generateGptInstructions.js +208 -0
  205. package/dist/tools/generateGptInstructions.js.map +1 -0
  206. package/dist/tools/getBacklinks.d.ts +12 -0
  207. package/dist/tools/getBacklinks.d.ts.map +1 -0
  208. package/dist/tools/getBacklinks.js +13 -0
  209. package/dist/tools/getBacklinks.js.map +1 -0
  210. package/dist/tools/getRelated.d.ts +12 -0
  211. package/dist/tools/getRelated.d.ts.map +1 -0
  212. package/dist/tools/getRelated.js +13 -0
  213. package/dist/tools/getRelated.js.map +1 -0
  214. package/dist/tools/healthCheck.d.ts +5 -0
  215. package/dist/tools/healthCheck.d.ts.map +1 -0
  216. package/dist/tools/healthCheck.js +167 -0
  217. package/dist/tools/healthCheck.js.map +1 -0
  218. package/dist/tools/importBrainSource.d.ts +36 -0
  219. package/dist/tools/importBrainSource.d.ts.map +1 -0
  220. package/dist/tools/importBrainSource.js +314 -0
  221. package/dist/tools/importBrainSource.js.map +1 -0
  222. package/dist/tools/importWikiZip.d.ts +60 -0
  223. package/dist/tools/importWikiZip.d.ts.map +1 -0
  224. package/dist/tools/importWikiZip.js +379 -0
  225. package/dist/tools/importWikiZip.js.map +1 -0
  226. package/dist/tools/initProject.d.ts +34 -0
  227. package/dist/tools/initProject.d.ts.map +1 -0
  228. package/dist/tools/initProject.js +443 -0
  229. package/dist/tools/initProject.js.map +1 -0
  230. package/dist/tools/instructionContext.d.ts +60 -0
  231. package/dist/tools/instructionContext.d.ts.map +1 -0
  232. package/dist/tools/instructionContext.js +210 -0
  233. package/dist/tools/instructionContext.js.map +1 -0
  234. package/dist/tools/listArchivedPages.d.ts +17 -0
  235. package/dist/tools/listArchivedPages.d.ts.map +1 -0
  236. package/dist/tools/listArchivedPages.js +51 -0
  237. package/dist/tools/listArchivedPages.js.map +1 -0
  238. package/dist/tools/listPages.d.ts +28 -0
  239. package/dist/tools/listPages.d.ts.map +1 -0
  240. package/dist/tools/listPages.js +166 -0
  241. package/dist/tools/listPages.js.map +1 -0
  242. package/dist/tools/listWebhooks.d.ts +7 -0
  243. package/dist/tools/listWebhooks.d.ts.map +1 -0
  244. package/dist/tools/listWebhooks.js +10 -0
  245. package/dist/tools/listWebhooks.js.map +1 -0
  246. package/dist/tools/movePage.d.ts +18 -0
  247. package/dist/tools/movePage.d.ts.map +1 -0
  248. package/dist/tools/movePage.js +182 -0
  249. package/dist/tools/movePage.js.map +1 -0
  250. package/dist/tools/pageHistory.d.ts +36 -0
  251. package/dist/tools/pageHistory.d.ts.map +1 -0
  252. package/dist/tools/pageHistory.js +60 -0
  253. package/dist/tools/pageHistory.js.map +1 -0
  254. package/dist/tools/patchPage.d.ts +141 -0
  255. package/dist/tools/patchPage.d.ts.map +1 -0
  256. package/dist/tools/patchPage.js +287 -0
  257. package/dist/tools/patchPage.js.map +1 -0
  258. package/dist/tools/pullSessionBundle.d.ts +34 -0
  259. package/dist/tools/pullSessionBundle.d.ts.map +1 -0
  260. package/dist/tools/pullSessionBundle.js +432 -0
  261. package/dist/tools/pullSessionBundle.js.map +1 -0
  262. package/dist/tools/purgeWiki.d.ts +23 -0
  263. package/dist/tools/purgeWiki.d.ts.map +1 -0
  264. package/dist/tools/purgeWiki.js +113 -0
  265. package/dist/tools/purgeWiki.js.map +1 -0
  266. package/dist/tools/pushSessionBundle.d.ts +139 -0
  267. package/dist/tools/pushSessionBundle.d.ts.map +1 -0
  268. package/dist/tools/pushSessionBundle.js +282 -0
  269. package/dist/tools/pushSessionBundle.js.map +1 -0
  270. package/dist/tools/readGovernanceAudit.d.ts +17 -0
  271. package/dist/tools/readGovernanceAudit.d.ts.map +1 -0
  272. package/dist/tools/readGovernanceAudit.js +21 -0
  273. package/dist/tools/readGovernanceAudit.js.map +1 -0
  274. package/dist/tools/readPage.d.ts +13 -0
  275. package/dist/tools/readPage.d.ts.map +1 -0
  276. package/dist/tools/readPage.js +46 -0
  277. package/dist/tools/readPage.js.map +1 -0
  278. package/dist/tools/removeWebhook.d.ts +14 -0
  279. package/dist/tools/removeWebhook.d.ts.map +1 -0
  280. package/dist/tools/removeWebhook.js +12 -0
  281. package/dist/tools/removeWebhook.js.map +1 -0
  282. package/dist/tools/resolveProject.d.ts +58 -0
  283. package/dist/tools/resolveProject.d.ts.map +1 -0
  284. package/dist/tools/resolveProject.js +175 -0
  285. package/dist/tools/resolveProject.js.map +1 -0
  286. package/dist/tools/restorePage.d.ts +47 -0
  287. package/dist/tools/restorePage.d.ts.map +1 -0
  288. package/dist/tools/restorePage.js +111 -0
  289. package/dist/tools/restorePage.js.map +1 -0
  290. package/dist/tools/searchPages.d.ts +28 -0
  291. package/dist/tools/searchPages.d.ts.map +1 -0
  292. package/dist/tools/searchPages.js +372 -0
  293. package/dist/tools/searchPages.js.map +1 -0
  294. package/dist/tools/setWebhook.d.ts +30 -0
  295. package/dist/tools/setWebhook.d.ts.map +1 -0
  296. package/dist/tools/setWebhook.js +30 -0
  297. package/dist/tools/setWebhook.js.map +1 -0
  298. package/dist/tools/shareProject.d.ts +25 -0
  299. package/dist/tools/shareProject.d.ts.map +1 -0
  300. package/dist/tools/shareProject.js +56 -0
  301. package/dist/tools/shareProject.js.map +1 -0
  302. package/dist/tools/suggestLinks.d.ts +29 -0
  303. package/dist/tools/suggestLinks.d.ts.map +1 -0
  304. package/dist/tools/suggestLinks.js +214 -0
  305. package/dist/tools/suggestLinks.js.map +1 -0
  306. package/dist/tools/updatePage.d.ts +27 -0
  307. package/dist/tools/updatePage.d.ts.map +1 -0
  308. package/dist/tools/updatePage.js +118 -0
  309. package/dist/tools/updatePage.js.map +1 -0
  310. package/dist/webhooks/dispatch.d.ts +49 -0
  311. package/dist/webhooks/dispatch.d.ts.map +1 -0
  312. package/dist/webhooks/dispatch.js +159 -0
  313. package/dist/webhooks/dispatch.js.map +1 -0
  314. package/dist/webhooks/webhookRules.d.ts +34 -0
  315. package/dist/webhooks/webhookRules.d.ts.map +1 -0
  316. package/dist/webhooks/webhookRules.js +129 -0
  317. package/dist/webhooks/webhookRules.js.map +1 -0
  318. package/dist/wiki/frontmatter.d.ts +45 -0
  319. package/dist/wiki/frontmatter.d.ts.map +1 -0
  320. package/dist/wiki/frontmatter.js +68 -0
  321. package/dist/wiki/frontmatter.js.map +1 -0
  322. package/dist/wiki/index.d.ts +22 -0
  323. package/dist/wiki/index.d.ts.map +1 -0
  324. package/dist/wiki/index.js +203 -0
  325. package/dist/wiki/index.js.map +1 -0
  326. package/dist/wiki/ingestPolicy.d.ts +18 -0
  327. package/dist/wiki/ingestPolicy.d.ts.map +1 -0
  328. package/dist/wiki/ingestPolicy.js +64 -0
  329. package/dist/wiki/ingestPolicy.js.map +1 -0
  330. package/dist/wiki/layoutLint.d.ts +51 -0
  331. package/dist/wiki/layoutLint.d.ts.map +1 -0
  332. package/dist/wiki/layoutLint.js +146 -0
  333. package/dist/wiki/layoutLint.js.map +1 -0
  334. package/dist/wiki/links.d.ts +6 -0
  335. package/dist/wiki/links.d.ts.map +1 -0
  336. package/dist/wiki/links.js +45 -0
  337. package/dist/wiki/links.js.map +1 -0
  338. package/dist/wiki/log.d.ts +58 -0
  339. package/dist/wiki/log.d.ts.map +1 -0
  340. package/dist/wiki/log.js +126 -0
  341. package/dist/wiki/log.js.map +1 -0
  342. package/dist/wiki/path.d.ts +8 -0
  343. package/dist/wiki/path.d.ts.map +1 -0
  344. package/dist/wiki/path.js +25 -0
  345. package/dist/wiki/path.js.map +1 -0
  346. package/dist/wiki/storageLimits.d.ts +163 -0
  347. package/dist/wiki/storageLimits.d.ts.map +1 -0
  348. package/dist/wiki/storageLimits.js +281 -0
  349. package/dist/wiki/storageLimits.js.map +1 -0
  350. package/package.json +81 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * metricsQuery.ts
3
+ *
4
+ * Aggregates request_events telemetry via a Supabase Postgres RPC function.
5
+ * Falls back to zero-filled defaults when Supabase is not configured.
6
+ *
7
+ * SQL function definition: scripts/metrics-rpc.sql
8
+ */
9
+ export const VALID_WINDOWS = ["1h", "6h", "24h", "7d"];
10
+ const WINDOW_MINUTES = {
11
+ "1h": 60,
12
+ "6h": 360,
13
+ "24h": 1440,
14
+ "7d": 10080,
15
+ };
16
+ export function isValidWindow(w) {
17
+ return VALID_WINDOWS.includes(w);
18
+ }
19
+ function emptyMetrics(window) {
20
+ return {
21
+ window,
22
+ search: {
23
+ p50: 0,
24
+ p95: 0,
25
+ p99: 0,
26
+ totalRequests: 0,
27
+ errorRate: 0,
28
+ zeroResultRate: 0,
29
+ fallbackRate: 0,
30
+ backendBreakdown: {},
31
+ topQueries: [],
32
+ },
33
+ writes: { p50: 0, p95: 0, totalRequests: 0 },
34
+ overall: { p95: 0, errorRate: 0 },
35
+ };
36
+ }
37
+ function toNum(v, fallback = 0) {
38
+ const n = Number(v);
39
+ return isFinite(n) ? n : fallback;
40
+ }
41
+ function parseRpcRow(window, row) {
42
+ const search = (row["search"] ?? {});
43
+ const writes = (row["writes"] ?? {});
44
+ const overall = (row["overall"] ?? {});
45
+ const backendRaw = search["backendBreakdown"];
46
+ const backendBreakdown = backendRaw && typeof backendRaw === "object" && !Array.isArray(backendRaw)
47
+ ? Object.fromEntries(Object.entries(backendRaw).map(([k, v]) => [k, toNum(v)]))
48
+ : {};
49
+ const topQueriesRaw = search["topQueries"];
50
+ const topQueries = Array.isArray(topQueriesRaw)
51
+ ? topQueriesRaw.filter((q) => typeof q === "string")
52
+ : [];
53
+ return {
54
+ window,
55
+ search: {
56
+ p50: toNum(search["p50"]),
57
+ p95: toNum(search["p95"]),
58
+ p99: toNum(search["p99"]),
59
+ totalRequests: toNum(search["totalRequests"]),
60
+ errorRate: toNum(search["errorRate"]),
61
+ zeroResultRate: toNum(search["zeroResultRate"]),
62
+ fallbackRate: toNum(search["fallbackRate"]),
63
+ backendBreakdown,
64
+ topQueries,
65
+ },
66
+ writes: {
67
+ p50: toNum(writes["p50"]),
68
+ p95: toNum(writes["p95"]),
69
+ totalRequests: toNum(writes["totalRequests"]),
70
+ },
71
+ overall: {
72
+ p95: toNum(overall["p95"]),
73
+ errorRate: toNum(overall["errorRate"]),
74
+ },
75
+ };
76
+ }
77
+ /**
78
+ * Query aggregated metrics for the given time window.
79
+ * Returns zero-filled defaults when Supabase is not configured.
80
+ * Throws on Supabase RPC errors so the HTTP handler can return 500.
81
+ */
82
+ export async function queryMetrics(window) {
83
+ const supabaseUrl = (process.env.SUPABASE_URL ?? "").trim().replace(/\/$/, "");
84
+ const serviceRoleKey = (process.env.SUPABASE_SERVICE_ROLE_KEY ?? "").trim();
85
+ if (!supabaseUrl || !serviceRoleKey) {
86
+ return emptyMetrics(window);
87
+ }
88
+ const minutes = WINDOW_MINUTES[window];
89
+ const url = `${supabaseUrl}/rest/v1/rpc/get_metrics`;
90
+ const res = await fetch(url, {
91
+ method: "POST",
92
+ headers: {
93
+ "content-type": "application/json",
94
+ "apikey": serviceRoleKey,
95
+ "authorization": `Bearer ${serviceRoleKey}`,
96
+ },
97
+ body: JSON.stringify({ minutes }),
98
+ });
99
+ if (!res.ok) {
100
+ const text = await res.text().catch(() => "");
101
+ throw new Error(`get_metrics RPC failed (${res.status}): ${text}`);
102
+ }
103
+ const data = (await res.json());
104
+ return parseRpcRow(window, data);
105
+ }
106
+ //# sourceMappingURL=metricsQuery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metricsQuery.js","sourceRoot":"","sources":["../../src/observability/metricsQuery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * relevanceSignal.ts
3
+ *
4
+ * Implicit relevance signal recorder.
5
+ * When a `read_page` call follows a `search_pages` call within 30 s from the
6
+ * same auth subject, and the page read was in the search results, record the
7
+ * (query_text, page_name) pair to the `search_relevance_signals` Supabase table.
8
+ *
9
+ * Phase 4 accuracy improvement.
10
+ *
11
+ * Table schema: scripts/search-relevance-signals.sql
12
+ */
13
+ /**
14
+ * Record a completed search for correlation tracking.
15
+ */
16
+ export declare function recordSearch(userId: string, queryText: string, resultNames: string[]): void;
17
+ /**
18
+ * Check whether a page read correlates with a recent search and, if so, fire a
19
+ * background insert into `search_relevance_signals`.
20
+ *
21
+ * Safe to call on every `read_page` — returns immediately without awaiting.
22
+ */
23
+ export declare function maybeRecordRelevanceSignal(userId: string, pageName: string, supabaseUrl: string, serviceRoleKey: string): void;
24
+ /** Reset state — used in tests. */
25
+ export declare function resetRelevanceSignalState(): void;
26
+ //# sourceMappingURL=relevanceSignal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relevanceSignal.d.ts","sourceRoot":"","sources":["../../src/observability/relevanceSignal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqBH;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EAAE,GACpB,IAAI,CASN;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,GACrB,IAAI,CAwBN;AAoCD,mCAAmC;AACnC,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * relevanceSignal.ts
3
+ *
4
+ * Implicit relevance signal recorder.
5
+ * When a `read_page` call follows a `search_pages` call within 30 s from the
6
+ * same auth subject, and the page read was in the search results, record the
7
+ * (query_text, page_name) pair to the `search_relevance_signals` Supabase table.
8
+ *
9
+ * Phase 4 accuracy improvement.
10
+ *
11
+ * Table schema: scripts/search-relevance-signals.sql
12
+ */
13
+ const CORRELATION_WINDOW_MS = 30_000; // 30 seconds
14
+ const MAX_TRACKED_USERS = 500;
15
+ // In-memory store: userId → most-recent search
16
+ const recentSearches = new Map();
17
+ function evictStale() {
18
+ const cutoff = Date.now() - CORRELATION_WINDOW_MS;
19
+ for (const [userId, entry] of recentSearches) {
20
+ if (entry.ts < cutoff)
21
+ recentSearches.delete(userId);
22
+ }
23
+ }
24
+ /**
25
+ * Record a completed search for correlation tracking.
26
+ */
27
+ export function recordSearch(userId, queryText, resultNames) {
28
+ if (!userId)
29
+ return;
30
+ evictStale();
31
+ if (recentSearches.size >= MAX_TRACKED_USERS && !recentSearches.has(userId)) {
32
+ // Evict the oldest entry to cap memory usage.
33
+ const oldest = recentSearches.keys().next().value;
34
+ if (oldest !== undefined)
35
+ recentSearches.delete(oldest);
36
+ }
37
+ recentSearches.set(userId, { queryText, resultNames, ts: Date.now() });
38
+ }
39
+ /**
40
+ * Check whether a page read correlates with a recent search and, if so, fire a
41
+ * background insert into `search_relevance_signals`.
42
+ *
43
+ * Safe to call on every `read_page` — returns immediately without awaiting.
44
+ */
45
+ export function maybeRecordRelevanceSignal(userId, pageName, supabaseUrl, serviceRoleKey) {
46
+ if (!userId || !supabaseUrl || !serviceRoleKey)
47
+ return;
48
+ evictStale();
49
+ const search = recentSearches.get(userId);
50
+ if (!search)
51
+ return;
52
+ const age = Date.now() - search.ts;
53
+ if (age > CORRELATION_WINDOW_MS) {
54
+ recentSearches.delete(userId);
55
+ return;
56
+ }
57
+ if (!search.resultNames.includes(pageName))
58
+ return;
59
+ // Fire-and-forget insert.
60
+ void insertRelevanceSignal(supabaseUrl, serviceRoleKey, {
61
+ authSubject: userId,
62
+ queryText: search.queryText,
63
+ pageName,
64
+ searchTs: new Date(search.ts).toISOString(),
65
+ }).catch((err) => {
66
+ console.error("[relevance] insert failed", err.message);
67
+ });
68
+ }
69
+ async function insertRelevanceSignal(supabaseUrl, serviceRoleKey, row) {
70
+ const url = `${supabaseUrl}/rest/v1/search_relevance_signals`;
71
+ const res = await fetch(url, {
72
+ method: "POST",
73
+ headers: {
74
+ "content-type": "application/json",
75
+ apikey: serviceRoleKey,
76
+ authorization: `Bearer ${serviceRoleKey}`,
77
+ prefer: "return=minimal",
78
+ },
79
+ body: JSON.stringify({
80
+ auth_subject: row.authSubject,
81
+ query_text: row.queryText,
82
+ page_name: row.pageName,
83
+ search_ts: row.searchTs,
84
+ }),
85
+ });
86
+ if (!res.ok) {
87
+ const text = await res.text().catch(() => "");
88
+ throw new Error(`insertRelevanceSignal failed (${res.status}): ${text}`);
89
+ }
90
+ }
91
+ /** Reset state — used in tests. */
92
+ export function resetRelevanceSignalState() {
93
+ recentSearches.clear();
94
+ }
95
+ //# sourceMappingURL=relevanceSignal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relevanceSignal.js","sourceRoot":"","sources":["../../src/observability/relevanceSignal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * requestEvents.ts
3
+ *
4
+ * Fire-and-forget insert of per-request telemetry into the Supabase
5
+ * `request_events` table. Uses the same raw-fetch / service-role-key
6
+ * pattern as PostgresTokenStore and PostgresSearchIndex — no extra SDK.
7
+ *
8
+ * When SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY are absent the insert
9
+ * is skipped entirely and the caller sees no error.
10
+ *
11
+ * Table schema: scripts/request-events.sql
12
+ */
13
+ export interface RequestEvent {
14
+ method: string;
15
+ path: string;
16
+ status: number;
17
+ duration_ms: number;
18
+ backend?: string;
19
+ query_ms?: number;
20
+ sync_ms?: number;
21
+ result_count?: number;
22
+ query_text?: string;
23
+ tool?: string;
24
+ page_count?: number;
25
+ }
26
+ /**
27
+ * Insert a single request event row into `request_events`.
28
+ * Throws on HTTP error so callers can decide how to handle failures.
29
+ */
30
+ export declare function insertRequestEvent(supabaseUrl: string, serviceRoleKey: string, event: RequestEvent): Promise<void>;
31
+ //# sourceMappingURL=requestEvents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestEvents.d.ts","sourceRoot":"","sources":["../../src/observability/requestEvents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CAgCf"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * requestEvents.ts
3
+ *
4
+ * Fire-and-forget insert of per-request telemetry into the Supabase
5
+ * `request_events` table. Uses the same raw-fetch / service-role-key
6
+ * pattern as PostgresTokenStore and PostgresSearchIndex — no extra SDK.
7
+ *
8
+ * When SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY are absent the insert
9
+ * is skipped entirely and the caller sees no error.
10
+ *
11
+ * Table schema: scripts/request-events.sql
12
+ */
13
+ /**
14
+ * Insert a single request event row into `request_events`.
15
+ * Throws on HTTP error so callers can decide how to handle failures.
16
+ */
17
+ export async function insertRequestEvent(supabaseUrl, serviceRoleKey, event) {
18
+ const url = `${supabaseUrl}/rest/v1/request_events`;
19
+ const body = {
20
+ method: event.method,
21
+ path: event.path,
22
+ status: event.status,
23
+ duration_ms: event.duration_ms,
24
+ };
25
+ if (event.backend !== undefined)
26
+ body["backend"] = event.backend;
27
+ if (event.query_ms !== undefined)
28
+ body["query_ms"] = event.query_ms;
29
+ if (event.sync_ms !== undefined)
30
+ body["sync_ms"] = event.sync_ms;
31
+ if (event.result_count !== undefined)
32
+ body["result_count"] = event.result_count;
33
+ if (event.query_text !== undefined)
34
+ body["query_text"] = event.query_text;
35
+ if (event.tool !== undefined)
36
+ body["tool"] = event.tool;
37
+ if (event.page_count !== undefined)
38
+ body["page_count"] = event.page_count;
39
+ const res = await fetch(url, {
40
+ method: "POST",
41
+ headers: {
42
+ "content-type": "application/json",
43
+ "apikey": serviceRoleKey,
44
+ "authorization": `Bearer ${serviceRoleKey}`,
45
+ "prefer": "return=minimal",
46
+ },
47
+ body: JSON.stringify(body),
48
+ });
49
+ if (!res.ok) {
50
+ const text = await res.text().catch(() => "");
51
+ throw new Error(`insertRequestEvent failed (${res.status}): ${text}`);
52
+ }
53
+ }
54
+ //# sourceMappingURL=requestEvents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestEvents.js","sourceRoot":"","sources":["../../src/observability/requestEvents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,cAAsB,EACtB,KAAmB;IAEnB,MAAM,GAAG,GAAG,GAAG,WAAW,yBAAyB,CAAC;IAEpD,MAAM,IAAI,GAA4B;QACpC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAC;IACF,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IACjE,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;QAAE,IAAI,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC;IACpE,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IACjE,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;QAAE,IAAI,CAAC,cAAc,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC;IAChF,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,IAAI,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;IAC1E,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;IACxD,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,IAAI,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;IAE1E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,QAAQ,EAAE,cAAc;YACxB,eAAe,EAAE,UAAU,cAAc,EAAE;YAC3C,QAAQ,EAAE,gBAAgB;SAC3B;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * slowQueryAlert.ts
3
+ *
4
+ * Rolling 5-minute p95 search-latency monitor.
5
+ * Emits a `[metrics] SLOW` warning to stdout when p95 exceeds the threshold.
6
+ * Threshold is configurable via SLOW_QUERY_THRESHOLD_MS (default 500 ms).
7
+ *
8
+ * Phase 4 speed improvement.
9
+ */
10
+ /**
11
+ * Record one search latency sample and emit a slow-query alert if warranted.
12
+ * Safe to call on every search request — low overhead.
13
+ */
14
+ export declare function recordSearchLatency(durationMs: number): void;
15
+ /**
16
+ * Return a snapshot of the current rolling window (for tests and diagnostics).
17
+ */
18
+ export declare function getSlowQueryStats(): {
19
+ sampleCount: number;
20
+ p95Ms: number;
21
+ thresholdMs: number;
22
+ };
23
+ /** Reset state — used in tests. */
24
+ export declare function resetSlowQueryAlert(): void;
25
+ //# sourceMappingURL=slowQueryAlert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slowQueryAlert.d.ts","sourceRoot":"","sources":["../../src/observability/slowQueryAlert.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6BH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAiB5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,CASA;AAED,mCAAmC;AACnC,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * slowQueryAlert.ts
3
+ *
4
+ * Rolling 5-minute p95 search-latency monitor.
5
+ * Emits a `[metrics] SLOW` warning to stdout when p95 exceeds the threshold.
6
+ * Threshold is configurable via SLOW_QUERY_THRESHOLD_MS (default 500 ms).
7
+ *
8
+ * Phase 4 speed improvement.
9
+ */
10
+ const WINDOW_MS = 5 * 60 * 1000; // 5 minutes
11
+ const MIN_SAMPLES = 5; // require at least this many samples before alerting
12
+ const ALERT_COOLDOWN_MS = 60_000; // emit at most one alert per minute
13
+ const THRESHOLD_MS = Number(process.env.SLOW_QUERY_THRESHOLD_MS ?? 500);
14
+ const samples = [];
15
+ let lastAlertAt = 0;
16
+ function evictOld(now) {
17
+ const cutoff = now - WINDOW_MS;
18
+ let i = 0;
19
+ while (i < samples.length && samples[i].ts < cutoff)
20
+ i++;
21
+ if (i > 0)
22
+ samples.splice(0, i);
23
+ }
24
+ function percentile(sorted, p) {
25
+ if (sorted.length === 0)
26
+ return 0;
27
+ const idx = Math.floor(sorted.length * p);
28
+ return sorted[Math.min(idx, sorted.length - 1)];
29
+ }
30
+ /**
31
+ * Record one search latency sample and emit a slow-query alert if warranted.
32
+ * Safe to call on every search request — low overhead.
33
+ */
34
+ export function recordSearchLatency(durationMs) {
35
+ const now = Date.now();
36
+ evictOld(now);
37
+ samples.push({ ts: now, ms: durationMs });
38
+ if (samples.length < MIN_SAMPLES)
39
+ return;
40
+ if (now - lastAlertAt < ALERT_COOLDOWN_MS)
41
+ return;
42
+ const sorted = samples.map((s) => s.ms).sort((a, b) => a - b);
43
+ const p95 = percentile(sorted, 0.95);
44
+ if (p95 > THRESHOLD_MS) {
45
+ lastAlertAt = now;
46
+ console.warn(`[metrics] SLOW p95=${p95}ms threshold=${THRESHOLD_MS}ms window=5m samples=${sorted.length}`);
47
+ }
48
+ }
49
+ /**
50
+ * Return a snapshot of the current rolling window (for tests and diagnostics).
51
+ */
52
+ export function getSlowQueryStats() {
53
+ const now = Date.now();
54
+ evictOld(now);
55
+ const sorted = samples.map((s) => s.ms).sort((a, b) => a - b);
56
+ return {
57
+ sampleCount: sorted.length,
58
+ p95Ms: percentile(sorted, 0.95),
59
+ thresholdMs: THRESHOLD_MS,
60
+ };
61
+ }
62
+ /** Reset state — used in tests. */
63
+ export function resetSlowQueryAlert() {
64
+ samples.length = 0;
65
+ lastAlertAt = 0;
66
+ }
67
+ //# sourceMappingURL=slowQueryAlert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slowQueryAlert.js","sourceRoot":"","sources":["../../src/observability/slowQueryAlert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * zeroResultTriage.ts
3
+ *
4
+ * Weekly zero-result query reporter.
5
+ * Queries `request_events` for searches that returned zero results (last 7 days),
6
+ * groups by query_text, and writes a triage page to the wiki at
7
+ * `data/zero-results-<YYYY-MM-DD>.md`.
8
+ *
9
+ * Phase 4 accuracy improvement.
10
+ */
11
+ import type { StorageClient } from "../storage/client.js";
12
+ export declare function runZeroResultTriage(supabaseUrl: string, serviceRoleKey: string, drive: StorageClient): Promise<{
13
+ page: string;
14
+ rowCount: number;
15
+ } | null>;
16
+ /**
17
+ * Start a weekly recurring triage job.
18
+ * Runs immediately on first call, then every 7 days.
19
+ * Returns a cleanup function that cancels the recurring timer.
20
+ */
21
+ export declare function startZeroResultTriageCron(supabaseUrl: string, serviceRoleKey: string, getDrive: () => StorageClient): () => void;
22
+ //# sourceMappingURL=zeroResultTriage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zeroResultTriage.d.ts","sourceRoot":"","sources":["../../src/observability/zeroResultTriage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAS1D,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAyFpD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,aAAa,GAC5B,MAAM,IAAI,CAiBZ"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * zeroResultTriage.ts
3
+ *
4
+ * Weekly zero-result query reporter.
5
+ * Queries `request_events` for searches that returned zero results (last 7 days),
6
+ * groups by query_text, and writes a triage page to the wiki at
7
+ * `data/zero-results-<YYYY-MM-DD>.md`.
8
+ *
9
+ * Phase 4 accuracy improvement.
10
+ */
11
+ const ZERO_RESULT_TRIAGE_LIMIT = 10;
12
+ export async function runZeroResultTriage(supabaseUrl, serviceRoleKey, drive) {
13
+ const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
14
+ // Fetch zero-result events from the last 7 days.
15
+ const url = `${supabaseUrl}/rest/v1/request_events` +
16
+ `?select=query_text` +
17
+ `&path=eq./api/v1/search` +
18
+ `&result_count=eq.0` +
19
+ `&query_text=not.is.null` +
20
+ `&ts=gte.${since}` +
21
+ `&limit=2000`;
22
+ let rows;
23
+ try {
24
+ const res = await fetch(url, {
25
+ headers: {
26
+ apikey: serviceRoleKey,
27
+ authorization: `Bearer ${serviceRoleKey}`,
28
+ },
29
+ });
30
+ if (!res.ok) {
31
+ console.error(`[triage] zero-result fetch failed (${res.status})`);
32
+ return null;
33
+ }
34
+ rows = (await res.json());
35
+ }
36
+ catch (err) {
37
+ console.error("[triage] zero-result fetch error", err.message);
38
+ return null;
39
+ }
40
+ // Aggregate counts.
41
+ const freq = new Map();
42
+ for (const row of rows) {
43
+ const q = row.query_text.trim().toLowerCase();
44
+ if (q)
45
+ freq.set(q, (freq.get(q) ?? 0) + 1);
46
+ }
47
+ const sorted = [...freq.entries()]
48
+ .map(([q, cnt]) => ({ query_text: q, cnt }))
49
+ .sort((a, b) => b.cnt - a.cnt)
50
+ .slice(0, ZERO_RESULT_TRIAGE_LIMIT);
51
+ if (sorted.length === 0) {
52
+ console.log("[triage] no zero-result queries in the last 7 days — skipping report");
53
+ return null;
54
+ }
55
+ const dateStr = new Date().toISOString().slice(0, 10);
56
+ const pageName = `data/zero-results-${dateStr}.md`;
57
+ const tableRows = sorted
58
+ .map((r, i) => `| ${i + 1} | \`${r.query_text}\` | ${r.cnt} |`)
59
+ .join("\n");
60
+ const content = `---
61
+ title: Zero-Result Triage ${dateStr}
62
+ updated: ${dateStr}
63
+ tags: [metrics, triage, zero-results]
64
+ summary: Weekly report of the top ${sorted.length} search queries that returned zero results (last 7 days).
65
+ ---
66
+
67
+ # Zero-Result Triage — ${dateStr}
68
+
69
+ Queries with the highest zero-result frequency over the last 7 days.
70
+ Action per entry: add a missing page, improve a page summary, or add a tag alias.
71
+
72
+ | # | Query | Count |
73
+ |---|-------|-------|
74
+ ${tableRows}
75
+
76
+ _Generated automatically by zero-result triage. Total zero-result events scanned: ${rows.length}._
77
+ `;
78
+ try {
79
+ // Try to create a fresh page; if it already exists (same date, re-run), update it.
80
+ try {
81
+ await drive.createFile(pageName, content);
82
+ }
83
+ catch {
84
+ // File may already exist — read current revision and overwrite.
85
+ const existing = await drive.readFile(pageName);
86
+ await drive.updateFile(existing.fileId, content, existing.revision);
87
+ }
88
+ console.log(`[triage] zero-result report written → ${pageName} (${sorted.length} entries)`);
89
+ return { page: pageName, rowCount: sorted.length };
90
+ }
91
+ catch (err) {
92
+ console.error("[triage] failed to write report page", err.message);
93
+ return null;
94
+ }
95
+ }
96
+ /**
97
+ * Start a weekly recurring triage job.
98
+ * Runs immediately on first call, then every 7 days.
99
+ * Returns a cleanup function that cancels the recurring timer.
100
+ */
101
+ export function startZeroResultTriageCron(supabaseUrl, serviceRoleKey, getDrive) {
102
+ const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
103
+ const run = () => {
104
+ runZeroResultTriage(supabaseUrl, serviceRoleKey, getDrive()).catch((err) => {
105
+ console.error("[triage] unhandled error", err.message);
106
+ });
107
+ };
108
+ // Run once immediately (non-blocking).
109
+ setTimeout(run, 0);
110
+ const timer = setInterval(run, WEEK_MS);
111
+ // Node timers should not keep the process alive just for telemetry.
112
+ if (typeof timer.unref === "function")
113
+ timer.unref();
114
+ return () => clearInterval(timer);
115
+ }
116
+ //# sourceMappingURL=zeroResultTriage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zeroResultTriage.js","sourceRoot":"","sources":["../../src/observability/zeroResultTriage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * embeddings.ts
3
+ *
4
+ * OpenAI text-embedding-3-small client for wiki page vectors.
5
+ * Used at ingest time (upsertPage) and at query time (hybrid search).
6
+ *
7
+ * Cost: $0.020 / 1M tokens (text-embedding-3-small).
8
+ * Falls back silently when OPENAI_API_KEY is unset so the server
9
+ * continues to function with FTS-only search.
10
+ */
11
+ export declare const EMBEDDING_MODEL = "text-embedding-3-small";
12
+ export declare const EMBEDDING_DIMS = 1536;
13
+ export declare function isEmbeddingEnabled(): boolean;
14
+ /**
15
+ * Generate a single embedding for `text`.
16
+ * Returns null when OPENAI_API_KEY is unset or the API call fails.
17
+ * Text is truncated to ~8000 tokens before sending (model max is 8192).
18
+ */
19
+ export declare function generateEmbedding(text: string): Promise<number[] | null>;
20
+ /**
21
+ * Generate embeddings for a batch of texts.
22
+ * OpenAI allows up to 2048 inputs per request; callers should chunk larger batches.
23
+ * Returns an array of the same length; failed entries are null.
24
+ */
25
+ export declare function generateEmbeddings(texts: string[]): Promise<Array<number[] | null>>;
26
+ /**
27
+ * Build the text to embed for a page: title + summary + (first 2000 chars of content).
28
+ * Keeping content short reduces token cost while preserving semantic signal.
29
+ */
30
+ export declare function buildEmbeddingInput(title: string, summary: string, content: string): string;
31
+ //# sourceMappingURL=embeddings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../src/search/embeddings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,eAAO,MAAM,eAAe,2BAA2B,CAAC;AACxD,eAAO,MAAM,cAAc,OAAQ,CAAC;AAWpC,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAe9E;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAoBzF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAM3F"}