@a-company/paradigm 5.10.0 → 5.21.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 (409) hide show
  1. package/dist/accept-orchestration-IUAWQZUN.js +18 -0
  2. package/dist/add-P76GEMGF.js +8 -0
  3. package/dist/agent-UUTYOFTH.js +33 -0
  4. package/dist/agent-loader-EPXURMO7.js +2 -0
  5. package/dist/agent-loader-PYGPX6D2.js +2 -0
  6. package/dist/agent-state-KSQ3S7OB.js +2 -0
  7. package/dist/agents-suggest-HYTFMQD3.js +10 -0
  8. package/dist/aggregate-W66DM3GA.js +10 -0
  9. package/dist/ambient-HYZR42NX.js +35 -0
  10. package/dist/assess-UFPYEJKP.js +9 -0
  11. package/dist/auto-RHJXOZFL.js +14 -0
  12. package/dist/beacon-5QVYV5DF.js +13 -0
  13. package/dist/calibration-OLJYB5HN.js +7 -0
  14. package/dist/check-THVGY4R5.js +14 -0
  15. package/dist/chunk-22ISTMKI.js +31 -0
  16. package/dist/chunk-2QO33NYK.js +3 -0
  17. package/dist/chunk-33LKBMVK.js +134 -0
  18. package/dist/chunk-4QADCWPU.js +7 -0
  19. package/dist/chunk-4TXOVRWD.js +3 -0
  20. package/dist/chunk-4UJ4NIEQ.js +4 -0
  21. package/dist/chunk-4ZGGWZ32.js +3 -0
  22. package/dist/chunk-5TAVYPOV.js +2 -0
  23. package/dist/chunk-6EX37L4Q.js +8 -0
  24. package/dist/chunk-AD5TAD2A.js +5 -0
  25. package/dist/chunk-AGVAHVUA.js +2 -0
  26. package/dist/chunk-AO7ZSRME.js +13 -0
  27. package/dist/chunk-BFD3GFRK.js +2 -0
  28. package/dist/chunk-D34YFK4M.js +6 -0
  29. package/dist/chunk-DVZWCXB6.js +2 -0
  30. package/dist/chunk-EKZDFEJW.js +2 -0
  31. package/dist/chunk-ET5XJTSX.js +2 -0
  32. package/dist/chunk-FYDRENK7.js +3 -0
  33. package/dist/chunk-HMQ5BHP2.js +743 -0
  34. package/dist/chunk-HXKWL3VK.js +29 -0
  35. package/dist/chunk-IZSBGW6E.js +2 -0
  36. package/dist/chunk-J32OPJEX.js +2 -0
  37. package/dist/chunk-JBDMCRPP.js +7 -0
  38. package/dist/chunk-JIXHEBGK.js +7 -0
  39. package/dist/chunk-JNSJVCTU.js +2 -0
  40. package/dist/chunk-JQKKVAAN.js +2 -0
  41. package/dist/chunk-KLBH26PA.js +429 -0
  42. package/dist/chunk-L6K5HNDL.js +24 -0
  43. package/dist/chunk-LKAT7IAK.js +2 -0
  44. package/dist/chunk-M4UMM6DC.js +5 -0
  45. package/dist/chunk-NKYNHSA5.js +424 -0
  46. package/dist/chunk-NW6OKHE6.js +5 -0
  47. package/dist/chunk-NXRVR37G.js +60 -0
  48. package/dist/chunk-ODVKPZZ4.js +2 -0
  49. package/dist/chunk-PUBB7LIT.js +5 -0
  50. package/dist/chunk-Q2J542ST.js +2 -0
  51. package/dist/chunk-QGZRM6ZB.js +2 -0
  52. package/dist/chunk-QNZEG7IT.js +13 -0
  53. package/dist/chunk-QT2LKB3P.js +7 -0
  54. package/dist/chunk-RVRARWSY.js +9 -0
  55. package/dist/chunk-T6IDXUUA.js +2 -0
  56. package/dist/chunk-TYWB5IQJ.js +3 -0
  57. package/dist/chunk-UIKLE3WD.js +2 -0
  58. package/dist/chunk-V7K6CQVX.js +111 -0
  59. package/dist/chunk-VCKKJDLP.js +8 -0
  60. package/dist/chunk-VG7FN2TU.js +4 -0
  61. package/dist/chunk-W5IWDW4Y.js +16 -0
  62. package/dist/chunk-W7C6FFMO.js +8 -0
  63. package/dist/chunk-WESTEMIM.js +10 -0
  64. package/dist/chunk-WR6D3SC6.js +5 -0
  65. package/dist/chunk-WS2N27RX.js +3 -0
  66. package/dist/chunk-XMAV5AG6.js +2 -0
  67. package/dist/chunk-XROULIQN.js +2 -0
  68. package/dist/chunk-XUFYOXOU.js +3 -0
  69. package/dist/chunk-Y4XFVDZC.js +3 -0
  70. package/dist/chunk-Y6KXTQBG.js +2 -0
  71. package/dist/{chunk-GTR2TBIJ.js → chunk-Z2UZUCY6.js} +237 -1047
  72. package/dist/chunk-Z34K6GY2.js +8 -0
  73. package/dist/claude-4LR3LJQZ.js +13 -0
  74. package/dist/claude-cli-UP6HGH7C.js +6 -0
  75. package/dist/claude-code-RLJ4GX77.js +11 -0
  76. package/dist/claude-code-teams-R37HJY3Y.js +29 -0
  77. package/dist/commands-LMUD5L6R.js +455 -0
  78. package/dist/compliance-GZ7UAOG3.js +6 -0
  79. package/dist/conductor-Y5IXELTL.js +4 -0
  80. package/dist/config-schema-HR7LDEQ6.js +2 -0
  81. package/dist/constellation-CG7C4WFE.js +13 -0
  82. package/dist/context-audit-RSSRX6XC.js +6 -0
  83. package/dist/cost-IDNVMAEV.js +10 -0
  84. package/dist/cost-PK4KIF7R.js +2 -0
  85. package/dist/cursor-cli-QKF7Z6M2.js +4 -0
  86. package/dist/cursorrules-U5O4G5T4.js +14 -0
  87. package/dist/decision-loader-2XPZE4EZ.js +2 -0
  88. package/dist/delete-P5VULXR4.js +14 -0
  89. package/dist/diff-RTFUKJHF.js +9 -0
  90. package/dist/discipline-H7LDI6NT.js +2 -0
  91. package/dist/dist-3ZCH25SG.js +2 -0
  92. package/dist/dist-KGRCLBJP-2QAPFYNF.js +39 -0
  93. package/dist/dist-R3RWD35F.js +2 -0
  94. package/dist/dist-VGFSP3XM.js +2 -0
  95. package/dist/dist-VXCZWVVJ.js +2 -0
  96. package/dist/dist-W3XCATBJ.js +2 -0
  97. package/dist/docs-EDQ2STFK.js +2 -0
  98. package/dist/docs-RXPN6CK7.js +12 -0
  99. package/dist/doctor-2T2QL6VM.js +2 -0
  100. package/dist/drift-ILZE5BFJ.js +13 -0
  101. package/dist/echo-UPTQUEDU.js +46 -0
  102. package/dist/edit-GUU3HBVW.js +9 -0
  103. package/dist/event-Y3VXC2RV.js +3 -0
  104. package/dist/explain-files-3GPZUETV.js +4 -0
  105. package/dist/export-CV5KCTPS.js +2 -0
  106. package/dist/flow-S3RQ4YPY.js +11 -0
  107. package/dist/global-C44FW4G2.js +13 -0
  108. package/dist/graduate-3BBSC27A.js +5 -0
  109. package/dist/graph-CNDE5TAT.js +2 -0
  110. package/dist/graph-server-COZR5C3Z.js +14 -0
  111. package/dist/habits-4J2KRYUL.js +42 -0
  112. package/dist/history-UW454SDP.js +12 -0
  113. package/dist/hooks-XH52VGOU.js +2 -0
  114. package/dist/index.js +15 -1039
  115. package/dist/init-24MAQJFM.js +2 -0
  116. package/dist/integrity-UYDOOJDP.js +7 -0
  117. package/dist/integrity-checker-DHGMZQDG.js +2 -0
  118. package/dist/journal-loader-GLH7XFTK.js +2 -0
  119. package/dist/lint-IGKE6UPS.js +26 -0
  120. package/dist/list-5IUGP3ZB.js +7 -0
  121. package/dist/list-YKIQNKGB.js +6 -0
  122. package/dist/lore-loader-RVQI5GXL.js +2 -0
  123. package/dist/lore-loader-XY5MZRR2.js +2 -0
  124. package/dist/lore-server-FC2GMDLT.js +14 -0
  125. package/dist/manual-RXSPSFLL.js +20 -0
  126. package/dist/mcp.js +93 -20484
  127. package/dist/migrate-X5OMAFAQ.js +59 -0
  128. package/dist/migrate-assessments-GEI5WMI2.js +4 -0
  129. package/dist/model-discovery-HMB3YI4L.js +2 -0
  130. package/dist/nomination-engine-XF7TWPAP.js +2 -0
  131. package/dist/notebook-PE3JSYZI.js +11 -0
  132. package/dist/notebook-loader-CENTDDUJ.js +2 -0
  133. package/dist/orchestrate-35HBQ3O3.js +8 -0
  134. package/dist/peers-P2KXU7ZK.js +5 -0
  135. package/dist/persona-STQWZH5P.js +29 -0
  136. package/dist/pipeline-MZUITRVN.js +4 -0
  137. package/dist/platform-server-2KXHGHFV.js +25 -0
  138. package/dist/plugin-update-checker-M7PW434O.js +2 -0
  139. package/dist/portal-check-3JVNLOEP.js +8 -0
  140. package/dist/portal-compliance-2UG4VKWH.js +2 -0
  141. package/dist/probe-5L5BQDGE.js +6 -0
  142. package/dist/project-type-SURTOIG7.js +2 -0
  143. package/dist/promote-NJQDZBZA.js +8 -0
  144. package/dist/providers-4PXMWA7V.js +8 -0
  145. package/dist/quiz-FE5UGAY2.js +10 -0
  146. package/dist/record-YXPB34MY.js +6 -0
  147. package/dist/reindex-PGDQSM42.js +2 -0
  148. package/dist/remember-MJRNTXYS.js +14 -0
  149. package/dist/retag-N5XF3KXP.js +6 -0
  150. package/dist/review-6UAH6V3R.js +11 -0
  151. package/dist/review-77QI6VOC.js +6 -0
  152. package/dist/ripple-ZGDITCGB.js +10 -0
  153. package/dist/roster-FLRZ5J42.js +2 -0
  154. package/dist/sentinel-HYAZ3CO5.js +19 -0
  155. package/dist/sentinel-bridge-VR357PKL.js +2 -0
  156. package/dist/sentinel-mcp.js +38 -3808
  157. package/dist/sentinel.js +1 -34
  158. package/dist/serve-JE6UF5P2.js +8 -0
  159. package/dist/serve-L52ZUTU6.js +10 -0
  160. package/dist/serve-OY6XYL7F.js +12 -0
  161. package/dist/server-2MNROHF6.js +7 -0
  162. package/dist/server-4YNUIK4W.js +8 -0
  163. package/dist/session-tracker-SKOMBDOW.js +2 -0
  164. package/dist/session-work-log-3MNUDEZ6.js +2 -0
  165. package/dist/session-work-log-J2H5OZBR.js +2 -0
  166. package/dist/setup-3F5IK7MO.js +31 -0
  167. package/dist/setup-KPIMRZ4Q.js +2 -0
  168. package/dist/shift-6GCNH666.js +11 -0
  169. package/dist/show-BOAVWZPZ.js +7 -0
  170. package/dist/show-PJ5LFLIL.js +5 -0
  171. package/dist/snapshot-L2G56RPL.js +8 -0
  172. package/dist/spawn-UH5RENSE.js +12 -0
  173. package/dist/status-77M3SDIF.js +8 -0
  174. package/dist/status-A37ECYNJ.js +6 -0
  175. package/dist/summary-ZJLQ6KHB.js +7 -0
  176. package/dist/sweep-HU74OPVW.js +17 -0
  177. package/dist/switch-CTW4PDGI.js +17 -0
  178. package/dist/symphony-3QMBGCZY.js +2 -0
  179. package/dist/symphony-QI63XL62.js +53 -0
  180. package/dist/symphony-loader-XACVV2E3.js +2 -0
  181. package/dist/symphony-peers-U4KHMKGI.js +2 -0
  182. package/dist/symphony-peers-X5NGWXFP.js +2 -0
  183. package/dist/symphony-relay-5GY5JIPT.js +3 -0
  184. package/dist/sync-QRDSFETO.js +2 -0
  185. package/dist/sync-llms-HL5PPW3M.js +15 -0
  186. package/dist/task-loader-NZFDTUQ5.js +2 -0
  187. package/dist/team-WGERKGNP.js +2 -0
  188. package/dist/test-BQJMS4Y2.js +111 -0
  189. package/dist/thread-HFXK65D4.js +41 -0
  190. package/dist/timeline-K3ZFKJ3R.js +6 -0
  191. package/dist/triage-FCWOZASE.js +18 -0
  192. package/dist/tutorial-UC6YQMNN.js +305 -0
  193. package/dist/university-content/courses/.purpose +74 -5
  194. package/dist/university-content/courses/para-701.json +807 -0
  195. package/dist/university-content/plsat/.purpose +3 -2
  196. package/dist/university-ui/assets/{index-BjewBB6s.js → index-CGFJczb1.js} +2 -2
  197. package/dist/university-ui/assets/{index-BjewBB6s.js.map → index-CGFJczb1.js.map} +1 -1
  198. package/dist/university-ui/index.html +1 -1
  199. package/dist/upgrade-B4IOLZYK.js +53 -0
  200. package/dist/validate-C6SMKGYD.js +9 -0
  201. package/dist/validate-LSCDOLBO.js +13 -0
  202. package/dist/validate-VZXTJHGO.js +6 -0
  203. package/dist/watch-LRM5XD46.js +8 -0
  204. package/dist/watch-PZCCUP6K.js +9 -0
  205. package/dist/wisdom-XZ3QKPNP.js +7 -0
  206. package/dist/work-log-loader-DL5GZ2BQ.js +2 -0
  207. package/dist/workspace-2ODL5WLY.js +2 -0
  208. package/package.json +1 -1
  209. package/platform-ui/dist/assets/AmbientSection-DqQ2mfCO.js +1 -0
  210. package/platform-ui/dist/assets/{CanvasSection-DwPTRdIN.js → CanvasSection-DLW0s-Bu.js} +1 -1
  211. package/platform-ui/dist/assets/{DocsSection-BJc_ws4I.js → DocsSection-BQ9Hi51g.js} +1 -1
  212. package/platform-ui/dist/assets/{GitSection-DhqBo1xc.js → GitSection-QQeUX6o3.js} +1 -1
  213. package/platform-ui/dist/assets/{GraphSection-D9fhVnZ0.js → GraphSection-0c9C-w7K.js} +2 -2
  214. package/platform-ui/dist/assets/{GraphSection-BlgXTl53.css → GraphSection-Bbs2a8sC.css} +1 -1
  215. package/platform-ui/dist/assets/LoreSection--t56kCj2.js +1 -0
  216. package/platform-ui/dist/assets/LoreSection-qnmm3o4y.css +1 -0
  217. package/platform-ui/dist/assets/SentinelSection-C4OoME9U.js +1 -0
  218. package/platform-ui/dist/assets/{SymphonySection-BRoBaUSS.js → SymphonySection-B-rvsLxq.js} +1 -1
  219. package/platform-ui/dist/assets/TeamSection-CBFmNEEo.js +1 -0
  220. package/platform-ui/dist/assets/index-Bxp0TVaf.css +1 -0
  221. package/platform-ui/dist/assets/index-DPpOdAtC.js +57 -0
  222. package/platform-ui/dist/index.html +2 -2
  223. package/dist/accept-orchestration-UQLM7PTQ.js +0 -189
  224. package/dist/add-FZRKEGH4.js +0 -81
  225. package/dist/agent-WERIO2XV.js +0 -523
  226. package/dist/agent-loader-TFIANSF4.js +0 -44
  227. package/dist/agent-state-S5DAWPTF.js +0 -24
  228. package/dist/agents-suggest-DNSYJ6IA.js +0 -83
  229. package/dist/aggregate-H57K7PNV.js +0 -88
  230. package/dist/assess-4WVXZLZQ.js +0 -68
  231. package/dist/auto-QFS5NHQU.js +0 -504
  232. package/dist/beacon-KXZXYQHX.js +0 -251
  233. package/dist/calibration-V46G7JTY.js +0 -135
  234. package/dist/check-OWAIWV23.js +0 -175
  235. package/dist/chunk-2IO7JAG2.js +0 -148
  236. package/dist/chunk-2T6BTYBN.js +0 -712
  237. package/dist/chunk-36TKPM5Z.js +0 -400
  238. package/dist/chunk-45MUDW6E.js +0 -804
  239. package/dist/chunk-4BLYIB7J.js +0 -5701
  240. package/dist/chunk-4L3UTYQX.js +0 -677
  241. package/dist/chunk-4NCFWYGG.js +0 -110
  242. package/dist/chunk-54LTTQBH.js +0 -138
  243. package/dist/chunk-5EWAQHFY.js +0 -269
  244. package/dist/chunk-5JGJACDU.js +0 -37
  245. package/dist/chunk-5OUOLN6M.js +0 -659
  246. package/dist/chunk-5VKJBNJL.js +0 -444
  247. package/dist/chunk-6N3JTACN.js +0 -2260
  248. package/dist/chunk-6QC3YGB6.js +0 -114
  249. package/dist/chunk-7N7GSU6K.js +0 -34
  250. package/dist/chunk-ABVQGRF7.js +0 -268
  251. package/dist/chunk-CL7JSK52.js +0 -129
  252. package/dist/chunk-CSD7IHSN.js +0 -152
  253. package/dist/chunk-EDOAWN7J.js +0 -717
  254. package/dist/chunk-EKGMAM62.js +0 -525
  255. package/dist/chunk-EZ3GOCYC.js +0 -132
  256. package/dist/chunk-F3BCHPYT.js +0 -143
  257. package/dist/chunk-FKJUBQU3.js +0 -3081
  258. package/dist/chunk-GT5QGC2H.js +0 -253
  259. package/dist/chunk-GZDFVP2N.js +0 -540
  260. package/dist/chunk-IRKUEJVW.js +0 -405
  261. package/dist/chunk-J2JEQRT3.js +0 -164
  262. package/dist/chunk-JASGXLK3.js +0 -679
  263. package/dist/chunk-KVDYJLTC.js +0 -121
  264. package/dist/chunk-L27I3CPZ.js +0 -357
  265. package/dist/chunk-LHLIAYQ3.js +0 -185
  266. package/dist/chunk-MCMOGQMU.js +0 -145
  267. package/dist/chunk-NQKQG45G.js +0 -688
  268. package/dist/chunk-PBHIFAL4.js +0 -259
  269. package/dist/chunk-PDX44BCA.js +0 -11
  270. package/dist/chunk-PMXRGPRQ.js +0 -305
  271. package/dist/chunk-RAB5IKPR.js +0 -487
  272. package/dist/chunk-RJE5G7WO.js +0 -590
  273. package/dist/chunk-RTHA3XRE.js +0 -880
  274. package/dist/chunk-S2HO5MLR.js +0 -621
  275. package/dist/chunk-S3ORKP3V.js +0 -716
  276. package/dist/chunk-TAIJOFOE.js +0 -124
  277. package/dist/chunk-TXESEO7Y.js +0 -245
  278. package/dist/chunk-UPLDI7CN.js +0 -1334
  279. package/dist/chunk-VL67H5IC.js +0 -506
  280. package/dist/chunk-VPPK3SY4.js +0 -1136
  281. package/dist/chunk-YMDLDELF.js +0 -314
  282. package/dist/chunk-YO6DVTL7.js +0 -99
  283. package/dist/chunk-YRZ5RPEB.js +0 -1561
  284. package/dist/chunk-ZDHLG5VP.js +0 -5676
  285. package/dist/chunk-ZGUAAVMA.js +0 -756
  286. package/dist/chunk-ZSYVKSY6.js +0 -2602
  287. package/dist/claude-FRRWJSTJ.js +0 -362
  288. package/dist/claude-cli-XJLK2X4L.js +0 -276
  289. package/dist/claude-code-HTBA4XRB.js +0 -126
  290. package/dist/claude-code-teams-T4SP24MD.js +0 -199
  291. package/dist/commands-5N4ILTPH.js +0 -5859
  292. package/dist/conductor-CQVGTEQN.js +0 -161
  293. package/dist/config-schema-EA4XALGG.js +0 -154
  294. package/dist/constellation-A26CCGQS.js +0 -225
  295. package/dist/context-audit-RLO3ETRP.js +0 -682
  296. package/dist/cost-BGM32XJU.js +0 -93
  297. package/dist/cost-VI46A4XL.js +0 -174
  298. package/dist/cursor-cli-JVEZGHWQ.js +0 -269
  299. package/dist/cursorrules-HLIKJJZT.js +0 -84
  300. package/dist/decision-loader-WWCLIQPJ.js +0 -20
  301. package/dist/delete-KBRPQLPC.js +0 -56
  302. package/dist/diff-D4X53HAC.js +0 -126
  303. package/dist/discipline-FA4OZXIS.js +0 -24
  304. package/dist/dist-34NA5RS5.js +0 -74
  305. package/dist/dist-5QE2BB2B-5S3T6Y3T.js +0 -6919
  306. package/dist/dist-77JDTVAY.js +0 -62
  307. package/dist/dist-OGTSAZ55.js +0 -1358
  308. package/dist/dist-QK4SQAK7.js +0 -64
  309. package/dist/dist-TA6LSC2Q.js +0 -20
  310. package/dist/docs-PBZB7LYP.js +0 -89
  311. package/dist/docs-QIYKO3BR.js +0 -155
  312. package/dist/doctor-ULBOHEIC.js +0 -11
  313. package/dist/drift-R5NRKFHI.js +0 -251
  314. package/dist/echo-O2LY7CC2.js +0 -248
  315. package/dist/edit-R2HNLMOG.js +0 -63
  316. package/dist/event-25OJKDCE.js +0 -31
  317. package/dist/export-IWVL7XLF.js +0 -38
  318. package/dist/flow-CRRVV3O3.js +0 -185
  319. package/dist/global-3NG5JXUB.js +0 -136
  320. package/dist/graduate-USAWGBJM.js +0 -160
  321. package/dist/graph-VHUMAAS6.js +0 -12
  322. package/dist/graph-server-YL22VBBN.js +0 -116
  323. package/dist/habits-OL5NGPXO.js +0 -1405
  324. package/dist/history-WOWC573W.js +0 -277
  325. package/dist/hooks-HFWSCGPV.js +0 -12
  326. package/dist/integrity-IHO4FZTS.js +0 -194
  327. package/dist/integrity-checker-PSKJA5SB.js +0 -12
  328. package/dist/journal-loader-5EYSBFFY.js +0 -18
  329. package/dist/lint-K6CJGGPH.js +0 -456
  330. package/dist/list-4YK7QKFF.js +0 -57
  331. package/dist/list-ENR7Q4CR.js +0 -56
  332. package/dist/lore-loader-7NO6N6FT.js +0 -26
  333. package/dist/lore-server-UNJY5KC3.js +0 -118
  334. package/dist/manual-G6FISID5.js +0 -204
  335. package/dist/migrate-LS45DNEV.js +0 -889
  336. package/dist/migrate-assessments-RGH4O6IX.js +0 -97
  337. package/dist/model-discovery-D2H3VBGC.js +0 -8
  338. package/dist/nomination-engine-RV5CNO5B.js +0 -42
  339. package/dist/notebook-YWIYGEHV.js +0 -155
  340. package/dist/orchestrate-JLILBBJE.js +0 -324
  341. package/dist/peers-DEOUIZM6.js +0 -82
  342. package/dist/persona-UHAHIVST.js +0 -390
  343. package/dist/pipeline-L4HCSBGN.js +0 -263
  344. package/dist/platform-server-H5YO3DQD.js +0 -1475
  345. package/dist/plugin-update-checker-KRRSGXMV.js +0 -12
  346. package/dist/portal-check-NPYGII2D.js +0 -42
  347. package/dist/portal-compliance-J7DGAPFX.js +0 -18
  348. package/dist/probe-MHL5HQZ2.js +0 -167
  349. package/dist/promote-F6ZYZZAL.js +0 -182
  350. package/dist/providers-GK7PB2OL.js +0 -104
  351. package/dist/quiz-M66SC7F7.js +0 -99
  352. package/dist/record-RA4WR2BO.js +0 -49
  353. package/dist/reindex-5LTD53ZC.js +0 -16
  354. package/dist/remember-HBWJ655S.js +0 -77
  355. package/dist/retag-3OLCVDEQ.js +0 -62
  356. package/dist/review-27ATYTD2.js +0 -33
  357. package/dist/review-57QMURZV.js +0 -334
  358. package/dist/ripple-JPBXP5I3.js +0 -215
  359. package/dist/sentinel-4XIG4STA.js +0 -63
  360. package/dist/sentinel-bridge-MDUXTQRL.js +0 -109
  361. package/dist/serve-CAH3PHE7.js +0 -33
  362. package/dist/serve-INL7SNBK.js +0 -36
  363. package/dist/serve-KBSE36PL.js +0 -49
  364. package/dist/server-54SKYFFY.js +0 -1647
  365. package/dist/server-XUOIO7E6.js +0 -338
  366. package/dist/session-tracker-C4BMD5WG.js +0 -13
  367. package/dist/session-work-log-MZ47OAPB.js +0 -20
  368. package/dist/setup-EDS27WUR.js +0 -25
  369. package/dist/setup-KO5AFC4K.js +0 -296
  370. package/dist/shift-D2JOHHBF.js +0 -668
  371. package/dist/show-5PV5KFJE.js +0 -176
  372. package/dist/show-NQKYX6WQ.js +0 -82
  373. package/dist/snapshot-BK4RBPCG.js +0 -62
  374. package/dist/spawn-RCHNXDHE.js +0 -196
  375. package/dist/status-WGIAQODY.js +0 -71
  376. package/dist/summary-NIRABMF5.js +0 -140
  377. package/dist/sweep-QMHNSIY5.js +0 -934
  378. package/dist/switch-6EJPZDIA.js +0 -232
  379. package/dist/symphony-4OCY36AI.js +0 -791
  380. package/dist/symphony-B75X2MME.js +0 -326
  381. package/dist/symphony-loader-UZGON56V.js +0 -90
  382. package/dist/symphony-peers-2ZQYLRNI.js +0 -34
  383. package/dist/symphony-peers-OL7F6M5S.js +0 -121
  384. package/dist/symphony-relay-UJYUXN65.js +0 -710
  385. package/dist/sync-VEHUH4OA.js +0 -11
  386. package/dist/sync-llms-YHCFIE6X.js +0 -166
  387. package/dist/task-loader-LDYWQSLM.js +0 -20
  388. package/dist/team-O5MIIFMA.js +0 -34
  389. package/dist/test-566CP5KC.js +0 -1017
  390. package/dist/thread-N754I4D5.js +0 -274
  391. package/dist/timeline-M3CICQFE.js +0 -112
  392. package/dist/triage-HHYGT3HY.js +0 -671
  393. package/dist/tutorial-KD22SUNO.js +0 -666
  394. package/dist/upgrade-H5PF32BW.js +0 -551
  395. package/dist/validate-CNKEKO6A.js +0 -134
  396. package/dist/validate-MB5ULIHS.js +0 -39
  397. package/dist/validate-QH3LADM6.js +0 -64
  398. package/dist/watch-2TKP5PVL.js +0 -199
  399. package/dist/watch-ZF4ML6CD.js +0 -111
  400. package/dist/wisdom-AATMGNFA.js +0 -319
  401. package/dist/work-log-loader-5L45XNYZ.js +0 -14
  402. package/dist/workspace-6E6OSRNU.js +0 -21
  403. package/platform-ui/dist/assets/AmbientSection-WtHfNedO.js +0 -1
  404. package/platform-ui/dist/assets/LoreSection-C3EixkjW.css +0 -1
  405. package/platform-ui/dist/assets/LoreSection-CwDDHrz9.js +0 -1
  406. package/platform-ui/dist/assets/SentinelSection-Cl7e5oZ6.js +0 -1
  407. package/platform-ui/dist/assets/TeamSection-CqSIMrKz.js +0 -1
  408. package/platform-ui/dist/assets/index-CfpZFjea.css +0 -1
  409. package/platform-ui/dist/assets/index-F0KL9xrN.js +0 -57
@@ -1,41 +1,5 @@
1
1
  #!/usr/bin/env node
2
-
3
- // ../sentinel/src/mcp.ts
4
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import {
7
- CallToolRequestSchema,
8
- ListToolsRequestSchema
9
- } from "@modelcontextprotocol/sdk/types.js";
10
-
11
- // ../sentinel/src/storage.ts
12
- import initSqlJs from "sql.js";
13
- import { v4 as uuidv4 } from "uuid";
14
- import * as path from "path";
15
- import * as fs from "fs";
16
- var SCHEMA_VERSION = 5;
17
- var DEFAULT_CONFIDENCE = {
18
- score: 50,
19
- timesMatched: 0,
20
- timesResolved: 0,
21
- timesRecurred: 0
22
- };
23
- var SQL = null;
24
- var SentinelStorage = class {
25
- db = null;
26
- dbPath;
27
- incidentCounter = 0;
28
- initialized = false;
29
- constructor(dbPath) {
30
- this.dbPath = dbPath || this.getDefaultDbPath();
31
- }
32
- getDefaultDbPath() {
33
- const dataDir = process.env.SENTINEL_DATA_DIR || process.env.PARADIGM_DATA_DIR || path.join(process.cwd(), ".paradigm", "sentinel");
34
- return path.join(dataDir, "sentinel.db");
35
- }
36
- createSchema() {
37
- if (!this.db) return;
38
- this.db.run(`
2
+ import {Server}from'@modelcontextprotocol/sdk/server/index.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {ListToolsRequestSchema,CallToolRequestSchema}from'@modelcontextprotocol/sdk/types.js';import H from'sql.js';import {v4}from'uuid';import*as I from'path';import*as y from'fs';import {fileURLToPath}from'url';var J=5,$={score:50,timesMatched:0,timesResolved:0,timesRecurred:0},b=null,O=class{db=null;dbPath;incidentCounter=0;initialized=false;constructor(e){this.dbPath=e||this.getDefaultDbPath();}getDefaultDbPath(){let e=process.env.SENTINEL_DATA_DIR||process.env.PARADIGM_DATA_DIR||I.join(process.cwd(),".paradigm","sentinel");return I.join(e,"sentinel.db")}createSchema(){this.db&&(this.db.run(`
39
3
  -- Metadata table for schema versioning
40
4
  CREATE TABLE IF NOT EXISTS metadata (
41
5
  key TEXT PRIMARY KEY,
@@ -220,109 +184,12 @@ var SentinelStorage = class {
220
184
  CREATE INDEX IF NOT EXISTS idx_traces_trace_id ON traces(trace_id);
221
185
  CREATE INDEX IF NOT EXISTS idx_traces_service ON traces(service);
222
186
  CREATE INDEX IF NOT EXISTS idx_traces_start_time ON traces(start_time);
223
- `);
224
- this.db.run(
225
- "INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', ?)",
226
- [String(SCHEMA_VERSION)]
227
- );
228
- }
229
- save() {
230
- if (!this.db) return;
231
- const data = this.db.export();
232
- const buffer = Buffer.from(data);
233
- fs.writeFileSync(this.dbPath, buffer);
234
- }
235
- // ─── Incidents ───────────────────────────────────────────────────
236
- recordIncident(input) {
237
- const db = this.db;
238
- if (!db) {
239
- this.initializeSync();
240
- }
241
- this.incidentCounter++;
242
- const id = `INC-${String(this.incidentCounter).padStart(3, "0")}`;
243
- const now = (/* @__PURE__ */ new Date()).toISOString();
244
- this.db.run(
245
- `INSERT INTO incidents (
187
+ `),this.db.run("INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', ?)",[String(J)]));}save(){if(!this.db)return;let e=this.db.export(),t=Buffer.from(e);y.writeFileSync(this.dbPath,t);}recordIncident(e){this.db||this.initializeSync(),this.incidentCounter++;let s=`INC-${String(this.incidentCounter).padStart(3,"0")}`,n=new Date().toISOString();return this.db.run(`INSERT INTO incidents (
246
188
  id, timestamp, status, error_message, error_stack, error_code, error_type,
247
189
  symbols, flow_position, environment, service, version, user_id, request_id,
248
190
  group_id, notes, related_incidents, resolved_at, resolved_by, resolution,
249
191
  created_at, updated_at
250
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
251
- [
252
- id,
253
- input.timestamp || now,
254
- input.status || "open",
255
- input.error.message,
256
- input.error.stack || null,
257
- input.error.code || null,
258
- input.error.type || null,
259
- JSON.stringify(input.symbols),
260
- input.flowPosition ? JSON.stringify(input.flowPosition) : null,
261
- input.environment,
262
- input.service || null,
263
- input.version || null,
264
- input.userId || null,
265
- input.requestId || null,
266
- input.groupId || null,
267
- "[]",
268
- "[]",
269
- input.resolvedAt || null,
270
- input.resolvedBy || null,
271
- input.resolution ? JSON.stringify(input.resolution) : null,
272
- now,
273
- now
274
- ]
275
- );
276
- this.save();
277
- return id;
278
- }
279
- initializeSync() {
280
- if (this.initialized && this.db) return;
281
- if (!this.db && SQL) {
282
- const dir = path.dirname(this.dbPath);
283
- if (!fs.existsSync(dir)) {
284
- fs.mkdirSync(dir, { recursive: true });
285
- }
286
- if (fs.existsSync(this.dbPath)) {
287
- const fileData = fs.readFileSync(this.dbPath);
288
- this.db = new SQL.Database(fileData);
289
- } else {
290
- this.db = new SQL.Database();
291
- this.createSchema();
292
- }
293
- try {
294
- const result = this.db.exec(
295
- "SELECT MAX(CAST(SUBSTR(id, 5) AS INTEGER)) as max FROM incidents"
296
- );
297
- if (result.length > 0 && result[0].values.length > 0 && result[0].values[0][0]) {
298
- this.incidentCounter = result[0].values[0][0];
299
- }
300
- } catch {
301
- this.incidentCounter = 0;
302
- }
303
- this.migrateSchema();
304
- this.initialized = true;
305
- this.save();
306
- }
307
- }
308
- /**
309
- * Run schema migrations from older versions
310
- */
311
- migrateSchema() {
312
- if (!this.db) return;
313
- let currentVersion = 1;
314
- try {
315
- const result = this.db.exec(
316
- "SELECT value FROM metadata WHERE key = 'schema_version'"
317
- );
318
- if (result.length > 0 && result[0].values.length > 0) {
319
- currentVersion = parseInt(result[0].values[0][0], 10) || 1;
320
- }
321
- } catch {
322
- }
323
- if (currentVersion < 2) {
324
- try {
325
- this.db.run(`
192
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[s,e.timestamp||n,e.status||"open",e.error.message,e.error.stack||null,e.error.code||null,e.error.type||null,JSON.stringify(e.symbols),e.flowPosition?JSON.stringify(e.flowPosition):null,e.environment,e.service||null,e.version||null,e.userId||null,e.requestId||null,e.groupId||null,"[]","[]",e.resolvedAt||null,e.resolvedBy||null,e.resolution?JSON.stringify(e.resolution):null,n,n]),this.save(),s}initializeSync(){if(!(this.initialized&&this.db)&&!this.db&&b){let e=I.dirname(this.dbPath);if(y.existsSync(e)||y.mkdirSync(e,{recursive:true}),y.existsSync(this.dbPath)){let t=y.readFileSync(this.dbPath);this.db=new b.Database(t);}else this.db=new b.Database,this.createSchema();try{let t=this.db.exec("SELECT MAX(CAST(SUBSTR(id, 5) AS INTEGER)) as max FROM incidents");t.length>0&&t[0].values.length>0&&t[0].values[0][0]&&(this.incidentCounter=t[0].values[0][0]);}catch{this.incidentCounter=0;}this.migrateSchema(),this.initialized=true,this.save();}}migrateSchema(){if(!this.db)return;let e=1;try{let t=this.db.exec("SELECT value FROM metadata WHERE key = 'schema_version'");t.length>0&&t[0].values.length>0&&(e=parseInt(t[0].values[0][0],10)||1);}catch{}if(e<2){try{this.db.run(`
326
193
  CREATE TABLE IF NOT EXISTS practice_events (
327
194
  id TEXT PRIMARY KEY,
328
195
  timestamp TEXT NOT NULL,
@@ -343,17 +210,7 @@ var SentinelStorage = class {
343
210
  CREATE INDEX IF NOT EXISTS idx_practice_events_habit_id ON practice_events(habit_id);
344
211
  CREATE INDEX IF NOT EXISTS idx_practice_events_engineer ON practice_events(engineer);
345
212
  CREATE INDEX IF NOT EXISTS idx_practice_events_session_id ON practice_events(session_id);
346
- `);
347
- } catch {
348
- }
349
- this.db.run(
350
- "INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '2')"
351
- );
352
- currentVersion = 2;
353
- }
354
- if (currentVersion < 3) {
355
- try {
356
- this.db.run(`
213
+ `);}catch{}this.db.run("INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '2')"),e=2;}if(e<3){try{this.db.run(`
357
214
  CREATE TABLE IF NOT EXISTS logs (
358
215
  id TEXT PRIMARY KEY,
359
216
  timestamp TEXT NOT NULL,
@@ -395,17 +252,7 @@ var SentinelStorage = class {
395
252
  CREATE INDEX IF NOT EXISTS idx_logs_service ON logs(service);
396
253
  CREATE INDEX IF NOT EXISTS idx_logs_session_id ON logs(session_id);
397
254
  CREATE INDEX IF NOT EXISTS idx_logs_correlation_id ON logs(correlation_id);
398
- `);
399
- } catch {
400
- }
401
- this.db.run(
402
- "INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '3')"
403
- );
404
- currentVersion = 3;
405
- }
406
- if (currentVersion < 4) {
407
- try {
408
- this.db.run(`
255
+ `);}catch{}this.db.run("INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '3')"),e=3;}if(e<4){try{this.db.run(`
409
256
  CREATE TABLE IF NOT EXISTS metrics (
410
257
  id TEXT PRIMARY KEY,
411
258
  timestamp TEXT NOT NULL,
@@ -438,17 +285,7 @@ var SentinelStorage = class {
438
285
  CREATE INDEX IF NOT EXISTS idx_traces_trace_id ON traces(trace_id);
439
286
  CREATE INDEX IF NOT EXISTS idx_traces_service ON traces(service);
440
287
  CREATE INDEX IF NOT EXISTS idx_traces_start_time ON traces(start_time);
441
- `);
442
- } catch {
443
- }
444
- this.db.run(
445
- "INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '4')"
446
- );
447
- currentVersion = 4;
448
- }
449
- if (currentVersion < 5) {
450
- try {
451
- this.db.run(`
288
+ `);}catch{}this.db.run("INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '4')"),e=4;}if(e<5){try{this.db.run(`
452
289
  CREATE TABLE IF NOT EXISTS schemas (
453
290
  id TEXT PRIMARY KEY,
454
291
  version TEXT NOT NULL,
@@ -486,1619 +323,69 @@ var SentinelStorage = class {
486
323
  CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
487
324
  CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
488
325
  CREATE INDEX IF NOT EXISTS idx_events_service ON events(service);
489
- `);
490
- } catch {
491
- }
492
- this.db.run(
493
- "INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '5')"
494
- );
495
- }
496
- }
497
- /**
498
- * Ensure the storage is ready for use. Must be called once before using storage methods.
499
- */
500
- async ensureReady() {
501
- if (!SQL) {
502
- SQL = await initSqlJs();
503
- }
504
- this.initializeSync();
505
- }
506
- getIncident(id) {
507
- this.initializeSync();
508
- const result = this.db.exec("SELECT * FROM incidents WHERE id = ?", [id]);
509
- if (result.length === 0 || result[0].values.length === 0) return null;
510
- return this.rowToIncident(result[0].columns, result[0].values[0]);
511
- }
512
- getRecentIncidents(options = {}) {
513
- this.initializeSync();
514
- const { limit = 50, offset = 0 } = options;
515
- const conditions = [];
516
- const params = [];
517
- if (options.status && options.status !== "all") {
518
- conditions.push("status = ?");
519
- params.push(options.status);
520
- }
521
- if (options.environment) {
522
- conditions.push("environment = ?");
523
- params.push(options.environment);
524
- }
525
- if (options.symbol) {
526
- conditions.push("symbols LIKE ?");
527
- params.push(`%${options.symbol}%`);
528
- }
529
- if (options.search) {
530
- conditions.push("(error_message LIKE ? OR notes LIKE ?)");
531
- params.push(`%${options.search}%`, `%${options.search}%`);
532
- }
533
- if (options.dateFrom) {
534
- conditions.push("timestamp >= ?");
535
- params.push(options.dateFrom);
536
- }
537
- if (options.dateTo) {
538
- conditions.push("timestamp <= ?");
539
- params.push(options.dateTo);
540
- }
541
- if (options.groupId) {
542
- conditions.push("group_id = ?");
543
- params.push(options.groupId);
544
- }
545
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
546
- const result = this.db.exec(
547
- `SELECT * FROM incidents ${whereClause} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,
548
- [...params, limit, offset]
549
- );
550
- if (result.length === 0) return [];
551
- return result[0].values.map(
552
- (row) => this.rowToIncident(result[0].columns, row)
553
- );
554
- }
555
- updateIncident(id, updates) {
556
- this.initializeSync();
557
- const now = (/* @__PURE__ */ new Date()).toISOString();
558
- const setClauses = ["updated_at = ?"];
559
- const params = [now];
560
- if (updates.status !== void 0) {
561
- setClauses.push("status = ?");
562
- params.push(updates.status);
563
- }
564
- if (updates.error !== void 0) {
565
- setClauses.push("error_message = ?");
566
- params.push(updates.error.message);
567
- if (updates.error.stack !== void 0) {
568
- setClauses.push("error_stack = ?");
569
- params.push(updates.error.stack || null);
570
- }
571
- }
572
- if (updates.symbols !== void 0) {
573
- setClauses.push("symbols = ?");
574
- params.push(JSON.stringify(updates.symbols));
575
- }
576
- if (updates.flowPosition !== void 0) {
577
- setClauses.push("flow_position = ?");
578
- params.push(
579
- updates.flowPosition ? JSON.stringify(updates.flowPosition) : null
580
- );
581
- }
582
- if (updates.groupId !== void 0) {
583
- setClauses.push("group_id = ?");
584
- params.push(updates.groupId || null);
585
- }
586
- if (updates.resolvedAt !== void 0) {
587
- setClauses.push("resolved_at = ?");
588
- params.push(updates.resolvedAt || null);
589
- }
590
- if (updates.resolvedBy !== void 0) {
591
- setClauses.push("resolved_by = ?");
592
- params.push(updates.resolvedBy || null);
593
- }
594
- if (updates.resolution !== void 0) {
595
- setClauses.push("resolution = ?");
596
- params.push(
597
- updates.resolution ? JSON.stringify(updates.resolution) : null
598
- );
599
- }
600
- params.push(id);
601
- this.db.run(
602
- `UPDATE incidents SET ${setClauses.join(", ")} WHERE id = ?`,
603
- params
604
- );
605
- this.save();
606
- }
607
- resolveIncident(id, options) {
608
- this.updateIncident(id, {
609
- status: "resolved",
610
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
611
- });
612
- if (options?.notes) {
613
- this.addIncidentNote(id, {
614
- author: "system",
615
- content: options.notes,
616
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
617
- });
618
- }
619
- }
620
- addIncidentNote(incidentId, note) {
621
- this.initializeSync();
622
- const incident = this.getIncident(incidentId);
623
- if (!incident) return;
624
- const newNote = {
625
- id: uuidv4(),
626
- ...note
627
- };
628
- const notes = [...incident.notes, newNote];
629
- this.db.run(
630
- "UPDATE incidents SET notes = ?, updated_at = ? WHERE id = ?",
631
- [JSON.stringify(notes), (/* @__PURE__ */ new Date()).toISOString(), incidentId]
632
- );
633
- this.save();
634
- }
635
- linkIncidents(incidentId, relatedId) {
636
- this.initializeSync();
637
- const incident = this.getIncident(incidentId);
638
- if (!incident) return;
639
- if (!incident.relatedIncidents.includes(relatedId)) {
640
- const related = [...incident.relatedIncidents, relatedId];
641
- this.db.run(
642
- "UPDATE incidents SET related_incidents = ?, updated_at = ? WHERE id = ?",
643
- [JSON.stringify(related), (/* @__PURE__ */ new Date()).toISOString(), incidentId]
644
- );
645
- }
646
- const relatedIncident = this.getIncident(relatedId);
647
- if (relatedIncident && !relatedIncident.relatedIncidents.includes(incidentId)) {
648
- const related = [...relatedIncident.relatedIncidents, incidentId];
649
- this.db.run(
650
- "UPDATE incidents SET related_incidents = ?, updated_at = ? WHERE id = ?",
651
- [JSON.stringify(related), (/* @__PURE__ */ new Date()).toISOString(), relatedId]
652
- );
653
- }
654
- this.save();
655
- }
656
- getIncidentCount(options = {}) {
657
- this.initializeSync();
658
- const conditions = [];
659
- const params = [];
660
- if (options.status && options.status !== "all") {
661
- conditions.push("status = ?");
662
- params.push(options.status);
663
- }
664
- if (options.environment) {
665
- conditions.push("environment = ?");
666
- params.push(options.environment);
667
- }
668
- if (options.dateFrom) {
669
- conditions.push("timestamp >= ?");
670
- params.push(options.dateFrom);
671
- }
672
- if (options.dateTo) {
673
- conditions.push("timestamp <= ?");
674
- params.push(options.dateTo);
675
- }
676
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
677
- const result = this.db.exec(
678
- `SELECT COUNT(*) as count FROM incidents ${whereClause}`,
679
- params
680
- );
681
- if (result.length === 0 || result[0].values.length === 0) return 0;
682
- return result[0].values[0][0];
683
- }
684
- // ─── Patterns ────────────────────────────────────────────────────
685
- addPattern(input) {
686
- this.initializeSync();
687
- const now = (/* @__PURE__ */ new Date()).toISOString();
688
- const confidence = {
689
- ...DEFAULT_CONFIDENCE,
690
- ...input.confidence
691
- };
692
- this.db.run(
693
- `INSERT INTO patterns (
326
+ `);}catch{}this.db.run("INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '5')");}}async ensureReady(){b||(b=await H()),this.initializeSync();}getIncident(e){this.initializeSync();let t=this.db.exec("SELECT * FROM incidents WHERE id = ?",[e]);return t.length===0||t[0].values.length===0?null:this.rowToIncident(t[0].columns,t[0].values[0])}getRecentIncidents(e={}){this.initializeSync();let{limit:t=50,offset:s=0}=e,n=[],i=[];e.status&&e.status!=="all"&&(n.push("status = ?"),i.push(e.status)),e.environment&&(n.push("environment = ?"),i.push(e.environment)),e.symbol&&(n.push("symbols LIKE ?"),i.push(`%${e.symbol}%`)),e.search&&(n.push("(error_message LIKE ? OR notes LIKE ?)"),i.push(`%${e.search}%`,`%${e.search}%`)),e.dateFrom&&(n.push("timestamp >= ?"),i.push(e.dateFrom)),e.dateTo&&(n.push("timestamp <= ?"),i.push(e.dateTo)),e.groupId&&(n.push("group_id = ?"),i.push(e.groupId));let r=n.length>0?`WHERE ${n.join(" AND ")}`:"",a=this.db.exec(`SELECT * FROM incidents ${r} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,[...i,t,s]);return a.length===0?[]:a[0].values.map(o=>this.rowToIncident(a[0].columns,o))}updateIncident(e,t){this.initializeSync();let s=new Date().toISOString(),n=["updated_at = ?"],i=[s];t.status!==void 0&&(n.push("status = ?"),i.push(t.status)),t.error!==void 0&&(n.push("error_message = ?"),i.push(t.error.message),t.error.stack!==void 0&&(n.push("error_stack = ?"),i.push(t.error.stack||null))),t.symbols!==void 0&&(n.push("symbols = ?"),i.push(JSON.stringify(t.symbols))),t.flowPosition!==void 0&&(n.push("flow_position = ?"),i.push(t.flowPosition?JSON.stringify(t.flowPosition):null)),t.groupId!==void 0&&(n.push("group_id = ?"),i.push(t.groupId||null)),t.resolvedAt!==void 0&&(n.push("resolved_at = ?"),i.push(t.resolvedAt||null)),t.resolvedBy!==void 0&&(n.push("resolved_by = ?"),i.push(t.resolvedBy||null)),t.resolution!==void 0&&(n.push("resolution = ?"),i.push(t.resolution?JSON.stringify(t.resolution):null)),i.push(e),this.db.run(`UPDATE incidents SET ${n.join(", ")} WHERE id = ?`,i),this.save();}resolveIncident(e,t){this.updateIncident(e,{status:"resolved",resolvedAt:new Date().toISOString()}),t?.notes&&this.addIncidentNote(e,{author:"system",content:t.notes,timestamp:new Date().toISOString()});}addIncidentNote(e,t){this.initializeSync();let s=this.getIncident(e);if(!s)return;let n={id:v4(),...t},i=[...s.notes,n];this.db.run("UPDATE incidents SET notes = ?, updated_at = ? WHERE id = ?",[JSON.stringify(i),new Date().toISOString(),e]),this.save();}linkIncidents(e,t){this.initializeSync();let s=this.getIncident(e);if(!s)return;if(!s.relatedIncidents.includes(t)){let i=[...s.relatedIncidents,t];this.db.run("UPDATE incidents SET related_incidents = ?, updated_at = ? WHERE id = ?",[JSON.stringify(i),new Date().toISOString(),e]);}let n=this.getIncident(t);if(n&&!n.relatedIncidents.includes(e)){let i=[...n.relatedIncidents,e];this.db.run("UPDATE incidents SET related_incidents = ?, updated_at = ? WHERE id = ?",[JSON.stringify(i),new Date().toISOString(),t]);}this.save();}getIncidentCount(e={}){this.initializeSync();let t=[],s=[];e.status&&e.status!=="all"&&(t.push("status = ?"),s.push(e.status)),e.environment&&(t.push("environment = ?"),s.push(e.environment)),e.dateFrom&&(t.push("timestamp >= ?"),s.push(e.dateFrom)),e.dateTo&&(t.push("timestamp <= ?"),s.push(e.dateTo));let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=this.db.exec(`SELECT COUNT(*) as count FROM incidents ${n}`,s);return i.length===0||i[0].values.length===0?0:i[0].values[0][0]}addPattern(e){this.initializeSync();let t=new Date().toISOString(),s={...$,...e.confidence};return this.db.run(`INSERT INTO patterns (
694
327
  id, name, description, pattern, resolution, confidence,
695
328
  source, private, tags, created_at, updated_at
696
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
697
- [
698
- input.id,
699
- input.name,
700
- input.description || null,
701
- JSON.stringify(input.pattern),
702
- JSON.stringify(input.resolution),
703
- JSON.stringify(confidence),
704
- input.source,
705
- input.private ? 1 : 0,
706
- JSON.stringify(input.tags || []),
707
- now,
708
- now
709
- ]
710
- );
711
- this.save();
712
- return input.id;
713
- }
714
- getPattern(id) {
715
- this.initializeSync();
716
- const result = this.db.exec("SELECT * FROM patterns WHERE id = ?", [id]);
717
- if (result.length === 0 || result[0].values.length === 0) return null;
718
- return this.rowToPattern(result[0].columns, result[0].values[0]);
719
- }
720
- getAllPatterns(options = {}) {
721
- this.initializeSync();
722
- const conditions = [];
723
- const params = [];
724
- if (options.source) {
725
- conditions.push("source = ?");
726
- params.push(options.source);
727
- }
728
- if (options.minConfidence !== void 0) {
729
- conditions.push("json_extract(confidence, '$.score') >= ?");
730
- params.push(options.minConfidence);
731
- }
732
- if (!options.includePrivate) {
733
- conditions.push("private = 0");
734
- }
735
- if (options.tags && options.tags.length > 0) {
736
- const tagConditions = options.tags.map(() => "tags LIKE ?");
737
- conditions.push(`(${tagConditions.join(" OR ")})`);
738
- params.push(...options.tags.map((tag) => `%"${tag}"%`));
739
- }
740
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
741
- const result = this.db.exec(
742
- `SELECT * FROM patterns ${whereClause} ORDER BY json_extract(confidence, '$.score') DESC`,
743
- params
744
- );
745
- if (result.length === 0) return [];
746
- return result[0].values.map(
747
- (row) => this.rowToPattern(result[0].columns, row)
748
- );
749
- }
750
- updatePattern(id, updates) {
751
- this.initializeSync();
752
- const now = (/* @__PURE__ */ new Date()).toISOString();
753
- const setClauses = ["updated_at = ?"];
754
- const params = [now];
755
- if (updates.name !== void 0) {
756
- setClauses.push("name = ?");
757
- params.push(updates.name);
758
- }
759
- if (updates.description !== void 0) {
760
- setClauses.push("description = ?");
761
- params.push(updates.description || null);
762
- }
763
- if (updates.pattern !== void 0) {
764
- setClauses.push("pattern = ?");
765
- params.push(JSON.stringify(updates.pattern));
766
- }
767
- if (updates.resolution !== void 0) {
768
- setClauses.push("resolution = ?");
769
- params.push(JSON.stringify(updates.resolution));
770
- }
771
- if (updates.confidence !== void 0) {
772
- setClauses.push("confidence = ?");
773
- params.push(JSON.stringify(updates.confidence));
774
- }
775
- if (updates.source !== void 0) {
776
- setClauses.push("source = ?");
777
- params.push(updates.source);
778
- }
779
- if (updates.private !== void 0) {
780
- setClauses.push("private = ?");
781
- params.push(updates.private ? 1 : 0);
782
- }
783
- if (updates.tags !== void 0) {
784
- setClauses.push("tags = ?");
785
- params.push(JSON.stringify(updates.tags));
786
- }
787
- params.push(id);
788
- this.db.run(
789
- `UPDATE patterns SET ${setClauses.join(", ")} WHERE id = ?`,
790
- params
791
- );
792
- this.save();
793
- }
794
- deletePattern(id) {
795
- this.initializeSync();
796
- this.db.run("DELETE FROM patterns WHERE id = ?", [id]);
797
- this.save();
798
- }
799
- updatePatternConfidence(patternId, event) {
800
- const pattern = this.getPattern(patternId);
801
- if (!pattern) return;
802
- const now = (/* @__PURE__ */ new Date()).toISOString();
803
- const confidence = { ...pattern.confidence };
804
- switch (event) {
805
- case "matched":
806
- confidence.timesMatched++;
807
- confidence.lastMatched = now;
808
- break;
809
- case "resolved":
810
- confidence.timesResolved++;
811
- confidence.lastResolved = now;
812
- confidence.score = Math.min(100, confidence.score + 2);
813
- break;
814
- case "recurred":
815
- confidence.timesRecurred++;
816
- confidence.score = Math.max(10, confidence.score - 5);
817
- break;
818
- }
819
- this.updatePattern(patternId, { confidence });
820
- }
821
- // ─── Groups ──────────────────────────────────────────────────────
822
- createGroup(input) {
823
- this.initializeSync();
824
- const id = `GRP-${uuidv4().substring(0, 8)}`;
825
- const now = (/* @__PURE__ */ new Date()).toISOString();
826
- this.db.run(
827
- `INSERT INTO groups (
329
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[e.id,e.name,e.description||null,JSON.stringify(e.pattern),JSON.stringify(e.resolution),JSON.stringify(s),e.source,e.private?1:0,JSON.stringify(e.tags||[]),t,t]),this.save(),e.id}getPattern(e){this.initializeSync();let t=this.db.exec("SELECT * FROM patterns WHERE id = ?",[e]);return t.length===0||t[0].values.length===0?null:this.rowToPattern(t[0].columns,t[0].values[0])}getAllPatterns(e={}){this.initializeSync();let t=[],s=[];if(e.source&&(t.push("source = ?"),s.push(e.source)),e.minConfidence!==void 0&&(t.push("json_extract(confidence, '$.score') >= ?"),s.push(e.minConfidence)),e.includePrivate||t.push("private = 0"),e.tags&&e.tags.length>0){let r=e.tags.map(()=>"tags LIKE ?");t.push(`(${r.join(" OR ")})`),s.push(...e.tags.map(a=>`%"${a}"%`));}let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=this.db.exec(`SELECT * FROM patterns ${n} ORDER BY json_extract(confidence, '$.score') DESC`,s);return i.length===0?[]:i[0].values.map(r=>this.rowToPattern(i[0].columns,r))}updatePattern(e,t){this.initializeSync();let s=new Date().toISOString(),n=["updated_at = ?"],i=[s];t.name!==void 0&&(n.push("name = ?"),i.push(t.name)),t.description!==void 0&&(n.push("description = ?"),i.push(t.description||null)),t.pattern!==void 0&&(n.push("pattern = ?"),i.push(JSON.stringify(t.pattern))),t.resolution!==void 0&&(n.push("resolution = ?"),i.push(JSON.stringify(t.resolution))),t.confidence!==void 0&&(n.push("confidence = ?"),i.push(JSON.stringify(t.confidence))),t.source!==void 0&&(n.push("source = ?"),i.push(t.source)),t.private!==void 0&&(n.push("private = ?"),i.push(t.private?1:0)),t.tags!==void 0&&(n.push("tags = ?"),i.push(JSON.stringify(t.tags))),i.push(e),this.db.run(`UPDATE patterns SET ${n.join(", ")} WHERE id = ?`,i),this.save();}deletePattern(e){this.initializeSync(),this.db.run("DELETE FROM patterns WHERE id = ?",[e]),this.save();}updatePatternConfidence(e,t){let s=this.getPattern(e);if(!s)return;let n=new Date().toISOString(),i={...s.confidence};switch(t){case "matched":i.timesMatched++,i.lastMatched=n;break;case "resolved":i.timesResolved++,i.lastResolved=n,i.score=Math.min(100,i.score+2);break;case "recurred":i.timesRecurred++,i.score=Math.max(10,i.score-5);break}this.updatePattern(e,{confidence:i});}createGroup(e){this.initializeSync();let t=`GRP-${v4().substring(0,8)}`,s=new Date().toISOString();this.db.run(`INSERT INTO groups (
828
330
  id, name, common_symbols, common_error_patterns,
829
331
  suggested_pattern_id, first_seen, last_seen, created_at, updated_at
830
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
831
- [
832
- id,
833
- input.name || null,
834
- JSON.stringify(input.commonSymbols),
835
- JSON.stringify(input.commonErrorPatterns),
836
- input.suggestedPattern?.id || null,
837
- input.firstSeen,
838
- input.lastSeen,
839
- now,
840
- now
841
- ]
842
- );
843
- for (const incidentId of input.incidents) {
844
- this.addToGroup(id, incidentId);
845
- }
846
- this.save();
847
- return id;
848
- }
849
- getGroup(id) {
850
- this.initializeSync();
851
- const result = this.db.exec("SELECT * FROM groups WHERE id = ?", [id]);
852
- if (result.length === 0 || result[0].values.length === 0) return null;
853
- return this.rowToGroup(result[0].columns, result[0].values[0]);
854
- }
855
- getGroups(options = {}) {
856
- this.initializeSync();
857
- const limit = options.limit || 100;
858
- const result = this.db.exec(
859
- "SELECT * FROM groups ORDER BY last_seen DESC LIMIT ?",
860
- [limit]
861
- );
862
- if (result.length === 0) return [];
863
- return result[0].values.map(
864
- (row) => this.rowToGroup(result[0].columns, row)
865
- );
866
- }
867
- addToGroup(groupId, incidentId) {
868
- this.initializeSync();
869
- const now = (/* @__PURE__ */ new Date()).toISOString();
870
- this.db.run(
871
- "INSERT OR IGNORE INTO group_members (group_id, incident_id, added_at) VALUES (?, ?, ?)",
872
- [groupId, incidentId, now]
873
- );
874
- this.db.run("UPDATE incidents SET group_id = ? WHERE id = ?", [
875
- groupId,
876
- incidentId
877
- ]);
878
- this.db.run(
879
- "UPDATE groups SET last_seen = ?, updated_at = ? WHERE id = ?",
880
- [now, now, groupId]
881
- );
882
- this.save();
883
- }
884
- // ─── Resolutions ─────────────────────────────────────────────────
885
- recordResolution(resolution) {
886
- this.initializeSync();
887
- const id = uuidv4();
888
- const now = (/* @__PURE__ */ new Date()).toISOString();
889
- this.db.run(
890
- `INSERT INTO resolutions (id, incident_id, pattern_id, commit_hash, pr_url, notes, resolved_at)
891
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
892
- [
893
- id,
894
- resolution.incidentId,
895
- resolution.patternId || null,
896
- resolution.commitHash || null,
897
- resolution.prUrl || null,
898
- resolution.notes || null,
899
- now
900
- ]
901
- );
902
- this.updateIncident(resolution.incidentId, {
903
- status: "resolved",
904
- resolvedAt: now,
905
- resolvedBy: resolution.patternId || "manual",
906
- resolution: {
907
- patternId: resolution.patternId,
908
- commitHash: resolution.commitHash,
909
- prUrl: resolution.prUrl,
910
- notes: resolution.notes
911
- }
912
- });
913
- if (resolution.patternId) {
914
- this.updatePatternConfidence(resolution.patternId, "resolved");
915
- }
916
- this.save();
917
- }
918
- markRecurred(incidentId) {
919
- this.initializeSync();
920
- this.db.run(
921
- "UPDATE resolutions SET recurred = 1 WHERE incident_id = ?",
922
- [incidentId]
923
- );
924
- const result = this.db.exec(
925
- "SELECT pattern_id FROM resolutions WHERE incident_id = ?",
926
- [incidentId]
927
- );
928
- if (result.length > 0 && result[0].values.length > 0 && result[0].values[0][0]) {
929
- this.updatePatternConfidence(result[0].values[0][0], "recurred");
930
- }
931
- this.save();
932
- }
933
- getResolutionHistory(options = {}) {
934
- this.initializeSync();
935
- const conditions = [];
936
- const params = [];
937
- if (options.patternId) {
938
- conditions.push("pattern_id = ?");
939
- params.push(options.patternId);
940
- }
941
- if (options.symbol) {
942
- conditions.push(`incident_id IN (
332
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,[t,e.name||null,JSON.stringify(e.commonSymbols),JSON.stringify(e.commonErrorPatterns),e.suggestedPattern?.id||null,e.firstSeen,e.lastSeen,s,s]);for(let n of e.incidents)this.addToGroup(t,n);return this.save(),t}getGroup(e){this.initializeSync();let t=this.db.exec("SELECT * FROM groups WHERE id = ?",[e]);return t.length===0||t[0].values.length===0?null:this.rowToGroup(t[0].columns,t[0].values[0])}getGroups(e={}){this.initializeSync();let t=e.limit||100,s=this.db.exec("SELECT * FROM groups ORDER BY last_seen DESC LIMIT ?",[t]);return s.length===0?[]:s[0].values.map(n=>this.rowToGroup(s[0].columns,n))}addToGroup(e,t){this.initializeSync();let s=new Date().toISOString();this.db.run("INSERT OR IGNORE INTO group_members (group_id, incident_id, added_at) VALUES (?, ?, ?)",[e,t,s]),this.db.run("UPDATE incidents SET group_id = ? WHERE id = ?",[e,t]),this.db.run("UPDATE groups SET last_seen = ?, updated_at = ? WHERE id = ?",[s,s,e]),this.save();}recordResolution(e){this.initializeSync();let t=v4(),s=new Date().toISOString();this.db.run(`INSERT INTO resolutions (id, incident_id, pattern_id, commit_hash, pr_url, notes, resolved_at)
333
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,[t,e.incidentId,e.patternId||null,e.commitHash||null,e.prUrl||null,e.notes||null,s]),this.updateIncident(e.incidentId,{status:"resolved",resolvedAt:s,resolvedBy:e.patternId||"manual",resolution:{patternId:e.patternId,commitHash:e.commitHash,prUrl:e.prUrl,notes:e.notes}}),e.patternId&&this.updatePatternConfidence(e.patternId,"resolved"),this.save();}markRecurred(e){this.initializeSync(),this.db.run("UPDATE resolutions SET recurred = 1 WHERE incident_id = ?",[e]);let t=this.db.exec("SELECT pattern_id FROM resolutions WHERE incident_id = ?",[e]);t.length>0&&t[0].values.length>0&&t[0].values[0][0]&&this.updatePatternConfidence(t[0].values[0][0],"recurred"),this.save();}getResolutionHistory(e={}){this.initializeSync();let t=[],s=[];e.patternId&&(t.push("pattern_id = ?"),s.push(e.patternId)),e.symbol&&(t.push(`incident_id IN (
943
334
  SELECT id FROM incidents WHERE symbols LIKE ?
944
- )`);
945
- params.push(`%${options.symbol}%`);
946
- }
947
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
948
- const limit = options.limit || 100;
949
- const result = this.db.exec(
950
- `SELECT * FROM resolutions ${whereClause} ORDER BY resolved_at DESC LIMIT ?`,
951
- [...params, limit]
952
- );
953
- if (result.length === 0) return [];
954
- const columns = result[0].columns;
955
- return result[0].values.map((row) => {
956
- const obj = {};
957
- columns.forEach((col, i) => {
958
- obj[col] = row[i];
959
- });
960
- return {
961
- id: obj.id,
962
- incidentId: obj.incident_id,
963
- patternId: obj.pattern_id || void 0,
964
- commitHash: obj.commit_hash || void 0,
965
- prUrl: obj.pr_url || void 0,
966
- notes: obj.notes || void 0,
967
- resolvedAt: obj.resolved_at,
968
- recurred: obj.recurred === 1
969
- };
970
- });
971
- }
972
- // ─── Stats ───────────────────────────────────────────────────────
973
- getStats(period) {
974
- this.initializeSync();
975
- const { start, end } = period;
976
- const total = this.getIncidentCount({ dateFrom: start, dateTo: end });
977
- const open = this.getIncidentCount({
978
- dateFrom: start,
979
- dateTo: end,
980
- status: "open"
981
- });
982
- const resolved = this.getIncidentCount({
983
- dateFrom: start,
984
- dateTo: end,
985
- status: "resolved"
986
- });
987
- const envResult = this.db.exec(
988
- `SELECT environment, COUNT(*) as count
335
+ )`),s.push(`%${e.symbol}%`));let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=e.limit||100,r=this.db.exec(`SELECT * FROM resolutions ${n} ORDER BY resolved_at DESC LIMIT ?`,[...s,i]);if(r.length===0)return [];let a=r[0].columns;return r[0].values.map(o=>{let c={};return a.forEach((l,d)=>{c[l]=o[d];}),{id:c.id,incidentId:c.incident_id,patternId:c.pattern_id||void 0,commitHash:c.commit_hash||void 0,prUrl:c.pr_url||void 0,notes:c.notes||void 0,resolvedAt:c.resolved_at,recurred:c.recurred===1}})}getStats(e){this.initializeSync();let{start:t,end:s}=e,n=this.getIncidentCount({dateFrom:t,dateTo:s}),i=this.getIncidentCount({dateFrom:t,dateTo:s,status:"open"}),r=this.getIncidentCount({dateFrom:t,dateTo:s,status:"resolved"}),a=this.db.exec(`SELECT environment, COUNT(*) as count
989
336
  FROM incidents
990
337
  WHERE timestamp >= ? AND timestamp <= ?
991
- GROUP BY environment`,
992
- [start, end]
993
- );
994
- const byEnvironment = {};
995
- if (envResult.length > 0) {
996
- for (const row of envResult[0].values) {
997
- byEnvironment[row[0]] = row[1];
998
- }
999
- }
1000
- const dayResult = this.db.exec(
1001
- `SELECT DATE(timestamp) as date, COUNT(*) as count
338
+ GROUP BY environment`,[t,s]),o={};if(a.length>0)for(let u of a[0].values)o[u[0]]=u[1];let c=this.db.exec(`SELECT DATE(timestamp) as date, COUNT(*) as count
1002
339
  FROM incidents
1003
340
  WHERE timestamp >= ? AND timestamp <= ?
1004
341
  GROUP BY DATE(timestamp)
1005
- ORDER BY date`,
1006
- [start, end]
1007
- );
1008
- const byDay = [];
1009
- if (dayResult.length > 0) {
1010
- for (const row of dayResult[0].values) {
1011
- byDay.push({ date: row[0], count: row[1] });
1012
- }
1013
- }
1014
- const patterns = this.getAllPatterns({ includePrivate: true });
1015
- const avgConfidence = patterns.length > 0 ? patterns.reduce((sum, p) => sum + p.confidence.score, 0) / patterns.length : 0;
1016
- const mostEffective = patterns.sort((a, b) => b.confidence.timesResolved - a.confidence.timesResolved).slice(0, 5).map((p) => ({ patternId: p.id, resolvedCount: p.confidence.timesResolved }));
1017
- const leastEffective = patterns.filter((p) => p.confidence.timesMatched > 0).map((p) => ({
1018
- patternId: p.id,
1019
- recurrenceRate: p.confidence.timesRecurred / Math.max(1, p.confidence.timesResolved)
1020
- })).sort((a, b) => b.recurrenceRate - a.recurrenceRate).slice(0, 5);
1021
- const symbolCounts = /* @__PURE__ */ new Map();
1022
- const incidents = this.getRecentIncidents({
1023
- dateFrom: start,
1024
- dateTo: end,
1025
- limit: 1e3
1026
- });
1027
- for (const incident of incidents) {
1028
- for (const [, value] of Object.entries(incident.symbols)) {
1029
- if (value) {
1030
- symbolCounts.set(value, (symbolCounts.get(value) || 0) + 1);
1031
- }
1032
- }
1033
- }
1034
- const mostIncidents = Array.from(symbolCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([symbol, count]) => ({ symbol, count }));
1035
- const resolutions = this.getResolutionHistory({ limit: 1e3 });
1036
- const periodResolutions = resolutions.filter(
1037
- (r) => r.resolvedAt >= start && r.resolvedAt <= end
1038
- );
1039
- const resolvedWithPattern = periodResolutions.filter(
1040
- (r) => r.patternId
1041
- ).length;
1042
- const resolvedManually = periodResolutions.length - resolvedWithPattern;
1043
- return {
1044
- period: { start, end },
1045
- incidents: {
1046
- total,
1047
- open,
1048
- resolved,
1049
- byEnvironment,
1050
- byDay
1051
- },
1052
- patterns: {
1053
- total: patterns.length,
1054
- avgConfidence: Math.round(avgConfidence),
1055
- mostEffective,
1056
- leastEffective
1057
- },
1058
- symbols: {
1059
- mostIncidents,
1060
- mostResolved: [],
1061
- hotspots: mostIncidents.slice(0, 5).map((s) => ({
1062
- symbol: s.symbol,
1063
- incidentRate: s.count / Math.max(1, total)
1064
- }))
1065
- },
1066
- resolution: {
1067
- avgTimeToResolve: 0,
1068
- resolvedWithPattern,
1069
- resolvedManually,
1070
- resolutionRate: total > 0 ? resolved / total * 100 : 0
1071
- }
1072
- };
1073
- }
1074
- getSymbolHealth(symbol) {
1075
- const incidents = this.getRecentIncidents({ symbol, limit: 1e3 });
1076
- const incidentCount = incidents.length;
1077
- const patternCounts = /* @__PURE__ */ new Map();
1078
- for (const incident of incidents) {
1079
- if (incident.resolution?.patternId) {
1080
- const count = patternCounts.get(incident.resolution.patternId) || 0;
1081
- patternCounts.set(incident.resolution.patternId, count + 1);
1082
- }
1083
- }
1084
- const topPatterns = Array.from(patternCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([patternId, count]) => ({ patternId, count }));
1085
- return {
1086
- incidentCount,
1087
- avgTimeToResolve: 0,
1088
- topPatterns
1089
- };
1090
- }
1091
- // ─── Import/Export ───────────────────────────────────────────────
1092
- exportPatterns(options = {}) {
1093
- const patterns = this.getAllPatterns({
1094
- includePrivate: options.includePrivate
1095
- });
1096
- return {
1097
- version: "1.0.0",
1098
- exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
1099
- patterns
1100
- };
1101
- }
1102
- importPatterns(data, options = {}) {
1103
- let imported = 0;
1104
- let skipped = 0;
1105
- for (const pattern of data.patterns) {
1106
- const existing = this.getPattern(pattern.id);
1107
- if (existing && !options.overwrite) {
1108
- skipped++;
1109
- continue;
1110
- }
1111
- if (existing) {
1112
- this.updatePattern(pattern.id, pattern);
1113
- } else {
1114
- this.addPattern({
1115
- ...pattern,
1116
- source: "imported"
1117
- });
1118
- }
1119
- imported++;
1120
- }
1121
- return { imported, skipped };
1122
- }
1123
- exportBackup() {
1124
- const incidents = this.getRecentIncidents({ limit: 1e5 });
1125
- const patterns = this.getAllPatterns({ includePrivate: true });
1126
- const groups = this.getGroups({ limit: 1e4 });
1127
- return {
1128
- version: "1.0.0",
1129
- exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
1130
- incidents,
1131
- patterns,
1132
- groups
1133
- };
1134
- }
1135
- importBackup(data) {
1136
- this.initializeSync();
1137
- this.db.run("DELETE FROM group_members");
1138
- this.db.run("DELETE FROM resolutions");
1139
- this.db.run("DELETE FROM groups");
1140
- this.db.run("DELETE FROM incidents");
1141
- this.db.run("DELETE FROM patterns");
1142
- for (const pattern of data.patterns) {
1143
- this.addPattern(pattern);
1144
- }
1145
- for (const incident of data.incidents) {
1146
- const now2 = incident.timestamp;
1147
- this.db.run(
1148
- `INSERT INTO incidents (
342
+ ORDER BY date`,[t,s]),l=[];if(c.length>0)for(let u of c[0].values)l.push({date:u[0],count:u[1]});let d=this.getAllPatterns({includePrivate:true}),m=d.length>0?d.reduce((u,T)=>u+T.confidence.score,0)/d.length:0,g=d.sort((u,T)=>T.confidence.timesResolved-u.confidence.timesResolved).slice(0,5).map(u=>({patternId:u.id,resolvedCount:u.confidence.timesResolved})),p=d.filter(u=>u.confidence.timesMatched>0).map(u=>({patternId:u.id,recurrenceRate:u.confidence.timesRecurred/Math.max(1,u.confidence.timesResolved)})).sort((u,T)=>T.recurrenceRate-u.recurrenceRate).slice(0,5),E=new Map,S=this.getRecentIncidents({dateFrom:t,dateTo:s,limit:1e3});for(let u of S)for(let[,T]of Object.entries(u.symbols))T&&E.set(T,(E.get(T)||0)+1);let v=Array.from(E.entries()).sort((u,T)=>T[1]-u[1]).slice(0,10).map(([u,T])=>({symbol:u,count:T})),P=this.getResolutionHistory({limit:1e3}).filter(u=>u.resolvedAt>=t&&u.resolvedAt<=s),D=P.filter(u=>u.patternId).length,j=P.length-D;return {period:{start:t,end:s},incidents:{total:n,open:i,resolved:r,byEnvironment:o,byDay:l},patterns:{total:d.length,avgConfidence:Math.round(m),mostEffective:g,leastEffective:p},symbols:{mostIncidents:v,mostResolved:[],hotspots:v.slice(0,5).map(u=>({symbol:u.symbol,incidentRate:u.count/Math.max(1,n)}))},resolution:{avgTimeToResolve:0,resolvedWithPattern:D,resolvedManually:j,resolutionRate:n>0?r/n*100:0}}}getSymbolHealth(e){let t=this.getRecentIncidents({symbol:e,limit:1e3}),s=t.length,n=new Map;for(let r of t)if(r.resolution?.patternId){let a=n.get(r.resolution.patternId)||0;n.set(r.resolution.patternId,a+1);}let i=Array.from(n.entries()).sort((r,a)=>a[1]-r[1]).slice(0,5).map(([r,a])=>({patternId:r,count:a}));return {incidentCount:s,avgTimeToResolve:0,topPatterns:i}}exportPatterns(e={}){let t=this.getAllPatterns({includePrivate:e.includePrivate});return {version:"1.0.0",exportedAt:new Date().toISOString(),patterns:t}}importPatterns(e,t={}){let s=0,n=0;for(let i of e.patterns){let r=this.getPattern(i.id);if(r&&!t.overwrite){n++;continue}r?this.updatePattern(i.id,i):this.addPattern({...i,source:"imported"}),s++;}return {imported:s,skipped:n}}exportBackup(){let e=this.getRecentIncidents({limit:1e5}),t=this.getAllPatterns({includePrivate:true}),s=this.getGroups({limit:1e4});return {version:"1.0.0",exportedAt:new Date().toISOString(),incidents:e,patterns:t,groups:s}}importBackup(e){this.initializeSync(),this.db.run("DELETE FROM group_members"),this.db.run("DELETE FROM resolutions"),this.db.run("DELETE FROM groups"),this.db.run("DELETE FROM incidents"),this.db.run("DELETE FROM patterns");for(let n of e.patterns)this.addPattern(n);for(let n of e.incidents){let i=n.timestamp;this.db.run(`INSERT INTO incidents (
1149
343
  id, timestamp, status, error_message, error_stack, error_code, error_type,
1150
344
  symbols, flow_position, environment, service, version, user_id, request_id,
1151
345
  group_id, notes, related_incidents, resolved_at, resolved_by, resolution,
1152
346
  created_at, updated_at
1153
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1154
- [
1155
- incident.id,
1156
- incident.timestamp,
1157
- incident.status,
1158
- incident.error.message,
1159
- incident.error.stack || null,
1160
- incident.error.code || null,
1161
- incident.error.type || null,
1162
- JSON.stringify(incident.symbols),
1163
- incident.flowPosition ? JSON.stringify(incident.flowPosition) : null,
1164
- incident.environment,
1165
- incident.service || null,
1166
- incident.version || null,
1167
- incident.userId || null,
1168
- incident.requestId || null,
1169
- incident.groupId || null,
1170
- JSON.stringify(incident.notes),
1171
- JSON.stringify(incident.relatedIncidents),
1172
- incident.resolvedAt || null,
1173
- incident.resolvedBy || null,
1174
- incident.resolution ? JSON.stringify(incident.resolution) : null,
1175
- now2,
1176
- now2
1177
- ]
1178
- );
1179
- }
1180
- const result = this.db.exec(
1181
- "SELECT MAX(CAST(SUBSTR(id, 5) AS INTEGER)) as max FROM incidents"
1182
- );
1183
- if (result.length > 0 && result[0].values.length > 0 && result[0].values[0][0]) {
1184
- this.incidentCounter = result[0].values[0][0];
1185
- }
1186
- const now = (/* @__PURE__ */ new Date()).toISOString();
1187
- for (const group of data.groups) {
1188
- this.db.run(
1189
- `INSERT INTO groups (
347
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[n.id,n.timestamp,n.status,n.error.message,n.error.stack||null,n.error.code||null,n.error.type||null,JSON.stringify(n.symbols),n.flowPosition?JSON.stringify(n.flowPosition):null,n.environment,n.service||null,n.version||null,n.userId||null,n.requestId||null,n.groupId||null,JSON.stringify(n.notes),JSON.stringify(n.relatedIncidents),n.resolvedAt||null,n.resolvedBy||null,n.resolution?JSON.stringify(n.resolution):null,i,i]);}let t=this.db.exec("SELECT MAX(CAST(SUBSTR(id, 5) AS INTEGER)) as max FROM incidents");t.length>0&&t[0].values.length>0&&t[0].values[0][0]&&(this.incidentCounter=t[0].values[0][0]);let s=new Date().toISOString();for(let n of e.groups){this.db.run(`INSERT INTO groups (
1190
348
  id, name, common_symbols, common_error_patterns,
1191
349
  suggested_pattern_id, first_seen, last_seen, created_at, updated_at
1192
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1193
- [
1194
- group.id,
1195
- group.name || null,
1196
- JSON.stringify(group.commonSymbols),
1197
- JSON.stringify(group.commonErrorPatterns),
1198
- group.suggestedPattern?.id || null,
1199
- group.firstSeen,
1200
- group.lastSeen,
1201
- now,
1202
- now
1203
- ]
1204
- );
1205
- for (const incidentId of group.incidents) {
1206
- this.db.run(
1207
- "INSERT OR IGNORE INTO group_members (group_id, incident_id, added_at) VALUES (?, ?, ?)",
1208
- [group.id, incidentId, now]
1209
- );
1210
- }
1211
- }
1212
- this.save();
1213
- }
1214
- // ─── Helper Methods ──────────────────────────────────────────────
1215
- rowToIncident(columns, row) {
1216
- const obj = {};
1217
- columns.forEach((col, i) => {
1218
- obj[col] = row[i];
1219
- });
1220
- return {
1221
- id: obj.id,
1222
- timestamp: obj.timestamp,
1223
- status: obj.status,
1224
- error: {
1225
- message: obj.error_message,
1226
- stack: obj.error_stack || void 0,
1227
- code: obj.error_code || void 0,
1228
- type: obj.error_type || void 0
1229
- },
1230
- symbols: JSON.parse(obj.symbols || "{}"),
1231
- flowPosition: obj.flow_position ? JSON.parse(obj.flow_position) : void 0,
1232
- environment: obj.environment,
1233
- service: obj.service || void 0,
1234
- version: obj.version || void 0,
1235
- userId: obj.user_id || void 0,
1236
- requestId: obj.request_id || void 0,
1237
- groupId: obj.group_id || void 0,
1238
- notes: JSON.parse(obj.notes || "[]"),
1239
- relatedIncidents: JSON.parse(obj.related_incidents || "[]"),
1240
- resolvedAt: obj.resolved_at || void 0,
1241
- resolvedBy: obj.resolved_by || void 0,
1242
- resolution: obj.resolution ? JSON.parse(obj.resolution) : void 0
1243
- };
1244
- }
1245
- rowToPattern(columns, row) {
1246
- const obj = {};
1247
- columns.forEach((col, i) => {
1248
- obj[col] = row[i];
1249
- });
1250
- return {
1251
- id: obj.id,
1252
- name: obj.name,
1253
- description: obj.description || "",
1254
- pattern: JSON.parse(obj.pattern),
1255
- resolution: JSON.parse(obj.resolution),
1256
- confidence: JSON.parse(obj.confidence),
1257
- source: obj.source,
1258
- private: obj.private === 1,
1259
- tags: JSON.parse(obj.tags || "[]"),
1260
- createdAt: obj.created_at,
1261
- updatedAt: obj.updated_at
1262
- };
1263
- }
1264
- rowToGroup(columns, row) {
1265
- const obj = {};
1266
- columns.forEach((col, i) => {
1267
- obj[col] = row[i];
1268
- });
1269
- const membersResult = this.db.exec(
1270
- "SELECT incident_id FROM group_members WHERE group_id = ?",
1271
- [obj.id]
1272
- );
1273
- const incidents = [];
1274
- if (membersResult.length > 0) {
1275
- for (const r of membersResult[0].values) {
1276
- incidents.push(r[0]);
1277
- }
1278
- }
1279
- const envResult = this.db.exec(
1280
- `SELECT DISTINCT environment FROM incidents
1281
- WHERE id IN (SELECT incident_id FROM group_members WHERE group_id = ?)`,
1282
- [obj.id]
1283
- );
1284
- const environments = [];
1285
- if (envResult.length > 0) {
1286
- for (const r of envResult[0].values) {
1287
- environments.push(r[0]);
1288
- }
1289
- }
1290
- return {
1291
- id: obj.id,
1292
- name: obj.name || void 0,
1293
- incidents,
1294
- commonSymbols: JSON.parse(obj.common_symbols || "{}"),
1295
- commonErrorPatterns: JSON.parse(
1296
- obj.common_error_patterns || "[]"
1297
- ),
1298
- count: incidents.length,
1299
- firstSeen: obj.first_seen,
1300
- lastSeen: obj.last_seen,
1301
- environments,
1302
- suggestedPattern: obj.suggested_pattern_id ? this.getPattern(obj.suggested_pattern_id) || void 0 : void 0
1303
- };
1304
- }
1305
- // ─── Practice Events ─────────────────────────────────────────────
1306
- recordPracticeEvent(input) {
1307
- this.initializeSync();
1308
- const id = `PE-${uuidv4().substring(0, 8)}`;
1309
- const now = (/* @__PURE__ */ new Date()).toISOString();
1310
- this.db.run(
1311
- `INSERT INTO practice_events (
350
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,[n.id,n.name||null,JSON.stringify(n.commonSymbols),JSON.stringify(n.commonErrorPatterns),n.suggestedPattern?.id||null,n.firstSeen,n.lastSeen,s,s]);for(let i of n.incidents)this.db.run("INSERT OR IGNORE INTO group_members (group_id, incident_id, added_at) VALUES (?, ?, ?)",[n.id,i,s]);}this.save();}rowToIncident(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{id:s.id,timestamp:s.timestamp,status:s.status,error:{message:s.error_message,stack:s.error_stack||void 0,code:s.error_code||void 0,type:s.error_type||void 0},symbols:JSON.parse(s.symbols||"{}"),flowPosition:s.flow_position?JSON.parse(s.flow_position):void 0,environment:s.environment,service:s.service||void 0,version:s.version||void 0,userId:s.user_id||void 0,requestId:s.request_id||void 0,groupId:s.group_id||void 0,notes:JSON.parse(s.notes||"[]"),relatedIncidents:JSON.parse(s.related_incidents||"[]"),resolvedAt:s.resolved_at||void 0,resolvedBy:s.resolved_by||void 0,resolution:s.resolution?JSON.parse(s.resolution):void 0}}rowToPattern(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{id:s.id,name:s.name,description:s.description||"",pattern:JSON.parse(s.pattern),resolution:JSON.parse(s.resolution),confidence:JSON.parse(s.confidence),source:s.source,private:s.private===1,tags:JSON.parse(s.tags||"[]"),createdAt:s.created_at,updatedAt:s.updated_at}}rowToGroup(e,t){let s={};e.forEach((o,c)=>{s[o]=t[c];});let n=this.db.exec("SELECT incident_id FROM group_members WHERE group_id = ?",[s.id]),i=[];if(n.length>0)for(let o of n[0].values)i.push(o[0]);let r=this.db.exec(`SELECT DISTINCT environment FROM incidents
351
+ WHERE id IN (SELECT incident_id FROM group_members WHERE group_id = ?)`,[s.id]),a=[];if(r.length>0)for(let o of r[0].values)a.push(o[0]);return {id:s.id,name:s.name||void 0,incidents:i,commonSymbols:JSON.parse(s.common_symbols||"{}"),commonErrorPatterns:JSON.parse(s.common_error_patterns||"[]"),count:i.length,firstSeen:s.first_seen,lastSeen:s.last_seen,environments:a,suggestedPattern:s.suggested_pattern_id&&this.getPattern(s.suggested_pattern_id)||void 0}}recordPracticeEvent(e){this.initializeSync();let t=`PE-${v4().substring(0,8)}`,s=new Date().toISOString();return this.db.run(`INSERT INTO practice_events (
1312
352
  id, timestamp, habit_id, habit_category, result,
1313
353
  engineer, session_id, lore_entry_id, task_description,
1314
354
  symbols_touched, files_modified, related_incident_id, notes
1315
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1316
- [
1317
- id,
1318
- now,
1319
- input.habitId,
1320
- input.habitCategory,
1321
- input.result,
1322
- input.engineer,
1323
- input.sessionId,
1324
- input.loreEntryId || null,
1325
- input.taskDescription || null,
1326
- JSON.stringify(input.symbolsTouched || []),
1327
- JSON.stringify(input.filesModified || []),
1328
- input.relatedIncidentId || null,
1329
- input.notes || null
1330
- ]
1331
- );
1332
- this.save();
1333
- return id;
1334
- }
1335
- getPracticeEvents(options = {}) {
1336
- this.initializeSync();
1337
- const { limit = 100, offset = 0 } = options;
1338
- const conditions = [];
1339
- const params = [];
1340
- if (options.habitId) {
1341
- conditions.push("habit_id = ?");
1342
- params.push(options.habitId);
1343
- }
1344
- if (options.habitCategory) {
1345
- conditions.push("habit_category = ?");
1346
- params.push(options.habitCategory);
1347
- }
1348
- if (options.result) {
1349
- conditions.push("result = ?");
1350
- params.push(options.result);
1351
- }
1352
- if (options.engineer) {
1353
- conditions.push("engineer = ?");
1354
- params.push(options.engineer);
1355
- }
1356
- if (options.sessionId) {
1357
- conditions.push("session_id = ?");
1358
- params.push(options.sessionId);
1359
- }
1360
- if (options.dateFrom) {
1361
- conditions.push("timestamp >= ?");
1362
- params.push(options.dateFrom);
1363
- }
1364
- if (options.dateTo) {
1365
- conditions.push("timestamp <= ?");
1366
- params.push(options.dateTo);
1367
- }
1368
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1369
- const result = this.db.exec(
1370
- `SELECT * FROM practice_events ${whereClause} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,
1371
- [...params, limit, offset]
1372
- );
1373
- if (result.length === 0) return [];
1374
- return result[0].values.map(
1375
- (row) => this.rowToPracticeEvent(result[0].columns, row)
1376
- );
1377
- }
1378
- getPracticeEventCount(options = {}) {
1379
- this.initializeSync();
1380
- const conditions = [];
1381
- const params = [];
1382
- if (options.habitId) {
1383
- conditions.push("habit_id = ?");
1384
- params.push(options.habitId);
1385
- }
1386
- if (options.habitCategory) {
1387
- conditions.push("habit_category = ?");
1388
- params.push(options.habitCategory);
1389
- }
1390
- if (options.result) {
1391
- conditions.push("result = ?");
1392
- params.push(options.result);
1393
- }
1394
- if (options.engineer) {
1395
- conditions.push("engineer = ?");
1396
- params.push(options.engineer);
1397
- }
1398
- if (options.dateFrom) {
1399
- conditions.push("timestamp >= ?");
1400
- params.push(options.dateFrom);
1401
- }
1402
- if (options.dateTo) {
1403
- conditions.push("timestamp <= ?");
1404
- params.push(options.dateTo);
1405
- }
1406
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1407
- const result = this.db.exec(
1408
- `SELECT COUNT(*) as count FROM practice_events ${whereClause}`,
1409
- params
1410
- );
1411
- if (result.length === 0 || result[0].values.length === 0) return 0;
1412
- return result[0].values[0][0];
1413
- }
1414
- getComplianceRate(options = {}) {
1415
- this.initializeSync();
1416
- const conditions = [];
1417
- const params = [];
1418
- if (options.habitId) {
1419
- conditions.push("habit_id = ?");
1420
- params.push(options.habitId);
1421
- }
1422
- if (options.habitCategory) {
1423
- conditions.push("habit_category = ?");
1424
- params.push(options.habitCategory);
1425
- }
1426
- if (options.engineer) {
1427
- conditions.push("engineer = ?");
1428
- params.push(options.engineer);
1429
- }
1430
- if (options.dateFrom) {
1431
- conditions.push("timestamp >= ?");
1432
- params.push(options.dateFrom);
1433
- }
1434
- if (options.dateTo) {
1435
- conditions.push("timestamp <= ?");
1436
- params.push(options.dateTo);
1437
- }
1438
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1439
- const result = this.db.exec(
1440
- `SELECT result, COUNT(*) as count
1441
- FROM practice_events ${whereClause}
1442
- GROUP BY result`,
1443
- params
1444
- );
1445
- let followed = 0;
1446
- let skipped = 0;
1447
- let partial = 0;
1448
- if (result.length > 0) {
1449
- for (const row of result[0].values) {
1450
- const r = row[0];
1451
- const count = row[1];
1452
- if (r === "followed") followed = count;
1453
- else if (r === "skipped") skipped = count;
1454
- else if (r === "partial") partial = count;
1455
- }
1456
- }
1457
- const total = followed + skipped + partial;
1458
- const rate = total > 0 ? (followed + partial * 0.5) / total * 100 : 100;
1459
- return { total, followed, skipped, partial, rate: Math.round(rate) };
1460
- }
1461
- rowToPracticeEvent(columns, row) {
1462
- const obj = {};
1463
- columns.forEach((col, i) => {
1464
- obj[col] = row[i];
1465
- });
1466
- return {
1467
- id: obj.id,
1468
- timestamp: obj.timestamp,
1469
- habitId: obj.habit_id,
1470
- habitCategory: obj.habit_category,
1471
- result: obj.result,
1472
- engineer: obj.engineer,
1473
- sessionId: obj.session_id,
1474
- loreEntryId: obj.lore_entry_id || void 0,
1475
- taskDescription: obj.task_description || void 0,
1476
- symbolsTouched: JSON.parse(obj.symbols_touched || "[]"),
1477
- filesModified: JSON.parse(obj.files_modified || "[]"),
1478
- relatedIncidentId: obj.related_incident_id || void 0,
1479
- notes: obj.notes || void 0
1480
- };
1481
- }
1482
- // ─── Structured Logs ─────────────────────────────────────────────
1483
- inferSymbolType(symbol) {
1484
- if (symbol.startsWith("#")) return "component";
1485
- if (symbol.startsWith("^")) return "gate";
1486
- if (symbol.startsWith("!")) return "signal";
1487
- if (symbol.startsWith("$")) return "flow";
1488
- if (symbol.startsWith("~")) return "aspect";
1489
- return "raw";
1490
- }
1491
- insertLog(input) {
1492
- this.initializeSync();
1493
- const id = input.id || uuidv4();
1494
- const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
1495
- const symbolType = input.symbolType || this.inferSymbolType(input.symbol);
1496
- this.db.run(
1497
- `INSERT INTO logs (
355
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[t,s,e.habitId,e.habitCategory,e.result,e.engineer,e.sessionId,e.loreEntryId||null,e.taskDescription||null,JSON.stringify(e.symbolsTouched||[]),JSON.stringify(e.filesModified||[]),e.relatedIncidentId||null,e.notes||null]),this.save(),t}getPracticeEvents(e={}){this.initializeSync();let{limit:t=100,offset:s=0}=e,n=[],i=[];e.habitId&&(n.push("habit_id = ?"),i.push(e.habitId)),e.habitCategory&&(n.push("habit_category = ?"),i.push(e.habitCategory)),e.result&&(n.push("result = ?"),i.push(e.result)),e.engineer&&(n.push("engineer = ?"),i.push(e.engineer)),e.sessionId&&(n.push("session_id = ?"),i.push(e.sessionId)),e.dateFrom&&(n.push("timestamp >= ?"),i.push(e.dateFrom)),e.dateTo&&(n.push("timestamp <= ?"),i.push(e.dateTo));let r=n.length>0?`WHERE ${n.join(" AND ")}`:"",a=this.db.exec(`SELECT * FROM practice_events ${r} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,[...i,t,s]);return a.length===0?[]:a[0].values.map(o=>this.rowToPracticeEvent(a[0].columns,o))}getPracticeEventCount(e={}){this.initializeSync();let t=[],s=[];e.habitId&&(t.push("habit_id = ?"),s.push(e.habitId)),e.habitCategory&&(t.push("habit_category = ?"),s.push(e.habitCategory)),e.result&&(t.push("result = ?"),s.push(e.result)),e.engineer&&(t.push("engineer = ?"),s.push(e.engineer)),e.dateFrom&&(t.push("timestamp >= ?"),s.push(e.dateFrom)),e.dateTo&&(t.push("timestamp <= ?"),s.push(e.dateTo));let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=this.db.exec(`SELECT COUNT(*) as count FROM practice_events ${n}`,s);return i.length===0||i[0].values.length===0?0:i[0].values[0][0]}getComplianceRate(e={}){this.initializeSync();let t=[],s=[];e.habitId&&(t.push("habit_id = ?"),s.push(e.habitId)),e.habitCategory&&(t.push("habit_category = ?"),s.push(e.habitCategory)),e.engineer&&(t.push("engineer = ?"),s.push(e.engineer)),e.dateFrom&&(t.push("timestamp >= ?"),s.push(e.dateFrom)),e.dateTo&&(t.push("timestamp <= ?"),s.push(e.dateTo));let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=this.db.exec(`SELECT result, COUNT(*) as count
356
+ FROM practice_events ${n}
357
+ GROUP BY result`,s),r=0,a=0,o=0;if(i.length>0)for(let d of i[0].values){let m=d[0],g=d[1];m==="followed"?r=g:m==="skipped"?a=g:m==="partial"&&(o=g);}let c=r+a+o,l=c>0?(r+o*.5)/c*100:100;return {total:c,followed:r,skipped:a,partial:o,rate:Math.round(l)}}rowToPracticeEvent(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{id:s.id,timestamp:s.timestamp,habitId:s.habit_id,habitCategory:s.habit_category,result:s.result,engineer:s.engineer,sessionId:s.session_id,loreEntryId:s.lore_entry_id||void 0,taskDescription:s.task_description||void 0,symbolsTouched:JSON.parse(s.symbols_touched||"[]"),filesModified:JSON.parse(s.files_modified||"[]"),relatedIncidentId:s.related_incident_id||void 0,notes:s.notes||void 0}}inferSymbolType(e){return e.startsWith("#")?"component":e.startsWith("^")?"gate":e.startsWith("!")?"signal":e.startsWith("$")?"flow":e.startsWith("~")?"aspect":"raw"}insertLog(e){this.initializeSync();let t=e.id||v4(),s=e.timestamp||new Date().toISOString(),n=e.symbolType||this.inferSymbolType(e.symbol);return this.db.run(`INSERT INTO logs (
1498
358
  id, timestamp, level, symbol, symbol_type, message, data_json,
1499
359
  service, session_id, correlation_id, duration_ms, environment
1500
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1501
- [
1502
- id,
1503
- timestamp,
1504
- input.level,
1505
- input.symbol,
1506
- symbolType,
1507
- input.message,
1508
- input.data ? JSON.stringify(input.data) : null,
1509
- input.service,
1510
- input.sessionId || null,
1511
- input.correlationId || null,
1512
- input.durationMs ?? null,
1513
- input.environment || null
1514
- ]
1515
- );
1516
- this.save();
1517
- return id;
1518
- }
1519
- insertLogBatch(entries) {
1520
- this.initializeSync();
1521
- let accepted = 0;
1522
- const errors = [];
1523
- for (const input of entries) {
1524
- try {
1525
- const id = input.id || uuidv4();
1526
- const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
1527
- const symbolType = input.symbolType || this.inferSymbolType(input.symbol);
1528
- this.db.run(
1529
- `INSERT INTO logs (
360
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[t,s,e.level,e.symbol,n,e.message,e.data?JSON.stringify(e.data):null,e.service,e.sessionId||null,e.correlationId||null,e.durationMs??null,e.environment||null]),this.save(),t}insertLogBatch(e){this.initializeSync();let t=0,s=[];for(let n of e)try{let i=n.id||v4(),r=n.timestamp||new Date().toISOString(),a=n.symbolType||this.inferSymbolType(n.symbol);this.db.run(`INSERT INTO logs (
1530
361
  id, timestamp, level, symbol, symbol_type, message, data_json,
1531
362
  service, session_id, correlation_id, duration_ms, environment
1532
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1533
- [
1534
- id,
1535
- timestamp,
1536
- input.level,
1537
- input.symbol,
1538
- symbolType,
1539
- input.message,
1540
- input.data ? JSON.stringify(input.data) : null,
1541
- input.service,
1542
- input.sessionId || null,
1543
- input.correlationId || null,
1544
- input.durationMs ?? null,
1545
- input.environment || null
1546
- ]
1547
- );
1548
- accepted++;
1549
- } catch (err) {
1550
- errors.push(err instanceof Error ? err.message : String(err));
1551
- }
1552
- }
1553
- this.save();
1554
- return { accepted, errors };
1555
- }
1556
- queryLogs(options = {}) {
1557
- this.initializeSync();
1558
- const { limit = 100, offset = 0 } = options;
1559
- const conditions = [];
1560
- const params = [];
1561
- if (options.level) {
1562
- conditions.push("level = ?");
1563
- params.push(options.level);
1564
- }
1565
- if (options.symbol) {
1566
- conditions.push("symbol LIKE ?");
1567
- params.push(`%${options.symbol}%`);
1568
- }
1569
- if (options.service) {
1570
- conditions.push("service = ?");
1571
- params.push(options.service);
1572
- }
1573
- if (options.sessionId) {
1574
- conditions.push("session_id = ?");
1575
- params.push(options.sessionId);
1576
- }
1577
- if (options.correlationId) {
1578
- conditions.push("correlation_id = ?");
1579
- params.push(options.correlationId);
1580
- }
1581
- if (options.search) {
1582
- conditions.push("message LIKE ?");
1583
- params.push(`%${options.search}%`);
1584
- }
1585
- if (options.since) {
1586
- conditions.push("timestamp >= ?");
1587
- params.push(options.since);
1588
- }
1589
- if (options.until) {
1590
- conditions.push("timestamp <= ?");
1591
- params.push(options.until);
1592
- }
1593
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1594
- const result = this.db.exec(
1595
- `SELECT * FROM logs ${whereClause} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,
1596
- [...params, limit, offset]
1597
- );
1598
- if (result.length === 0) return [];
1599
- return result[0].values.map(
1600
- (row) => this.rowToLogEntry(result[0].columns, row)
1601
- );
1602
- }
1603
- getLogCount(options = {}) {
1604
- this.initializeSync();
1605
- const conditions = [];
1606
- const params = [];
1607
- if (options.level) {
1608
- conditions.push("level = ?");
1609
- params.push(options.level);
1610
- }
1611
- if (options.symbol) {
1612
- conditions.push("symbol LIKE ?");
1613
- params.push(`%${options.symbol}%`);
1614
- }
1615
- if (options.service) {
1616
- conditions.push("service = ?");
1617
- params.push(options.service);
1618
- }
1619
- if (options.since) {
1620
- conditions.push("timestamp >= ?");
1621
- params.push(options.since);
1622
- }
1623
- if (options.until) {
1624
- conditions.push("timestamp <= ?");
1625
- params.push(options.until);
1626
- }
1627
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1628
- const result = this.db.exec(
1629
- `SELECT COUNT(*) as count FROM logs ${whereClause}`,
1630
- params
1631
- );
1632
- if (result.length === 0 || result[0].values.length === 0) return 0;
1633
- return result[0].values[0][0];
1634
- }
1635
- pruneLogs(maxCount) {
1636
- this.initializeSync();
1637
- if (maxCount <= 0) return 0;
1638
- const currentCount = this.getLogCount();
1639
- if (currentCount <= maxCount) return 0;
1640
- const deleteCount = currentCount - maxCount;
1641
- this.db.run(
1642
- `DELETE FROM logs WHERE id IN (
363
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[i,r,n.level,n.symbol,a,n.message,n.data?JSON.stringify(n.data):null,n.service,n.sessionId||null,n.correlationId||null,n.durationMs??null,n.environment||null]),t++;}catch(i){s.push(i instanceof Error?i.message:String(i));}return this.save(),{accepted:t,errors:s}}queryLogs(e={}){this.initializeSync();let{limit:t=100,offset:s=0}=e,n=[],i=[];e.level&&(n.push("level = ?"),i.push(e.level)),e.symbol&&(n.push("symbol LIKE ?"),i.push(`%${e.symbol}%`)),e.service&&(n.push("service = ?"),i.push(e.service)),e.sessionId&&(n.push("session_id = ?"),i.push(e.sessionId)),e.correlationId&&(n.push("correlation_id = ?"),i.push(e.correlationId)),e.search&&(n.push("message LIKE ?"),i.push(`%${e.search}%`)),e.since&&(n.push("timestamp >= ?"),i.push(e.since)),e.until&&(n.push("timestamp <= ?"),i.push(e.until));let r=n.length>0?`WHERE ${n.join(" AND ")}`:"",a=this.db.exec(`SELECT * FROM logs ${r} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,[...i,t,s]);return a.length===0?[]:a[0].values.map(o=>this.rowToLogEntry(a[0].columns,o))}getLogCount(e={}){this.initializeSync();let t=[],s=[];e.level&&(t.push("level = ?"),s.push(e.level)),e.symbol&&(t.push("symbol LIKE ?"),s.push(`%${e.symbol}%`)),e.service&&(t.push("service = ?"),s.push(e.service)),e.since&&(t.push("timestamp >= ?"),s.push(e.since)),e.until&&(t.push("timestamp <= ?"),s.push(e.until));let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=this.db.exec(`SELECT COUNT(*) as count FROM logs ${n}`,s);return i.length===0||i[0].values.length===0?0:i[0].values[0][0]}pruneLogs(e){if(this.initializeSync(),e<=0)return 0;let t=this.getLogCount();if(t<=e)return 0;let s=t-e;return this.db.run(`DELETE FROM logs WHERE id IN (
1643
364
  SELECT id FROM logs ORDER BY timestamp ASC LIMIT ?
1644
- )`,
1645
- [deleteCount]
1646
- );
1647
- this.save();
1648
- return deleteCount;
1649
- }
1650
- rowToLogEntry(columns, row) {
1651
- const obj = {};
1652
- columns.forEach((col, i) => {
1653
- obj[col] = row[i];
1654
- });
1655
- return {
1656
- id: obj.id,
1657
- timestamp: obj.timestamp,
1658
- level: obj.level,
1659
- symbol: obj.symbol,
1660
- symbolType: obj.symbol_type || "raw",
1661
- message: obj.message,
1662
- data: obj.data_json ? JSON.parse(obj.data_json) : void 0,
1663
- service: obj.service,
1664
- sessionId: obj.session_id || void 0,
1665
- correlationId: obj.correlation_id || void 0,
1666
- durationMs: obj.duration_ms || void 0,
1667
- environment: obj.environment || void 0
1668
- };
1669
- }
1670
- // ─── Service Registry ──────────────────────────────────────────
1671
- registerService(reg) {
1672
- this.initializeSync();
1673
- const now = (/* @__PURE__ */ new Date()).toISOString();
1674
- this.db.run(
1675
- `INSERT INTO services (name, version, pid, started_at, last_seen_at, environment, metadata_json)
365
+ )`,[s]),this.save(),s}rowToLogEntry(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{id:s.id,timestamp:s.timestamp,level:s.level,symbol:s.symbol,symbolType:s.symbol_type||"raw",message:s.message,data:s.data_json?JSON.parse(s.data_json):void 0,service:s.service,sessionId:s.session_id||void 0,correlationId:s.correlation_id||void 0,durationMs:s.duration_ms||void 0,environment:s.environment||void 0}}registerService(e){this.initializeSync();let t=new Date().toISOString();this.db.run(`INSERT INTO services (name, version, pid, started_at, last_seen_at, environment, metadata_json)
1676
366
  VALUES (?, ?, ?, ?, ?, ?, ?)
1677
367
  ON CONFLICT(name) DO UPDATE SET
1678
368
  version = excluded.version,
1679
369
  pid = excluded.pid,
1680
370
  last_seen_at = excluded.last_seen_at,
1681
371
  environment = excluded.environment,
1682
- metadata_json = excluded.metadata_json`,
1683
- [
1684
- reg.name,
1685
- reg.version || null,
1686
- reg.pid ?? null,
1687
- now,
1688
- now,
1689
- reg.environment || null,
1690
- reg.metadata ? JSON.stringify(reg.metadata) : null
1691
- ]
1692
- );
1693
- this.save();
1694
- }
1695
- updateServiceLastSeen(name) {
1696
- this.initializeSync();
1697
- const now = (/* @__PURE__ */ new Date()).toISOString();
1698
- this.db.run(
1699
- "UPDATE services SET last_seen_at = ? WHERE name = ?",
1700
- [now, name]
1701
- );
1702
- this.save();
1703
- }
1704
- getServices() {
1705
- this.initializeSync();
1706
- const result = this.db.exec(
1707
- "SELECT * FROM services ORDER BY last_seen_at DESC"
1708
- );
1709
- if (result.length === 0) return [];
1710
- return result[0].values.map((row) => {
1711
- const obj = {};
1712
- result[0].columns.forEach((col, i) => {
1713
- obj[col] = row[i];
1714
- });
1715
- return {
1716
- name: obj.name,
1717
- version: obj.version || void 0,
1718
- pid: obj.pid || void 0,
1719
- startedAt: obj.started_at,
1720
- lastSeenAt: obj.last_seen_at,
1721
- environment: obj.environment || void 0,
1722
- metadata: obj.metadata_json ? JSON.parse(obj.metadata_json) : void 0
1723
- };
1724
- });
1725
- }
1726
- // ─── App State ──────────────────────────────────────────────────
1727
- upsertAppState(state) {
1728
- this.initializeSync();
1729
- this.db.run(
1730
- `INSERT INTO app_state (service, session_id, timestamp, state_json, active_flows_json, active_gates_json)
372
+ metadata_json = excluded.metadata_json`,[e.name,e.version||null,e.pid??null,t,t,e.environment||null,e.metadata?JSON.stringify(e.metadata):null]),this.save();}updateServiceLastSeen(e){this.initializeSync();let t=new Date().toISOString();this.db.run("UPDATE services SET last_seen_at = ? WHERE name = ?",[t,e]),this.save();}getServices(){this.initializeSync();let e=this.db.exec("SELECT * FROM services ORDER BY last_seen_at DESC");return e.length===0?[]:e[0].values.map(t=>{let s={};return e[0].columns.forEach((n,i)=>{s[n]=t[i];}),{name:s.name,version:s.version||void 0,pid:s.pid||void 0,startedAt:s.started_at,lastSeenAt:s.last_seen_at,environment:s.environment||void 0,metadata:s.metadata_json?JSON.parse(s.metadata_json):void 0}})}upsertAppState(e){this.initializeSync(),this.db.run(`INSERT INTO app_state (service, session_id, timestamp, state_json, active_flows_json, active_gates_json)
1731
373
  VALUES (?, ?, ?, ?, ?, ?)
1732
374
  ON CONFLICT(service, session_id) DO UPDATE SET
1733
375
  timestamp = excluded.timestamp,
1734
376
  state_json = excluded.state_json,
1735
377
  active_flows_json = excluded.active_flows_json,
1736
- active_gates_json = excluded.active_gates_json`,
1737
- [
1738
- state.service,
1739
- state.sessionId,
1740
- state.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
1741
- JSON.stringify(state.state),
1742
- state.activeFlows ? JSON.stringify(state.activeFlows) : null,
1743
- state.activeGates ? JSON.stringify(state.activeGates) : null
1744
- ]
1745
- );
1746
- this.save();
1747
- }
1748
- getAppState(service, sessionId) {
1749
- this.initializeSync();
1750
- let query = "SELECT * FROM app_state WHERE service = ?";
1751
- const params = [service];
1752
- if (sessionId) {
1753
- query += " AND session_id = ?";
1754
- params.push(sessionId);
1755
- }
1756
- query += " ORDER BY timestamp DESC";
1757
- const result = this.db.exec(query, params);
1758
- if (result.length === 0) return [];
1759
- return result[0].values.map((row) => this.rowToAppState(result[0].columns, row));
1760
- }
1761
- getAllAppStates() {
1762
- this.initializeSync();
1763
- const result = this.db.exec(
1764
- "SELECT * FROM app_state ORDER BY timestamp DESC"
1765
- );
1766
- if (result.length === 0) return [];
1767
- return result[0].values.map((row) => this.rowToAppState(result[0].columns, row));
1768
- }
1769
- rowToAppState(columns, row) {
1770
- const obj = {};
1771
- columns.forEach((col, i) => {
1772
- obj[col] = row[i];
1773
- });
1774
- return {
1775
- service: obj.service,
1776
- sessionId: obj.session_id,
1777
- timestamp: obj.timestamp,
1778
- state: JSON.parse(obj.state_json),
1779
- activeFlows: obj.active_flows_json ? JSON.parse(obj.active_flows_json) : void 0,
1780
- activeGates: obj.active_gates_json ? JSON.parse(obj.active_gates_json) : void 0
1781
- };
1782
- }
1783
- // ─── Metrics ───────────────────────────────────────────────────
1784
- insertMetric(input) {
1785
- this.initializeSync();
1786
- const id = uuidv4();
1787
- const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
1788
- this.db.run(
1789
- `INSERT INTO metrics (
378
+ active_gates_json = excluded.active_gates_json`,[e.service,e.sessionId,e.timestamp||new Date().toISOString(),JSON.stringify(e.state),e.activeFlows?JSON.stringify(e.activeFlows):null,e.activeGates?JSON.stringify(e.activeGates):null]),this.save();}getAppState(e,t){this.initializeSync();let s="SELECT * FROM app_state WHERE service = ?",n=[e];t&&(s+=" AND session_id = ?",n.push(t)),s+=" ORDER BY timestamp DESC";let i=this.db.exec(s,n);return i.length===0?[]:i[0].values.map(r=>this.rowToAppState(i[0].columns,r))}getAllAppStates(){this.initializeSync();let e=this.db.exec("SELECT * FROM app_state ORDER BY timestamp DESC");return e.length===0?[]:e[0].values.map(t=>this.rowToAppState(e[0].columns,t))}rowToAppState(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{service:s.service,sessionId:s.session_id,timestamp:s.timestamp,state:JSON.parse(s.state_json),activeFlows:s.active_flows_json?JSON.parse(s.active_flows_json):void 0,activeGates:s.active_gates_json?JSON.parse(s.active_gates_json):void 0}}insertMetric(e){this.initializeSync();let t=v4(),s=e.timestamp||new Date().toISOString();return this.db.run(`INSERT INTO metrics (
1790
379
  id, timestamp, name, type, value, tags_json, service, environment
1791
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
1792
- [
1793
- id,
1794
- timestamp,
1795
- input.name,
1796
- input.type,
1797
- input.value,
1798
- JSON.stringify(input.tags || {}),
1799
- input.service,
1800
- input.environment || null
1801
- ]
1802
- );
1803
- this.save();
1804
- return id;
1805
- }
1806
- insertMetricBatch(entries) {
1807
- this.initializeSync();
1808
- let accepted = 0;
1809
- const errors = [];
1810
- for (const input of entries) {
1811
- try {
1812
- const id = uuidv4();
1813
- const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
1814
- this.db.run(
1815
- `INSERT INTO metrics (
380
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,[t,s,e.name,e.type,e.value,JSON.stringify(e.tags||{}),e.service,e.environment||null]),this.save(),t}insertMetricBatch(e){this.initializeSync();let t=0,s=[];for(let n of e)try{let i=v4(),r=n.timestamp||new Date().toISOString();this.db.run(`INSERT INTO metrics (
1816
381
  id, timestamp, name, type, value, tags_json, service, environment
1817
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
1818
- [
1819
- id,
1820
- timestamp,
1821
- input.name,
1822
- input.type,
1823
- input.value,
1824
- JSON.stringify(input.tags || {}),
1825
- input.service,
1826
- input.environment || null
1827
- ]
1828
- );
1829
- accepted++;
1830
- } catch (err) {
1831
- errors.push(err instanceof Error ? err.message : String(err));
1832
- }
1833
- }
1834
- this.save();
1835
- return { accepted, errors };
1836
- }
1837
- queryMetrics(options = {}) {
1838
- this.initializeSync();
1839
- const { limit = 100, offset = 0 } = options;
1840
- const conditions = [];
1841
- const params = [];
1842
- if (options.name) {
1843
- conditions.push("name = ?");
1844
- params.push(options.name);
1845
- }
1846
- if (options.type) {
1847
- conditions.push("type = ?");
1848
- params.push(options.type);
1849
- }
1850
- if (options.service) {
1851
- conditions.push("service = ?");
1852
- params.push(options.service);
1853
- }
1854
- if (options.tag) {
1855
- const eqIdx = options.tag.indexOf("=");
1856
- if (eqIdx > 0) {
1857
- const tagKey = options.tag.substring(0, eqIdx);
1858
- const tagValue = options.tag.substring(eqIdx + 1);
1859
- conditions.push("tags_json LIKE ?");
1860
- params.push(`%"${tagKey}":"${tagValue}"%`);
1861
- }
1862
- }
1863
- if (options.since) {
1864
- conditions.push("timestamp >= ?");
1865
- params.push(options.since);
1866
- }
1867
- if (options.until) {
1868
- conditions.push("timestamp <= ?");
1869
- params.push(options.until);
1870
- }
1871
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1872
- const result = this.db.exec(
1873
- `SELECT * FROM metrics ${whereClause} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,
1874
- [...params, limit, offset]
1875
- );
1876
- if (result.length === 0) return [];
1877
- return result[0].values.map(
1878
- (row) => this.rowToMetricEntry(result[0].columns, row)
1879
- );
1880
- }
1881
- getMetricCount(options = {}) {
1882
- this.initializeSync();
1883
- const conditions = [];
1884
- const params = [];
1885
- if (options.name) {
1886
- conditions.push("name = ?");
1887
- params.push(options.name);
1888
- }
1889
- if (options.type) {
1890
- conditions.push("type = ?");
1891
- params.push(options.type);
1892
- }
1893
- if (options.service) {
1894
- conditions.push("service = ?");
1895
- params.push(options.service);
1896
- }
1897
- if (options.tag) {
1898
- const eqIdx = options.tag.indexOf("=");
1899
- if (eqIdx > 0) {
1900
- const tagKey = options.tag.substring(0, eqIdx);
1901
- const tagValue = options.tag.substring(eqIdx + 1);
1902
- conditions.push("tags_json LIKE ?");
1903
- params.push(`%"${tagKey}":"${tagValue}"%`);
1904
- }
1905
- }
1906
- if (options.since) {
1907
- conditions.push("timestamp >= ?");
1908
- params.push(options.since);
1909
- }
1910
- if (options.until) {
1911
- conditions.push("timestamp <= ?");
1912
- params.push(options.until);
1913
- }
1914
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1915
- const result = this.db.exec(
1916
- `SELECT COUNT(*) as count FROM metrics ${whereClause}`,
1917
- params
1918
- );
1919
- if (result.length === 0 || result[0].values.length === 0) return 0;
1920
- return result[0].values[0][0];
1921
- }
1922
- aggregateMetric(name, options) {
1923
- this.initializeSync();
1924
- const conditions = ["name = ?"];
1925
- const params = [name];
1926
- if (options?.service) {
1927
- conditions.push("service = ?");
1928
- params.push(options.service);
1929
- }
1930
- if (options?.since) {
1931
- conditions.push("timestamp >= ?");
1932
- params.push(options.since);
1933
- }
1934
- if (options?.until) {
1935
- conditions.push("timestamp <= ?");
1936
- params.push(options.until);
1937
- }
1938
- const whereClause = `WHERE ${conditions.join(" AND ")}`;
1939
- const result = this.db.exec(
1940
- `SELECT COUNT(*) as count, SUM(value) as sum, MIN(value) as min, MAX(value) as max, AVG(value) as avg
1941
- FROM metrics ${whereClause}`,
1942
- params
1943
- );
1944
- if (result.length === 0 || result[0].values.length === 0) {
1945
- return { name, count: 0, sum: 0, min: 0, max: 0, avg: 0 };
1946
- }
1947
- const row = result[0].values[0];
1948
- return {
1949
- name,
1950
- count: row[0] || 0,
1951
- sum: row[1] || 0,
1952
- min: row[2] || 0,
1953
- max: row[3] || 0,
1954
- avg: row[4] || 0
1955
- };
1956
- }
1957
- pruneMetrics(maxCount) {
1958
- this.initializeSync();
1959
- if (maxCount <= 0) return 0;
1960
- const currentCount = this.getMetricCount();
1961
- if (currentCount <= maxCount) return 0;
1962
- const deleteCount = currentCount - maxCount;
1963
- this.db.run(
1964
- `DELETE FROM metrics WHERE id IN (
382
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,[i,r,n.name,n.type,n.value,JSON.stringify(n.tags||{}),n.service,n.environment||null]),t++;}catch(i){s.push(i instanceof Error?i.message:String(i));}return this.save(),{accepted:t,errors:s}}queryMetrics(e={}){this.initializeSync();let{limit:t=100,offset:s=0}=e,n=[],i=[];if(e.name&&(n.push("name = ?"),i.push(e.name)),e.type&&(n.push("type = ?"),i.push(e.type)),e.service&&(n.push("service = ?"),i.push(e.service)),e.tag){let o=e.tag.indexOf("=");if(o>0){let c=e.tag.substring(0,o),l=e.tag.substring(o+1);n.push("tags_json LIKE ?"),i.push(`%"${c}":"${l}"%`);}}e.since&&(n.push("timestamp >= ?"),i.push(e.since)),e.until&&(n.push("timestamp <= ?"),i.push(e.until));let r=n.length>0?`WHERE ${n.join(" AND ")}`:"",a=this.db.exec(`SELECT * FROM metrics ${r} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,[...i,t,s]);return a.length===0?[]:a[0].values.map(o=>this.rowToMetricEntry(a[0].columns,o))}getMetricCount(e={}){this.initializeSync();let t=[],s=[];if(e.name&&(t.push("name = ?"),s.push(e.name)),e.type&&(t.push("type = ?"),s.push(e.type)),e.service&&(t.push("service = ?"),s.push(e.service)),e.tag){let r=e.tag.indexOf("=");if(r>0){let a=e.tag.substring(0,r),o=e.tag.substring(r+1);t.push("tags_json LIKE ?"),s.push(`%"${a}":"${o}"%`);}}e.since&&(t.push("timestamp >= ?"),s.push(e.since)),e.until&&(t.push("timestamp <= ?"),s.push(e.until));let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=this.db.exec(`SELECT COUNT(*) as count FROM metrics ${n}`,s);return i.length===0||i[0].values.length===0?0:i[0].values[0][0]}aggregateMetric(e,t){this.initializeSync();let s=["name = ?"],n=[e];t?.service&&(s.push("service = ?"),n.push(t.service)),t?.since&&(s.push("timestamp >= ?"),n.push(t.since)),t?.until&&(s.push("timestamp <= ?"),n.push(t.until));let i=`WHERE ${s.join(" AND ")}`,r=this.db.exec(`SELECT COUNT(*) as count, SUM(value) as sum, MIN(value) as min, MAX(value) as max, AVG(value) as avg
383
+ FROM metrics ${i}`,n);if(r.length===0||r[0].values.length===0)return {name:e,count:0,sum:0,min:0,max:0,avg:0};let a=r[0].values[0];return {name:e,count:a[0]||0,sum:a[1]||0,min:a[2]||0,max:a[3]||0,avg:a[4]||0}}pruneMetrics(e){if(this.initializeSync(),e<=0)return 0;let t=this.getMetricCount();if(t<=e)return 0;let s=t-e;return this.db.run(`DELETE FROM metrics WHERE id IN (
1965
384
  SELECT id FROM metrics ORDER BY timestamp ASC LIMIT ?
1966
- )`,
1967
- [deleteCount]
1968
- );
1969
- this.save();
1970
- return deleteCount;
1971
- }
1972
- rowToMetricEntry(columns, row) {
1973
- const obj = {};
1974
- columns.forEach((col, i) => {
1975
- obj[col] = row[i];
1976
- });
1977
- return {
1978
- id: obj.id,
1979
- timestamp: obj.timestamp,
1980
- name: obj.name,
1981
- type: obj.type,
1982
- value: obj.value,
1983
- tags: obj.tags_json ? JSON.parse(obj.tags_json) : {},
1984
- service: obj.service,
1985
- environment: obj.environment || void 0
1986
- };
1987
- }
1988
- // ─── Traces ───────────────────────────────────────────────────
1989
- insertSpan(input) {
1990
- this.initializeSync();
1991
- const spanId = input.spanId || uuidv4();
1992
- const startTime = input.startTime || (/* @__PURE__ */ new Date()).toISOString();
1993
- this.db.run(
1994
- `INSERT INTO traces (
385
+ )`,[s]),this.save(),s}rowToMetricEntry(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{id:s.id,timestamp:s.timestamp,name:s.name,type:s.type,value:s.value,tags:s.tags_json?JSON.parse(s.tags_json):{},service:s.service,environment:s.environment||void 0}}insertSpan(e){this.initializeSync();let t=e.spanId||v4(),s=e.startTime||new Date().toISOString();return this.db.run(`INSERT INTO traces (
1995
386
  trace_id, span_id, parent_span_id, service, symbol, operation,
1996
387
  start_time, end_time, duration_ms, status, tags_json, log_ids_json
1997
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1998
- [
1999
- input.traceId,
2000
- spanId,
2001
- input.parentSpanId || null,
2002
- input.service,
2003
- input.symbol,
2004
- input.operation,
2005
- startTime,
2006
- input.endTime || null,
2007
- input.durationMs ?? null,
2008
- input.status || "ok",
2009
- JSON.stringify(input.tags || {}),
2010
- JSON.stringify(input.logIds || [])
2011
- ]
2012
- );
2013
- this.save();
2014
- return spanId;
2015
- }
2016
- getTrace(traceId) {
2017
- this.initializeSync();
2018
- const result = this.db.exec(
2019
- "SELECT * FROM traces WHERE trace_id = ? ORDER BY start_time ASC",
2020
- [traceId]
2021
- );
2022
- if (result.length === 0 || result[0].values.length === 0) return null;
2023
- const spans = result[0].values.map(
2024
- (row) => this.rowToTraceSpan(result[0].columns, row)
2025
- );
2026
- const services = [...new Set(spans.map((s) => s.service))];
2027
- const startTimes = spans.map((s) => s.startTime);
2028
- const endTimes = spans.filter((s) => s.endTime).map((s) => s.endTime);
2029
- const startTime = startTimes.sort()[0];
2030
- const endTime = endTimes.length > 0 ? endTimes.sort().reverse()[0] : startTime;
2031
- const startMs = new Date(startTime).getTime();
2032
- const endMs = new Date(endTime).getTime();
2033
- const totalDurationMs = endMs - startMs;
2034
- return {
2035
- traceId,
2036
- spans,
2037
- services,
2038
- totalDurationMs: totalDurationMs > 0 ? totalDurationMs : 0,
2039
- startTime,
2040
- endTime
2041
- };
2042
- }
2043
- queryTraces(options = {}) {
2044
- this.initializeSync();
2045
- const conditions = [];
2046
- const params = [];
2047
- if (options.service) {
2048
- conditions.push("service = ?");
2049
- params.push(options.service);
2050
- }
2051
- if (options.symbol) {
2052
- conditions.push("symbol = ?");
2053
- params.push(options.symbol);
2054
- }
2055
- if (options.since) {
2056
- conditions.push("start_time >= ?");
2057
- params.push(options.since);
2058
- }
2059
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2060
- const traceLimit = Math.min(options.limit || 20, 20);
2061
- const result = this.db.exec(
2062
- `SELECT DISTINCT trace_id FROM traces ${whereClause} ORDER BY start_time DESC LIMIT ?`,
2063
- [...params, traceLimit]
2064
- );
2065
- if (result.length === 0) return [];
2066
- const traces = [];
2067
- for (const row of result[0].values) {
2068
- const traceId = row[0];
2069
- const trace = this.getTrace(traceId);
2070
- if (trace) {
2071
- traces.push(trace);
2072
- }
2073
- }
2074
- return traces;
2075
- }
2076
- rowToTraceSpan(columns, row) {
2077
- const obj = {};
2078
- columns.forEach((col, i) => {
2079
- obj[col] = row[i];
2080
- });
2081
- return {
2082
- traceId: obj.trace_id,
2083
- spanId: obj.span_id,
2084
- parentSpanId: obj.parent_span_id || void 0,
2085
- service: obj.service,
2086
- symbol: obj.symbol,
2087
- operation: obj.operation,
2088
- startTime: obj.start_time,
2089
- endTime: obj.end_time || void 0,
2090
- durationMs: obj.duration_ms || void 0,
2091
- status: obj.status || "ok",
2092
- tags: obj.tags_json ? JSON.parse(obj.tags_json) : {},
2093
- logs: obj.log_ids_json ? JSON.parse(obj.log_ids_json) : []
2094
- };
2095
- }
2096
- // ─── Schema Registry ─────────────────────────────────────────────
2097
- registerSchema(schema) {
2098
- this.initializeSync();
2099
- const now = (/* @__PURE__ */ new Date()).toISOString();
2100
- this.db.run(
2101
- `INSERT INTO schemas (
388
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[e.traceId,t,e.parentSpanId||null,e.service,e.symbol,e.operation,s,e.endTime||null,e.durationMs??null,e.status||"ok",JSON.stringify(e.tags||{}),JSON.stringify(e.logIds||[])]),this.save(),t}getTrace(e){this.initializeSync();let t=this.db.exec("SELECT * FROM traces WHERE trace_id = ? ORDER BY start_time ASC",[e]);if(t.length===0||t[0].values.length===0)return null;let s=t[0].values.map(m=>this.rowToTraceSpan(t[0].columns,m)),n=[...new Set(s.map(m=>m.service))],i=s.map(m=>m.startTime),r=s.filter(m=>m.endTime).map(m=>m.endTime),a=i.sort()[0],o=r.length>0?r.sort().reverse()[0]:a,c=new Date(a).getTime(),d=new Date(o).getTime()-c;return {traceId:e,spans:s,services:n,totalDurationMs:d>0?d:0,startTime:a,endTime:o}}queryTraces(e={}){this.initializeSync();let t=[],s=[];e.service&&(t.push("service = ?"),s.push(e.service)),e.symbol&&(t.push("symbol = ?"),s.push(e.symbol)),e.since&&(t.push("start_time >= ?"),s.push(e.since));let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=Math.min(e.limit||20,20),r=this.db.exec(`SELECT DISTINCT trace_id FROM traces ${n} ORDER BY start_time DESC LIMIT ?`,[...s,i]);if(r.length===0)return [];let a=[];for(let o of r[0].values){let c=o[0],l=this.getTrace(c);l&&a.push(l);}return a}rowToTraceSpan(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{traceId:s.trace_id,spanId:s.span_id,parentSpanId:s.parent_span_id||void 0,service:s.service,symbol:s.symbol,operation:s.operation,startTime:s.start_time,endTime:s.end_time||void 0,durationMs:s.duration_ms||void 0,status:s.status||"ok",tags:s.tags_json?JSON.parse(s.tags_json):{},logs:s.log_ids_json?JSON.parse(s.log_ids_json):[]}}registerSchema(e){this.initializeSync();let t=new Date().toISOString();return this.db.run(`INSERT INTO schemas (
2102
389
  id, version, name, description, scope_json, event_types_json,
2103
390
  causality_json, visualization_json, tags_json, registered_at, updated_at
2104
391
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
@@ -2111,2084 +398,27 @@ var SentinelStorage = class {
2111
398
  causality_json = excluded.causality_json,
2112
399
  visualization_json = excluded.visualization_json,
2113
400
  tags_json = excluded.tags_json,
2114
- updated_at = excluded.updated_at`,
2115
- [
2116
- schema.id,
2117
- schema.version,
2118
- schema.name,
2119
- schema.description || null,
2120
- JSON.stringify(schema.scope),
2121
- JSON.stringify(schema.eventTypes),
2122
- schema.causality ? JSON.stringify(schema.causality) : null,
2123
- schema.visualization ? JSON.stringify(schema.visualization) : null,
2124
- JSON.stringify(schema.tags || []),
2125
- now,
2126
- now
2127
- ]
2128
- );
2129
- this.save();
2130
- return {
2131
- id: schema.id,
2132
- version: schema.version,
2133
- name: schema.name,
2134
- description: schema.description,
2135
- scope: schema.scope,
2136
- eventTypes: schema.eventTypes,
2137
- causality: schema.causality,
2138
- visualization: schema.visualization,
2139
- tags: schema.tags || [],
2140
- registeredAt: now,
2141
- updatedAt: now
2142
- };
2143
- }
2144
- getSchema(id) {
2145
- this.initializeSync();
2146
- const result = this.db.exec("SELECT * FROM schemas WHERE id = ?", [id]);
2147
- if (result.length === 0 || result[0].values.length === 0) return null;
2148
- return this.rowToSchema(result[0].columns, result[0].values[0]);
2149
- }
2150
- listSchemas() {
2151
- this.initializeSync();
2152
- const result = this.db.exec("SELECT * FROM schemas ORDER BY name ASC");
2153
- if (result.length === 0) return [];
2154
- return result[0].values.map(
2155
- (row) => this.rowToSchema(result[0].columns, row)
2156
- );
2157
- }
2158
- rowToSchema(columns, row) {
2159
- const obj = {};
2160
- columns.forEach((col, i) => {
2161
- obj[col] = row[i];
2162
- });
2163
- return {
2164
- id: obj.id,
2165
- version: obj.version,
2166
- name: obj.name,
2167
- description: obj.description || void 0,
2168
- scope: JSON.parse(obj.scope_json),
2169
- eventTypes: JSON.parse(obj.event_types_json),
2170
- causality: obj.causality_json ? JSON.parse(obj.causality_json) : void 0,
2171
- visualization: obj.visualization_json ? JSON.parse(obj.visualization_json) : void 0,
2172
- tags: JSON.parse(obj.tags_json || "[]"),
2173
- registeredAt: obj.registered_at,
2174
- updatedAt: obj.updated_at
2175
- };
2176
- }
2177
- // ─── Generic Events ────────────────────────────────────────────
2178
- insertEventBatch(schemaId, service, inputs) {
2179
- this.initializeSync();
2180
- const schema = this.getSchema(schemaId);
2181
- const typeMap = /* @__PURE__ */ new Map();
2182
- if (schema) {
2183
- for (const et of schema.eventTypes) {
2184
- typeMap.set(et.type, {
2185
- category: et.category,
2186
- severity: et.severity || "info"
2187
- });
2188
- }
2189
- }
2190
- let accepted = 0;
2191
- const errors = [];
2192
- for (const input of inputs) {
2193
- try {
2194
- const id = input.id || uuidv4();
2195
- const timestamp = input.timestamp || (/* @__PURE__ */ new Date()).toISOString();
2196
- const resolved = typeMap.get(input.type);
2197
- const category = resolved?.category || "unknown";
2198
- const severity = input.severity || resolved?.severity || "info";
2199
- const scopeValue = input.scopeValue != null ? String(input.scopeValue) : null;
2200
- const scopeOrdinal = typeof input.scopeValue === "number" ? input.scopeValue : null;
2201
- this.db.run(
2202
- `INSERT INTO events (
401
+ updated_at = excluded.updated_at`,[e.id,e.version,e.name,e.description||null,JSON.stringify(e.scope),JSON.stringify(e.eventTypes),e.causality?JSON.stringify(e.causality):null,e.visualization?JSON.stringify(e.visualization):null,JSON.stringify(e.tags||[]),t,t]),this.save(),{id:e.id,version:e.version,name:e.name,description:e.description,scope:e.scope,eventTypes:e.eventTypes,causality:e.causality,visualization:e.visualization,tags:e.tags||[],registeredAt:t,updatedAt:t}}getSchema(e){this.initializeSync();let t=this.db.exec("SELECT * FROM schemas WHERE id = ?",[e]);return t.length===0||t[0].values.length===0?null:this.rowToSchema(t[0].columns,t[0].values[0])}listSchemas(){this.initializeSync();let e=this.db.exec("SELECT * FROM schemas ORDER BY name ASC");return e.length===0?[]:e[0].values.map(t=>this.rowToSchema(e[0].columns,t))}rowToSchema(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{id:s.id,version:s.version,name:s.name,description:s.description||void 0,scope:JSON.parse(s.scope_json),eventTypes:JSON.parse(s.event_types_json),causality:s.causality_json?JSON.parse(s.causality_json):void 0,visualization:s.visualization_json?JSON.parse(s.visualization_json):void 0,tags:JSON.parse(s.tags_json||"[]"),registeredAt:s.registered_at,updatedAt:s.updated_at}}insertEventBatch(e,t,s){this.initializeSync();let n=this.getSchema(e),i=new Map;if(n)for(let o of n.eventTypes)i.set(o.type,{category:o.category,severity:o.severity||"info"});let r=0,a=[];for(let o of s)try{let c=o.id||v4(),l=o.timestamp||new Date().toISOString(),d=i.get(o.type),m=d?.category||"unknown",g=o.severity||d?.severity||"info",p=o.scopeValue!=null?String(o.scopeValue):null,E=typeof o.scopeValue=="number"?o.scopeValue:null;this.db.run(`INSERT INTO events (
2203
402
  id, schema_id, event_type, category, timestamp, scope_value,
2204
403
  scope_ordinal, session_id, service, data_json, severity,
2205
404
  parent_event_id, depth
2206
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2207
- [
2208
- id,
2209
- schemaId,
2210
- input.type,
2211
- category,
2212
- timestamp,
2213
- scopeValue,
2214
- scopeOrdinal,
2215
- input.sessionId || null,
2216
- service,
2217
- input.data ? JSON.stringify(input.data) : null,
2218
- severity,
2219
- input.parentEventId || null,
2220
- input.depth ?? 0
2221
- ]
2222
- );
2223
- accepted++;
2224
- } catch (err) {
2225
- errors.push(err instanceof Error ? err.message : String(err));
2226
- }
2227
- }
2228
- this.save();
2229
- return { accepted, errors };
2230
- }
2231
- queryEvents(options = {}) {
2232
- this.initializeSync();
2233
- const { limit = 100, offset = 0 } = options;
2234
- const conditions = [];
2235
- const params = [];
2236
- if (options.schemaId) {
2237
- conditions.push("schema_id = ?");
2238
- params.push(options.schemaId);
2239
- }
2240
- if (options.eventType) {
2241
- conditions.push("event_type = ?");
2242
- params.push(options.eventType);
2243
- }
2244
- if (options.category) {
2245
- conditions.push("category = ?");
2246
- params.push(options.category);
2247
- }
2248
- if (options.service) {
2249
- conditions.push("service = ?");
2250
- params.push(options.service);
2251
- }
2252
- if (options.sessionId) {
2253
- conditions.push("session_id = ?");
2254
- params.push(options.sessionId);
2255
- }
2256
- if (options.scopeValue) {
2257
- conditions.push("scope_value = ?");
2258
- params.push(options.scopeValue);
2259
- }
2260
- if (options.scopeFrom) {
2261
- conditions.push("scope_value >= ?");
2262
- params.push(options.scopeFrom);
2263
- }
2264
- if (options.scopeTo) {
2265
- conditions.push("scope_value <= ?");
2266
- params.push(options.scopeTo);
2267
- }
2268
- if (options.severity) {
2269
- conditions.push("severity = ?");
2270
- params.push(options.severity);
2271
- }
2272
- if (options.since) {
2273
- conditions.push("timestamp >= ?");
2274
- params.push(options.since);
2275
- }
2276
- if (options.until) {
2277
- conditions.push("timestamp <= ?");
2278
- params.push(options.until);
2279
- }
2280
- if (options.search) {
2281
- conditions.push("data_json LIKE ?");
2282
- params.push(`%${options.search}%`);
2283
- }
2284
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2285
- const result = this.db.exec(
2286
- `SELECT * FROM events ${whereClause} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,
2287
- [...params, limit, offset]
2288
- );
2289
- if (result.length === 0) return [];
2290
- return result[0].values.map(
2291
- (row) => this.rowToGenericEvent(result[0].columns, row)
2292
- );
2293
- }
2294
- queryEventsByScope(schemaId, scopeValue) {
2295
- this.initializeSync();
2296
- const result = this.db.exec(
2297
- `SELECT * FROM events
405
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[c,e,o.type,m,l,p,E,o.sessionId||null,t,o.data?JSON.stringify(o.data):null,g,o.parentEventId||null,o.depth??0]),r++;}catch(c){a.push(c instanceof Error?c.message:String(c));}return this.save(),{accepted:r,errors:a}}queryEvents(e={}){this.initializeSync();let{limit:t=100,offset:s=0}=e,n=[],i=[];e.schemaId&&(n.push("schema_id = ?"),i.push(e.schemaId)),e.eventType&&(n.push("event_type = ?"),i.push(e.eventType)),e.category&&(n.push("category = ?"),i.push(e.category)),e.service&&(n.push("service = ?"),i.push(e.service)),e.sessionId&&(n.push("session_id = ?"),i.push(e.sessionId)),e.scopeValue&&(n.push("scope_value = ?"),i.push(e.scopeValue)),e.scopeFrom&&(n.push("scope_value >= ?"),i.push(e.scopeFrom)),e.scopeTo&&(n.push("scope_value <= ?"),i.push(e.scopeTo)),e.severity&&(n.push("severity = ?"),i.push(e.severity)),e.since&&(n.push("timestamp >= ?"),i.push(e.since)),e.until&&(n.push("timestamp <= ?"),i.push(e.until)),e.search&&(n.push("data_json LIKE ?"),i.push(`%${e.search}%`));let r=n.length>0?`WHERE ${n.join(" AND ")}`:"",a=this.db.exec(`SELECT * FROM events ${r} ORDER BY timestamp DESC LIMIT ? OFFSET ?`,[...i,t,s]);return a.length===0?[]:a[0].values.map(o=>this.rowToGenericEvent(a[0].columns,o))}queryEventsByScope(e,t){this.initializeSync();let s=this.db.exec(`SELECT * FROM events
2298
406
  WHERE schema_id = ? AND scope_value = ?
2299
- ORDER BY timestamp ASC`,
2300
- [schemaId, scopeValue]
2301
- );
2302
- if (result.length === 0) return [];
2303
- return result[0].values.map(
2304
- (row) => this.rowToGenericEvent(result[0].columns, row)
2305
- );
2306
- }
2307
- getEventScopes(schemaId, options = {}) {
2308
- this.initializeSync();
2309
- const { limit = 100, offset = 0 } = options;
2310
- const conditions = ["schema_id = ?"];
2311
- const params = [schemaId];
2312
- if (options.sessionId) {
2313
- conditions.push("session_id = ?");
2314
- params.push(options.sessionId);
2315
- }
2316
- const whereClause = `WHERE ${conditions.join(" AND ")}`;
2317
- const result = this.db.exec(
2318
- `SELECT
407
+ ORDER BY timestamp ASC`,[e,t]);return s.length===0?[]:s[0].values.map(n=>this.rowToGenericEvent(s[0].columns,n))}getEventScopes(e,t={}){this.initializeSync();let{limit:s=100,offset:n=0}=t,i=["schema_id = ?"],r=[e];t.sessionId&&(i.push("session_id = ?"),r.push(t.sessionId));let a=`WHERE ${i.join(" AND ")}`,o=this.db.exec(`SELECT
2319
408
  scope_value,
2320
409
  MIN(scope_ordinal) as scope_ordinal,
2321
410
  COUNT(*) as event_count,
2322
411
  MIN(timestamp) as first_timestamp,
2323
412
  MAX(timestamp) as last_timestamp
2324
413
  FROM events
2325
- ${whereClause}
414
+ ${a}
2326
415
  AND scope_value IS NOT NULL
2327
416
  GROUP BY scope_value
2328
417
  ORDER BY MIN(COALESCE(scope_ordinal, 0)) DESC, MIN(timestamp) DESC
2329
- LIMIT ? OFFSET ?`,
2330
- [...params, limit, offset]
2331
- );
2332
- if (result.length === 0) return [];
2333
- const scopes = [];
2334
- for (const row of result[0].values) {
2335
- const scopeValue = row[0];
2336
- const scopeOrdinal = row[1] != null ? row[1] : void 0;
2337
- const eventCount = row[2];
2338
- const firstTimestamp = row[3];
2339
- const lastTimestamp = row[4];
2340
- const catResult = this.db.exec(
2341
- `SELECT category, COUNT(*) as count FROM events
418
+ LIMIT ? OFFSET ?`,[...r,s,n]);if(o.length===0)return [];let c=[];for(let l of o[0].values){let d=l[0],m=l[1]!=null?l[1]:void 0,g=l[2],p=l[3],E=l[4],S=this.db.exec(`SELECT category, COUNT(*) as count FROM events
2342
419
  WHERE schema_id = ? AND scope_value = ?
2343
- GROUP BY category`,
2344
- [schemaId, scopeValue]
2345
- );
2346
- const categories = {};
2347
- if (catResult.length > 0) {
2348
- for (const catRow of catResult[0].values) {
2349
- categories[catRow[0]] = catRow[1];
2350
- }
2351
- }
2352
- scopes.push({
2353
- scopeValue,
2354
- scopeOrdinal,
2355
- eventCount,
2356
- categories,
2357
- firstTimestamp,
2358
- lastTimestamp
2359
- });
2360
- }
2361
- return scopes;
2362
- }
2363
- getEventCount(options = {}) {
2364
- this.initializeSync();
2365
- const conditions = [];
2366
- const params = [];
2367
- if (options.schemaId) {
2368
- conditions.push("schema_id = ?");
2369
- params.push(options.schemaId);
2370
- }
2371
- if (options.eventType) {
2372
- conditions.push("event_type = ?");
2373
- params.push(options.eventType);
2374
- }
2375
- if (options.service) {
2376
- conditions.push("service = ?");
2377
- params.push(options.service);
2378
- }
2379
- if (options.since) {
2380
- conditions.push("timestamp >= ?");
2381
- params.push(options.since);
2382
- }
2383
- if (options.until) {
2384
- conditions.push("timestamp <= ?");
2385
- params.push(options.until);
2386
- }
2387
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2388
- const result = this.db.exec(
2389
- `SELECT COUNT(*) as count FROM events ${whereClause}`,
2390
- params
2391
- );
2392
- if (result.length === 0 || result[0].values.length === 0) return 0;
2393
- return result[0].values[0][0];
2394
- }
2395
- pruneEvents(maxCount) {
2396
- this.initializeSync();
2397
- if (maxCount <= 0) return 0;
2398
- const currentCount = this.getEventCount();
2399
- if (currentCount <= maxCount) return 0;
2400
- const deleteCount = currentCount - maxCount;
2401
- this.db.run(
2402
- `DELETE FROM events WHERE id IN (
420
+ GROUP BY category`,[e,d]),v={};if(S.length>0)for(let A of S[0].values)v[A[0]]=A[1];c.push({scopeValue:d,scopeOrdinal:m,eventCount:g,categories:v,firstTimestamp:p,lastTimestamp:E});}return c}getEventCount(e={}){this.initializeSync();let t=[],s=[];e.schemaId&&(t.push("schema_id = ?"),s.push(e.schemaId)),e.eventType&&(t.push("event_type = ?"),s.push(e.eventType)),e.service&&(t.push("service = ?"),s.push(e.service)),e.since&&(t.push("timestamp >= ?"),s.push(e.since)),e.until&&(t.push("timestamp <= ?"),s.push(e.until));let n=t.length>0?`WHERE ${t.join(" AND ")}`:"",i=this.db.exec(`SELECT COUNT(*) as count FROM events ${n}`,s);return i.length===0||i[0].values.length===0?0:i[0].values[0][0]}pruneEvents(e){if(this.initializeSync(),e<=0)return 0;let t=this.getEventCount();if(t<=e)return 0;let s=t-e;return this.db.run(`DELETE FROM events WHERE id IN (
2403
421
  SELECT id FROM events ORDER BY timestamp ASC LIMIT ?
2404
- )`,
2405
- [deleteCount]
2406
- );
2407
- this.save();
2408
- return deleteCount;
2409
- }
2410
- rowToGenericEvent(columns, row) {
2411
- const obj = {};
2412
- columns.forEach((col, i) => {
2413
- obj[col] = row[i];
2414
- });
2415
- return {
2416
- id: obj.id,
2417
- schemaId: obj.schema_id,
2418
- eventType: obj.event_type,
2419
- category: obj.category,
2420
- timestamp: obj.timestamp,
2421
- scopeValue: obj.scope_value || void 0,
2422
- scopeOrdinal: obj.scope_ordinal != null ? obj.scope_ordinal : void 0,
2423
- sessionId: obj.session_id || void 0,
2424
- service: obj.service,
2425
- data: obj.data_json ? JSON.parse(obj.data_json) : void 0,
2426
- severity: obj.severity || "info",
2427
- parentEventId: obj.parent_event_id || void 0,
2428
- depth: obj.depth || 0
2429
- };
2430
- }
2431
- close() {
2432
- if (this.db) {
2433
- this.save();
2434
- this.db.close();
2435
- this.db = null;
2436
- }
2437
- }
2438
- };
2439
-
2440
- // ../sentinel/src/matcher.ts
2441
- var DEFAULT_CONFIG = {
2442
- minScore: 30,
2443
- maxResults: 5,
2444
- boostConfidence: true
2445
- };
2446
- var PatternMatcher = class {
2447
- constructor(storage2) {
2448
- this.storage = storage2;
2449
- }
2450
- /**
2451
- * Match an incident against all patterns and return ranked results
2452
- */
2453
- match(incident, config = {}) {
2454
- const { minScore, maxResults, boostConfidence } = {
2455
- ...DEFAULT_CONFIG,
2456
- ...config
2457
- };
2458
- const patterns = this.storage.getAllPatterns({ includePrivate: true });
2459
- const matches = [];
2460
- for (const pattern of patterns) {
2461
- if (!this.matchEnvironment(pattern, incident)) {
2462
- continue;
2463
- }
2464
- const { score, matchedCriteria } = this.scoreMatch(pattern, incident);
2465
- if (score >= minScore) {
2466
- let confidence = score;
2467
- if (boostConfidence) {
2468
- const confidenceFactor = pattern.confidence.score / 100;
2469
- confidence = score * (0.5 + 0.5 * confidenceFactor);
2470
- }
2471
- matches.push({
2472
- pattern,
2473
- score,
2474
- matchedCriteria,
2475
- confidence: Math.round(confidence)
2476
- });
2477
- this.storage.updatePatternConfidence(pattern.id, "matched");
2478
- }
2479
- }
2480
- return matches.sort((a, b) => b.confidence - a.confidence).slice(0, maxResults);
2481
- }
2482
- /**
2483
- * Test a pattern against historical incidents
2484
- */
2485
- testPattern(pattern, limit = 100) {
2486
- const incidents = this.storage.getRecentIncidents({ limit });
2487
- const wouldMatch = [];
2488
- let totalScore = 0;
2489
- for (const incident of incidents) {
2490
- if (!this.matchEnvironment(pattern, incident)) {
2491
- continue;
2492
- }
2493
- const { score } = this.scoreMatch(pattern, incident);
2494
- if (score >= 30) {
2495
- wouldMatch.push(incident);
2496
- totalScore += score;
2497
- }
2498
- }
2499
- return {
2500
- wouldMatch,
2501
- matchCount: wouldMatch.length,
2502
- avgScore: wouldMatch.length > 0 ? Math.round(totalScore / wouldMatch.length) : 0
2503
- };
2504
- }
2505
- /**
2506
- * Score how well a pattern matches an incident
2507
- */
2508
- scoreMatch(pattern, incident) {
2509
- let score = 0;
2510
- const matchedCriteria = {
2511
- symbols: [],
2512
- errorKeywords: [],
2513
- missingSignals: []
2514
- };
2515
- const symbolScore = this.matchSymbols(
2516
- pattern.pattern.symbols,
2517
- incident.symbols,
2518
- matchedCriteria.symbols
2519
- );
2520
- score += Math.min(symbolScore, 50);
2521
- const errorScore = this.matchErrorText(
2522
- pattern,
2523
- incident,
2524
- matchedCriteria.errorKeywords
2525
- );
2526
- score += Math.min(errorScore, 25);
2527
- const signalScore = this.matchMissingSignals(
2528
- pattern,
2529
- incident,
2530
- matchedCriteria.missingSignals
2531
- );
2532
- score += Math.min(signalScore, 25);
2533
- score = Math.min(score, 100);
2534
- return { score, matchedCriteria };
2535
- }
2536
- /**
2537
- * Match symbols between pattern and incident
2538
- */
2539
- matchSymbols(patternSymbols, incidentSymbols, matched) {
2540
- let score = 0;
2541
- const symbolTypes = [
2542
- "feature",
2543
- "component",
2544
- "flow",
2545
- "gate",
2546
- "signal",
2547
- "state",
2548
- "integration"
2549
- ];
2550
- for (const type of symbolTypes) {
2551
- const patternValue = patternSymbols[type];
2552
- const incidentValue = incidentSymbols[type];
2553
- if (!patternValue || !incidentValue) {
2554
- continue;
2555
- }
2556
- if (typeof patternValue === "string") {
2557
- if (this.matchSingleSymbol(patternValue, incidentValue)) {
2558
- score += patternValue.includes("*") ? 5 : 10;
2559
- matched.push(type);
2560
- }
2561
- } else if (Array.isArray(patternValue)) {
2562
- for (const pv of patternValue) {
2563
- if (this.matchSingleSymbol(pv, incidentValue)) {
2564
- score += 7;
2565
- matched.push(type);
2566
- break;
2567
- }
2568
- }
2569
- }
2570
- }
2571
- return score;
2572
- }
2573
- /**
2574
- * Match a single symbol value (supports wildcards)
2575
- */
2576
- matchSingleSymbol(pattern, value) {
2577
- if (pattern === "*") {
2578
- return true;
2579
- }
2580
- if (pattern.endsWith("*")) {
2581
- const prefix = pattern.slice(0, -1);
2582
- return value.startsWith(prefix);
2583
- }
2584
- if (pattern.startsWith("*")) {
2585
- const suffix = pattern.slice(1);
2586
- return value.endsWith(suffix);
2587
- }
2588
- if (pattern.includes("*")) {
2589
- const regex = new RegExp(
2590
- "^" + pattern.replace(/\*/g, ".*") + "$"
2591
- );
2592
- return regex.test(value);
2593
- }
2594
- return pattern === value;
2595
- }
2596
- /**
2597
- * Match error text keywords and regex
2598
- */
2599
- matchErrorText(pattern, incident, matched) {
2600
- let score = 0;
2601
- const errorMessage = incident.error.message.toLowerCase();
2602
- const errorType = incident.error.type?.toLowerCase();
2603
- if (pattern.pattern.errorContains) {
2604
- for (const keyword of pattern.pattern.errorContains) {
2605
- if (errorMessage.includes(keyword.toLowerCase())) {
2606
- score += 5;
2607
- matched.push(keyword);
2608
- }
2609
- }
2610
- }
2611
- if (pattern.pattern.errorMatches) {
2612
- try {
2613
- const regex = new RegExp(pattern.pattern.errorMatches, "i");
2614
- if (regex.test(incident.error.message)) {
2615
- score += 10;
2616
- matched.push(`regex:${pattern.pattern.errorMatches}`);
2617
- }
2618
- } catch {
2619
- }
2620
- }
2621
- if (pattern.pattern.errorType && errorType) {
2622
- for (const type of pattern.pattern.errorType) {
2623
- if (errorType.includes(type.toLowerCase())) {
2624
- score += 5;
2625
- matched.push(`type:${type}`);
2626
- }
2627
- }
2628
- }
2629
- return score;
2630
- }
2631
- /**
2632
- * Match missing signals from flow position
2633
- */
2634
- matchMissingSignals(pattern, incident, matched) {
2635
- if (!pattern.pattern.missingSignals || !incident.flowPosition?.missing) {
2636
- return 0;
2637
- }
2638
- let score = 0;
2639
- for (const expectedSignal of pattern.pattern.missingSignals) {
2640
- for (const missingSignal of incident.flowPosition.missing) {
2641
- if (this.matchSingleSymbol(expectedSignal, missingSignal)) {
2642
- score += 12;
2643
- matched.push(missingSignal);
2644
- break;
2645
- }
2646
- }
2647
- }
2648
- return score;
2649
- }
2650
- /**
2651
- * Check if pattern's environment filter matches incident
2652
- */
2653
- matchEnvironment(pattern, incident) {
2654
- if (!pattern.pattern.environment || pattern.pattern.environment.length === 0) {
2655
- return true;
2656
- }
2657
- return pattern.pattern.environment.includes(incident.environment);
2658
- }
2659
- };
2660
-
2661
- // ../sentinel/src/timeline.ts
2662
- var TimelineBuilder = class {
2663
- /**
2664
- * Build a timeline from an incident with flow position
2665
- */
2666
- build(incident) {
2667
- if (!incident.flowPosition) {
2668
- return null;
2669
- }
2670
- const events = [];
2671
- const baseTime = new Date(incident.timestamp).getTime();
2672
- events.push({
2673
- timestamp: new Date(baseTime - 5e3).toISOString(),
2674
- symbol: incident.flowPosition.flowId,
2675
- type: "flow-started"
2676
- });
2677
- let eventOffset = 1e3;
2678
- for (const signal of incident.flowPosition.actual) {
2679
- const type = this.inferEventType(signal);
2680
- events.push({
2681
- timestamp: new Date(baseTime - 4e3 + eventOffset).toISOString(),
2682
- symbol: signal,
2683
- type
2684
- });
2685
- eventOffset += Math.random() * 1e3 + 500;
2686
- }
2687
- const failedSymbol = incident.flowPosition.failedAt || incident.flowPosition.missing[0] || incident.symbols.gate || incident.symbols.signal || "unknown";
2688
- events.push({
2689
- timestamp: incident.timestamp,
2690
- symbol: failedSymbol,
2691
- type: "error",
2692
- data: {
2693
- message: incident.error.message,
2694
- missing: incident.flowPosition.missing
2695
- }
2696
- });
2697
- return {
2698
- incidentId: incident.id,
2699
- flowId: incident.flowPosition.flowId,
2700
- events,
2701
- failure: {
2702
- at: incident.timestamp,
2703
- symbol: failedSymbol,
2704
- reason: incident.error.message
2705
- }
2706
- };
2707
- }
2708
- /**
2709
- * Render timeline as ASCII art
2710
- */
2711
- renderAscii(timeline) {
2712
- const lines = [];
2713
- lines.push(`${timeline.flowId} Timeline`);
2714
- lines.push("\u2550".repeat(40));
2715
- lines.push("");
2716
- for (const event of timeline.events) {
2717
- const time = this.formatTime(event.timestamp);
2718
- const icon = this.getEventIcon(event.type);
2719
- const status = this.getEventStatus(event.type);
2720
- let line = `${time} ${icon} ${event.symbol}`;
2721
- if (status) {
2722
- line += ` (${status})`;
2723
- }
2724
- lines.push(line);
2725
- if (event.type === "error" && event.data) {
2726
- lines.push(` \u2514\u2500 ${event.data.message}`);
2727
- if (event.data.missing && Array.isArray(event.data.missing) && event.data.missing.length > 0) {
2728
- lines.push(
2729
- ` \u2514\u2500 Expected: ${event.data.missing.join(", ")}`
2730
- );
2731
- }
2732
- }
2733
- }
2734
- const missing = timeline.events.find((e) => e.type === "error")?.data?.missing;
2735
- if (missing && missing.length > 0) {
2736
- lines.push("");
2737
- lines.push(`Missing signals: ${missing.join(", ")}`);
2738
- }
2739
- return lines.join("\n");
2740
- }
2741
- /**
2742
- * Render timeline as structured data (for MCP/JSON output)
2743
- */
2744
- renderStructured(timeline) {
2745
- return {
2746
- incidentId: timeline.incidentId,
2747
- flow: {
2748
- id: timeline.flowId,
2749
- eventCount: timeline.events.length
2750
- },
2751
- events: timeline.events.map((event) => ({
2752
- time: this.formatTime(event.timestamp),
2753
- symbol: event.symbol,
2754
- type: event.type,
2755
- status: this.getEventStatus(event.type),
2756
- data: event.data
2757
- })),
2758
- failure: {
2759
- at: this.formatTime(timeline.failure.at),
2760
- symbol: timeline.failure.symbol,
2761
- reason: timeline.failure.reason
2762
- }
2763
- };
2764
- }
2765
- /**
2766
- * Infer event type from symbol prefix
2767
- */
2768
- inferEventType(symbol) {
2769
- if (symbol.startsWith("^")) {
2770
- return "gate-passed";
2771
- }
2772
- if (symbol.startsWith("!")) {
2773
- return "signal-emitted";
2774
- }
2775
- if (symbol.startsWith("%")) {
2776
- return "state-changed";
2777
- }
2778
- return "signal-emitted";
2779
- }
2780
- /**
2781
- * Get icon for event type
2782
- */
2783
- getEventIcon(type) {
2784
- switch (type) {
2785
- case "flow-started":
2786
- return "\u25B6";
2787
- case "flow-ended":
2788
- return "\u25A0";
2789
- case "gate-passed":
2790
- return "\u2713";
2791
- case "gate-failed":
2792
- return "\u2717";
2793
- case "signal-emitted":
2794
- return "\u26A1";
2795
- case "state-changed":
2796
- return "\u25C6";
2797
- case "error":
2798
- return "\u2717";
2799
- default:
2800
- return "\u2022";
2801
- }
2802
- }
2803
- /**
2804
- * Get status text for event type
2805
- */
2806
- getEventStatus(type) {
2807
- switch (type) {
2808
- case "gate-passed":
2809
- return "PASSED";
2810
- case "gate-failed":
2811
- return "FAILED";
2812
- case "signal-emitted":
2813
- return "EMITTED";
2814
- case "state-changed":
2815
- return "CHANGED";
2816
- case "error":
2817
- return "ERROR";
2818
- default:
2819
- return "";
2820
- }
2821
- }
2822
- /**
2823
- * Format timestamp for display
2824
- */
2825
- formatTime(timestamp) {
2826
- const date = new Date(timestamp);
2827
- const hours = String(date.getHours()).padStart(2, "0");
2828
- const minutes = String(date.getMinutes()).padStart(2, "0");
2829
- const seconds = String(date.getSeconds()).padStart(2, "0");
2830
- const millis = String(date.getMilliseconds()).padStart(3, "0");
2831
- return `${hours}:${minutes}:${seconds}.${millis}`;
2832
- }
2833
- };
2834
-
2835
- // ../sentinel/src/stats.ts
2836
- var StatsCalculator = class {
2837
- constructor(storage2) {
2838
- this.storage = storage2;
2839
- }
2840
- /**
2841
- * Get comprehensive statistics for a time period
2842
- */
2843
- getStats(periodDays = 7) {
2844
- const end = (/* @__PURE__ */ new Date()).toISOString();
2845
- const start = new Date(
2846
- Date.now() - periodDays * 24 * 60 * 60 * 1e3
2847
- ).toISOString();
2848
- return this.storage.getStats({ start, end });
2849
- }
2850
- /**
2851
- * Get health metrics for a specific symbol
2852
- */
2853
- getSymbolHealth(symbol) {
2854
- return this.storage.getSymbolHealth(symbol);
2855
- }
2856
- /**
2857
- * Get trending issues (symbols with increasing incident rates)
2858
- */
2859
- getTrendingIssues(days = 7) {
2860
- const now = Date.now();
2861
- const halfPeriod = days * 24 * 60 * 60 * 1e3 / 2;
2862
- const firstHalfStart = new Date(now - days * 24 * 60 * 60 * 1e3).toISOString();
2863
- const midpoint = new Date(now - halfPeriod).toISOString();
2864
- const secondHalfEnd = new Date(now).toISOString();
2865
- const firstHalfIncidents = this.storage.getRecentIncidents({
2866
- dateFrom: firstHalfStart,
2867
- dateTo: midpoint,
2868
- limit: 1e3
2869
- });
2870
- const secondHalfIncidents = this.storage.getRecentIncidents({
2871
- dateFrom: midpoint,
2872
- dateTo: secondHalfEnd,
2873
- limit: 1e3
2874
- });
2875
- const firstHalfCounts = this.countSymbols(firstHalfIncidents);
2876
- const secondHalfCounts = this.countSymbols(secondHalfIncidents);
2877
- const trends = [];
2878
- const allSymbols = /* @__PURE__ */ new Set([
2879
- ...firstHalfCounts.keys(),
2880
- ...secondHalfCounts.keys()
2881
- ]);
2882
- for (const symbol of allSymbols) {
2883
- const first = firstHalfCounts.get(symbol) || 0;
2884
- const second = secondHalfCounts.get(symbol) || 0;
2885
- if (first === 0 && second > 0) {
2886
- trends.push({ symbol, trend: second * 100 });
2887
- } else if (first > 0) {
2888
- const change = (second - first) / first * 100;
2889
- trends.push({ symbol, trend: change });
2890
- }
2891
- }
2892
- return trends.filter((t) => t.trend > 0).sort((a, b) => b.trend - a.trend).slice(0, 10);
2893
- }
2894
- /**
2895
- * Get resolution metrics
2896
- */
2897
- getResolutionMetrics() {
2898
- const stats = this.getStats(30);
2899
- return {
2900
- avgTimeToResolve: stats.resolution.avgTimeToResolve,
2901
- resolvedWithPattern: stats.resolution.resolvedWithPattern,
2902
- resolvedManually: stats.resolution.resolvedManually,
2903
- totalResolved: stats.incidents.resolved,
2904
- resolutionRate: stats.resolution.resolutionRate
2905
- };
2906
- }
2907
- /**
2908
- * Get pattern effectiveness metrics
2909
- */
2910
- getPatternEffectiveness() {
2911
- const patterns = this.storage.getAllPatterns({ includePrivate: true });
2912
- return patterns.filter((p) => p.confidence.timesMatched > 0).map((p) => ({
2913
- patternId: p.id,
2914
- name: p.name,
2915
- matches: p.confidence.timesMatched,
2916
- resolutions: p.confidence.timesResolved,
2917
- recurrences: p.confidence.timesRecurred,
2918
- effectiveness: p.confidence.timesMatched > 0 ? Math.round(
2919
- (p.confidence.timesResolved - p.confidence.timesRecurred) / p.confidence.timesMatched * 100
2920
- ) : 0
2921
- })).sort((a, b) => b.effectiveness - a.effectiveness);
2922
- }
2923
- /**
2924
- * Get incident rate by hour of day
2925
- */
2926
- getIncidentsByHour(days = 7) {
2927
- const start = new Date(
2928
- Date.now() - days * 24 * 60 * 60 * 1e3
2929
- ).toISOString();
2930
- const incidents = this.storage.getRecentIncidents({
2931
- dateFrom: start,
2932
- limit: 1e4
2933
- });
2934
- const hourCounts = /* @__PURE__ */ new Map();
2935
- for (let i = 0; i < 24; i++) {
2936
- hourCounts.set(i, 0);
2937
- }
2938
- for (const incident of incidents) {
2939
- const hour = new Date(incident.timestamp).getHours();
2940
- hourCounts.set(hour, (hourCounts.get(hour) || 0) + 1);
2941
- }
2942
- return Array.from(hourCounts.entries()).map(([hour, count]) => ({
2943
- hour,
2944
- count
2945
- }));
2946
- }
2947
- /**
2948
- * Get incident rate by environment
2949
- */
2950
- getIncidentsByEnvironment() {
2951
- const stats = this.getStats(30);
2952
- const total = stats.incidents.total;
2953
- return Object.entries(stats.incidents.byEnvironment).map(([environment, count]) => ({
2954
- environment,
2955
- count,
2956
- percentage: total > 0 ? Math.round(count / total * 100) : 0
2957
- })).sort((a, b) => b.count - a.count);
2958
- }
2959
- /**
2960
- * Get symbol correlation matrix (which symbols fail together)
2961
- */
2962
- getSymbolCorrelation() {
2963
- const incidents = this.storage.getRecentIncidents({ limit: 1e3 });
2964
- const correlations = /* @__PURE__ */ new Map();
2965
- const symbolCounts = /* @__PURE__ */ new Map();
2966
- for (const incident of incidents) {
2967
- const symbols = this.getSymbolsFromIncident(incident);
2968
- for (const symbol of symbols) {
2969
- symbolCounts.set(symbol, (symbolCounts.get(symbol) || 0) + 1);
2970
- }
2971
- for (let i = 0; i < symbols.length; i++) {
2972
- for (let j = i + 1; j < symbols.length; j++) {
2973
- const key = [symbols[i], symbols[j]].sort().join("|");
2974
- correlations.set(key, (correlations.get(key) || 0) + 1);
2975
- }
2976
- }
2977
- }
2978
- const results = [];
2979
- for (const [key, count] of correlations) {
2980
- const [symbol1, symbol2] = key.split("|");
2981
- const count1 = symbolCounts.get(symbol1) || 1;
2982
- const count2 = symbolCounts.get(symbol2) || 1;
2983
- const correlation = count / Math.max(count1, count2);
2984
- if (correlation > 0.3) {
2985
- results.push({
2986
- symbol1,
2987
- symbol2,
2988
- correlation: Math.round(correlation * 100) / 100
2989
- });
2990
- }
2991
- }
2992
- return results.sort((a, b) => b.correlation - a.correlation).slice(0, 20);
2993
- }
2994
- /**
2995
- * Generate a summary dashboard string
2996
- */
2997
- generateDashboard(periodDays = 7) {
2998
- const stats = this.getStats(periodDays);
2999
- const lines = [];
3000
- lines.push("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
3001
- lines.push("\u2551 PARADIGM SENTINEL DASHBOARD \u2551");
3002
- lines.push("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");
3003
- const todayCount = stats.incidents.byDay[stats.incidents.byDay.length - 1]?.count || 0;
3004
- lines.push(
3005
- `\u2551 Open: ${String(stats.incidents.open).padEnd(4)} \u2502 Investigating: ${String(stats.incidents.total - stats.incidents.open - stats.incidents.resolved).padEnd(3)} \u2502 Resolved: ${String(stats.incidents.resolved).padEnd(4)} \u2502 Today: +${todayCount} \u2551`
3006
- );
3007
- lines.push("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
3008
- lines.push("");
3009
- lines.push("Incidents by Day (last 7 days):");
3010
- lines.push("\u2500".repeat(50));
3011
- const maxDayCount = Math.max(...stats.incidents.byDay.map((d) => d.count), 1);
3012
- for (const day of stats.incidents.byDay.slice(-7)) {
3013
- const barLength = Math.round(day.count / maxDayCount * 30);
3014
- const bar = "\u2588".repeat(barLength);
3015
- lines.push(`${day.date.substring(5)} ${bar} ${day.count}`);
3016
- }
3017
- lines.push("");
3018
- lines.push("Most Affected Symbols:");
3019
- lines.push("\u2500".repeat(50));
3020
- for (const { symbol, count } of stats.symbols.mostIncidents.slice(0, 5)) {
3021
- lines.push(` ${symbol.padEnd(25)} ${count} incidents`);
3022
- }
3023
- lines.push("");
3024
- lines.push("Top Patterns:");
3025
- lines.push("\u2500".repeat(50));
3026
- for (const { patternId, resolvedCount } of stats.patterns.mostEffective.slice(0, 5)) {
3027
- lines.push(` ${patternId.padEnd(25)} ${resolvedCount} resolved`);
3028
- }
3029
- lines.push("");
3030
- lines.push("Resolution Stats:");
3031
- lines.push("\u2500".repeat(50));
3032
- lines.push(` Resolution rate: ${Math.round(stats.resolution.resolutionRate)}%`);
3033
- lines.push(` With pattern: ${stats.resolution.resolvedWithPattern}`);
3034
- lines.push(` Manual: ${stats.resolution.resolvedManually}`);
3035
- return lines.join("\n");
3036
- }
3037
- /**
3038
- * Helper: Count symbols across incidents
3039
- */
3040
- countSymbols(incidents) {
3041
- const counts = /* @__PURE__ */ new Map();
3042
- for (const incident of incidents) {
3043
- for (const [, value] of Object.entries(incident.symbols)) {
3044
- if (value) {
3045
- counts.set(value, (counts.get(value) || 0) + 1);
3046
- }
3047
- }
3048
- }
3049
- return counts;
3050
- }
3051
- /**
3052
- * Helper: Get all symbols from incident
3053
- */
3054
- getSymbolsFromIncident(incident) {
3055
- const symbols = [];
3056
- for (const [, value] of Object.entries(incident.symbols)) {
3057
- if (value) {
3058
- symbols.push(value);
3059
- }
3060
- }
3061
- return symbols;
3062
- }
3063
- };
3064
-
3065
- // ../sentinel/src/suggester.ts
3066
- var PatternSuggester = class {
3067
- constructor(storage2) {
3068
- this.storage = storage2;
3069
- }
3070
- /**
3071
- * Suggest a pattern from a resolved incident
3072
- */
3073
- suggestFromIncident(incident) {
3074
- const baseId = this.generatePatternId(incident);
3075
- const symbols = this.buildSymbolCriteria(incident.symbols);
3076
- const errorKeywords = this.extractErrorKeywords(incident.error.message);
3077
- const pattern = {
3078
- id: baseId,
3079
- name: this.generatePatternName(incident),
3080
- description: `Auto-suggested pattern from incident ${incident.id}`,
3081
- pattern: {
3082
- symbols,
3083
- errorContains: errorKeywords.length > 0 ? errorKeywords : void 0,
3084
- missingSignals: incident.flowPosition?.missing
3085
- },
3086
- resolution: {
3087
- description: incident.resolution?.notes || "Resolution approach TBD",
3088
- strategy: this.inferStrategy([incident]),
3089
- priority: "medium"
3090
- },
3091
- source: "suggested",
3092
- private: false,
3093
- tags: this.generateTags(incident)
3094
- };
3095
- return pattern;
3096
- }
3097
- /**
3098
- * Suggest a pattern from an incident group
3099
- */
3100
- suggestFromGroup(group) {
3101
- const baseId = `group-${group.id.toLowerCase().replace(/[^a-z0-9]/g, "-")}`;
3102
- const symbols = this.buildSymbolCriteria(group.commonSymbols);
3103
- const groupIncidents = group.incidents.slice(0, 20).map((id) => this.storage.getIncident(id)).filter((i) => i != null);
3104
- const pattern = {
3105
- id: baseId,
3106
- name: group.name || `Pattern from group ${group.id}`,
3107
- description: `Auto-suggested pattern from incident group with ${group.count} incidents`,
3108
- pattern: {
3109
- symbols,
3110
- errorContains: group.commonErrorPatterns.length > 0 ? group.commonErrorPatterns : void 0
3111
- },
3112
- resolution: {
3113
- description: "Resolution approach TBD based on grouped incidents",
3114
- strategy: groupIncidents.length > 0 ? this.inferStrategy(groupIncidents) : "fix-code",
3115
- priority: this.getPriorityFromCount(group.count)
3116
- },
3117
- source: "suggested",
3118
- private: false,
3119
- tags: this.generateTagsFromGroup(group)
3120
- };
3121
- return pattern;
3122
- }
3123
- /**
3124
- * Find incidents that could become patterns
3125
- */
3126
- findPatternCandidates(minOccurrences = 3) {
3127
- const incidents = this.storage.getRecentIncidents({
3128
- limit: 1e3,
3129
- status: "resolved"
3130
- });
3131
- const signatureGroups = /* @__PURE__ */ new Map();
3132
- for (const incident of incidents) {
3133
- const signature = this.getSymbolSignature(incident.symbols);
3134
- const existing = signatureGroups.get(signature) || [];
3135
- existing.push(incident);
3136
- signatureGroups.set(signature, existing);
3137
- }
3138
- const candidates = [];
3139
- for (const [, groupIncidents] of signatureGroups) {
3140
- if (groupIncidents.length >= minOccurrences) {
3141
- const hasPattern = this.hasMatchingPattern(groupIncidents[0]);
3142
- if (hasPattern) continue;
3143
- const suggestedPattern = this.suggestFromIncidents(groupIncidents);
3144
- candidates.push({
3145
- incidents: groupIncidents,
3146
- suggestedPattern,
3147
- occurrenceCount: groupIncidents.length
3148
- });
3149
- }
3150
- }
3151
- return candidates.sort((a, b) => b.occurrenceCount - a.occurrenceCount);
3152
- }
3153
- /**
3154
- * Generate pattern from multiple similar incidents
3155
- */
3156
- suggestFromIncidents(incidents) {
3157
- const commonSymbols = this.extractCommonSymbols(incidents);
3158
- const symbols = this.buildSymbolCriteria(commonSymbols);
3159
- const errorKeywords = this.extractCommonErrorKeywords(incidents);
3160
- const missingSignals = this.extractCommonMissingSignals(incidents);
3161
- const baseId = this.generatePatternId(incidents[0]);
3162
- return {
3163
- id: baseId,
3164
- name: this.generatePatternName(incidents[0]),
3165
- description: `Auto-suggested pattern from ${incidents.length} similar incidents`,
3166
- pattern: {
3167
- symbols,
3168
- errorContains: errorKeywords.length > 0 ? errorKeywords : void 0,
3169
- missingSignals: missingSignals.length > 0 ? missingSignals : void 0
3170
- },
3171
- resolution: {
3172
- description: "Resolution approach based on previous resolutions",
3173
- strategy: this.inferStrategy(incidents),
3174
- priority: this.getPriorityFromCount(incidents.length)
3175
- },
3176
- source: "suggested",
3177
- private: false,
3178
- tags: this.generateTagsFromIncidents(incidents)
3179
- };
3180
- }
3181
- /**
3182
- * Build symbol criteria for pattern, adding wildcards where appropriate
3183
- */
3184
- buildSymbolCriteria(symbols) {
3185
- const criteria = {};
3186
- for (const [key, value] of Object.entries(symbols)) {
3187
- if (value) {
3188
- criteria[key] = value;
3189
- }
3190
- }
3191
- return criteria;
3192
- }
3193
- /**
3194
- * Extract keywords from error message
3195
- */
3196
- extractErrorKeywords(message) {
3197
- const stopWords = /* @__PURE__ */ new Set([
3198
- "the",
3199
- "a",
3200
- "an",
3201
- "is",
3202
- "are",
3203
- "was",
3204
- "were",
3205
- "in",
3206
- "on",
3207
- "at",
3208
- "to",
3209
- "for",
3210
- "of",
3211
- "with",
3212
- "and",
3213
- "or",
3214
- "but",
3215
- "not",
3216
- "no",
3217
- "be",
3218
- "been",
3219
- "have",
3220
- "has",
3221
- "had",
3222
- "do",
3223
- "does",
3224
- "did"
3225
- ]);
3226
- const words = message.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
3227
- const unique = [...new Set(words)];
3228
- return unique.slice(0, 5);
3229
- }
3230
- /**
3231
- * Extract common error keywords from multiple incidents
3232
- */
3233
- extractCommonErrorKeywords(incidents) {
3234
- const wordCounts = /* @__PURE__ */ new Map();
3235
- for (const incident of incidents) {
3236
- const keywords = this.extractErrorKeywords(incident.error.message);
3237
- for (const keyword of keywords) {
3238
- wordCounts.set(keyword, (wordCounts.get(keyword) || 0) + 1);
3239
- }
3240
- }
3241
- const threshold = Math.ceil(incidents.length * 0.5);
3242
- return Array.from(wordCounts.entries()).filter(([, count]) => count >= threshold).map(([word]) => word).slice(0, 5);
3243
- }
3244
- /**
3245
- * Extract symbols common to all incidents
3246
- */
3247
- extractCommonSymbols(incidents) {
3248
- if (incidents.length === 0) return {};
3249
- const first = incidents[0].symbols;
3250
- const common = {};
3251
- for (const [key, value] of Object.entries(first)) {
3252
- if (!value) continue;
3253
- const allMatch = incidents.every(
3254
- (i) => i.symbols[key] === value
3255
- );
3256
- if (allMatch) {
3257
- common[key] = value;
3258
- }
3259
- }
3260
- return common;
3261
- }
3262
- /**
3263
- * Extract missing signals common to multiple incidents
3264
- */
3265
- extractCommonMissingSignals(incidents) {
3266
- const signalCounts = /* @__PURE__ */ new Map();
3267
- for (const incident of incidents) {
3268
- if (!incident.flowPosition?.missing) continue;
3269
- for (const signal of incident.flowPosition.missing) {
3270
- signalCounts.set(signal, (signalCounts.get(signal) || 0) + 1);
3271
- }
3272
- }
3273
- const threshold = Math.ceil(incidents.length * 0.5);
3274
- return Array.from(signalCounts.entries()).filter(([, count]) => count >= threshold).map(([signal]) => signal);
3275
- }
3276
- /**
3277
- * Generate a pattern ID from incident
3278
- */
3279
- generatePatternId(incident) {
3280
- const parts = [];
3281
- if (incident.symbols.gate) {
3282
- parts.push(incident.symbols.gate.replace(/[^a-z0-9]/gi, ""));
3283
- } else if (incident.symbols.feature) {
3284
- parts.push(incident.symbols.feature.replace(/[^a-z0-9]/gi, ""));
3285
- } else if (incident.symbols.component) {
3286
- parts.push(incident.symbols.component.replace(/[^a-z0-9]/gi, ""));
3287
- } else if (incident.symbols.integration) {
3288
- parts.push(incident.symbols.integration.replace(/[^a-z0-9]/gi, ""));
3289
- } else {
3290
- parts.push("unknown");
3291
- }
3292
- const errorType = incident.error.type?.toLowerCase() || "error";
3293
- parts.push(errorType.replace(/[^a-z0-9]/gi, ""));
3294
- parts.push(String(Date.now() % 1e3).padStart(3, "0"));
3295
- return parts.join("-");
3296
- }
3297
- /**
3298
- * Generate a human-readable pattern name
3299
- */
3300
- generatePatternName(incident) {
3301
- const parts = [];
3302
- if (incident.symbols.feature) {
3303
- parts.push(
3304
- incident.symbols.feature.replace("@", "").replace(/-/g, " ")
3305
- );
3306
- }
3307
- if (incident.symbols.gate) {
3308
- parts.push("gate " + incident.symbols.gate.replace("^", ""));
3309
- }
3310
- if (incident.error.type) {
3311
- parts.push(incident.error.type);
3312
- }
3313
- if (parts.length === 0) {
3314
- return "Unnamed Pattern";
3315
- }
3316
- const name = parts.join(" - ");
3317
- return name.charAt(0).toUpperCase() + name.slice(1);
3318
- }
3319
- /**
3320
- * Generate tags from incident
3321
- */
3322
- generateTags(incident) {
3323
- const tags = [];
3324
- if (incident.symbols.feature) {
3325
- tags.push("feature");
3326
- }
3327
- if (incident.symbols.gate) {
3328
- tags.push("gate");
3329
- }
3330
- if (incident.symbols.integration) {
3331
- tags.push("integration");
3332
- tags.push(incident.symbols.integration.replace("&", ""));
3333
- }
3334
- if (incident.error.type) {
3335
- tags.push(incident.error.type.toLowerCase());
3336
- }
3337
- tags.push(incident.environment);
3338
- return [...new Set(tags)].slice(0, 5);
3339
- }
3340
- /**
3341
- * Generate tags from incident group
3342
- */
3343
- generateTagsFromGroup(group) {
3344
- const tags = ["grouped"];
3345
- if (group.commonSymbols.feature) {
3346
- tags.push("feature");
3347
- }
3348
- if (group.commonSymbols.gate) {
3349
- tags.push("gate");
3350
- }
3351
- if (group.commonSymbols.integration) {
3352
- tags.push("integration");
3353
- }
3354
- for (const env of group.environments) {
3355
- tags.push(env);
3356
- }
3357
- return [...new Set(tags)].slice(0, 5);
3358
- }
3359
- /**
3360
- * Generate tags from multiple incidents
3361
- */
3362
- generateTagsFromIncidents(incidents) {
3363
- const tagCounts = /* @__PURE__ */ new Map();
3364
- for (const incident of incidents) {
3365
- const tags = this.generateTags(incident);
3366
- for (const tag of tags) {
3367
- tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
3368
- }
3369
- }
3370
- return Array.from(tagCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([tag]) => tag);
3371
- }
3372
- /**
3373
- * Get symbol signature for grouping
3374
- */
3375
- getSymbolSignature(symbols) {
3376
- const parts = [];
3377
- if (symbols.feature) parts.push(`f:${symbols.feature}`);
3378
- if (symbols.component) parts.push(`c:${symbols.component}`);
3379
- if (symbols.flow) parts.push(`fl:${symbols.flow}`);
3380
- if (symbols.gate) parts.push(`g:${symbols.gate}`);
3381
- if (symbols.integration) parts.push(`i:${symbols.integration}`);
3382
- return parts.sort().join("|");
3383
- }
3384
- /**
3385
- * Check if there's already a pattern matching this incident
3386
- */
3387
- hasMatchingPattern(incident) {
3388
- const patterns = this.storage.getAllPatterns({ includePrivate: true });
3389
- for (const pattern of patterns) {
3390
- let matchCount = 0;
3391
- const symbolTypes = [
3392
- "feature",
3393
- "component",
3394
- "flow",
3395
- "gate",
3396
- "signal",
3397
- "integration"
3398
- ];
3399
- for (const type of symbolTypes) {
3400
- const patternValue = pattern.pattern.symbols[type];
3401
- const incidentValue = incident.symbols[type];
3402
- if (patternValue && incidentValue && patternValue === incidentValue) {
3403
- matchCount++;
3404
- }
3405
- }
3406
- if (matchCount >= 2) {
3407
- return true;
3408
- }
3409
- }
3410
- return false;
3411
- }
3412
- /**
3413
- * Infer resolution strategy from incident error patterns and context.
3414
- * Uses keyword heuristics across all incident messages to pick the
3415
- * most likely resolution approach.
3416
- */
3417
- inferStrategy(incidents) {
3418
- const messages = incidents.map((i) => i.error.message.toLowerCase());
3419
- const hasKeyword = (keywords) => messages.some((m) => keywords.some((k) => m.includes(k)));
3420
- if (hasKeyword(["revert", "rollback", "regression", "broke after deploy", "since deploy"])) {
3421
- return "rollback";
3422
- }
3423
- if (hasKeyword(["config", "environment variable", "env var", "missing key", "secret", "credential"])) {
3424
- return "config-change";
3425
- }
3426
- if (hasKeyword(["out of memory", "oom", "heap", "memory limit", "capacity", "too many connections", "pool exhausted"])) {
3427
- return "scale-up";
3428
- }
3429
- if (hasKeyword(["timeout", "network", "econnrefused", "econnreset", "dns", "socket hang up"])) {
3430
- return "retry";
3431
- }
3432
- if (hasKeyword(["unavailable", "service down", "circuit breaker", "fallback", "503", "502"])) {
3433
- return "fallback";
3434
- }
3435
- if (hasKeyword(["validation", "invalid", "required", "constraint", "duplicate", "not found", "404"])) {
3436
- return "fix-data";
3437
- }
3438
- if (hasKeyword(["permission", "forbidden", "403", "401", "unauthorized", "access denied"])) {
3439
- return "escalate";
3440
- }
3441
- const uniqueTypes = new Set(incidents.map((i) => i.error.type).filter(Boolean));
3442
- if (uniqueTypes.size > 2) {
3443
- return "investigate";
3444
- }
3445
- return "fix-code";
3446
- }
3447
- /**
3448
- * Get priority based on occurrence count
3449
- */
3450
- getPriorityFromCount(count) {
3451
- if (count >= 20) return "critical";
3452
- if (count >= 10) return "high";
3453
- if (count >= 5) return "medium";
3454
- return "low";
3455
- }
3456
- };
3457
-
3458
- // ../sentinel/src/seeds/loader.ts
3459
- import * as path2 from "path";
3460
- import * as fs2 from "fs";
3461
- import { fileURLToPath } from "url";
3462
- var __filename = fileURLToPath(import.meta.url);
3463
- var __dirname = path2.dirname(__filename);
3464
- function loadUniversalPatterns() {
3465
- const filePath = path2.join(__dirname, "universal-patterns.json");
3466
- const content = fs2.readFileSync(filePath, "utf-8");
3467
- return JSON.parse(content);
3468
- }
3469
- function loadParadigmPatterns() {
3470
- const filePath = path2.join(__dirname, "paradigm-patterns.json");
3471
- const content = fs2.readFileSync(filePath, "utf-8");
3472
- return JSON.parse(content);
3473
- }
3474
- function loadAllSeedPatterns() {
3475
- const universal = loadUniversalPatterns();
3476
- const paradigm = loadParadigmPatterns();
3477
- return {
3478
- version: "1.0.0",
3479
- exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
3480
- patterns: [...universal.patterns, ...paradigm.patterns]
3481
- };
3482
- }
3483
-
3484
- // ../sentinel/src/mcp.ts
3485
- var storage = null;
3486
- var initialized = false;
3487
- function getStorage() {
3488
- if (!storage) {
3489
- storage = new SentinelStorage();
3490
- }
3491
- if (!initialized) {
3492
- try {
3493
- const { patterns } = loadAllSeedPatterns();
3494
- for (const pattern of patterns) {
3495
- try {
3496
- storage.addPattern(pattern);
3497
- } catch {
3498
- }
3499
- }
3500
- } catch {
3501
- }
3502
- initialized = true;
3503
- }
3504
- return storage;
3505
- }
3506
- function getToolsList() {
3507
- return [
3508
- {
3509
- name: "sentinel_triage",
3510
- description: "View and filter incidents with pattern matches. Returns recent errors with symbolic context and resolution suggestions.",
3511
- annotations: {
3512
- readOnlyHint: true,
3513
- destructiveHint: false
3514
- },
3515
- inputSchema: {
3516
- type: "object",
3517
- properties: {
3518
- symbol: { type: "string", description: "Filter by symbol (e.g., #checkout, ^auth)" },
3519
- status: {
3520
- type: "string",
3521
- enum: ["open", "investigating", "resolved", "wont-fix", "all"],
3522
- description: "Filter by status (default: all)"
3523
- },
3524
- environment: { type: "string", description: "Filter by environment" },
3525
- search: { type: "string", description: "Search in error messages" },
3526
- limit: { type: "number", description: "Max results (default: 10)" }
3527
- }
3528
- }
3529
- },
3530
- {
3531
- name: "sentinel_show",
3532
- description: "Get full details of a specific incident including timeline and matched patterns.",
3533
- annotations: {
3534
- readOnlyHint: true,
3535
- destructiveHint: false
3536
- },
3537
- inputSchema: {
3538
- type: "object",
3539
- properties: {
3540
- incidentId: { type: "string", description: "Incident ID (e.g., INC-001)" },
3541
- includeTimeline: { type: "boolean", description: "Include flow timeline" },
3542
- includeSimilar: { type: "boolean", description: "Include similar incidents" }
3543
- },
3544
- required: ["incidentId"]
3545
- }
3546
- },
3547
- {
3548
- name: "sentinel_resolve",
3549
- description: "Mark an incident as resolved with optional pattern and commit reference.",
3550
- annotations: {
3551
- readOnlyHint: false,
3552
- destructiveHint: false
3553
- },
3554
- inputSchema: {
3555
- type: "object",
3556
- properties: {
3557
- incidentId: { type: "string", description: "Incident ID" },
3558
- patternId: { type: "string", description: "Pattern that led to resolution" },
3559
- commitHash: { type: "string", description: "Fix commit hash" },
3560
- prUrl: { type: "string", description: "PR URL" },
3561
- notes: { type: "string", description: "Resolution notes" },
3562
- wontFix: { type: "boolean", description: "Mark as wont-fix instead of resolved" }
3563
- },
3564
- required: ["incidentId"]
3565
- }
3566
- },
3567
- {
3568
- name: "sentinel_patterns",
3569
- description: "List and filter failure patterns with confidence scores.",
3570
- annotations: {
3571
- readOnlyHint: true,
3572
- destructiveHint: false
3573
- },
3574
- inputSchema: {
3575
- type: "object",
3576
- properties: {
3577
- symbol: { type: "string", description: "Filter patterns for this symbol" },
3578
- minConfidence: { type: "number", description: "Minimum confidence score" },
3579
- source: {
3580
- type: "string",
3581
- enum: ["manual", "suggested", "imported", "community"],
3582
- description: "Filter by source"
3583
- }
3584
- }
3585
- }
3586
- },
3587
- {
3588
- name: "sentinel_add_pattern",
3589
- description: "Create a new failure pattern.",
3590
- annotations: {
3591
- readOnlyHint: false,
3592
- destructiveHint: false
3593
- },
3594
- inputSchema: {
3595
- type: "object",
3596
- properties: {
3597
- id: { type: "string", description: "Pattern ID (kebab-case)" },
3598
- name: { type: "string", description: "Human-readable name" },
3599
- description: { type: "string", description: "What this pattern matches" },
3600
- pattern: {
3601
- type: "object",
3602
- properties: {
3603
- symbols: { type: "object", description: "Symbol criteria" },
3604
- errorContains: { type: "array", items: { type: "string" }, description: "Error keywords" },
3605
- missingSignals: { type: "array", items: { type: "string" }, description: "Expected missing signals" }
3606
- }
3607
- },
3608
- resolution: {
3609
- type: "object",
3610
- properties: {
3611
- description: { type: "string", description: "Resolution steps" },
3612
- strategy: {
3613
- type: "string",
3614
- enum: ["retry", "fallback", "fix-data", "fix-code", "ignore", "escalate"]
3615
- },
3616
- priority: { type: "string", enum: ["low", "medium", "high", "critical"] },
3617
- codeHint: { type: "string", description: "Code fix hint" }
3618
- },
3619
- required: ["description", "strategy"]
3620
- },
3621
- tags: { type: "array", items: { type: "string" }, description: "Tags for categorization" }
3622
- },
3623
- required: ["id", "name", "pattern", "resolution"]
3624
- }
3625
- },
3626
- {
3627
- name: "sentinel_record",
3628
- description: "Manually record a new incident.",
3629
- annotations: {
3630
- readOnlyHint: false,
3631
- destructiveHint: false
3632
- },
3633
- inputSchema: {
3634
- type: "object",
3635
- properties: {
3636
- error: {
3637
- type: "object",
3638
- properties: {
3639
- message: { type: "string", description: "Error message" },
3640
- stack: { type: "string", description: "Stack trace" },
3641
- code: { type: "string", description: "Error code" },
3642
- type: { type: "string", description: "Error type" }
3643
- },
3644
- required: ["message"]
3645
- },
3646
- symbols: {
3647
- type: "object",
3648
- properties: {
3649
- component: { type: "string" },
3650
- flow: { type: "string" },
3651
- gate: { type: "string" },
3652
- signal: { type: "string" }
3653
- }
3654
- },
3655
- environment: { type: "string", description: "Environment (required)" },
3656
- service: { type: "string", description: "Service name" },
3657
- version: { type: "string", description: "App version" },
3658
- flowPosition: {
3659
- type: "object",
3660
- properties: {
3661
- flowId: { type: "string" },
3662
- expected: { type: "array", items: { type: "string" } },
3663
- actual: { type: "array", items: { type: "string" } },
3664
- missing: { type: "array", items: { type: "string" } },
3665
- failedAt: { type: "string" }
3666
- }
3667
- }
3668
- },
3669
- required: ["error", "symbols", "environment"]
3670
- }
3671
- },
3672
- {
3673
- name: "sentinel_stats",
3674
- description: "Get statistics and health metrics.",
3675
- annotations: {
3676
- readOnlyHint: true,
3677
- destructiveHint: false
3678
- },
3679
- inputSchema: {
3680
- type: "object",
3681
- properties: {
3682
- period: { type: "string", description: "Period: 1d, 7d, 30d, 90d (default: 7d)" },
3683
- symbol: { type: "string", description: "Get health for specific symbol" }
3684
- }
3685
- }
3686
- },
3687
- {
3688
- name: "sentinel_suggest_pattern",
3689
- description: "Get AI suggestions for patterns based on incidents.",
3690
- annotations: {
3691
- readOnlyHint: true,
3692
- destructiveHint: false
3693
- },
3694
- inputSchema: {
3695
- type: "object",
3696
- properties: {
3697
- incidentId: { type: "string", description: "Suggest from specific incident" },
3698
- minOccurrences: { type: "number", description: "Min similar incidents for suggestion" }
3699
- }
3700
- }
3701
- },
3702
- // ─── Observability Tools ──────────────────────────────────────
3703
- {
3704
- name: "sentinel_logs",
3705
- description: "Query structured logs from connected apps. Filters by level, symbol, service, search text, time range.",
3706
- annotations: { readOnlyHint: true, destructiveHint: false },
3707
- inputSchema: {
3708
- type: "object",
3709
- properties: {
3710
- level: { type: "string", enum: ["debug", "info", "warn", "error"], description: "Filter by log level" },
3711
- symbol: { type: "string", description: "Filter by symbol (partial match)" },
3712
- service: { type: "string", description: "Filter by service name" },
3713
- search: { type: "string", description: "Search in log messages" },
3714
- since: { type: "string", description: "ISO timestamp \u2014 logs after this time" },
3715
- sessionId: { type: "string", description: "Filter by session ID" },
3716
- correlationId: { type: "string", description: "Filter by correlation ID" },
3717
- limit: { type: "number", description: "Max results (default: 50)" }
3718
- }
3719
- }
3720
- },
3721
- {
3722
- name: "sentinel_services",
3723
- description: "List all registered services with version, environment, and last-seen time.",
3724
- annotations: { readOnlyHint: true, destructiveHint: false },
3725
- inputSchema: {
3726
- type: "object",
3727
- properties: {}
3728
- }
3729
- },
3730
- {
3731
- name: "sentinel_app_state",
3732
- description: "Get live app state snapshots. Shows current state, active flows, and held gates for connected services.",
3733
- annotations: { readOnlyHint: true, destructiveHint: false },
3734
- inputSchema: {
3735
- type: "object",
3736
- properties: {
3737
- service: { type: "string", description: "Filter by service name" }
3738
- }
3739
- }
3740
- },
3741
- {
3742
- name: "sentinel_validate_symbol",
3743
- description: "Check if a symbol has been used in logs. Returns usage count and suggestions.",
3744
- annotations: { readOnlyHint: true, destructiveHint: false },
3745
- inputSchema: {
3746
- type: "object",
3747
- properties: {
3748
- symbol: { type: "string", description: "Symbol to validate (e.g., #checkout, ^auth)" }
3749
- },
3750
- required: ["symbol"]
3751
- }
3752
- },
3753
- {
3754
- name: "sentinel_flow_activity",
3755
- description: "Get recent flow events \u2014 which flow nodes were hit, in what order, by which service.",
3756
- annotations: { readOnlyHint: true, destructiveHint: false },
3757
- inputSchema: {
3758
- type: "object",
3759
- properties: {
3760
- flowId: { type: "string", description: "Filter by flow ID (e.g., $checkout-flow)" },
3761
- service: { type: "string", description: "Filter by service name" },
3762
- since: { type: "string", description: "ISO timestamp \u2014 events after this time" }
3763
- }
3764
- }
3765
- },
3766
- {
3767
- name: "sentinel_metrics",
3768
- description: "Query metrics (counters, gauges, histograms) from connected apps. Supports filtering and aggregation.",
3769
- annotations: { readOnlyHint: true, destructiveHint: false },
3770
- inputSchema: {
3771
- type: "object",
3772
- properties: {
3773
- name: { type: "string", description: "Metric name filter" },
3774
- type: { type: "string", enum: ["counter", "gauge", "histogram"], description: "Metric type filter" },
3775
- service: { type: "string", description: "Service name filter" },
3776
- since: { type: "string", description: "ISO timestamp \u2014 metrics after this time" },
3777
- aggregate: { type: "boolean", description: "If true and name is provided, return aggregation instead of raw data" },
3778
- limit: { type: "number", description: "Max results (default: 50)" }
3779
- }
3780
- }
3781
- },
3782
- {
3783
- name: "sentinel_traces",
3784
- description: "Query distributed traces across services. Shows span trees with timing, status, and service hops.",
3785
- annotations: { readOnlyHint: true, destructiveHint: false },
3786
- inputSchema: {
3787
- type: "object",
3788
- properties: {
3789
- traceId: { type: "string", description: "Get a specific trace by ID" },
3790
- service: { type: "string", description: "Filter by service name" },
3791
- symbol: { type: "string", description: "Filter by symbol" },
3792
- since: { type: "string", description: "ISO timestamp \u2014 traces after this time" },
3793
- limit: { type: "number", description: "Max traces (default: 10, max: 20)" }
3794
- }
3795
- }
3796
- }
3797
- ];
3798
- }
3799
- async function handleTool(name, args) {
3800
- const store = getStorage();
3801
- const matcher = new PatternMatcher(store);
3802
- switch (name) {
3803
- case "sentinel_triage": {
3804
- const { symbol, status = "all", environment, search, limit = 10 } = args;
3805
- const incidents = store.getRecentIncidents({
3806
- limit,
3807
- status,
3808
- symbol,
3809
- environment,
3810
- search
3811
- });
3812
- if (incidents.length === 0) {
3813
- return JSON.stringify({
3814
- count: 0,
3815
- incidents: [],
3816
- tip: "No incidents recorded yet. Use sentinel_record to create incidents or integrate the SDK."
3817
- }, null, 2);
3818
- }
3819
- const results = incidents.map((incident) => {
3820
- const matches = matcher.match(incident, { maxResults: 3 });
3821
- return {
3822
- id: incident.id,
3823
- timestamp: incident.timestamp,
3824
- status: incident.status,
3825
- error: incident.error.message,
3826
- symbols: incident.symbols,
3827
- environment: incident.environment,
3828
- matches: matches.map((m) => ({
3829
- patternId: m.pattern.id,
3830
- name: m.pattern.name,
3831
- confidence: m.confidence,
3832
- strategy: m.pattern.resolution.strategy,
3833
- description: m.pattern.resolution.description
3834
- }))
3835
- };
3836
- });
3837
- return JSON.stringify({ count: results.length, incidents: results }, null, 2);
3838
- }
3839
- case "sentinel_show": {
3840
- const { incidentId, includeTimeline, includeSimilar } = args;
3841
- const incident = store.getIncident(incidentId);
3842
- if (!incident) {
3843
- return JSON.stringify({ error: `Incident ${incidentId} not found` });
3844
- }
3845
- const matches = matcher.match(incident, { maxResults: 5 });
3846
- const result = {
3847
- incident,
3848
- matches: matches.map((m) => ({
3849
- patternId: m.pattern.id,
3850
- name: m.pattern.name,
3851
- confidence: m.confidence,
3852
- matchedCriteria: m.matchedCriteria,
3853
- resolution: m.pattern.resolution
3854
- }))
3855
- };
3856
- if (includeTimeline && incident.flowPosition) {
3857
- const timeline = new TimelineBuilder().build(incident);
3858
- if (timeline) {
3859
- result.timeline = new TimelineBuilder().renderStructured(timeline);
3860
- }
3861
- }
3862
- if (includeSimilar) {
3863
- const similar = store.getRecentIncidents({ symbol: Object.values(incident.symbols)[0], limit: 5 }).filter((i) => i.id !== incidentId);
3864
- result.similar = similar.map((i) => ({
3865
- id: i.id,
3866
- error: i.error.message,
3867
- status: i.status
3868
- }));
3869
- }
3870
- return JSON.stringify(result, null, 2);
3871
- }
3872
- case "sentinel_resolve": {
3873
- const { incidentId, patternId, commitHash, prUrl, notes, wontFix } = args;
3874
- const incident = store.getIncident(incidentId);
3875
- if (!incident) {
3876
- return JSON.stringify({ error: `Incident ${incidentId} not found` });
3877
- }
3878
- if (wontFix) {
3879
- store.updateIncident(incidentId, {
3880
- status: "wont-fix",
3881
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
3882
- resolvedBy: "manual",
3883
- resolution: { notes }
3884
- });
3885
- return JSON.stringify({
3886
- success: true,
3887
- message: `Incident ${incidentId} marked as won't fix`
3888
- });
3889
- }
3890
- store.recordResolution({ incidentId, patternId, commitHash, prUrl, notes });
3891
- return JSON.stringify({
3892
- success: true,
3893
- message: `Incident ${incidentId} resolved`,
3894
- patternId,
3895
- commitHash,
3896
- prUrl
3897
- });
3898
- }
3899
- case "sentinel_patterns": {
3900
- const { symbol, minConfidence, source } = args;
3901
- const patterns = store.getAllPatterns({
3902
- source,
3903
- minConfidence,
3904
- includePrivate: false
3905
- });
3906
- let filtered = patterns;
3907
- if (symbol) {
3908
- filtered = patterns.filter((p) => {
3909
- const symbols = p.pattern.symbols;
3910
- return Object.values(symbols).some((v) => {
3911
- if (!v) return false;
3912
- if (Array.isArray(v)) return v.includes(symbol) || v.some((s) => symbol.match(s.replace("*", ".*")));
3913
- return v === symbol || symbol.match(v.replace("*", ".*"));
3914
- });
3915
- });
3916
- }
3917
- return JSON.stringify(
3918
- {
3919
- count: filtered.length,
3920
- patterns: filtered.map((p) => ({
3921
- id: p.id,
3922
- name: p.name,
3923
- description: p.description,
3924
- confidence: p.confidence.score,
3925
- resolution: p.resolution,
3926
- tags: p.tags
3927
- }))
3928
- },
3929
- null,
3930
- 2
3931
- );
3932
- }
3933
- case "sentinel_add_pattern": {
3934
- const { id, name: name2, description, pattern, resolution, tags } = args;
3935
- const input = {
3936
- id,
3937
- name: name2,
3938
- description: description || "",
3939
- pattern: {
3940
- symbols: pattern.symbols || {},
3941
- errorContains: pattern.errorContains,
3942
- missingSignals: pattern.missingSignals
3943
- },
3944
- resolution: {
3945
- description: resolution.description,
3946
- strategy: resolution.strategy,
3947
- priority: resolution.priority || "medium",
3948
- codeHint: resolution.codeHint
3949
- },
3950
- source: "manual",
3951
- private: false,
3952
- tags: tags || []
3953
- };
3954
- store.addPattern(input);
3955
- return JSON.stringify({
3956
- success: true,
3957
- message: `Pattern ${id} created`,
3958
- pattern: input
3959
- });
3960
- }
3961
- case "sentinel_record": {
3962
- const { error, symbols, environment, service, version, flowPosition } = args;
3963
- const incidentId = store.recordIncident({
3964
- error,
3965
- symbols,
3966
- environment,
3967
- service,
3968
- version,
3969
- flowPosition
3970
- });
3971
- const incident = store.getIncident(incidentId);
3972
- const matches = incident ? matcher.match(incident, { maxResults: 3 }) : [];
3973
- return JSON.stringify(
3974
- {
3975
- success: true,
3976
- incidentId,
3977
- matches: matches.map((m) => ({
3978
- patternId: m.pattern.id,
3979
- confidence: m.confidence,
3980
- resolution: m.pattern.resolution.description
3981
- }))
3982
- },
3983
- null,
3984
- 2
3985
- );
3986
- }
3987
- case "sentinel_stats": {
3988
- const { period = "7d", symbol } = args;
3989
- const calculator = new StatsCalculator(store);
3990
- if (symbol) {
3991
- const health = calculator.getSymbolHealth(symbol);
3992
- return JSON.stringify({ symbol, health }, null, 2);
3993
- }
3994
- const match = period.match(/^(\d+)d$/);
3995
- const periodDays = match ? parseInt(match[1], 10) : 7;
3996
- const stats = calculator.getStats(periodDays);
3997
- return JSON.stringify({ period: `${periodDays}d`, stats }, null, 2);
3998
- }
3999
- case "sentinel_suggest_pattern": {
4000
- const { incidentId, minOccurrences } = args;
4001
- const suggester = new PatternSuggester(store);
4002
- if (incidentId) {
4003
- const incident = store.getIncident(incidentId);
4004
- if (!incident) {
4005
- return JSON.stringify({ error: `Incident ${incidentId} not found` });
4006
- }
4007
- const suggestion = suggester.suggestFromIncident(incident);
4008
- return JSON.stringify({ source: "incident", incidentId, suggestion }, null, 2);
4009
- }
4010
- const candidates = suggester.findPatternCandidates(minOccurrences || 3);
4011
- return JSON.stringify(
4012
- {
4013
- source: "analysis",
4014
- candidates: candidates.slice(0, 5).map((c) => ({
4015
- occurrences: c.occurrenceCount,
4016
- sampleIncidents: c.incidents.slice(0, 3).map((i) => i.id),
4017
- suggestion: c.suggestedPattern
4018
- }))
4019
- },
4020
- null,
4021
- 2
4022
- );
4023
- }
4024
- // ─── Observability Tools ──────────────────────────────────────
4025
- case "sentinel_logs": {
4026
- const { level, symbol, service, search, since, sessionId, correlationId, limit = 50 } = args;
4027
- const logs = store.queryLogs({
4028
- level,
4029
- symbol,
4030
- service,
4031
- search,
4032
- since,
4033
- sessionId,
4034
- correlationId,
4035
- limit
4036
- });
4037
- const total = store.getLogCount({ level, symbol, service, since });
4038
- return JSON.stringify({
4039
- count: logs.length,
4040
- total,
4041
- logs: logs.map((l) => ({
4042
- timestamp: l.timestamp,
4043
- level: l.level,
4044
- symbol: l.symbol,
4045
- service: l.service,
4046
- message: l.message,
4047
- data: l.data,
4048
- sessionId: l.sessionId,
4049
- correlationId: l.correlationId,
4050
- durationMs: l.durationMs
4051
- }))
4052
- }, null, 2);
4053
- }
4054
- case "sentinel_services": {
4055
- const services = store.getServices();
4056
- return JSON.stringify({
4057
- count: services.length,
4058
- services: services.map((s) => ({
4059
- name: s.name,
4060
- version: s.version,
4061
- environment: s.environment,
4062
- lastSeen: s.lastSeenAt,
4063
- startedAt: s.startedAt,
4064
- pid: s.pid
4065
- }))
4066
- }, null, 2);
4067
- }
4068
- case "sentinel_app_state": {
4069
- const { service: svc } = args;
4070
- const states = svc ? store.getAppState(svc) : store.getAllAppStates();
4071
- return JSON.stringify({
4072
- states: states.map((s) => ({
4073
- service: s.service,
4074
- sessionId: s.sessionId,
4075
- state: s.state,
4076
- activeFlows: s.activeFlows,
4077
- activeGates: s.activeGates,
4078
- timestamp: s.timestamp
4079
- }))
4080
- }, null, 2);
4081
- }
4082
- case "sentinel_validate_symbol": {
4083
- const { symbol: sym } = args;
4084
- const logCount = store.getLogCount({ symbol: sym });
4085
- return JSON.stringify({
4086
- symbol: sym,
4087
- usedInLogs: logCount > 0,
4088
- logCount,
4089
- tip: logCount === 0 ? "This symbol has not appeared in any logs. It may be a typo or unused." : `This symbol has been used in ${logCount} log entries.`
4090
- }, null, 2);
4091
- }
4092
- case "sentinel_flow_activity": {
4093
- const { flowId, service: flowSvc, since: flowSince } = args;
4094
- const flowLogs = store.queryLogs({ symbol: flowId, service: flowSvc, since: flowSince, limit: 100 });
4095
- const flowEvents = flowLogs.filter((l) => ["flow", "signal", "gate"].includes(l.symbolType)).map((l) => ({
4096
- timestamp: l.timestamp,
4097
- symbol: l.symbol,
4098
- symbolType: l.symbolType,
4099
- service: l.service,
4100
- message: l.message,
4101
- level: l.level
4102
- }));
4103
- return JSON.stringify({ count: flowEvents.length, events: flowEvents }, null, 2);
4104
- }
4105
- case "sentinel_metrics": {
4106
- const { name: metricName, type: metricType, service: metricSvc, since: metricSince, aggregate, limit: metricLimit } = args;
4107
- if (aggregate && metricName) {
4108
- const agg = store.aggregateMetric(metricName, { service: metricSvc, since: metricSince });
4109
- return JSON.stringify(agg, null, 2);
4110
- }
4111
- const metrics = store.queryMetrics({
4112
- name: metricName,
4113
- type: metricType,
4114
- service: metricSvc,
4115
- since: metricSince,
4116
- limit: Math.min(metricLimit || 50, 100)
4117
- });
4118
- return JSON.stringify({
4119
- count: metrics.length,
4120
- metrics: metrics.map((m) => ({
4121
- timestamp: m.timestamp,
4122
- name: m.name,
4123
- type: m.type,
4124
- value: m.value,
4125
- tags: m.tags,
4126
- service: m.service
4127
- }))
4128
- }, null, 2);
4129
- }
4130
- case "sentinel_traces": {
4131
- const { traceId: tid, service: traceSvc, symbol: traceSym, since: traceSince, limit: traceLimit } = args;
4132
- if (tid) {
4133
- const trace = store.getTrace(tid);
4134
- if (!trace) return JSON.stringify({ error: "Trace not found" });
4135
- return JSON.stringify(trace, null, 2);
4136
- }
4137
- const traces = store.queryTraces({
4138
- service: traceSvc,
4139
- symbol: traceSym,
4140
- since: traceSince,
4141
- limit: Math.min(traceLimit || 10, 20)
4142
- });
4143
- return JSON.stringify({
4144
- count: traces.length,
4145
- traces: traces.map((t) => ({
4146
- traceId: t.traceId,
4147
- services: t.services,
4148
- spanCount: t.spans.length,
4149
- totalDurationMs: t.totalDurationMs,
4150
- startTime: t.startTime,
4151
- endTime: t.endTime
4152
- }))
4153
- }, null, 2);
4154
- }
4155
- default:
4156
- return JSON.stringify({ error: `Unknown tool: ${name}` });
4157
- }
4158
- }
4159
- async function main() {
4160
- console.error("[sentinel-mcp] Starting...");
4161
- const store = getStorage();
4162
- await store.ensureReady();
4163
- console.error("[sentinel-mcp] Storage initialized");
4164
- const server = new Server(
4165
- { name: "sentinel", version: "0.2.0" },
4166
- { capabilities: { tools: {} } }
4167
- );
4168
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
4169
- tools: getToolsList()
4170
- }));
4171
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
4172
- const { name, arguments: args } = request.params;
4173
- try {
4174
- const text = await handleTool(name, args || {});
4175
- return { content: [{ type: "text", text }] };
4176
- } catch (error) {
4177
- const message = error instanceof Error ? error.message : String(error);
4178
- return {
4179
- content: [{ type: "text", text: JSON.stringify({ error: message }) }],
4180
- isError: true
4181
- };
4182
- }
4183
- });
4184
- server.onerror = (error) => {
4185
- console.error("[sentinel-mcp] Server error:", error);
4186
- };
4187
- const transport = new StdioServerTransport();
4188
- await server.connect(transport);
4189
- console.error("[sentinel-mcp] Running on stdio");
4190
- }
4191
- main().catch((err) => {
4192
- console.error("[sentinel-mcp] Fatal:", err);
4193
- process.exit(1);
4194
- });
422
+ )`,[s]),this.save(),s}rowToGenericEvent(e,t){let s={};return e.forEach((n,i)=>{s[n]=t[i];}),{id:s.id,schemaId:s.schema_id,eventType:s.event_type,category:s.category,timestamp:s.timestamp,scopeValue:s.scope_value||void 0,scopeOrdinal:s.scope_ordinal!=null?s.scope_ordinal:void 0,sessionId:s.session_id||void 0,service:s.service,data:s.data_json?JSON.parse(s.data_json):void 0,severity:s.severity||"info",parentEventId:s.parent_event_id||void 0,depth:s.depth||0}}close(){this.db&&(this.save(),this.db.close(),this.db=null);}};var k={minScore:30,maxResults:5,boostConfidence:true},R=class{constructor(e){this.storage=e;}match(e,t={}){let{minScore:s,maxResults:n,boostConfidence:i}={...k,...t},r=this.storage.getAllPatterns({includePrivate:true}),a=[];for(let o of r){if(!this.matchEnvironment(o,e))continue;let{score:c,matchedCriteria:l}=this.scoreMatch(o,e);if(c>=s){let d=c;if(i){let m=o.confidence.score/100;d=c*(.5+.5*m);}a.push({pattern:o,score:c,matchedCriteria:l,confidence:Math.round(d)}),this.storage.updatePatternConfidence(o.id,"matched");}}return a.sort((o,c)=>c.confidence-o.confidence).slice(0,n)}testPattern(e,t=100){let s=this.storage.getRecentIncidents({limit:t}),n=[],i=0;for(let r of s){if(!this.matchEnvironment(e,r))continue;let{score:a}=this.scoreMatch(e,r);a>=30&&(n.push(r),i+=a);}return {wouldMatch:n,matchCount:n.length,avgScore:n.length>0?Math.round(i/n.length):0}}scoreMatch(e,t){let s=0,n={symbols:[],errorKeywords:[],missingSignals:[]},i=this.matchSymbols(e.pattern.symbols,t.symbols,n.symbols);s+=Math.min(i,50);let r=this.matchErrorText(e,t,n.errorKeywords);s+=Math.min(r,25);let a=this.matchMissingSignals(e,t,n.missingSignals);return s+=Math.min(a,25),s=Math.min(s,100),{score:s,matchedCriteria:n}}matchSymbols(e,t,s){let n=0,i=["feature","component","flow","gate","signal","state","integration"];for(let r of i){let a=e[r],o=t[r];if(!(!a||!o)){if(typeof a=="string")this.matchSingleSymbol(a,o)&&(n+=a.includes("*")?5:10,s.push(r));else if(Array.isArray(a)){for(let c of a)if(this.matchSingleSymbol(c,o)){n+=7,s.push(r);break}}}}return n}matchSingleSymbol(e,t){if(e==="*")return true;if(e.endsWith("*")){let s=e.slice(0,-1);return t.startsWith(s)}if(e.startsWith("*")){let s=e.slice(1);return t.endsWith(s)}return e.includes("*")?new RegExp("^"+e.replace(/\*/g,".*")+"$").test(t):e===t}matchErrorText(e,t,s){let n=0,i=t.error.message.toLowerCase(),r=t.error.type?.toLowerCase();if(e.pattern.errorContains)for(let a of e.pattern.errorContains)i.includes(a.toLowerCase())&&(n+=5,s.push(a));if(e.pattern.errorMatches)try{new RegExp(e.pattern.errorMatches,"i").test(t.error.message)&&(n+=10,s.push(`regex:${e.pattern.errorMatches}`));}catch{}if(e.pattern.errorType&&r)for(let a of e.pattern.errorType)r.includes(a.toLowerCase())&&(n+=5,s.push(`type:${a}`));return n}matchMissingSignals(e,t,s){if(!e.pattern.missingSignals||!t.flowPosition?.missing)return 0;let n=0;for(let i of e.pattern.missingSignals)for(let r of t.flowPosition.missing)if(this.matchSingleSymbol(i,r)){n+=12,s.push(r);break}return n}matchEnvironment(e,t){return !e.pattern.environment||e.pattern.environment.length===0?true:e.pattern.environment.includes(t.environment)}};var _=class{build(e){if(!e.flowPosition)return null;let t=[],s=new Date(e.timestamp).getTime();t.push({timestamp:new Date(s-5e3).toISOString(),symbol:e.flowPosition.flowId,type:"flow-started"});let n=1e3;for(let r of e.flowPosition.actual){let a=this.inferEventType(r);t.push({timestamp:new Date(s-4e3+n).toISOString(),symbol:r,type:a}),n+=Math.random()*1e3+500;}let i=e.flowPosition.failedAt||e.flowPosition.missing[0]||e.symbols.gate||e.symbols.signal||"unknown";return t.push({timestamp:e.timestamp,symbol:i,type:"error",data:{message:e.error.message,missing:e.flowPosition.missing}}),{incidentId:e.id,flowId:e.flowPosition.flowId,events:t,failure:{at:e.timestamp,symbol:i,reason:e.error.message}}}renderAscii(e){let t=[];t.push(`${e.flowId} Timeline`),t.push("\u2550".repeat(40)),t.push("");for(let n of e.events){let i=this.formatTime(n.timestamp),r=this.getEventIcon(n.type),a=this.getEventStatus(n.type),o=`${i} ${r} ${n.symbol}`;a&&(o+=` (${a})`),t.push(o),n.type==="error"&&n.data&&(t.push(` \u2514\u2500 ${n.data.message}`),n.data.missing&&Array.isArray(n.data.missing)&&n.data.missing.length>0&&t.push(` \u2514\u2500 Expected: ${n.data.missing.join(", ")}`));}let s=e.events.find(n=>n.type==="error")?.data?.missing;return s&&s.length>0&&(t.push(""),t.push(`Missing signals: ${s.join(", ")}`)),t.join(`
423
+ `)}renderStructured(e){return {incidentId:e.incidentId,flow:{id:e.flowId,eventCount:e.events.length},events:e.events.map(t=>({time:this.formatTime(t.timestamp),symbol:t.symbol,type:t.type,status:this.getEventStatus(t.type),data:t.data})),failure:{at:this.formatTime(e.failure.at),symbol:e.failure.symbol,reason:e.failure.reason}}}inferEventType(e){return e.startsWith("^")?"gate-passed":e.startsWith("!")?"signal-emitted":e.startsWith("%")?"state-changed":"signal-emitted"}getEventIcon(e){switch(e){case "flow-started":return "\u25B6";case "flow-ended":return "\u25A0";case "gate-passed":return "\u2713";case "gate-failed":return "\u2717";case "signal-emitted":return "\u26A1";case "state-changed":return "\u25C6";case "error":return "\u2717";default:return "\u2022"}}getEventStatus(e){switch(e){case "gate-passed":return "PASSED";case "gate-failed":return "FAILED";case "signal-emitted":return "EMITTED";case "state-changed":return "CHANGED";case "error":return "ERROR";default:return ""}}formatTime(e){let t=new Date(e),s=String(t.getHours()).padStart(2,"0"),n=String(t.getMinutes()).padStart(2,"0"),i=String(t.getSeconds()).padStart(2,"0"),r=String(t.getMilliseconds()).padStart(3,"0");return `${s}:${n}:${i}.${r}`}};var L=class{constructor(e){this.storage=e;}getStats(e=7){let t=new Date().toISOString(),s=new Date(Date.now()-e*24*60*60*1e3).toISOString();return this.storage.getStats({start:s,end:t})}getSymbolHealth(e){return this.storage.getSymbolHealth(e)}getTrendingIssues(e=7){let t=Date.now(),s=e*24*60*60*1e3/2,n=new Date(t-e*24*60*60*1e3).toISOString(),i=new Date(t-s).toISOString(),r=new Date(t).toISOString(),a=this.storage.getRecentIncidents({dateFrom:n,dateTo:i,limit:1e3}),o=this.storage.getRecentIncidents({dateFrom:i,dateTo:r,limit:1e3}),c=this.countSymbols(a),l=this.countSymbols(o),d=[],m=new Set([...c.keys(),...l.keys()]);for(let g of m){let p=c.get(g)||0,E=l.get(g)||0;if(p===0&&E>0)d.push({symbol:g,trend:E*100});else if(p>0){let S=(E-p)/p*100;d.push({symbol:g,trend:S});}}return d.filter(g=>g.trend>0).sort((g,p)=>p.trend-g.trend).slice(0,10)}getResolutionMetrics(){let e=this.getStats(30);return {avgTimeToResolve:e.resolution.avgTimeToResolve,resolvedWithPattern:e.resolution.resolvedWithPattern,resolvedManually:e.resolution.resolvedManually,totalResolved:e.incidents.resolved,resolutionRate:e.resolution.resolutionRate}}getPatternEffectiveness(){return this.storage.getAllPatterns({includePrivate:true}).filter(t=>t.confidence.timesMatched>0).map(t=>({patternId:t.id,name:t.name,matches:t.confidence.timesMatched,resolutions:t.confidence.timesResolved,recurrences:t.confidence.timesRecurred,effectiveness:t.confidence.timesMatched>0?Math.round((t.confidence.timesResolved-t.confidence.timesRecurred)/t.confidence.timesMatched*100):0})).sort((t,s)=>s.effectiveness-t.effectiveness)}getIncidentsByHour(e=7){let t=new Date(Date.now()-e*24*60*60*1e3).toISOString(),s=this.storage.getRecentIncidents({dateFrom:t,limit:1e4}),n=new Map;for(let i=0;i<24;i++)n.set(i,0);for(let i of s){let r=new Date(i.timestamp).getHours();n.set(r,(n.get(r)||0)+1);}return Array.from(n.entries()).map(([i,r])=>({hour:i,count:r}))}getIncidentsByEnvironment(){let e=this.getStats(30),t=e.incidents.total;return Object.entries(e.incidents.byEnvironment).map(([s,n])=>({environment:s,count:n,percentage:t>0?Math.round(n/t*100):0})).sort((s,n)=>n.count-s.count)}getSymbolCorrelation(){let e=this.storage.getRecentIncidents({limit:1e3}),t=new Map,s=new Map;for(let i of e){let r=this.getSymbolsFromIncident(i);for(let a of r)s.set(a,(s.get(a)||0)+1);for(let a=0;a<r.length;a++)for(let o=a+1;o<r.length;o++){let c=[r[a],r[o]].sort().join("|");t.set(c,(t.get(c)||0)+1);}}let n=[];for(let[i,r]of t){let[a,o]=i.split("|"),c=s.get(a)||1,l=s.get(o)||1,d=r/Math.max(c,l);d>.3&&n.push({symbol1:a,symbol2:o,correlation:Math.round(d*100)/100});}return n.sort((i,r)=>r.correlation-i.correlation).slice(0,20)}generateDashboard(e=7){let t=this.getStats(e),s=[];s.push("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),s.push("\u2551 PARADIGM SENTINEL DASHBOARD \u2551"),s.push("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");let n=t.incidents.byDay[t.incidents.byDay.length-1]?.count||0;s.push(`\u2551 Open: ${String(t.incidents.open).padEnd(4)} \u2502 Investigating: ${String(t.incidents.total-t.incidents.open-t.incidents.resolved).padEnd(3)} \u2502 Resolved: ${String(t.incidents.resolved).padEnd(4)} \u2502 Today: +${n} \u2551`),s.push("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),s.push(""),s.push("Incidents by Day (last 7 days):"),s.push("\u2500".repeat(50));let i=Math.max(...t.incidents.byDay.map(r=>r.count),1);for(let r of t.incidents.byDay.slice(-7)){let a=Math.round(r.count/i*30),o="\u2588".repeat(a);s.push(`${r.date.substring(5)} ${o} ${r.count}`);}s.push(""),s.push("Most Affected Symbols:"),s.push("\u2500".repeat(50));for(let{symbol:r,count:a}of t.symbols.mostIncidents.slice(0,5))s.push(` ${r.padEnd(25)} ${a} incidents`);s.push(""),s.push("Top Patterns:"),s.push("\u2500".repeat(50));for(let{patternId:r,resolvedCount:a}of t.patterns.mostEffective.slice(0,5))s.push(` ${r.padEnd(25)} ${a} resolved`);return s.push(""),s.push("Resolution Stats:"),s.push("\u2500".repeat(50)),s.push(` Resolution rate: ${Math.round(t.resolution.resolutionRate)}%`),s.push(` With pattern: ${t.resolution.resolvedWithPattern}`),s.push(` Manual: ${t.resolution.resolvedManually}`),s.join(`
424
+ `)}countSymbols(e){let t=new Map;for(let s of e)for(let[,n]of Object.entries(s.symbols))n&&t.set(n,(t.get(n)||0)+1);return t}getSymbolsFromIncident(e){let t=[];for(let[,s]of Object.entries(e.symbols))s&&t.push(s);return t}};var C=class{constructor(e){this.storage=e;}suggestFromIncident(e){let t=this.generatePatternId(e),s=this.buildSymbolCriteria(e.symbols),n=this.extractErrorKeywords(e.error.message);return {id:t,name:this.generatePatternName(e),description:`Auto-suggested pattern from incident ${e.id}`,pattern:{symbols:s,errorContains:n.length>0?n:void 0,missingSignals:e.flowPosition?.missing},resolution:{description:e.resolution?.notes||"Resolution approach TBD",strategy:this.inferStrategy([e]),priority:"medium"},source:"suggested",private:false,tags:this.generateTags(e)}}suggestFromGroup(e){let t=`group-${e.id.toLowerCase().replace(/[^a-z0-9]/g,"-")}`,s=this.buildSymbolCriteria(e.commonSymbols),n=e.incidents.slice(0,20).map(r=>this.storage.getIncident(r)).filter(r=>r!=null);return {id:t,name:e.name||`Pattern from group ${e.id}`,description:`Auto-suggested pattern from incident group with ${e.count} incidents`,pattern:{symbols:s,errorContains:e.commonErrorPatterns.length>0?e.commonErrorPatterns:void 0},resolution:{description:"Resolution approach TBD based on grouped incidents",strategy:n.length>0?this.inferStrategy(n):"fix-code",priority:this.getPriorityFromCount(e.count)},source:"suggested",private:false,tags:this.generateTagsFromGroup(e)}}findPatternCandidates(e=3){let t=this.storage.getRecentIncidents({limit:1e3,status:"resolved"}),s=new Map;for(let i of t){let r=this.getSymbolSignature(i.symbols),a=s.get(r)||[];a.push(i),s.set(r,a);}let n=[];for(let[,i]of s)if(i.length>=e){if(this.hasMatchingPattern(i[0]))continue;let a=this.suggestFromIncidents(i);n.push({incidents:i,suggestedPattern:a,occurrenceCount:i.length});}return n.sort((i,r)=>r.occurrenceCount-i.occurrenceCount)}suggestFromIncidents(e){let t=this.extractCommonSymbols(e),s=this.buildSymbolCriteria(t),n=this.extractCommonErrorKeywords(e),i=this.extractCommonMissingSignals(e);return {id:this.generatePatternId(e[0]),name:this.generatePatternName(e[0]),description:`Auto-suggested pattern from ${e.length} similar incidents`,pattern:{symbols:s,errorContains:n.length>0?n:void 0,missingSignals:i.length>0?i:void 0},resolution:{description:"Resolution approach based on previous resolutions",strategy:this.inferStrategy(e),priority:this.getPriorityFromCount(e.length)},source:"suggested",private:false,tags:this.generateTagsFromIncidents(e)}}buildSymbolCriteria(e){let t={};for(let[s,n]of Object.entries(e))n&&(t[s]=n);return t}extractErrorKeywords(e){let t=new Set(["the","a","an","is","are","was","were","in","on","at","to","for","of","with","and","or","but","not","no","be","been","have","has","had","do","does","did"]),s=e.toLowerCase().replace(/[^a-z0-9\s]/g," ").split(/\s+/).filter(i=>i.length>2&&!t.has(i));return [...new Set(s)].slice(0,5)}extractCommonErrorKeywords(e){let t=new Map;for(let n of e){let i=this.extractErrorKeywords(n.error.message);for(let r of i)t.set(r,(t.get(r)||0)+1);}let s=Math.ceil(e.length*.5);return Array.from(t.entries()).filter(([,n])=>n>=s).map(([n])=>n).slice(0,5)}extractCommonSymbols(e){if(e.length===0)return {};let t=e[0].symbols,s={};for(let[n,i]of Object.entries(t)){if(!i)continue;e.every(a=>a.symbols[n]===i)&&(s[n]=i);}return s}extractCommonMissingSignals(e){let t=new Map;for(let n of e)if(n.flowPosition?.missing)for(let i of n.flowPosition.missing)t.set(i,(t.get(i)||0)+1);let s=Math.ceil(e.length*.5);return Array.from(t.entries()).filter(([,n])=>n>=s).map(([n])=>n)}generatePatternId(e){let t=[];e.symbols.gate?t.push(e.symbols.gate.replace(/[^a-z0-9]/gi,"")):e.symbols.feature?t.push(e.symbols.feature.replace(/[^a-z0-9]/gi,"")):e.symbols.component?t.push(e.symbols.component.replace(/[^a-z0-9]/gi,"")):e.symbols.integration?t.push(e.symbols.integration.replace(/[^a-z0-9]/gi,"")):t.push("unknown");let s=e.error.type?.toLowerCase()||"error";return t.push(s.replace(/[^a-z0-9]/gi,"")),t.push(String(Date.now()%1e3).padStart(3,"0")),t.join("-")}generatePatternName(e){let t=[];if(e.symbols.feature&&t.push(e.symbols.feature.replace("@","").replace(/-/g," ")),e.symbols.gate&&t.push("gate "+e.symbols.gate.replace("^","")),e.error.type&&t.push(e.error.type),t.length===0)return "Unnamed Pattern";let s=t.join(" - ");return s.charAt(0).toUpperCase()+s.slice(1)}generateTags(e){let t=[];return e.symbols.feature&&t.push("feature"),e.symbols.gate&&t.push("gate"),e.symbols.integration&&(t.push("integration"),t.push(e.symbols.integration.replace("&",""))),e.error.type&&t.push(e.error.type.toLowerCase()),t.push(e.environment),[...new Set(t)].slice(0,5)}generateTagsFromGroup(e){let t=["grouped"];e.commonSymbols.feature&&t.push("feature"),e.commonSymbols.gate&&t.push("gate"),e.commonSymbols.integration&&t.push("integration");for(let s of e.environments)t.push(s);return [...new Set(t)].slice(0,5)}generateTagsFromIncidents(e){let t=new Map;for(let s of e){let n=this.generateTags(s);for(let i of n)t.set(i,(t.get(i)||0)+1);}return Array.from(t.entries()).sort((s,n)=>n[1]-s[1]).slice(0,5).map(([s])=>s)}getSymbolSignature(e){let t=[];return e.feature&&t.push(`f:${e.feature}`),e.component&&t.push(`c:${e.component}`),e.flow&&t.push(`fl:${e.flow}`),e.gate&&t.push(`g:${e.gate}`),e.integration&&t.push(`i:${e.integration}`),t.sort().join("|")}hasMatchingPattern(e){let t=this.storage.getAllPatterns({includePrivate:true});for(let s of t){let n=0,i=["feature","component","flow","gate","signal","integration"];for(let r of i){let a=s.pattern.symbols[r],o=e.symbols[r];a&&o&&a===o&&n++;}if(n>=2)return true}return false}inferStrategy(e){let t=e.map(i=>i.error.message.toLowerCase()),s=i=>t.some(r=>i.some(a=>r.includes(a)));return s(["revert","rollback","regression","broke after deploy","since deploy"])?"rollback":s(["config","environment variable","env var","missing key","secret","credential"])?"config-change":s(["out of memory","oom","heap","memory limit","capacity","too many connections","pool exhausted"])?"scale-up":s(["timeout","network","econnrefused","econnreset","dns","socket hang up"])?"retry":s(["unavailable","service down","circuit breaker","fallback","503","502"])?"fallback":s(["validation","invalid","required","constraint","duplicate","not found","404"])?"fix-data":s(["permission","forbidden","403","401","unauthorized","access denied"])?"escalate":new Set(e.map(i=>i.error.type).filter(Boolean)).size>2?"investigate":"fix-code"}getPriorityFromCount(e){return e>=20?"critical":e>=10?"high":e>=5?"medium":"low"}};var B=fileURLToPath(import.meta.url),F=I.dirname(B);function G(){let h=I.join(F,"universal-patterns.json"),e=y.readFileSync(h,"utf-8");return JSON.parse(e)}function W(){let h=I.join(F,"paradigm-patterns.json"),e=y.readFileSync(h,"utf-8");return JSON.parse(e)}function M(){let h=G(),e=W();return {version:"1.0.0",exportedAt:new Date().toISOString(),patterns:[...h.patterns,...e.patterns]}}var w=null,x=false;function U(){if(w||(w=new O),!x){try{let{patterns:h}=M();for(let e of h)try{w.addPattern(e);}catch{}}catch{}x=true;}return w}function Q(){return [{name:"sentinel_triage",description:"View and filter incidents with pattern matches. Returns recent errors with symbolic context and resolution suggestions.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{symbol:{type:"string",description:"Filter by symbol (e.g., #checkout, ^auth)"},status:{type:"string",enum:["open","investigating","resolved","wont-fix","all"],description:"Filter by status (default: all)"},environment:{type:"string",description:"Filter by environment"},search:{type:"string",description:"Search in error messages"},limit:{type:"number",description:"Max results (default: 10)"}}}},{name:"sentinel_show",description:"Get full details of a specific incident including timeline and matched patterns.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{incidentId:{type:"string",description:"Incident ID (e.g., INC-001)"},includeTimeline:{type:"boolean",description:"Include flow timeline"},includeSimilar:{type:"boolean",description:"Include similar incidents"}},required:["incidentId"]}},{name:"sentinel_resolve",description:"Mark an incident as resolved with optional pattern and commit reference.",annotations:{readOnlyHint:false,destructiveHint:false},inputSchema:{type:"object",properties:{incidentId:{type:"string",description:"Incident ID"},patternId:{type:"string",description:"Pattern that led to resolution"},commitHash:{type:"string",description:"Fix commit hash"},prUrl:{type:"string",description:"PR URL"},notes:{type:"string",description:"Resolution notes"},wontFix:{type:"boolean",description:"Mark as wont-fix instead of resolved"}},required:["incidentId"]}},{name:"sentinel_patterns",description:"List and filter failure patterns with confidence scores.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{symbol:{type:"string",description:"Filter patterns for this symbol"},minConfidence:{type:"number",description:"Minimum confidence score"},source:{type:"string",enum:["manual","suggested","imported","community"],description:"Filter by source"}}}},{name:"sentinel_add_pattern",description:"Create a new failure pattern.",annotations:{readOnlyHint:false,destructiveHint:false},inputSchema:{type:"object",properties:{id:{type:"string",description:"Pattern ID (kebab-case)"},name:{type:"string",description:"Human-readable name"},description:{type:"string",description:"What this pattern matches"},pattern:{type:"object",properties:{symbols:{type:"object",description:"Symbol criteria"},errorContains:{type:"array",items:{type:"string"},description:"Error keywords"},missingSignals:{type:"array",items:{type:"string"},description:"Expected missing signals"}}},resolution:{type:"object",properties:{description:{type:"string",description:"Resolution steps"},strategy:{type:"string",enum:["retry","fallback","fix-data","fix-code","ignore","escalate"]},priority:{type:"string",enum:["low","medium","high","critical"]},codeHint:{type:"string",description:"Code fix hint"}},required:["description","strategy"]},tags:{type:"array",items:{type:"string"},description:"Tags for categorization"}},required:["id","name","pattern","resolution"]}},{name:"sentinel_record",description:"Manually record a new incident.",annotations:{readOnlyHint:false,destructiveHint:false},inputSchema:{type:"object",properties:{error:{type:"object",properties:{message:{type:"string",description:"Error message"},stack:{type:"string",description:"Stack trace"},code:{type:"string",description:"Error code"},type:{type:"string",description:"Error type"}},required:["message"]},symbols:{type:"object",properties:{component:{type:"string"},flow:{type:"string"},gate:{type:"string"},signal:{type:"string"}}},environment:{type:"string",description:"Environment (required)"},service:{type:"string",description:"Service name"},version:{type:"string",description:"App version"},flowPosition:{type:"object",properties:{flowId:{type:"string"},expected:{type:"array",items:{type:"string"}},actual:{type:"array",items:{type:"string"}},missing:{type:"array",items:{type:"string"}},failedAt:{type:"string"}}}},required:["error","symbols","environment"]}},{name:"sentinel_stats",description:"Get statistics and health metrics.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{period:{type:"string",description:"Period: 1d, 7d, 30d, 90d (default: 7d)"},symbol:{type:"string",description:"Get health for specific symbol"}}}},{name:"sentinel_suggest_pattern",description:"Get AI suggestions for patterns based on incidents.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{incidentId:{type:"string",description:"Suggest from specific incident"},minOccurrences:{type:"number",description:"Min similar incidents for suggestion"}}}},{name:"sentinel_logs",description:"Query structured logs from connected apps. Filters by level, symbol, service, search text, time range.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{level:{type:"string",enum:["debug","info","warn","error"],description:"Filter by log level"},symbol:{type:"string",description:"Filter by symbol (partial match)"},service:{type:"string",description:"Filter by service name"},search:{type:"string",description:"Search in log messages"},since:{type:"string",description:"ISO timestamp \u2014 logs after this time"},sessionId:{type:"string",description:"Filter by session ID"},correlationId:{type:"string",description:"Filter by correlation ID"},limit:{type:"number",description:"Max results (default: 50)"}}}},{name:"sentinel_services",description:"List all registered services with version, environment, and last-seen time.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{}}},{name:"sentinel_app_state",description:"Get live app state snapshots. Shows current state, active flows, and held gates for connected services.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{service:{type:"string",description:"Filter by service name"}}}},{name:"sentinel_validate_symbol",description:"Check if a symbol has been used in logs. Returns usage count and suggestions.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{symbol:{type:"string",description:"Symbol to validate (e.g., #checkout, ^auth)"}},required:["symbol"]}},{name:"sentinel_flow_activity",description:"Get recent flow events \u2014 which flow nodes were hit, in what order, by which service.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{flowId:{type:"string",description:"Filter by flow ID (e.g., $checkout-flow)"},service:{type:"string",description:"Filter by service name"},since:{type:"string",description:"ISO timestamp \u2014 events after this time"}}}},{name:"sentinel_metrics",description:"Query metrics (counters, gauges, histograms) from connected apps. Supports filtering and aggregation.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{name:{type:"string",description:"Metric name filter"},type:{type:"string",enum:["counter","gauge","histogram"],description:"Metric type filter"},service:{type:"string",description:"Service name filter"},since:{type:"string",description:"ISO timestamp \u2014 metrics after this time"},aggregate:{type:"boolean",description:"If true and name is provided, return aggregation instead of raw data"},limit:{type:"number",description:"Max results (default: 50)"}}}},{name:"sentinel_traces",description:"Query distributed traces across services. Shows span trees with timing, status, and service hops.",annotations:{readOnlyHint:true,destructiveHint:false},inputSchema:{type:"object",properties:{traceId:{type:"string",description:"Get a specific trace by ID"},service:{type:"string",description:"Filter by service name"},symbol:{type:"string",description:"Filter by symbol"},since:{type:"string",description:"ISO timestamp \u2014 traces after this time"},limit:{type:"number",description:"Max traces (default: 10, max: 20)"}}}}]}async function Z(h,e){let t=U(),s=new R(t);switch(h){case "sentinel_triage":{let{symbol:n,status:i="all",environment:r,search:a,limit:o=10}=e,c=t.getRecentIncidents({limit:o,status:i,symbol:n,environment:r,search:a});if(c.length===0)return JSON.stringify({count:0,incidents:[],tip:"No incidents recorded yet. Use sentinel_record to create incidents or integrate the SDK."},null,2);let l=c.map(d=>{let m=s.match(d,{maxResults:3});return {id:d.id,timestamp:d.timestamp,status:d.status,error:d.error.message,symbols:d.symbols,environment:d.environment,matches:m.map(g=>({patternId:g.pattern.id,name:g.pattern.name,confidence:g.confidence,strategy:g.pattern.resolution.strategy,description:g.pattern.resolution.description}))}});return JSON.stringify({count:l.length,incidents:l},null,2)}case "sentinel_show":{let{incidentId:n,includeTimeline:i,includeSimilar:r}=e,a=t.getIncident(n);if(!a)return JSON.stringify({error:`Incident ${n} not found`});let o=s.match(a,{maxResults:5}),c={incident:a,matches:o.map(l=>({patternId:l.pattern.id,name:l.pattern.name,confidence:l.confidence,matchedCriteria:l.matchedCriteria,resolution:l.pattern.resolution}))};if(i&&a.flowPosition){let l=new _().build(a);l&&(c.timeline=new _().renderStructured(l));}if(r){let l=t.getRecentIncidents({symbol:Object.values(a.symbols)[0],limit:5}).filter(d=>d.id!==n);c.similar=l.map(d=>({id:d.id,error:d.error.message,status:d.status}));}return JSON.stringify(c,null,2)}case "sentinel_resolve":{let{incidentId:n,patternId:i,commitHash:r,prUrl:a,notes:o,wontFix:c}=e;return t.getIncident(n)?c?(t.updateIncident(n,{status:"wont-fix",resolvedAt:new Date().toISOString(),resolvedBy:"manual",resolution:{notes:o}}),JSON.stringify({success:true,message:`Incident ${n} marked as won't fix`})):(t.recordResolution({incidentId:n,patternId:i,commitHash:r,prUrl:a,notes:o}),JSON.stringify({success:true,message:`Incident ${n} resolved`,patternId:i,commitHash:r,prUrl:a})):JSON.stringify({error:`Incident ${n} not found`})}case "sentinel_patterns":{let{symbol:n,minConfidence:i,source:r}=e,a=t.getAllPatterns({source:r,minConfidence:i,includePrivate:false}),o=a;return n&&(o=a.filter(c=>{let l=c.pattern.symbols;return Object.values(l).some(d=>d?Array.isArray(d)?d.includes(n)||d.some(m=>n.match(m.replace("*",".*"))):d===n||n.match(d.replace("*",".*")):false)})),JSON.stringify({count:o.length,patterns:o.map(c=>({id:c.id,name:c.name,description:c.description,confidence:c.confidence.score,resolution:c.resolution,tags:c.tags}))},null,2)}case "sentinel_add_pattern":{let{id:n,name:i,description:r,pattern:a,resolution:o,tags:c}=e,l={id:n,name:i,description:r||"",pattern:{symbols:a.symbols||{},errorContains:a.errorContains,missingSignals:a.missingSignals},resolution:{description:o.description,strategy:o.strategy,priority:o.priority||"medium",codeHint:o.codeHint},source:"manual",private:false,tags:c||[]};return t.addPattern(l),JSON.stringify({success:true,message:`Pattern ${n} created`,pattern:l})}case "sentinel_record":{let{error:n,symbols:i,environment:r,service:a,version:o,flowPosition:c}=e,l=t.recordIncident({error:n,symbols:i,environment:r,service:a,version:o,flowPosition:c}),d=t.getIncident(l),m=d?s.match(d,{maxResults:3}):[];return JSON.stringify({success:true,incidentId:l,matches:m.map(g=>({patternId:g.pattern.id,confidence:g.confidence,resolution:g.pattern.resolution.description}))},null,2)}case "sentinel_stats":{let{period:n="7d",symbol:i}=e,r=new L(t);if(i){let l=r.getSymbolHealth(i);return JSON.stringify({symbol:i,health:l},null,2)}let a=n.match(/^(\d+)d$/),o=a?parseInt(a[1],10):7,c=r.getStats(o);return JSON.stringify({period:`${o}d`,stats:c},null,2)}case "sentinel_suggest_pattern":{let{incidentId:n,minOccurrences:i}=e,r=new C(t);if(n){let o=t.getIncident(n);if(!o)return JSON.stringify({error:`Incident ${n} not found`});let c=r.suggestFromIncident(o);return JSON.stringify({source:"incident",incidentId:n,suggestion:c},null,2)}let a=r.findPatternCandidates(i||3);return JSON.stringify({source:"analysis",candidates:a.slice(0,5).map(o=>({occurrences:o.occurrenceCount,sampleIncidents:o.incidents.slice(0,3).map(c=>c.id),suggestion:o.suggestedPattern}))},null,2)}case "sentinel_logs":{let{level:n,symbol:i,service:r,search:a,since:o,sessionId:c,correlationId:l,limit:d=50}=e,m=t.queryLogs({level:n,symbol:i,service:r,search:a,since:o,sessionId:c,correlationId:l,limit:d}),g=t.getLogCount({level:n,symbol:i,service:r,since:o});return JSON.stringify({count:m.length,total:g,logs:m.map(p=>({timestamp:p.timestamp,level:p.level,symbol:p.symbol,service:p.service,message:p.message,data:p.data,sessionId:p.sessionId,correlationId:p.correlationId,durationMs:p.durationMs}))},null,2)}case "sentinel_services":{let n=t.getServices();return JSON.stringify({count:n.length,services:n.map(i=>({name:i.name,version:i.version,environment:i.environment,lastSeen:i.lastSeenAt,startedAt:i.startedAt,pid:i.pid}))},null,2)}case "sentinel_app_state":{let{service:n}=e,i=n?t.getAppState(n):t.getAllAppStates();return JSON.stringify({states:i.map(r=>({service:r.service,sessionId:r.sessionId,state:r.state,activeFlows:r.activeFlows,activeGates:r.activeGates,timestamp:r.timestamp}))},null,2)}case "sentinel_validate_symbol":{let{symbol:n}=e,i=t.getLogCount({symbol:n});return JSON.stringify({symbol:n,usedInLogs:i>0,logCount:i,tip:i===0?"This symbol has not appeared in any logs. It may be a typo or unused.":`This symbol has been used in ${i} log entries.`},null,2)}case "sentinel_flow_activity":{let{flowId:n,service:i,since:r}=e,o=t.queryLogs({symbol:n,service:i,since:r,limit:100}).filter(c=>["flow","signal","gate"].includes(c.symbolType)).map(c=>({timestamp:c.timestamp,symbol:c.symbol,symbolType:c.symbolType,service:c.service,message:c.message,level:c.level}));return JSON.stringify({count:o.length,events:o},null,2)}case "sentinel_metrics":{let{name:n,type:i,service:r,since:a,aggregate:o,limit:c}=e;if(o&&n){let d=t.aggregateMetric(n,{service:r,since:a});return JSON.stringify(d,null,2)}let l=t.queryMetrics({name:n,type:i,service:r,since:a,limit:Math.min(c||50,100)});return JSON.stringify({count:l.length,metrics:l.map(d=>({timestamp:d.timestamp,name:d.name,type:d.type,value:d.value,tags:d.tags,service:d.service}))},null,2)}case "sentinel_traces":{let{traceId:n,service:i,symbol:r,since:a,limit:o}=e;if(n){let l=t.getTrace(n);return l?JSON.stringify(l,null,2):JSON.stringify({error:"Trace not found"})}let c=t.queryTraces({service:i,symbol:r,since:a,limit:Math.min(o||10,20)});return JSON.stringify({count:c.length,traces:c.map(l=>({traceId:l.traceId,services:l.services,spanCount:l.spans.length,totalDurationMs:l.totalDurationMs,startTime:l.startTime,endTime:l.endTime}))},null,2)}default:return JSON.stringify({error:`Unknown tool: ${h}`})}}async function ee(){console.error("[sentinel-mcp] Starting..."),await U().ensureReady(),console.error("[sentinel-mcp] Storage initialized");let e=new Server({name:"sentinel",version:"0.2.0"},{capabilities:{tools:{}}});e.setRequestHandler(ListToolsRequestSchema,async()=>({tools:Q()})),e.setRequestHandler(CallToolRequestSchema,async s=>{let{name:n,arguments:i}=s.params;try{return {content:[{type:"text",text:await Z(n,i||{})}]}}catch(r){let a=r instanceof Error?r.message:String(r);return {content:[{type:"text",text:JSON.stringify({error:a})}],isError:true}}}),e.onerror=s=>{console.error("[sentinel-mcp] Server error:",s);};let t=new StdioServerTransport;await e.connect(t),console.error("[sentinel-mcp] Running on stdio");}ee().catch(h=>{console.error("[sentinel-mcp] Fatal:",h),process.exit(1);});