libre-webui 0.25.0 → 0.27.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 (588) hide show
  1. package/README.md +32 -22
  2. package/backend/dist/index.d.ts.map +1 -1
  3. package/backend/dist/index.js +33 -4
  4. package/backend/dist/index.js.map +1 -1
  5. package/backend/dist/middleware/auth.d.ts +7 -0
  6. package/backend/dist/middleware/auth.d.ts.map +1 -1
  7. package/backend/dist/middleware/auth.js +26 -0
  8. package/backend/dist/middleware/auth.js.map +1 -1
  9. package/backend/dist/middleware/index.d.ts +20 -0
  10. package/backend/dist/middleware/index.d.ts.map +1 -1
  11. package/backend/dist/middleware/index.js +105 -2
  12. package/backend/dist/middleware/index.js.map +1 -1
  13. package/backend/dist/models/personaModel.d.ts.map +1 -1
  14. package/backend/dist/models/personaModel.js +2 -0
  15. package/backend/dist/models/personaModel.js.map +1 -1
  16. package/backend/dist/observability/otel.d.ts +40 -0
  17. package/backend/dist/observability/otel.d.ts.map +1 -0
  18. package/backend/dist/observability/otel.js +337 -0
  19. package/backend/dist/observability/otel.js.map +1 -0
  20. package/backend/dist/observability/requestContext.d.ts +14 -0
  21. package/backend/dist/observability/requestContext.d.ts.map +1 -0
  22. package/backend/dist/observability/requestContext.js +21 -0
  23. package/backend/dist/observability/requestContext.js.map +1 -0
  24. package/backend/dist/persistence/chatGenerationTypes.d.ts +9 -0
  25. package/backend/dist/persistence/chatGenerationTypes.d.ts.map +1 -1
  26. package/backend/dist/persistence/extensionTypes.d.ts +13 -0
  27. package/backend/dist/persistence/extensionTypes.d.ts.map +1 -1
  28. package/backend/dist/persistence/extensionTypes.js.map +1 -1
  29. package/backend/dist/persistence/index.d.ts +1 -1
  30. package/backend/dist/persistence/index.d.ts.map +1 -1
  31. package/backend/dist/persistence/index.js.map +1 -1
  32. package/backend/dist/persistence/postgresAgentFoundationMigration.d.ts +9 -0
  33. package/backend/dist/persistence/postgresAgentFoundationMigration.d.ts.map +1 -0
  34. package/backend/dist/persistence/postgresAgentFoundationMigration.js +164 -0
  35. package/backend/dist/persistence/postgresAgentFoundationMigration.js.map +1 -0
  36. package/backend/dist/persistence/postgresExtensionRepositories.d.ts.map +1 -1
  37. package/backend/dist/persistence/postgresExtensionRepositories.js +46 -2
  38. package/backend/dist/persistence/postgresExtensionRepositories.js.map +1 -1
  39. package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.d.ts +10 -0
  40. package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.d.ts.map +1 -0
  41. package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.js +148 -0
  42. package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.js.map +1 -0
  43. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts +9 -0
  44. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts.map +1 -0
  45. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js +107 -0
  46. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js.map +1 -0
  47. package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
  48. package/backend/dist/persistence/postgresMigrationRegistry.js +12 -0
  49. package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
  50. package/backend/dist/persistence/postgresNotesV2Migration.d.ts +4 -0
  51. package/backend/dist/persistence/postgresNotesV2Migration.d.ts.map +1 -0
  52. package/backend/dist/persistence/postgresNotesV2Migration.js +57 -0
  53. package/backend/dist/persistence/postgresNotesV2Migration.js.map +1 -0
  54. package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
  55. package/backend/dist/persistence/postgresResourceRepositories.js +1930 -133
  56. package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
  57. package/backend/dist/persistence/postgresSecurityRepositories.d.ts.map +1 -1
  58. package/backend/dist/persistence/postgresSecurityRepositories.js +150 -0
  59. package/backend/dist/persistence/postgresSecurityRepositories.js.map +1 -1
  60. package/backend/dist/persistence/postgresSkillFilesMigration.d.ts +9 -0
  61. package/backend/dist/persistence/postgresSkillFilesMigration.d.ts.map +1 -0
  62. package/backend/dist/persistence/postgresSkillFilesMigration.js +50 -0
  63. package/backend/dist/persistence/postgresSkillFilesMigration.js.map +1 -0
  64. package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts +10 -0
  65. package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts.map +1 -0
  66. package/backend/dist/persistence/postgresTeamCollaborationMigration.js +161 -0
  67. package/backend/dist/persistence/postgresTeamCollaborationMigration.js.map +1 -0
  68. package/backend/dist/persistence/resourceTypes.d.ts +600 -3
  69. package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
  70. package/backend/dist/persistence/resourceTypes.js.map +1 -1
  71. package/backend/dist/persistence/securityTypes.d.ts +59 -0
  72. package/backend/dist/persistence/securityTypes.d.ts.map +1 -1
  73. package/backend/dist/persistence/sqliteExtensionRepositories.d.ts.map +1 -1
  74. package/backend/dist/persistence/sqliteExtensionRepositories.js +26 -4
  75. package/backend/dist/persistence/sqliteExtensionRepositories.js.map +1 -1
  76. package/backend/dist/persistence/sqliteMigrations.d.ts +10 -0
  77. package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
  78. package/backend/dist/persistence/sqliteMigrations.js +1492 -131
  79. package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
  80. package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
  81. package/backend/dist/persistence/sqliteResourceRepositories.js +1657 -134
  82. package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
  83. package/backend/dist/persistence/sqliteSecurityRepositories.d.ts.map +1 -1
  84. package/backend/dist/persistence/sqliteSecurityRepositories.js +134 -0
  85. package/backend/dist/persistence/sqliteSecurityRepositories.js.map +1 -1
  86. package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
  87. package/backend/dist/persistence/sqliteToPostgresMigration.js +302 -3
  88. package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
  89. package/backend/dist/platform/jobs/domainJobContracts.d.ts +10 -0
  90. package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
  91. package/backend/dist/platform/jobs/domainJobContracts.js +10 -0
  92. package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
  93. package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
  94. package/backend/dist/platform/jobs/domainJobHandlers.js +374 -4
  95. package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
  96. package/backend/dist/platform/jobs/embeddedDurableJobWorker.d.ts.map +1 -1
  97. package/backend/dist/platform/jobs/embeddedDurableJobWorker.js +4 -2
  98. package/backend/dist/platform/jobs/embeddedDurableJobWorker.js.map +1 -1
  99. package/backend/dist/platform/jobs/postgresDurableJobWorker.d.ts.map +1 -1
  100. package/backend/dist/platform/jobs/postgresDurableJobWorker.js +4 -2
  101. package/backend/dist/platform/jobs/postgresDurableJobWorker.js.map +1 -1
  102. package/backend/dist/platform/storage/pgVectorStore.d.ts +2 -1
  103. package/backend/dist/platform/storage/pgVectorStore.d.ts.map +1 -1
  104. package/backend/dist/platform/storage/pgVectorStore.js +51 -0
  105. package/backend/dist/platform/storage/pgVectorStore.js.map +1 -1
  106. package/backend/dist/platform/storage/platformDomainRepositories.d.ts +8 -2
  107. package/backend/dist/platform/storage/platformDomainRepositories.d.ts.map +1 -1
  108. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.d.ts.map +1 -1
  109. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js +25 -4
  110. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js.map +1 -1
  111. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts +2 -1
  112. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts.map +1 -1
  113. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js +35 -0
  114. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js.map +1 -1
  115. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.d.ts.map +1 -1
  116. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js +26 -3
  117. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js.map +1 -1
  118. package/backend/dist/platform/storage/vectorStore.d.ts +14 -0
  119. package/backend/dist/platform/storage/vectorStore.d.ts.map +1 -1
  120. package/backend/dist/platform/storage/vectorStore.js.map +1 -1
  121. package/backend/dist/routes/access.d.ts.map +1 -1
  122. package/backend/dist/routes/access.js +84 -3
  123. package/backend/dist/routes/access.js.map +1 -1
  124. package/backend/dist/routes/auth.d.ts.map +1 -1
  125. package/backend/dist/routes/auth.js +419 -1
  126. package/backend/dist/routes/auth.js.map +1 -1
  127. package/backend/dist/routes/calendar.d.ts.map +1 -1
  128. package/backend/dist/routes/calendar.js +125 -22
  129. package/backend/dist/routes/calendar.js.map +1 -1
  130. package/backend/dist/routes/channels.d.ts +3 -0
  131. package/backend/dist/routes/channels.d.ts.map +1 -0
  132. package/backend/dist/routes/channels.js +447 -0
  133. package/backend/dist/routes/channels.js.map +1 -0
  134. package/backend/dist/routes/chat.d.ts.map +1 -1
  135. package/backend/dist/routes/chat.js +195 -7
  136. package/backend/dist/routes/chat.js.map +1 -1
  137. package/backend/dist/routes/costs.d.ts +3 -0
  138. package/backend/dist/routes/costs.d.ts.map +1 -0
  139. package/backend/dist/routes/costs.js +191 -0
  140. package/backend/dist/routes/costs.js.map +1 -0
  141. package/backend/dist/routes/documents.d.ts.map +1 -1
  142. package/backend/dist/routes/documents.js +68 -21
  143. package/backend/dist/routes/documents.js.map +1 -1
  144. package/backend/dist/routes/evaluations.d.ts +3 -0
  145. package/backend/dist/routes/evaluations.d.ts.map +1 -0
  146. package/backend/dist/routes/evaluations.js +352 -0
  147. package/backend/dist/routes/evaluations.js.map +1 -0
  148. package/backend/dist/routes/imageGen.d.ts.map +1 -1
  149. package/backend/dist/routes/imageGen.js +2 -1
  150. package/backend/dist/routes/imageGen.js.map +1 -1
  151. package/backend/dist/routes/media.d.ts.map +1 -1
  152. package/backend/dist/routes/media.js +228 -7
  153. package/backend/dist/routes/media.js.map +1 -1
  154. package/backend/dist/routes/notes.d.ts.map +1 -1
  155. package/backend/dist/routes/notes.js +149 -36
  156. package/backend/dist/routes/notes.js.map +1 -1
  157. package/backend/dist/routes/notifications.d.ts +3 -0
  158. package/backend/dist/routes/notifications.d.ts.map +1 -0
  159. package/backend/dist/routes/notifications.js +213 -0
  160. package/backend/dist/routes/notifications.js.map +1 -0
  161. package/backend/dist/routes/openaiCompat.d.ts +3 -0
  162. package/backend/dist/routes/openaiCompat.d.ts.map +1 -0
  163. package/backend/dist/routes/openaiCompat.js +314 -0
  164. package/backend/dist/routes/openaiCompat.js.map +1 -0
  165. package/backend/dist/routes/personas.js +1 -1
  166. package/backend/dist/routes/personas.js.map +1 -1
  167. package/backend/dist/routes/prompts.d.ts +3 -0
  168. package/backend/dist/routes/prompts.d.ts.map +1 -0
  169. package/backend/dist/routes/prompts.js +173 -0
  170. package/backend/dist/routes/prompts.js.map +1 -0
  171. package/backend/dist/routes/push.d.ts +3 -0
  172. package/backend/dist/routes/push.d.ts.map +1 -0
  173. package/backend/dist/routes/push.js +105 -0
  174. package/backend/dist/routes/push.js.map +1 -0
  175. package/backend/dist/routes/recovery.d.ts +3 -0
  176. package/backend/dist/routes/recovery.d.ts.map +1 -0
  177. package/backend/dist/routes/recovery.js +83 -0
  178. package/backend/dist/routes/recovery.js.map +1 -0
  179. package/backend/dist/routes/search.d.ts.map +1 -1
  180. package/backend/dist/routes/search.js +29 -1
  181. package/backend/dist/routes/search.js.map +1 -1
  182. package/backend/dist/routes/skills.d.ts +3 -0
  183. package/backend/dist/routes/skills.d.ts.map +1 -0
  184. package/backend/dist/routes/skills.js +246 -0
  185. package/backend/dist/routes/skills.js.map +1 -0
  186. package/backend/dist/routes/stt.d.ts.map +1 -1
  187. package/backend/dist/routes/stt.js +2 -1
  188. package/backend/dist/routes/stt.js.map +1 -1
  189. package/backend/dist/routes/tools.d.ts +3 -0
  190. package/backend/dist/routes/tools.d.ts.map +1 -0
  191. package/backend/dist/routes/tools.js +359 -0
  192. package/backend/dist/routes/tools.js.map +1 -0
  193. package/backend/dist/routes/tts.d.ts.map +1 -1
  194. package/backend/dist/routes/tts.js +63 -8
  195. package/backend/dist/routes/tts.js.map +1 -1
  196. package/backend/dist/routes/users.d.ts.map +1 -1
  197. package/backend/dist/routes/users.js +35 -0
  198. package/backend/dist/routes/users.js.map +1 -1
  199. package/backend/dist/routes/voiceAccess.d.ts +3 -0
  200. package/backend/dist/routes/voiceAccess.d.ts.map +1 -0
  201. package/backend/dist/routes/voiceAccess.js +102 -0
  202. package/backend/dist/routes/voiceAccess.js.map +1 -0
  203. package/backend/dist/services/apiTokenService.d.ts +2 -2
  204. package/backend/dist/services/apiTokenService.d.ts.map +1 -1
  205. package/backend/dist/services/apiTokenService.js +7 -0
  206. package/backend/dist/services/apiTokenService.js.map +1 -1
  207. package/backend/dist/services/authService.d.ts +6 -0
  208. package/backend/dist/services/authService.d.ts.map +1 -1
  209. package/backend/dist/services/authService.js +9 -0
  210. package/backend/dist/services/authService.js.map +1 -1
  211. package/backend/dist/services/authorizationService.d.ts +8 -4
  212. package/backend/dist/services/authorizationService.d.ts.map +1 -1
  213. package/backend/dist/services/authorizationService.js +31 -1
  214. package/backend/dist/services/authorizationService.js.map +1 -1
  215. package/backend/dist/services/automationSchedulerService.d.ts.map +1 -1
  216. package/backend/dist/services/automationSchedulerService.js +40 -3
  217. package/backend/dist/services/automationSchedulerService.js.map +1 -1
  218. package/backend/dist/services/automationService.d.ts +4 -1
  219. package/backend/dist/services/automationService.d.ts.map +1 -1
  220. package/backend/dist/services/automationService.js +25 -2
  221. package/backend/dist/services/automationService.js.map +1 -1
  222. package/backend/dist/services/builtinToolsService.d.ts +20 -0
  223. package/backend/dist/services/builtinToolsService.d.ts.map +1 -0
  224. package/backend/dist/services/builtinToolsService.js +661 -0
  225. package/backend/dist/services/builtinToolsService.js.map +1 -0
  226. package/backend/dist/services/calendarService.d.ts +74 -0
  227. package/backend/dist/services/calendarService.d.ts.map +1 -0
  228. package/backend/dist/services/calendarService.js +339 -0
  229. package/backend/dist/services/calendarService.js.map +1 -0
  230. package/backend/dist/services/channelService.d.ts +195 -0
  231. package/backend/dist/services/channelService.d.ts.map +1 -0
  232. package/backend/dist/services/channelService.js +1057 -0
  233. package/backend/dist/services/channelService.js.map +1 -0
  234. package/backend/dist/services/chatRequestService.d.ts.map +1 -1
  235. package/backend/dist/services/chatRequestService.js +29 -1
  236. package/backend/dist/services/chatRequestService.js.map +1 -1
  237. package/backend/dist/services/chatService.d.ts +61 -1
  238. package/backend/dist/services/chatService.d.ts.map +1 -1
  239. package/backend/dist/services/chatService.js +218 -1
  240. package/backend/dist/services/chatService.js.map +1 -1
  241. package/backend/dist/services/chatToolRuntimeService.d.ts +55 -0
  242. package/backend/dist/services/chatToolRuntimeService.d.ts.map +1 -0
  243. package/backend/dist/services/chatToolRuntimeService.js +389 -0
  244. package/backend/dist/services/chatToolRuntimeService.js.map +1 -0
  245. package/backend/dist/services/costGovernanceService.d.ts +102 -0
  246. package/backend/dist/services/costGovernanceService.d.ts.map +1 -0
  247. package/backend/dist/services/costGovernanceService.js +463 -0
  248. package/backend/dist/services/costGovernanceService.js.map +1 -0
  249. package/backend/dist/services/dataArchiveService.d.ts +6 -0
  250. package/backend/dist/services/dataArchiveService.d.ts.map +1 -1
  251. package/backend/dist/services/dataArchiveService.js +26 -4
  252. package/backend/dist/services/dataArchiveService.js.map +1 -1
  253. package/backend/dist/services/documentMediaExtractionService.d.ts +40 -0
  254. package/backend/dist/services/documentMediaExtractionService.d.ts.map +1 -0
  255. package/backend/dist/services/documentMediaExtractionService.js +157 -0
  256. package/backend/dist/services/documentMediaExtractionService.js.map +1 -0
  257. package/backend/dist/services/documentService.d.ts +64 -1
  258. package/backend/dist/services/documentService.d.ts.map +1 -1
  259. package/backend/dist/services/documentService.js +342 -83
  260. package/backend/dist/services/documentService.js.map +1 -1
  261. package/backend/dist/services/durableChatGenerationService.d.ts +9 -0
  262. package/backend/dist/services/durableChatGenerationService.d.ts.map +1 -1
  263. package/backend/dist/services/durableChatGenerationService.js +152 -11
  264. package/backend/dist/services/durableChatGenerationService.js.map +1 -1
  265. package/backend/dist/services/encryptionService.d.ts +6 -0
  266. package/backend/dist/services/encryptionService.d.ts.map +1 -1
  267. package/backend/dist/services/encryptionService.js +12 -0
  268. package/backend/dist/services/encryptionService.js.map +1 -1
  269. package/backend/dist/services/evaluationService.d.ts +111 -0
  270. package/backend/dist/services/evaluationService.d.ts.map +1 -0
  271. package/backend/dist/services/evaluationService.js +403 -0
  272. package/backend/dist/services/evaluationService.js.map +1 -0
  273. package/backend/dist/services/galleryService.d.ts +6 -0
  274. package/backend/dist/services/galleryService.d.ts.map +1 -1
  275. package/backend/dist/services/galleryService.js +26 -0
  276. package/backend/dist/services/galleryService.js.map +1 -1
  277. package/backend/dist/services/mcpClientService.d.ts +29 -0
  278. package/backend/dist/services/mcpClientService.d.ts.map +1 -0
  279. package/backend/dist/services/mcpClientService.js +189 -0
  280. package/backend/dist/services/mcpClientService.js.map +1 -0
  281. package/backend/dist/services/mfaService.d.ts +64 -0
  282. package/backend/dist/services/mfaService.d.ts.map +1 -0
  283. package/backend/dist/services/mfaService.js +263 -0
  284. package/backend/dist/services/mfaService.js.map +1 -0
  285. package/backend/dist/services/noteService.d.ts +71 -0
  286. package/backend/dist/services/noteService.d.ts.map +1 -0
  287. package/backend/dist/services/noteService.js +397 -0
  288. package/backend/dist/services/noteService.js.map +1 -0
  289. package/backend/dist/services/notificationService.d.ts +60 -0
  290. package/backend/dist/services/notificationService.d.ts.map +1 -0
  291. package/backend/dist/services/notificationService.js +340 -0
  292. package/backend/dist/services/notificationService.js.map +1 -0
  293. package/backend/dist/services/openApiToolService.d.ts +38 -0
  294. package/backend/dist/services/openApiToolService.d.ts.map +1 -0
  295. package/backend/dist/services/openApiToolService.js +254 -0
  296. package/backend/dist/services/openApiToolService.js.map +1 -0
  297. package/backend/dist/services/personaService.d.ts +18 -2
  298. package/backend/dist/services/personaService.d.ts.map +1 -1
  299. package/backend/dist/services/personaService.js +113 -4
  300. package/backend/dist/services/personaService.js.map +1 -1
  301. package/backend/dist/services/pluginImageGenerationService.d.ts +19 -0
  302. package/backend/dist/services/pluginImageGenerationService.d.ts.map +1 -1
  303. package/backend/dist/services/pluginImageGenerationService.js +121 -0
  304. package/backend/dist/services/pluginImageGenerationService.js.map +1 -1
  305. package/backend/dist/services/pluginService.d.ts +7 -0
  306. package/backend/dist/services/pluginService.d.ts.map +1 -1
  307. package/backend/dist/services/pluginService.js +8 -2
  308. package/backend/dist/services/pluginService.js.map +1 -1
  309. package/backend/dist/services/preferencesService.d.ts +1 -1
  310. package/backend/dist/services/preferencesService.d.ts.map +1 -1
  311. package/backend/dist/services/preferencesService.js +1 -0
  312. package/backend/dist/services/preferencesService.js.map +1 -1
  313. package/backend/dist/services/promptService.d.ts +74 -0
  314. package/backend/dist/services/promptService.d.ts.map +1 -0
  315. package/backend/dist/services/promptService.js +513 -0
  316. package/backend/dist/services/promptService.js.map +1 -0
  317. package/backend/dist/services/recoveryDrillService.d.ts +57 -0
  318. package/backend/dist/services/recoveryDrillService.d.ts.map +1 -0
  319. package/backend/dist/services/recoveryDrillService.js +363 -0
  320. package/backend/dist/services/recoveryDrillService.js.map +1 -0
  321. package/backend/dist/services/resourceGrantService.d.ts.map +1 -1
  322. package/backend/dist/services/resourceGrantService.js +62 -17
  323. package/backend/dist/services/resourceGrantService.js.map +1 -1
  324. package/backend/dist/services/searchService.d.ts +29 -0
  325. package/backend/dist/services/searchService.d.ts.map +1 -0
  326. package/backend/dist/services/searchService.js +0 -0
  327. package/backend/dist/services/searchService.js.map +1 -0
  328. package/backend/dist/services/securityAuditService.d.ts.map +1 -1
  329. package/backend/dist/services/securityAuditService.js +2 -1
  330. package/backend/dist/services/securityAuditService.js.map +1 -1
  331. package/backend/dist/services/sharedResourceAccess.d.ts +25 -0
  332. package/backend/dist/services/sharedResourceAccess.d.ts.map +1 -0
  333. package/backend/dist/services/sharedResourceAccess.js +59 -0
  334. package/backend/dist/services/sharedResourceAccess.js.map +1 -0
  335. package/backend/dist/services/skillService.d.ts +137 -0
  336. package/backend/dist/services/skillService.d.ts.map +1 -0
  337. package/backend/dist/services/skillService.js +852 -0
  338. package/backend/dist/services/skillService.js.map +1 -0
  339. package/backend/dist/services/toolAccessService.d.ts +9 -0
  340. package/backend/dist/services/toolAccessService.d.ts.map +1 -0
  341. package/backend/dist/services/toolAccessService.js +60 -0
  342. package/backend/dist/services/toolAccessService.js.map +1 -0
  343. package/backend/dist/services/toolApprovalService.d.ts +34 -0
  344. package/backend/dist/services/toolApprovalService.d.ts.map +1 -0
  345. package/backend/dist/services/toolApprovalService.js +188 -0
  346. package/backend/dist/services/toolApprovalService.js.map +1 -0
  347. package/backend/dist/services/toolGatewayService.d.ts +53 -0
  348. package/backend/dist/services/toolGatewayService.d.ts.map +1 -0
  349. package/backend/dist/services/toolGatewayService.js +249 -0
  350. package/backend/dist/services/toolGatewayService.js.map +1 -0
  351. package/backend/dist/services/toolServerService.d.ts +57 -0
  352. package/backend/dist/services/toolServerService.d.ts.map +1 -0
  353. package/backend/dist/services/toolServerService.js +521 -0
  354. package/backend/dist/services/toolServerService.js.map +1 -0
  355. package/backend/dist/services/voiceAccessService.d.ts +10 -0
  356. package/backend/dist/services/voiceAccessService.d.ts.map +1 -0
  357. package/backend/dist/services/voiceAccessService.js +74 -0
  358. package/backend/dist/services/voiceAccessService.js.map +1 -0
  359. package/backend/dist/services/voiceProfileService.d.ts +20 -0
  360. package/backend/dist/services/voiceProfileService.d.ts.map +1 -1
  361. package/backend/dist/services/voiceProfileService.js +60 -0
  362. package/backend/dist/services/voiceProfileService.js.map +1 -1
  363. package/backend/dist/services/webPushService.d.ts +71 -0
  364. package/backend/dist/services/webPushService.d.ts.map +1 -0
  365. package/backend/dist/services/webPushService.js +328 -0
  366. package/backend/dist/services/webPushService.js.map +1 -0
  367. package/backend/dist/services/webauthnService.d.ts +58 -0
  368. package/backend/dist/services/webauthnService.d.ts.map +1 -0
  369. package/backend/dist/services/webauthnService.js +300 -0
  370. package/backend/dist/services/webauthnService.js.map +1 -0
  371. package/backend/dist/storage.d.ts +28 -1
  372. package/backend/dist/storage.d.ts.map +1 -1
  373. package/backend/dist/storage.js +100 -1
  374. package/backend/dist/storage.js.map +1 -1
  375. package/backend/dist/storageMappers.d.ts +2 -2
  376. package/backend/dist/storageMappers.d.ts.map +1 -1
  377. package/backend/dist/storageMappers.js.map +1 -1
  378. package/backend/dist/types/index.d.ts +199 -3
  379. package/backend/dist/types/index.d.ts.map +1 -1
  380. package/backend/dist/types/index.js.map +1 -1
  381. package/backend/dist/types/tools.d.ts +137 -0
  382. package/backend/dist/types/tools.d.ts.map +1 -0
  383. package/backend/dist/types/tools.js +25 -0
  384. package/backend/dist/types/tools.js.map +1 -0
  385. package/backend/dist/utils/chatDocumentContext.d.ts +26 -1
  386. package/backend/dist/utils/chatDocumentContext.d.ts.map +1 -1
  387. package/backend/dist/utils/chatDocumentContext.js +87 -18
  388. package/backend/dist/utils/chatDocumentContext.js.map +1 -1
  389. package/backend/dist/utils/documentExtraction.d.ts +97 -0
  390. package/backend/dist/utils/documentExtraction.d.ts.map +1 -0
  391. package/backend/dist/utils/documentExtraction.js +651 -0
  392. package/backend/dist/utils/documentExtraction.js.map +1 -0
  393. package/backend/dist/utils/hybridRetrieval.d.ts +34 -0
  394. package/backend/dist/utils/hybridRetrieval.d.ts.map +1 -0
  395. package/backend/dist/utils/hybridRetrieval.js +119 -0
  396. package/backend/dist/utils/hybridRetrieval.js.map +1 -0
  397. package/backend/dist/utils/icsCalendar.d.ts +27 -0
  398. package/backend/dist/utils/icsCalendar.d.ts.map +1 -0
  399. package/backend/dist/utils/icsCalendar.js +313 -0
  400. package/backend/dist/utils/icsCalendar.js.map +1 -0
  401. package/backend/dist/utils/imageEditUpload.d.ts +29 -0
  402. package/backend/dist/utils/imageEditUpload.d.ts.map +1 -0
  403. package/backend/dist/utils/imageEditUpload.js +82 -0
  404. package/backend/dist/utils/imageEditUpload.js.map +1 -0
  405. package/backend/dist/utils/logRedaction.d.ts +2 -0
  406. package/backend/dist/utils/logRedaction.d.ts.map +1 -0
  407. package/backend/dist/utils/logRedaction.js +66 -0
  408. package/backend/dist/utils/logRedaction.js.map +1 -0
  409. package/backend/dist/utils/logger.d.ts +3 -0
  410. package/backend/dist/utils/logger.d.ts.map +1 -1
  411. package/backend/dist/utils/logger.js +63 -20
  412. package/backend/dist/utils/logger.js.map +1 -1
  413. package/backend/dist/utils/pluginChatAdapter.d.ts +5 -1
  414. package/backend/dist/utils/pluginChatAdapter.d.ts.map +1 -1
  415. package/backend/dist/utils/pluginChatAdapter.js +83 -0
  416. package/backend/dist/utils/pluginChatAdapter.js.map +1 -1
  417. package/backend/dist/utils/pluginDefinitionTrust.js +1 -1
  418. package/backend/dist/utils/resourceLimits.d.ts +61 -2
  419. package/backend/dist/utils/resourceLimits.d.ts.map +1 -1
  420. package/backend/dist/utils/resourceLimits.js +59 -0
  421. package/backend/dist/utils/resourceLimits.js.map +1 -1
  422. package/backend/dist/utils/toolEgress.d.ts +23 -0
  423. package/backend/dist/utils/toolEgress.d.ts.map +1 -0
  424. package/backend/dist/utils/toolEgress.js +169 -0
  425. package/backend/dist/utils/toolEgress.js.map +1 -0
  426. package/backend/dist/utils/totp.d.ts +19 -0
  427. package/backend/dist/utils/totp.d.ts.map +1 -0
  428. package/backend/dist/utils/totp.js +117 -0
  429. package/backend/dist/utils/totp.js.map +1 -0
  430. package/backend/dist/utils/webauthn.d.ts +43 -0
  431. package/backend/dist/utils/webauthn.d.ts.map +1 -0
  432. package/backend/dist/utils/webauthn.js +294 -0
  433. package/backend/dist/utils/webauthn.js.map +1 -0
  434. package/backend/dist/utils/websocketMessages.d.ts +6 -0
  435. package/backend/dist/utils/websocketMessages.d.ts.map +1 -1
  436. package/backend/dist/utils/websocketMessages.js +8 -0
  437. package/backend/dist/utils/websocketMessages.js.map +1 -1
  438. package/backend/dist/utils/zipArchive.d.ts +32 -0
  439. package/backend/dist/utils/zipArchive.d.ts.map +1 -0
  440. package/backend/dist/utils/zipArchive.js +171 -0
  441. package/backend/dist/utils/zipArchive.js.map +1 -0
  442. package/backend/dist/websocketServer.d.ts.map +1 -1
  443. package/backend/dist/websocketServer.js +142 -35
  444. package/backend/dist/websocketServer.js.map +1 -1
  445. package/backend/package.json +1 -1
  446. package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
  447. package/frontend/dist/assets/index-DBAf_VaS.js +47 -0
  448. package/frontend/dist/assets/index-kn_Axjts.css +1 -0
  449. package/frontend/dist/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  450. package/frontend/dist/fonts/jetbrains-mono-latin-700-normal.woff2 +0 -0
  451. package/frontend/dist/fonts/jetbrains-mono.css +24 -0
  452. package/frontend/dist/index.html +27 -20
  453. package/frontend/dist/js/ArtifactContainer-C9zNtCay.js +1 -0
  454. package/frontend/dist/js/{ArtifactDemoPage-BeNs3gHp.js → ArtifactDemoPage-C-EBFBCt.js} +1 -1
  455. package/frontend/dist/js/ArtifactSandboxFrame-CTA1j_d1.js +111 -0
  456. package/frontend/dist/js/ArtifactSlideOutPanel-BOENbOiv.js +1 -0
  457. package/frontend/dist/js/AutomationsPage-C58MQ4te.js +1 -0
  458. package/frontend/dist/js/{Button-BPf_pOxH.js → Button-C0r3J9G_.js} +1 -1
  459. package/frontend/dist/js/CalendarPage-BcdAZ292.js +1 -0
  460. package/frontend/dist/js/ChannelsPage-DPr9Yajs.js +1 -0
  461. package/frontend/dist/js/ChatPage-DsbB748s.js +14 -0
  462. package/frontend/dist/js/EvaluationsPage-FWIJu2fI.js +2 -0
  463. package/frontend/dist/js/FirstTimeSetup-Cy-w6xko.js +1 -0
  464. package/frontend/dist/js/GalleryPage-DDAtxM-x.js +1 -0
  465. package/frontend/dist/js/HomePage-DLKwmE7n.js +1 -0
  466. package/frontend/dist/js/LibreClawPage-vKOj-9w9.js +1 -0
  467. package/frontend/dist/js/{ModelManagerModals-DcLGYN3l.js → ModelManagerModals-BY5TK38_.js} +1 -1
  468. package/frontend/dist/js/ModelSelector-CoCS6MZ9.js +1 -0
  469. package/frontend/dist/js/NotesPage-BElIpXgA.js +3 -0
  470. package/frontend/dist/js/OptimizedSyntaxHighlighter-xgyE3dRA.js +1 -0
  471. package/frontend/dist/js/PersonasPage-P40jVbfU.js +13 -0
  472. package/frontend/dist/js/PluginUsagePage-CKUEPCJN.js +1 -0
  473. package/frontend/dist/js/RichMessageContent-Bm2WaEGi.js +3 -0
  474. package/frontend/dist/js/SettingsModal-DxSGzx9A.js +19 -0
  475. package/frontend/dist/js/{SettingsToggle-woG_WebF.js → SettingsToggle-BIVyfGjl.js} +1 -1
  476. package/frontend/dist/js/SystemPage-BT07dTMT.js +1 -0
  477. package/frontend/dist/js/UserManagementPage-B9Vz5cm3.js +1 -0
  478. package/frontend/dist/js/WorkPage-qoN_e_jK.js +6 -0
  479. package/frontend/dist/js/api-D7Ff5M2Q.js +14 -0
  480. package/frontend/dist/js/appStore-DmV5wqDz.js +2 -0
  481. package/frontend/dist/js/ar-YHHTVP8f.js +2 -0
  482. package/frontend/dist/js/artifactHtml-DDmfpcB0.js +41 -0
  483. package/frontend/dist/js/{authStore-DbMdvNgJ.js → authStore-BYW8-kP6.js} +3 -3
  484. package/frontend/dist/js/{babel-BsYoxD-7.js → babel-DSqqk5kt.js} +1 -1
  485. package/frontend/dist/js/bn-C8VTxBVl.js +2 -0
  486. package/frontend/dist/js/chatStore-D-NTlArO.js +3 -0
  487. package/frontend/dist/js/{config-DSOYQ9a3.js → config-BqZ5304Q.js} +1 -1
  488. package/frontend/dist/js/cs-CCngB5fC.js +2 -0
  489. package/frontend/dist/js/da-CM2PP2ma.js +2 -0
  490. package/frontend/dist/js/de-BqVkD-dw.js +2 -0
  491. package/frontend/dist/js/es-BtWsWBjZ.js +2 -0
  492. package/frontend/dist/js/{estree-BcnBcOAx.js → estree-DPkpBuOU.js} +3 -3
  493. package/frontend/dist/js/evaluationsApi-Dnf2dTd-.js +1 -0
  494. package/frontend/dist/js/fr-C68w9abT.js +2 -0
  495. package/frontend/dist/js/hi-DtBlcyXZ.js +2 -0
  496. package/frontend/dist/js/{html-DkY55Jys.js → html-BpGUXG8E.js} +2 -2
  497. package/frontend/dist/js/id-BWzIu8tn.js +2 -0
  498. package/frontend/dist/js/is-yeFwu4dJ.js +2 -0
  499. package/frontend/dist/js/it-AxEMG8Jg.js +2 -0
  500. package/frontend/dist/js/ja-B-VyokB8.js +2 -0
  501. package/frontend/dist/js/ko-CvwzYr9k.js +2 -0
  502. package/frontend/dist/js/{markdown-IZSI9DXw.js → markdown-DCBRWJSK.js} +3 -3
  503. package/frontend/dist/js/markdown-core-BK7WVOb7.js +32 -0
  504. package/frontend/dist/js/{markdown-math-DK846gXU.js → markdown-math-e1ByS3KY.js} +5 -5
  505. package/frontend/dist/js/messageCodeStyles-BQwf7sSz.js +2 -0
  506. package/frontend/dist/js/ms-BhEwb4b7.js +2 -0
  507. package/frontend/dist/js/nl-Dq-QRx1L.js +2 -0
  508. package/frontend/dist/js/pl-BNEy8mQm.js +2 -0
  509. package/frontend/dist/js/{postcss-BJ0okz7n.js → postcss-Bg15EcFE.js} +1 -1
  510. package/frontend/dist/js/pt-_3Jl3FdY.js +2 -0
  511. package/frontend/dist/js/{react-vendor-C5yShNlM.js → react-vendor-BG_HPV6X.js} +1 -1
  512. package/frontend/dist/js/router-vendor-CbexJIsv.js +1 -0
  513. package/frontend/dist/js/ru-IhQiJ8ta.js +2 -0
  514. package/frontend/dist/js/{standalone-Csw3jfJ_.js → standalone-BFrK0R2A.js} +3 -3
  515. package/frontend/dist/js/sv-DWunXUNz.js +2 -0
  516. package/frontend/dist/js/{syntax-highlight-3EEX6CGL.js → syntax-highlight-P2zo9o06.js} +4 -4
  517. package/frontend/dist/js/th-DVnW2yyh.js +2 -0
  518. package/frontend/dist/js/tr-Do8CFmhR.js +2 -0
  519. package/frontend/dist/js/{typescript-CnF4UVx7.js → typescript-DYVyvmm_.js} +1 -1
  520. package/frontend/dist/js/ui-db9meqAB.js +2 -0
  521. package/frontend/dist/js/ui-vendor-C0DZ8-Cm.js +177 -0
  522. package/frontend/dist/js/uk-BNPKIZga.js +2 -0
  523. package/frontend/dist/js/{useTranslation-Bho4HCyg.js → useTranslation-5b5UhAc9.js} +1 -1
  524. package/frontend/dist/js/{utils-D9W1Lnkn.js → utils-BGrdX9ie.js} +1 -1
  525. package/frontend/dist/js/{utils-vendor-DWh39_3x.js → utils-vendor-9bQ62WeD.js} +1 -1
  526. package/frontend/dist/js/vi-CZMFo9je.js +2 -0
  527. package/frontend/dist/js/{workApi-C3uy9EDT.js → workApi-CcprMH_1.js} +3 -3
  528. package/frontend/dist/js/{yaml-DzT5hBSH.js → yaml-DUeWcOOH.js} +2 -2
  529. package/frontend/dist/js/zh-Pg0881v0.js +2 -0
  530. package/frontend/dist/manifest.webmanifest +32 -0
  531. package/frontend/dist/sw.js +125 -0
  532. package/package.json +2 -2
  533. package/plugins/openai.json +52 -9
  534. package/frontend/dist/assets/index-6o-Hc2wS.js +0 -41
  535. package/frontend/dist/assets/index-D-pM957b.css +0 -1
  536. package/frontend/dist/js/ArtifactContainer-CpUi6ekF.js +0 -1
  537. package/frontend/dist/js/ArtifactSandboxFrame-qsj_Y3LV.js +0 -151
  538. package/frontend/dist/js/ArtifactSlideOutPanel-CSFRluWA.js +0 -1
  539. package/frontend/dist/js/AutomationsPage-KUeWoZNv.js +0 -1
  540. package/frontend/dist/js/CalendarPage-D9I972w8.js +0 -1
  541. package/frontend/dist/js/ChatPage-CmBx4oAL.js +0 -16
  542. package/frontend/dist/js/FirstTimeSetup-Df0YJni3.js +0 -1
  543. package/frontend/dist/js/GalleryPage-bLCrdJN_.js +0 -1
  544. package/frontend/dist/js/HomePage-Uc6PEg-U.js +0 -1
  545. package/frontend/dist/js/LibreClawPage-CG63DHg1.js +0 -1
  546. package/frontend/dist/js/ModelSelector-yxJC64rM.js +0 -1
  547. package/frontend/dist/js/ModelsPage-1R_TxgmJ.js +0 -2
  548. package/frontend/dist/js/NotesPage-L65j5HoW.js +0 -1
  549. package/frontend/dist/js/OptimizedSyntaxHighlighter-Dz7D1LPB.js +0 -1
  550. package/frontend/dist/js/PersonasPage-CKeBpHIH.js +0 -13
  551. package/frontend/dist/js/PluginUsagePage-BX1SKMbW.js +0 -1
  552. package/frontend/dist/js/RichMessageContent-DiySVYGB.js +0 -3
  553. package/frontend/dist/js/SettingsModal-68SG1uZ2.js +0 -1
  554. package/frontend/dist/js/SystemPage-CytpZiQV.js +0 -1
  555. package/frontend/dist/js/UserManagementPage-BzJBvZiS.js +0 -1
  556. package/frontend/dist/js/WorkPage-C8gr0QJe.js +0 -6
  557. package/frontend/dist/js/api-BDtqBtqw.js +0 -14
  558. package/frontend/dist/js/appStore-0JnCUlEq.js +0 -2
  559. package/frontend/dist/js/ar-CSpi37Gr.js +0 -2
  560. package/frontend/dist/js/bn-CyH1BeDD.js +0 -2
  561. package/frontend/dist/js/chatStore-CHQ2uINS.js +0 -3
  562. package/frontend/dist/js/cs-Cv2NfRK5.js +0 -2
  563. package/frontend/dist/js/da-D6D6IvPK.js +0 -2
  564. package/frontend/dist/js/de-BNoJJTrT.js +0 -2
  565. package/frontend/dist/js/es-kzo9C7q_.js +0 -2
  566. package/frontend/dist/js/fr-CjHYvJRp.js +0 -2
  567. package/frontend/dist/js/hi-BU98AdCQ.js +0 -2
  568. package/frontend/dist/js/id-CpdedejX.js +0 -2
  569. package/frontend/dist/js/is-Cp5dDWBH.js +0 -2
  570. package/frontend/dist/js/it-CuH_cK4U.js +0 -2
  571. package/frontend/dist/js/ja-BWXXXiHn.js +0 -2
  572. package/frontend/dist/js/ko-DQgv2M1t.js +0 -2
  573. package/frontend/dist/js/markdown-core-BmPPZiuD.js +0 -31
  574. package/frontend/dist/js/messageCodeStyles-BFNR9Wv6.js +0 -2
  575. package/frontend/dist/js/ms-DCcRoIcX.js +0 -2
  576. package/frontend/dist/js/nl-CbKR1_EU.js +0 -2
  577. package/frontend/dist/js/pl-CRyJZccn.js +0 -2
  578. package/frontend/dist/js/pt-BXYmHFGH.js +0 -2
  579. package/frontend/dist/js/router-vendor-DGFGM3EP.js +0 -1
  580. package/frontend/dist/js/ru-M9SyZG4b.js +0 -2
  581. package/frontend/dist/js/sv-DJ486LTU.js +0 -2
  582. package/frontend/dist/js/th-Ch7gKtZv.js +0 -2
  583. package/frontend/dist/js/tr-ClehWhSC.js +0 -2
  584. package/frontend/dist/js/ui-CC-K19wO.js +0 -2
  585. package/frontend/dist/js/ui-vendor-B1UgO3vd.js +0 -177
  586. package/frontend/dist/js/uk-BqAd5zHF.js +0 -2
  587. package/frontend/dist/js/vi-D9gjlDVB.js +0 -2
  588. package/frontend/dist/js/zh-DKTyHkiR.js +0 -2
@@ -172,6 +172,11 @@ class SQLiteKnowledgeCollectionRepository {
172
172
  .prepare('SELECT * FROM knowledge_collections WHERE user_id = ? ORDER BY name COLLATE NOCASE ASC')
173
173
  .all(userId);
174
174
  }
175
+ async findById(collectionId) {
176
+ return (this.database
177
+ .prepare('SELECT * FROM knowledge_collections WHERE id = ?')
178
+ .get(collectionId) ?? null);
179
+ }
175
180
  async replace(collection) {
176
181
  ensureSameOwner(this.database, 'knowledge_collections', collection.id, collection.user_id);
177
182
  this.database
@@ -204,7 +209,7 @@ class SQLiteNoteRepository {
204
209
  return this.database
205
210
  .prepare(`SELECT * FROM notes
206
211
  WHERE user_id = ?
207
- ORDER BY updated_at DESC
212
+ ORDER BY pinned DESC, updated_at DESC
208
213
  LIMIT ?`)
209
214
  .all(userId, maximum);
210
215
  }
@@ -226,14 +231,15 @@ class SQLiteNoteRepository {
226
231
  }
227
232
  this.database
228
233
  .prepare(`INSERT INTO notes
229
- (id, user_id, title, content, created_at, updated_at)
230
- VALUES (?, ?, ?, ?, ?, ?)
234
+ (id, user_id, title, content, pinned, created_at, updated_at)
235
+ VALUES (?, ?, ?, ?, ?, ?, ?)
231
236
  ON CONFLICT(id) DO UPDATE SET
232
237
  title = excluded.title,
233
238
  content = excluded.content,
239
+ pinned = excluded.pinned,
234
240
  updated_at = excluded.updated_at
235
241
  WHERE notes.user_id = excluded.user_id`)
236
- .run(note.id, note.user_id, note.title, note.content, note.created_at, note.updated_at);
242
+ .run(note.id, note.user_id, note.title, note.content, note.pinned, note.created_at, note.updated_at);
237
243
  });
238
244
  replace();
239
245
  }
@@ -249,6 +255,10 @@ class SQLiteNoteRepository {
249
255
  assignments.push('content = ?');
250
256
  values.push(patch.content);
251
257
  }
258
+ if (patch.pinned !== undefined) {
259
+ assignments.push('pinned = ?');
260
+ values.push(patch.pinned);
261
+ }
252
262
  assignments.push('updated_at = ?');
253
263
  values.push(patch.updated_at);
254
264
  const result = this.database
@@ -268,6 +278,110 @@ class SQLiteNoteRepository {
268
278
  .prepare('DELETE FROM notes WHERE id = ? AND user_id = ?')
269
279
  .run(noteId, userId).changes > 0);
270
280
  }
281
+ async findById(noteId) {
282
+ return (this.database.prepare('SELECT * FROM notes WHERE id = ?').get(noteId) ?? null);
283
+ }
284
+ async patchById(noteId, patch) {
285
+ const update = this.database.transaction(() => {
286
+ const assignments = [];
287
+ const values = [];
288
+ if (patch.title !== undefined) {
289
+ assignments.push('title = ?');
290
+ values.push(patch.title);
291
+ }
292
+ if (patch.content !== undefined) {
293
+ assignments.push('content = ?');
294
+ values.push(patch.content);
295
+ }
296
+ if (patch.pinned !== undefined) {
297
+ assignments.push('pinned = ?');
298
+ values.push(patch.pinned);
299
+ }
300
+ assignments.push('updated_at = ?');
301
+ values.push(patch.updated_at);
302
+ const result = this.database
303
+ .prepare(`UPDATE notes SET ${assignments.join(', ')} WHERE id = ?`)
304
+ .run(...values, noteId);
305
+ if (result.changes !== 1)
306
+ return null;
307
+ return this.database
308
+ .prepare('SELECT * FROM notes WHERE id = ?')
309
+ .get(noteId);
310
+ });
311
+ return update.immediate();
312
+ }
313
+ async listRevisions(noteId, maximum) {
314
+ return this.database
315
+ .prepare(`SELECT * FROM note_revisions
316
+ WHERE note_id = ?
317
+ ORDER BY created_at DESC, id DESC
318
+ LIMIT ?`)
319
+ .all(noteId, maximum);
320
+ }
321
+ async findRevision(revisionId) {
322
+ return (this.database
323
+ .prepare('SELECT * FROM note_revisions WHERE id = ?')
324
+ .get(revisionId) ?? null);
325
+ }
326
+ async insertRevision(revision, maximum) {
327
+ const insert = this.database.transaction(() => {
328
+ // Strictly increasing per note so same-millisecond snapshots keep a
329
+ // deterministic order for listing, restore, and pruning.
330
+ const latest = this.database
331
+ .prepare('SELECT COALESCE(MAX(created_at), 0) AS latest FROM note_revisions WHERE note_id = ?')
332
+ .get(revision.note_id);
333
+ const createdAt = Math.max(revision.created_at, latest.latest + 1);
334
+ this.database
335
+ .prepare(`INSERT INTO note_revisions
336
+ (id, note_id, title, content, created_at)
337
+ VALUES (?, ?, ?, ?, ?)`)
338
+ .run(revision.id, revision.note_id, revision.title, revision.content, createdAt);
339
+ this.database
340
+ .prepare(`DELETE FROM note_revisions
341
+ WHERE note_id = ?
342
+ AND id NOT IN (
343
+ SELECT id FROM note_revisions
344
+ WHERE note_id = ?
345
+ ORDER BY created_at DESC, id DESC
346
+ LIMIT ?
347
+ )`)
348
+ .run(revision.note_id, revision.note_id, maximum);
349
+ });
350
+ insert();
351
+ }
352
+ async listAttachments(noteId) {
353
+ return this.database
354
+ .prepare(`SELECT * FROM note_attachments
355
+ WHERE note_id = ?
356
+ ORDER BY created_at ASC, id ASC`)
357
+ .all(noteId);
358
+ }
359
+ async findAttachment(attachmentId) {
360
+ return (this.database
361
+ .prepare('SELECT * FROM note_attachments WHERE id = ?')
362
+ .get(attachmentId) ?? null);
363
+ }
364
+ async insertAttachmentWithLimit(attachment, maximum) {
365
+ const insert = this.database.transaction(() => {
366
+ const row = this.database
367
+ .prepare('SELECT COUNT(*) AS count FROM note_attachments WHERE note_id = ?')
368
+ .get(attachment.note_id);
369
+ if (row.count >= maximum) {
370
+ throw new PersistenceResourceLimitError('note-attachment', maximum);
371
+ }
372
+ this.database
373
+ .prepare(`INSERT INTO note_attachments
374
+ (id, note_id, blob_id, filename, content_type, size, created_at)
375
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
376
+ .run(attachment.id, attachment.note_id, attachment.blob_id, attachment.filename, attachment.content_type, attachment.size, attachment.created_at);
377
+ });
378
+ insert();
379
+ }
380
+ async deleteAttachment(attachmentId) {
381
+ return (this.database
382
+ .prepare('DELETE FROM note_attachments WHERE id = ?')
383
+ .run(attachmentId).changes > 0);
384
+ }
271
385
  }
272
386
  class SQLiteCalendarEventRepository {
273
387
  constructor(database) {
@@ -289,11 +403,57 @@ class SQLiteCalendarEventRepository {
289
403
  LIMIT ?`)
290
404
  .all(userId, maximum);
291
405
  }
406
+ async listByCalendarsBetween(calendarIds, from, to, maximum) {
407
+ if (calendarIds.length === 0)
408
+ return [];
409
+ const placeholders = calendarIds.map(() => '?').join(', ');
410
+ return this.database
411
+ .prepare(`SELECT * FROM calendar_events
412
+ WHERE calendar_id IN (${placeholders})
413
+ AND start_at >= ? AND start_at < ?
414
+ ORDER BY start_at ASC
415
+ LIMIT ?`)
416
+ .all(...calendarIds, from, to, maximum);
417
+ }
418
+ async listRecurringByCalendars(calendarIds, maximum) {
419
+ if (calendarIds.length === 0)
420
+ return [];
421
+ const placeholders = calendarIds.map(() => '?').join(', ');
422
+ return this.database
423
+ .prepare(`SELECT * FROM calendar_events
424
+ WHERE calendar_id IN (${placeholders})
425
+ AND recurrence IS NOT NULL
426
+ ORDER BY start_at ASC
427
+ LIMIT ?`)
428
+ .all(...calendarIds, maximum);
429
+ }
292
430
  async findByOwner(eventId, userId) {
293
431
  return (this.database
294
432
  .prepare('SELECT * FROM calendar_events WHERE id = ? AND user_id = ?')
295
433
  .get(eventId, userId) ?? null);
296
434
  }
435
+ async findById(eventId) {
436
+ return (this.database
437
+ .prepare('SELECT * FROM calendar_events WHERE id = ?')
438
+ .get(eventId) ?? null);
439
+ }
440
+ async listWithReminders(maximum) {
441
+ return this.database
442
+ .prepare(`SELECT * FROM calendar_events
443
+ WHERE reminder_minutes IS NOT NULL
444
+ ORDER BY start_at ASC
445
+ LIMIT ?`)
446
+ .all(maximum);
447
+ }
448
+ async markReminded(eventId, occurrenceStart) {
449
+ return (this.database
450
+ .prepare(`UPDATE calendar_events
451
+ SET last_reminded_occurrence = ?
452
+ WHERE id = ?
453
+ AND (last_reminded_occurrence IS NULL
454
+ OR last_reminded_occurrence < ?)`)
455
+ .run(occurrenceStart, eventId, occurrenceStart).changes > 0);
456
+ }
297
457
  async replaceWithLimit(event, maximum) {
298
458
  const replace = this.database.transaction(() => {
299
459
  const exists = ensureSameOwner(this.database, 'calendar_events', event.id, event.user_id);
@@ -308,8 +468,9 @@ class SQLiteCalendarEventRepository {
308
468
  this.database
309
469
  .prepare(`INSERT INTO calendar_events
310
470
  (id, user_id, title, notes, start_at, end_at, all_day,
311
- recurrence, created_at, updated_at)
312
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
471
+ recurrence, calendar_id, reminder_minutes,
472
+ last_reminded_occurrence, created_at, updated_at)
473
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
313
474
  ON CONFLICT(id) DO UPDATE SET
314
475
  title = excluded.title,
315
476
  notes = excluded.notes,
@@ -317,186 +478,1307 @@ class SQLiteCalendarEventRepository {
317
478
  end_at = excluded.end_at,
318
479
  all_day = excluded.all_day,
319
480
  recurrence = excluded.recurrence,
481
+ calendar_id = excluded.calendar_id,
482
+ reminder_minutes = excluded.reminder_minutes,
483
+ last_reminded_occurrence = excluded.last_reminded_occurrence,
320
484
  updated_at = excluded.updated_at
321
485
  WHERE calendar_events.user_id = excluded.user_id`)
322
- .run(event.id, event.user_id, event.title, event.notes, event.start_at, event.end_at, event.all_day, event.recurrence, event.created_at, event.updated_at);
486
+ .run(event.id, event.user_id, event.title, event.notes, event.start_at, event.end_at, event.all_day, event.recurrence, event.calendar_id, event.reminder_minutes, event.last_reminded_occurrence, event.created_at, event.updated_at);
487
+ });
488
+ replace();
489
+ }
490
+ async deleteByOwner(eventId, userId) {
491
+ return (this.database
492
+ .prepare('DELETE FROM calendar_events WHERE id = ? AND user_id = ?')
493
+ .run(eventId, userId).changes > 0);
494
+ }
495
+ }
496
+ class SQLiteCalendarRepository {
497
+ constructor(database) {
498
+ this.database = database;
499
+ }
500
+ async listByOwner(userId, maximum) {
501
+ return this.database
502
+ .prepare(`SELECT * FROM calendars
503
+ WHERE user_id = ?
504
+ ORDER BY created_at ASC
505
+ LIMIT ?`)
506
+ .all(userId, maximum);
507
+ }
508
+ async findByOwner(calendarId, userId) {
509
+ return (this.database
510
+ .prepare('SELECT * FROM calendars WHERE id = ? AND user_id = ?')
511
+ .get(calendarId, userId) ?? null);
512
+ }
513
+ async findById(calendarId) {
514
+ return (this.database
515
+ .prepare('SELECT * FROM calendars WHERE id = ?')
516
+ .get(calendarId) ?? null);
517
+ }
518
+ async replaceWithLimit(calendar, maximum) {
519
+ const replace = this.database.transaction(() => {
520
+ const exists = ensureSameOwner(this.database, 'calendars', calendar.id, calendar.user_id);
521
+ if (!exists) {
522
+ const row = this.database
523
+ .prepare('SELECT COUNT(*) AS count FROM calendars WHERE user_id = ?')
524
+ .get(calendar.user_id);
525
+ if (row.count >= maximum) {
526
+ throw new PersistenceResourceLimitError('calendar', maximum);
527
+ }
528
+ }
529
+ this.database
530
+ .prepare(`INSERT INTO calendars
531
+ (id, user_id, name, color, created_at, updated_at)
532
+ VALUES (?, ?, ?, ?, ?, ?)
533
+ ON CONFLICT(id) DO UPDATE SET
534
+ name = excluded.name,
535
+ color = excluded.color,
536
+ updated_at = excluded.updated_at
537
+ WHERE calendars.user_id = excluded.user_id`)
538
+ .run(calendar.id, calendar.user_id, calendar.name, calendar.color, calendar.created_at, calendar.updated_at);
539
+ });
540
+ replace();
541
+ }
542
+ async deleteAndDetach(calendarId, userId) {
543
+ const remove = this.database.transaction(() => {
544
+ const deleted = this.database
545
+ .prepare('DELETE FROM calendars WHERE id = ? AND user_id = ?')
546
+ .run(calendarId, userId).changes > 0;
547
+ if (deleted) {
548
+ this.database
549
+ .prepare(`UPDATE calendar_events
550
+ SET calendar_id = NULL
551
+ WHERE calendar_id = ? AND user_id = ?`)
552
+ .run(calendarId, userId);
553
+ }
554
+ return deleted;
555
+ });
556
+ return remove();
557
+ }
558
+ }
559
+ class SQLiteChannelRepository {
560
+ constructor(database) {
561
+ this.database = database;
562
+ }
563
+ async insertWithOwner(channel, owner, maximumPerUser) {
564
+ const insert = this.database.transaction(() => {
565
+ const count = this.database
566
+ .prepare(`SELECT COUNT(*) AS count
567
+ FROM channels
568
+ WHERE created_by = ?`)
569
+ .get(owner.user_id);
570
+ if (count.count >= maximumPerUser) {
571
+ throw new PersistenceResourceLimitError('channel', maximumPerUser);
572
+ }
573
+ this.database
574
+ .prepare(`INSERT INTO channels
575
+ (id, type, name, description, dm_key, created_by,
576
+ created_at, updated_at, archived_at)
577
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
578
+ .run(channel.id, channel.type, channel.name, channel.description, channel.dm_key, channel.created_by, channel.created_at, channel.updated_at, channel.archived_at);
579
+ this.database
580
+ .prepare(`INSERT INTO channel_members
581
+ (channel_id, user_id, role, joined_at, last_read_at)
582
+ VALUES (?, ?, ?, ?, ?)`)
583
+ .run(owner.channel_id, owner.user_id, owner.role, owner.joined_at, owner.last_read_at);
584
+ });
585
+ insert();
586
+ }
587
+ async findById(channelId) {
588
+ return (this.database
589
+ .prepare('SELECT * FROM channels WHERE id = ?')
590
+ .get(channelId) ?? null);
591
+ }
592
+ async findByDmKey(dmKey) {
593
+ return (this.database
594
+ .prepare('SELECT * FROM channels WHERE dm_key = ?')
595
+ .get(dmKey) ?? null);
596
+ }
597
+ async listForUser(userId, maximum) {
598
+ const rows = this.database
599
+ .prepare(`SELECT
600
+ c.id, c.type, c.name, c.description, c.dm_key, c.created_by,
601
+ c.created_at, c.updated_at, c.archived_at,
602
+ m.channel_id AS member_channel_id, m.user_id AS member_user_id,
603
+ m.role AS member_role, m.joined_at AS member_joined_at,
604
+ m.last_read_at AS member_last_read_at
605
+ FROM channel_members m
606
+ JOIN channels c ON c.id = m.channel_id
607
+ WHERE m.user_id = ?
608
+ ORDER BY c.updated_at DESC
609
+ LIMIT ?`)
610
+ .all(userId, maximum);
611
+ return rows.map(row => ({
612
+ channel: {
613
+ id: row.id,
614
+ type: row.type,
615
+ name: row.name,
616
+ description: row.description,
617
+ dm_key: row.dm_key,
618
+ created_by: row.created_by,
619
+ created_at: row.created_at,
620
+ updated_at: row.updated_at,
621
+ archived_at: row.archived_at,
622
+ },
623
+ member: {
624
+ channel_id: row.member_channel_id,
625
+ user_id: row.member_user_id,
626
+ role: row.member_role,
627
+ joined_at: row.member_joined_at,
628
+ last_read_at: row.member_last_read_at,
629
+ },
630
+ }));
631
+ }
632
+ async listPublic(maximum) {
633
+ return this.database
634
+ .prepare(`SELECT * FROM channels
635
+ WHERE type = 'public' AND archived_at IS NULL
636
+ ORDER BY updated_at DESC
637
+ LIMIT ?`)
638
+ .all(maximum);
639
+ }
640
+ async update(channel) {
641
+ this.database
642
+ .prepare(`UPDATE channels SET
643
+ name = ?,
644
+ description = ?,
645
+ updated_at = ?,
646
+ archived_at = ?
647
+ WHERE id = ?`)
648
+ .run(channel.name, channel.description, channel.updated_at, channel.archived_at, channel.id);
649
+ }
650
+ async delete(channelId) {
651
+ return (this.database.prepare('DELETE FROM channels WHERE id = ?').run(channelId)
652
+ .changes > 0);
653
+ }
654
+ async upsertMember(member, maximumMembers) {
655
+ const upsert = this.database.transaction(() => {
656
+ const existing = this.database
657
+ .prepare('SELECT 1 FROM channel_members WHERE channel_id = ? AND user_id = ?')
658
+ .get(member.channel_id, member.user_id);
659
+ if (!existing) {
660
+ const count = this.database
661
+ .prepare('SELECT COUNT(*) AS count FROM channel_members WHERE channel_id = ?')
662
+ .get(member.channel_id);
663
+ if (count.count >= maximumMembers) {
664
+ throw new PersistenceResourceLimitError('channel-member', maximumMembers);
665
+ }
666
+ }
667
+ this.database
668
+ .prepare(`INSERT INTO channel_members
669
+ (channel_id, user_id, role, joined_at, last_read_at)
670
+ VALUES (?, ?, ?, ?, ?)
671
+ ON CONFLICT(channel_id, user_id) DO UPDATE SET
672
+ role = excluded.role`)
673
+ .run(member.channel_id, member.user_id, member.role, member.joined_at, member.last_read_at);
674
+ });
675
+ upsert();
676
+ }
677
+ async findMember(channelId, userId) {
678
+ return (this.database
679
+ .prepare('SELECT * FROM channel_members WHERE channel_id = ? AND user_id = ?')
680
+ .get(channelId, userId) ??
681
+ null);
682
+ }
683
+ async listMembers(channelId, maximum) {
684
+ return this.database
685
+ .prepare(`SELECT * FROM channel_members
686
+ WHERE channel_id = ?
687
+ ORDER BY joined_at ASC
688
+ LIMIT ?`)
689
+ .all(channelId, maximum);
690
+ }
691
+ async removeMember(channelId, userId) {
692
+ return (this.database
693
+ .prepare('DELETE FROM channel_members WHERE channel_id = ? AND user_id = ?')
694
+ .run(channelId, userId).changes > 0);
695
+ }
696
+ async advanceLastRead(channelId, userId, lastReadAt) {
697
+ return (this.database
698
+ .prepare(`UPDATE channel_members
699
+ SET last_read_at = ?
700
+ WHERE channel_id = ? AND user_id = ? AND last_read_at < ?`)
701
+ .run(lastReadAt, channelId, userId, lastReadAt).changes > 0);
702
+ }
703
+ async unreadSummaryForUser(userId) {
704
+ return this.database
705
+ .prepare(`SELECT
706
+ m.channel_id,
707
+ COUNT(msg.id) AS unread_count,
708
+ MAX(msg.created_at) AS latest_message_at
709
+ FROM channel_members m
710
+ LEFT JOIN channel_messages msg
711
+ ON msg.channel_id = m.channel_id
712
+ AND msg.created_at > m.last_read_at
713
+ AND msg.deleted_at IS NULL
714
+ AND (msg.user_id IS NULL OR msg.user_id != m.user_id
715
+ OR msg.author_kind != 'user')
716
+ WHERE m.user_id = ?
717
+ GROUP BY m.channel_id`)
718
+ .all(userId);
719
+ }
720
+ }
721
+ class SQLiteChannelMessageRepository {
722
+ constructor(database) {
723
+ this.database = database;
724
+ }
725
+ insertMessageRow(message) {
726
+ this.database
727
+ .prepare(`INSERT INTO channel_messages
728
+ (id, channel_id, user_id, parent_id, author_kind, model, content,
729
+ metadata, created_at, updated_at, edited_at, deleted_at,
730
+ pinned_at, pinned_by)
731
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
732
+ .run(message.id, message.channel_id, message.user_id, message.parent_id, message.author_kind, message.model, message.content, message.metadata, message.created_at, message.updated_at, message.edited_at, message.deleted_at, message.pinned_at, message.pinned_by);
733
+ }
734
+ async insertIfAbsent(message, attachments, maximumPerChannel) {
735
+ const insert = this.database.transaction(() => {
736
+ const existing = this.database
737
+ .prepare('SELECT * FROM channel_messages WHERE id = ?')
738
+ .get(message.id);
739
+ if (existing) {
740
+ return { stored: existing, inserted: false };
741
+ }
742
+ const count = this.database
743
+ .prepare('SELECT COUNT(*) AS count FROM channel_messages WHERE channel_id = ?')
744
+ .get(message.channel_id);
745
+ if (count.count >= maximumPerChannel) {
746
+ throw new PersistenceResourceLimitError('channel-message', maximumPerChannel);
747
+ }
748
+ this.insertMessageRow(message);
749
+ const insertAttachment = this.database.prepare(`INSERT INTO channel_attachments
750
+ (id, message_id, channel_id, blob_id, filename, content_type,
751
+ size, created_by, created_at)
752
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
753
+ for (const attachment of attachments) {
754
+ if (attachment.message_id !== message.id) {
755
+ throw new Error('A channel attachment does not belong to its message');
756
+ }
757
+ insertAttachment.run(attachment.id, attachment.message_id, attachment.channel_id, attachment.blob_id, attachment.filename, attachment.content_type, attachment.size, attachment.created_by, attachment.created_at);
758
+ }
759
+ this.database
760
+ .prepare('UPDATE channels SET updated_at = ? WHERE id = ?')
761
+ .run(message.created_at, message.channel_id);
762
+ return { stored: message, inserted: true };
763
+ });
764
+ return insert();
765
+ }
766
+ async findById(messageId) {
767
+ return (this.database
768
+ .prepare('SELECT * FROM channel_messages WHERE id = ?')
769
+ .get(messageId) ?? null);
770
+ }
771
+ async listPage(channelId, options) {
772
+ const clauses = ['channel_id = ?'];
773
+ const parameters = [channelId];
774
+ if (options.parentId === null) {
775
+ clauses.push('parent_id IS NULL');
776
+ }
777
+ else if (options.parentId !== undefined) {
778
+ clauses.push('parent_id = ?');
779
+ parameters.push(options.parentId);
780
+ }
781
+ if (options.before) {
782
+ clauses.push('(created_at < ? OR (created_at = ? AND id < ?))');
783
+ parameters.push(options.before.created_at, options.before.created_at, options.before.id);
784
+ }
785
+ if (options.after) {
786
+ clauses.push('(created_at > ? OR (created_at = ? AND id > ?))');
787
+ parameters.push(options.after.created_at, options.after.created_at, options.after.id);
788
+ }
789
+ const direction = options.after ? 'ASC' : 'DESC';
790
+ parameters.push(options.limit);
791
+ return this.database
792
+ .prepare(`SELECT * FROM channel_messages
793
+ WHERE ${clauses.join(' AND ')}
794
+ ORDER BY created_at ${direction}, id ${direction}
795
+ LIMIT ?`)
796
+ .all(...parameters);
797
+ }
798
+ async listThread(parentId, maximum) {
799
+ return this.database
800
+ .prepare(`SELECT * FROM channel_messages
801
+ WHERE parent_id = ?
802
+ ORDER BY created_at ASC, id ASC
803
+ LIMIT ?`)
804
+ .all(parentId, maximum);
805
+ }
806
+ async countThreadReplies(parentIds) {
807
+ if (parentIds.length === 0)
808
+ return {};
809
+ const placeholders = parentIds.map(() => '?').join(', ');
810
+ const rows = this.database
811
+ .prepare(`SELECT parent_id, COUNT(*) AS count
812
+ FROM channel_messages
813
+ WHERE parent_id IN (${placeholders}) AND deleted_at IS NULL
814
+ GROUP BY parent_id`)
815
+ .all(...parentIds);
816
+ const counts = {};
817
+ for (const row of rows)
818
+ counts[row.parent_id] = row.count;
819
+ return counts;
820
+ }
821
+ async update(message) {
822
+ this.database
823
+ .prepare(`UPDATE channel_messages SET
824
+ content = ?,
825
+ metadata = ?,
826
+ updated_at = ?,
827
+ edited_at = ?,
828
+ deleted_at = ?,
829
+ pinned_at = ?,
830
+ pinned_by = ?
831
+ WHERE id = ?`)
832
+ .run(message.content, message.metadata, message.updated_at, message.edited_at, message.deleted_at, message.pinned_at, message.pinned_by, message.id);
833
+ }
834
+ async listPinned(channelId, maximum) {
835
+ return this.database
836
+ .prepare(`SELECT * FROM channel_messages
837
+ WHERE channel_id = ? AND pinned_at IS NOT NULL AND deleted_at IS NULL
838
+ ORDER BY pinned_at DESC
839
+ LIMIT ?`)
840
+ .all(channelId, maximum);
841
+ }
842
+ async listRecentForChannels(channelIds, maximum) {
843
+ if (channelIds.length === 0)
844
+ return [];
845
+ const placeholders = channelIds.map(() => '?').join(', ');
846
+ return this.database
847
+ .prepare(`SELECT * FROM channel_messages
848
+ WHERE channel_id IN (${placeholders}) AND deleted_at IS NULL
849
+ ORDER BY created_at DESC
850
+ LIMIT ?`)
851
+ .all(...channelIds, maximum);
852
+ }
853
+ async addReaction(reaction, maximumPerMessage) {
854
+ const add = this.database.transaction(() => {
855
+ const existing = this.database
856
+ .prepare(`SELECT 1 FROM channel_reactions
857
+ WHERE message_id = ? AND user_id = ? AND emoji = ?`)
858
+ .get(reaction.message_id, reaction.user_id, reaction.emoji);
859
+ if (existing)
860
+ return false;
861
+ const count = this.database
862
+ .prepare('SELECT COUNT(*) AS count FROM channel_reactions WHERE message_id = ?')
863
+ .get(reaction.message_id);
864
+ if (count.count >= maximumPerMessage) {
865
+ throw new PersistenceResourceLimitError('channel-reaction', maximumPerMessage);
866
+ }
867
+ this.database
868
+ .prepare(`INSERT INTO channel_reactions
869
+ (id, message_id, user_id, emoji, created_at)
870
+ VALUES (?, ?, ?, ?, ?)`)
871
+ .run(reaction.id, reaction.message_id, reaction.user_id, reaction.emoji, reaction.created_at);
872
+ return true;
873
+ });
874
+ return add();
875
+ }
876
+ async removeReaction(messageId, userId, emoji) {
877
+ return (this.database
878
+ .prepare(`DELETE FROM channel_reactions
879
+ WHERE message_id = ? AND user_id = ? AND emoji = ?`)
880
+ .run(messageId, userId, emoji).changes > 0);
881
+ }
882
+ async listReactionsForMessages(messageIds) {
883
+ if (messageIds.length === 0)
884
+ return [];
885
+ const placeholders = messageIds.map(() => '?').join(', ');
886
+ return this.database
887
+ .prepare(`SELECT * FROM channel_reactions
888
+ WHERE message_id IN (${placeholders})
889
+ ORDER BY created_at ASC`)
890
+ .all(...messageIds);
891
+ }
892
+ async findAttachment(attachmentId) {
893
+ return (this.database
894
+ .prepare('SELECT * FROM channel_attachments WHERE id = ?')
895
+ .get(attachmentId) ?? null);
896
+ }
897
+ async listAttachmentsForMessages(messageIds) {
898
+ if (messageIds.length === 0)
899
+ return [];
900
+ const placeholders = messageIds.map(() => '?').join(', ');
901
+ return this.database
902
+ .prepare(`SELECT * FROM channel_attachments
903
+ WHERE message_id IN (${placeholders})
904
+ ORDER BY created_at ASC`)
905
+ .all(...messageIds);
906
+ }
907
+ async listAttachmentBlobIds(channelId) {
908
+ return this.database
909
+ .prepare('SELECT blob_id, created_by FROM channel_attachments WHERE channel_id = ?')
910
+ .all(channelId);
911
+ }
912
+ }
913
+ class SQLiteNotificationRepository {
914
+ constructor(database) {
915
+ this.database = database;
916
+ }
917
+ async insertWithLimit(notification, maximumPerUser) {
918
+ const insert = this.database.transaction(() => {
919
+ const result = this.database
920
+ .prepare(`INSERT INTO notifications
921
+ (id, user_id, type, title, body, href, source_key,
922
+ created_at, read_at)
923
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
924
+ ON CONFLICT(user_id, source_key)
925
+ WHERE source_key IS NOT NULL
926
+ DO NOTHING`)
927
+ .run(notification.id, notification.user_id, notification.type, notification.title, notification.body, notification.href, notification.source_key, notification.created_at, notification.read_at);
928
+ if (result.changes === 0)
929
+ return false;
930
+ this.database
931
+ .prepare(`DELETE FROM notifications
932
+ WHERE user_id = ?
933
+ AND id NOT IN (
934
+ SELECT id FROM notifications
935
+ WHERE user_id = ?
936
+ ORDER BY created_at DESC, id DESC
937
+ LIMIT ?
938
+ )`)
939
+ .run(notification.user_id, notification.user_id, maximumPerUser);
940
+ return true;
941
+ });
942
+ return insert();
943
+ }
944
+ async listByOwner(userId, options) {
945
+ const clauses = ['user_id = ?'];
946
+ const parameters = [userId];
947
+ if (options.before !== undefined) {
948
+ clauses.push('created_at < ?');
949
+ parameters.push(options.before);
950
+ }
951
+ if (options.unreadOnly) {
952
+ clauses.push('read_at IS NULL');
953
+ }
954
+ parameters.push(options.limit);
955
+ return this.database
956
+ .prepare(`SELECT * FROM notifications
957
+ WHERE ${clauses.join(' AND ')}
958
+ ORDER BY created_at DESC, id DESC
959
+ LIMIT ?`)
960
+ .all(...parameters);
961
+ }
962
+ async countUnread(userId) {
963
+ const row = this.database
964
+ .prepare(`SELECT COUNT(*) AS count FROM notifications
965
+ WHERE user_id = ? AND read_at IS NULL`)
966
+ .get(userId);
967
+ return row.count;
968
+ }
969
+ async markRead(notificationId, userId, readAt) {
970
+ return (this.database
971
+ .prepare(`UPDATE notifications SET read_at = ?
972
+ WHERE id = ? AND user_id = ? AND read_at IS NULL`)
973
+ .run(readAt, notificationId, userId).changes > 0);
974
+ }
975
+ async markAllRead(userId, readAt) {
976
+ return this.database
977
+ .prepare(`UPDATE notifications SET read_at = ?
978
+ WHERE user_id = ? AND read_at IS NULL`)
979
+ .run(readAt, userId).changes;
980
+ }
981
+ async deleteByOwner(notificationId, userId) {
982
+ return (this.database
983
+ .prepare('DELETE FROM notifications WHERE id = ? AND user_id = ?')
984
+ .run(notificationId, userId).changes > 0);
985
+ }
986
+ }
987
+ class SQLiteWebhookTargetRepository {
988
+ constructor(database) {
989
+ this.database = database;
990
+ }
991
+ async list(maximum) {
992
+ return this.database
993
+ .prepare('SELECT * FROM webhook_targets ORDER BY created_at ASC LIMIT ?')
994
+ .all(maximum);
995
+ }
996
+ async findById(targetId) {
997
+ return (this.database
998
+ .prepare('SELECT * FROM webhook_targets WHERE id = ?')
999
+ .get(targetId) ?? null);
1000
+ }
1001
+ async replaceWithLimit(target, maximum) {
1002
+ const replace = this.database.transaction(() => {
1003
+ const existing = this.database
1004
+ .prepare('SELECT 1 FROM webhook_targets WHERE id = ?')
1005
+ .get(target.id);
1006
+ if (!existing) {
1007
+ const row = this.database
1008
+ .prepare('SELECT COUNT(*) AS count FROM webhook_targets')
1009
+ .get();
1010
+ if (row.count >= maximum) {
1011
+ throw new PersistenceResourceLimitError('webhook-target', maximum);
1012
+ }
1013
+ }
1014
+ this.database
1015
+ .prepare(`INSERT INTO webhook_targets
1016
+ (id, name, url, secret, events, enabled, created_by,
1017
+ created_at, updated_at)
1018
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1019
+ ON CONFLICT(id) DO UPDATE SET
1020
+ name = excluded.name,
1021
+ url = excluded.url,
1022
+ secret = excluded.secret,
1023
+ events = excluded.events,
1024
+ enabled = excluded.enabled,
1025
+ updated_at = excluded.updated_at`)
1026
+ .run(target.id, target.name, target.url, target.secret, target.events, target.enabled, target.created_by, target.created_at, target.updated_at);
323
1027
  });
324
1028
  replace();
325
1029
  }
326
- async deleteByOwner(eventId, userId) {
1030
+ async delete(targetId) {
1031
+ return (this.database
1032
+ .prepare('DELETE FROM webhook_targets WHERE id = ?')
1033
+ .run(targetId).changes > 0);
1034
+ }
1035
+ }
1036
+ class SQLitePushSubscriptionRepository {
1037
+ constructor(database) {
1038
+ this.database = database;
1039
+ }
1040
+ async upsertByEndpoint(record) {
1041
+ this.database
1042
+ .prepare(`INSERT INTO push_subscriptions
1043
+ (id, user_id, session_id, endpoint_lookup, subscription,
1044
+ user_agent, created_at, last_used_at)
1045
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1046
+ ON CONFLICT(endpoint_lookup) DO UPDATE SET
1047
+ user_id = excluded.user_id,
1048
+ session_id = excluded.session_id,
1049
+ subscription = excluded.subscription,
1050
+ user_agent = excluded.user_agent,
1051
+ last_used_at = excluded.last_used_at`)
1052
+ .run(record.id, record.user_id, record.session_id, record.endpoint_lookup, record.subscription, record.user_agent, record.created_at, record.last_used_at);
1053
+ }
1054
+ async listByUser(userId) {
1055
+ return this.database
1056
+ .prepare(`SELECT * FROM push_subscriptions
1057
+ WHERE user_id = ?
1058
+ ORDER BY created_at ASC`)
1059
+ .all(userId);
1060
+ }
1061
+ async findByLookup(endpointLookup) {
1062
+ return (this.database
1063
+ .prepare('SELECT * FROM push_subscriptions WHERE endpoint_lookup = ?')
1064
+ .get(endpointLookup) ??
1065
+ null);
1066
+ }
1067
+ async findById(id) {
1068
+ return (this.database
1069
+ .prepare('SELECT * FROM push_subscriptions WHERE id = ?')
1070
+ .get(id) ?? null);
1071
+ }
1072
+ async touch(id, lastUsedAt) {
1073
+ return (this.database
1074
+ .prepare('UPDATE push_subscriptions SET last_used_at = ? WHERE id = ?')
1075
+ .run(lastUsedAt, id).changes > 0);
1076
+ }
1077
+ async delete(id) {
1078
+ return (this.database
1079
+ .prepare('DELETE FROM push_subscriptions WHERE id = ?')
1080
+ .run(id).changes > 0);
1081
+ }
1082
+ async deleteByLookup(userId, endpointLookup) {
1083
+ return (this.database
1084
+ .prepare(`DELETE FROM push_subscriptions
1085
+ WHERE user_id = ? AND endpoint_lookup = ?`)
1086
+ .run(userId, endpointLookup).changes > 0);
1087
+ }
1088
+ async deleteForSession(sessionId) {
1089
+ return this.database
1090
+ .prepare('DELETE FROM push_subscriptions WHERE session_id = ?')
1091
+ .run(sessionId).changes;
1092
+ }
1093
+ async deleteForUser(userId) {
1094
+ return this.database
1095
+ .prepare('DELETE FROM push_subscriptions WHERE user_id = ?')
1096
+ .run(userId).changes;
1097
+ }
1098
+ }
1099
+ class SQLiteRecoveryDrillRepository {
1100
+ constructor(database) {
1101
+ this.database = database;
1102
+ }
1103
+ async insert(record) {
1104
+ this.database
1105
+ .prepare(`INSERT INTO recovery_drills
1106
+ (id, status, origin, started_at, finished_at, snapshot_bytes,
1107
+ rpo_seconds, restore_ms, error, report, created_by, created_at)
1108
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1109
+ .run(record.id, record.status, record.origin, record.started_at, record.finished_at, record.snapshot_bytes, record.rpo_seconds, record.restore_ms, record.error, record.report, record.created_by, record.created_at);
1110
+ }
1111
+ async update(id, patch) {
1112
+ const assignments = [];
1113
+ const parameters = [];
1114
+ for (const [column, value] of Object.entries(patch)) {
1115
+ if (value === undefined)
1116
+ continue;
1117
+ assignments.push(`${column} = ?`);
1118
+ parameters.push(value);
1119
+ }
1120
+ if (assignments.length === 0)
1121
+ return false;
1122
+ parameters.push(id);
1123
+ return (this.database
1124
+ .prepare(`UPDATE recovery_drills SET ${assignments.join(', ')} WHERE id = ?`)
1125
+ .run(...parameters).changes > 0);
1126
+ }
1127
+ async findById(id) {
1128
+ return (this.database
1129
+ .prepare('SELECT * FROM recovery_drills WHERE id = ?')
1130
+ .get(id) ?? null);
1131
+ }
1132
+ async list(limit) {
1133
+ return this.database
1134
+ .prepare(`SELECT * FROM recovery_drills
1135
+ ORDER BY started_at DESC, id DESC
1136
+ LIMIT ?`)
1137
+ .all(limit);
1138
+ }
1139
+ async findLatestFinished() {
1140
+ return (this.database
1141
+ .prepare(`SELECT * FROM recovery_drills
1142
+ WHERE status != 'running'
1143
+ ORDER BY started_at DESC, id DESC
1144
+ LIMIT 1`)
1145
+ .get() ?? null);
1146
+ }
1147
+ async findRunning() {
1148
+ return (this.database
1149
+ .prepare(`SELECT * FROM recovery_drills
1150
+ WHERE status = 'running'
1151
+ ORDER BY started_at DESC, id DESC
1152
+ LIMIT 1`)
1153
+ .get() ?? null);
1154
+ }
1155
+ async pruneToLimit(limit) {
1156
+ return this.database
1157
+ .prepare(`DELETE FROM recovery_drills
1158
+ WHERE status != 'running'
1159
+ AND id NOT IN (
1160
+ SELECT id FROM recovery_drills
1161
+ WHERE status != 'running'
1162
+ ORDER BY started_at DESC, id DESC
1163
+ LIMIT ?
1164
+ )`)
1165
+ .run(limit).changes;
1166
+ }
1167
+ }
1168
+ class SQLiteAutomationRepository {
1169
+ constructor(database) {
1170
+ this.database = database;
1171
+ }
1172
+ async listByOwner(userId, maximum) {
1173
+ return this.database
1174
+ .prepare(`SELECT * FROM automations
1175
+ WHERE user_id = ?
1176
+ ORDER BY updated_at DESC
1177
+ LIMIT ?`)
1178
+ .all(userId, maximum);
1179
+ }
1180
+ async findByOwner(automationId, userId) {
1181
+ return (this.database
1182
+ .prepare('SELECT * FROM automations WHERE id = ? AND user_id = ?')
1183
+ .get(automationId, userId) ??
1184
+ null);
1185
+ }
1186
+ async findById(automationId) {
1187
+ return (this.database
1188
+ .prepare('SELECT * FROM automations WHERE id = ?')
1189
+ .get(automationId) ?? null);
1190
+ }
1191
+ async replaceWithLimit(automation, maximum) {
1192
+ const replace = this.database.transaction(() => {
1193
+ const exists = ensureSameOwner(this.database, 'automations', automation.id, automation.user_id);
1194
+ if (!exists) {
1195
+ const row = this.database
1196
+ .prepare('SELECT COUNT(*) AS count FROM automations WHERE user_id = ?')
1197
+ .get(automation.user_id);
1198
+ if (row.count >= maximum) {
1199
+ throw new PersistenceResourceLimitError('automation', maximum);
1200
+ }
1201
+ }
1202
+ this.database
1203
+ .prepare(`INSERT INTO automations
1204
+ (id, user_id, name, instructions, triggers, provider, model,
1205
+ notify, status, next_run_at, last_run_at, created_at, updated_at)
1206
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1207
+ ON CONFLICT(id) DO UPDATE SET
1208
+ name = excluded.name,
1209
+ instructions = excluded.instructions,
1210
+ triggers = excluded.triggers,
1211
+ provider = excluded.provider,
1212
+ model = excluded.model,
1213
+ notify = excluded.notify,
1214
+ status = excluded.status,
1215
+ next_run_at = excluded.next_run_at,
1216
+ last_run_at = excluded.last_run_at,
1217
+ updated_at = excluded.updated_at
1218
+ WHERE automations.user_id = excluded.user_id`)
1219
+ .run(automation.id, automation.user_id, automation.name, automation.instructions, automation.triggers, automation.provider, automation.model, automation.notify, automation.status, automation.next_run_at, automation.last_run_at, automation.created_at, automation.updated_at);
1220
+ });
1221
+ replace();
1222
+ }
1223
+ async listDue(now, maximum) {
1224
+ return this.database
1225
+ .prepare(`SELECT * FROM automations
1226
+ WHERE status = 'active'
1227
+ AND next_run_at IS NOT NULL
1228
+ AND next_run_at <= ?
1229
+ ORDER BY next_run_at ASC
1230
+ LIMIT ?`)
1231
+ .all(now, maximum);
1232
+ }
1233
+ async advanceNextRun(automationId, observedNextRunAt, nextRunAt, lastRunAt) {
1234
+ return (this.database
1235
+ .prepare(`UPDATE automations
1236
+ SET next_run_at = ?, last_run_at = ?
1237
+ WHERE id = ? AND next_run_at = ?`)
1238
+ .run(nextRunAt, lastRunAt, automationId, observedNextRunAt).changes > 0);
1239
+ }
1240
+ async setStatus(automationId, userId, status, nextRunAt, updatedAt) {
1241
+ return (this.database
1242
+ .prepare(`UPDATE automations
1243
+ SET status = ?, next_run_at = ?, updated_at = ?
1244
+ WHERE id = ? AND user_id = ?`)
1245
+ .run(status, nextRunAt, updatedAt, automationId, userId).changes > 0);
1246
+ }
1247
+ async deleteByOwner(automationId, userId) {
1248
+ return (this.database
1249
+ .prepare('DELETE FROM automations WHERE id = ? AND user_id = ?')
1250
+ .run(automationId, userId).changes > 0);
1251
+ }
1252
+ }
1253
+ class SQLiteAutomationRunRepository {
1254
+ constructor(database) {
1255
+ this.database = database;
1256
+ }
1257
+ async insert(run) {
1258
+ this.database
1259
+ .prepare(`INSERT INTO automation_runs
1260
+ (id, automation_id, user_id, scheduled_for, started_at,
1261
+ finished_at, status, session_id, assistant_message_id, error,
1262
+ seen_at, created_at)
1263
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1264
+ .run(run.id, run.automation_id, run.user_id, run.scheduled_for, run.started_at, run.finished_at, run.status, run.session_id, run.assistant_message_id, run.error, run.seen_at, run.created_at);
1265
+ }
1266
+ async findByOwner(runId, userId) {
1267
+ return (this.database
1268
+ .prepare('SELECT * FROM automation_runs WHERE id = ? AND user_id = ?')
1269
+ .get(runId, userId) ?? null);
1270
+ }
1271
+ async listByOwner(userId, options) {
1272
+ const clauses = ['user_id = ?'];
1273
+ const values = [userId];
1274
+ if (options.automationId !== undefined) {
1275
+ clauses.push('automation_id = ?');
1276
+ values.push(options.automationId);
1277
+ }
1278
+ if (options.from !== undefined) {
1279
+ clauses.push('scheduled_for >= ?');
1280
+ values.push(options.from);
1281
+ }
1282
+ if (options.to !== undefined) {
1283
+ clauses.push('scheduled_for < ?');
1284
+ values.push(options.to);
1285
+ }
1286
+ values.push(options.maximum);
1287
+ return this.database
1288
+ .prepare(`SELECT * FROM automation_runs
1289
+ WHERE ${clauses.join(' AND ')}
1290
+ ORDER BY scheduled_for DESC
1291
+ LIMIT ?`)
1292
+ .all(...values);
1293
+ }
1294
+ async listUnfinished(maximum) {
1295
+ return this.database
1296
+ .prepare(`SELECT * FROM automation_runs
1297
+ WHERE status IN ('queued', 'running')
1298
+ ORDER BY scheduled_for ASC
1299
+ LIMIT ?`)
1300
+ .all(maximum);
1301
+ }
1302
+ async markStarted(runId, sessionId, assistantMessageId, startedAt) {
1303
+ return (this.database
1304
+ .prepare(`UPDATE automation_runs
1305
+ SET session_id = ?, assistant_message_id = ?, started_at = ?,
1306
+ status = 'running'
1307
+ WHERE id = ? AND status = 'queued'`)
1308
+ .run(sessionId, assistantMessageId, startedAt, runId).changes > 0);
1309
+ }
1310
+ async finalize(runId, status, finishedAt, error) {
1311
+ return (this.database
1312
+ .prepare(`UPDATE automation_runs
1313
+ SET status = ?, finished_at = ?, error = ?
1314
+ WHERE id = ? AND status IN ('queued', 'running')`)
1315
+ .run(status, finishedAt, error, runId).changes > 0);
1316
+ }
1317
+ async countUnseenFinished(userId) {
1318
+ const row = this.database
1319
+ .prepare(`SELECT COUNT(*) AS count FROM automation_runs
1320
+ WHERE user_id = ?
1321
+ AND status IN ('succeeded', 'failed')
1322
+ AND seen_at IS NULL`)
1323
+ .get(userId);
1324
+ return row.count;
1325
+ }
1326
+ async markSeenBefore(userId, seenAt) {
1327
+ return this.database
1328
+ .prepare(`UPDATE automation_runs
1329
+ SET seen_at = ?
1330
+ WHERE user_id = ?
1331
+ AND status IN ('succeeded', 'failed')
1332
+ AND seen_at IS NULL
1333
+ AND finished_at IS NOT NULL
1334
+ AND finished_at <= ?`)
1335
+ .run(seenAt, userId, seenAt).changes;
1336
+ }
1337
+ }
1338
+ class SQLiteToolServerRepository {
1339
+ constructor(database) {
1340
+ this.database = database;
1341
+ }
1342
+ async list(maximum) {
1343
+ return this.database
1344
+ .prepare(`SELECT * FROM tool_servers
1345
+ ORDER BY updated_at DESC
1346
+ LIMIT ?`)
1347
+ .all(maximum);
1348
+ }
1349
+ async findById(serverId) {
1350
+ return (this.database
1351
+ .prepare('SELECT * FROM tool_servers WHERE id = ?')
1352
+ .get(serverId) ?? null);
1353
+ }
1354
+ async replaceWithLimit(server, maximum) {
1355
+ const replace = this.database.transaction(() => {
1356
+ const exists = this.database
1357
+ .prepare('SELECT id FROM tool_servers WHERE id = ?')
1358
+ .get(server.id);
1359
+ if (!exists) {
1360
+ const row = this.database
1361
+ .prepare('SELECT COUNT(*) AS count FROM tool_servers')
1362
+ .get();
1363
+ if (row.count >= maximum) {
1364
+ throw new PersistenceResourceLimitError('tool-server', maximum);
1365
+ }
1366
+ }
1367
+ this.database
1368
+ .prepare(`INSERT INTO tool_servers
1369
+ (id, user_id, name, description, kind, base_url, spec,
1370
+ spec_digest, spec_revision, auth_mode, auth_header, access_mode,
1371
+ enabled, timeout_ms, max_response_bytes, created_at, updated_at)
1372
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1373
+ ON CONFLICT(id) DO UPDATE SET
1374
+ name = excluded.name,
1375
+ description = excluded.description,
1376
+ base_url = excluded.base_url,
1377
+ spec = excluded.spec,
1378
+ spec_digest = excluded.spec_digest,
1379
+ spec_revision = excluded.spec_revision,
1380
+ auth_mode = excluded.auth_mode,
1381
+ auth_header = excluded.auth_header,
1382
+ access_mode = excluded.access_mode,
1383
+ enabled = excluded.enabled,
1384
+ timeout_ms = excluded.timeout_ms,
1385
+ max_response_bytes = excluded.max_response_bytes,
1386
+ updated_at = excluded.updated_at`)
1387
+ .run(server.id, server.user_id, server.name, server.description, server.kind, server.base_url, server.spec, server.spec_digest, server.spec_revision, server.auth_mode, server.auth_header, server.access_mode, server.enabled, server.timeout_ms, server.max_response_bytes, server.created_at, server.updated_at);
1388
+ });
1389
+ replace();
1390
+ }
1391
+ async delete(serverId) {
1392
+ return (this.database
1393
+ .prepare('DELETE FROM tool_servers WHERE id = ?')
1394
+ .run(serverId).changes > 0);
1395
+ }
1396
+ }
1397
+ class SQLiteToolServerToolRepository {
1398
+ constructor(database) {
1399
+ this.database = database;
1400
+ }
1401
+ async listByServer(serverId) {
1402
+ return this.database
1403
+ .prepare(`SELECT * FROM tool_server_tools
1404
+ WHERE server_id = ?
1405
+ ORDER BY name ASC`)
1406
+ .all(serverId);
1407
+ }
1408
+ async replaceAllForServer(serverId, tools) {
1409
+ const replace = this.database.transaction(() => {
1410
+ const previous = new Map(this.database
1411
+ .prepare('SELECT name, enabled, side_effect FROM tool_server_tools WHERE server_id = ?')
1412
+ .all(serverId).map(row => [row.name, row]));
1413
+ this.database
1414
+ .prepare('DELETE FROM tool_server_tools WHERE server_id = ?')
1415
+ .run(serverId);
1416
+ const insert = this.database.prepare(`INSERT INTO tool_server_tools
1417
+ (id, server_id, name, description, params_schema, detail,
1418
+ side_effect, enabled, created_at, updated_at)
1419
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
1420
+ for (const tool of tools) {
1421
+ if (tool.server_id !== serverId) {
1422
+ throw new Error('A tool row does not belong to its server');
1423
+ }
1424
+ const kept = previous.get(tool.name);
1425
+ insert.run(tool.id, tool.server_id, tool.name, tool.description, tool.params_schema, tool.detail, kept ? kept.side_effect : tool.side_effect, kept ? kept.enabled : tool.enabled, tool.created_at, tool.updated_at);
1426
+ }
1427
+ });
1428
+ replace();
1429
+ }
1430
+ async updateOverrides(serverId, toolName, overrides, updatedAt) {
1431
+ const assignments = ['updated_at = ?'];
1432
+ const values = [updatedAt];
1433
+ if (overrides.enabled !== undefined) {
1434
+ assignments.push('enabled = ?');
1435
+ values.push(overrides.enabled);
1436
+ }
1437
+ if (overrides.side_effect !== undefined) {
1438
+ assignments.push('side_effect = ?');
1439
+ values.push(overrides.side_effect);
1440
+ }
1441
+ const changed = this.database
1442
+ .prepare(`UPDATE tool_server_tools
1443
+ SET ${assignments.join(', ')}
1444
+ WHERE server_id = ? AND name = ?`)
1445
+ .run(...values, serverId, toolName).changes;
1446
+ if (changed === 0)
1447
+ return null;
1448
+ return (this.database
1449
+ .prepare('SELECT * FROM tool_server_tools WHERE server_id = ? AND name = ?')
1450
+ .get(serverId, toolName) ??
1451
+ null);
1452
+ }
1453
+ }
1454
+ class SQLiteToolServerCredentialRepository {
1455
+ constructor(database) {
1456
+ this.database = database;
1457
+ }
1458
+ async find(serverId, userId) {
1459
+ return (this.database
1460
+ .prepare('SELECT * FROM tool_server_credentials WHERE server_id = ? AND user_id = ?')
1461
+ .get(serverId, userId) ?? null);
1462
+ }
1463
+ async upsert(credential) {
1464
+ this.database
1465
+ .prepare(`INSERT INTO tool_server_credentials
1466
+ (id, server_id, user_id, secret, created_at, updated_at)
1467
+ VALUES (?, ?, ?, ?, ?, ?)
1468
+ ON CONFLICT(server_id, user_id) DO UPDATE SET
1469
+ secret = excluded.secret,
1470
+ updated_at = excluded.updated_at`)
1471
+ .run(credential.id, credential.server_id, credential.user_id, credential.secret, credential.created_at, credential.updated_at);
1472
+ }
1473
+ async delete(serverId, userId) {
1474
+ return (this.database
1475
+ .prepare('DELETE FROM tool_server_credentials WHERE server_id = ? AND user_id = ?')
1476
+ .run(serverId, userId).changes > 0);
1477
+ }
1478
+ }
1479
+ class SQLiteToolApprovalRepository {
1480
+ constructor(database) {
1481
+ this.database = database;
1482
+ }
1483
+ async insert(approval) {
1484
+ this.database
1485
+ .prepare(`INSERT INTO tool_approvals
1486
+ (id, user_id, session_id, server_id, tool_name, call_id,
1487
+ arguments_digest, scope, status, created_at, resolved_at,
1488
+ expires_at)
1489
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1490
+ .run(approval.id, approval.user_id, approval.session_id, approval.server_id, approval.tool_name, approval.call_id, approval.arguments_digest, approval.scope, approval.status, approval.created_at, approval.resolved_at, approval.expires_at);
1491
+ }
1492
+ async findByOwner(approvalId, userId) {
1493
+ return (this.database
1494
+ .prepare('SELECT * FROM tool_approvals WHERE id = ? AND user_id = ?')
1495
+ .get(approvalId, userId) ??
1496
+ null);
1497
+ }
1498
+ async findStanding(userId, serverId, toolName, sessionId) {
1499
+ return (this.database
1500
+ .prepare(`SELECT * FROM tool_approvals
1501
+ WHERE user_id = ?
1502
+ AND status = 'approved'
1503
+ AND tool_name = ?
1504
+ AND ((server_id IS NULL AND ? IS NULL) OR server_id = ?)
1505
+ AND (scope = 'always'
1506
+ OR (scope = 'session' AND session_id IS NOT NULL AND session_id = ?))
1507
+ ORDER BY resolved_at DESC
1508
+ LIMIT 1`)
1509
+ .get(userId, toolName, serverId, serverId, sessionId) ?? null);
1510
+ }
1511
+ async resolvePending(approvalId, userId, status, scope, resolvedAt) {
1512
+ const changed = this.database
1513
+ .prepare(`UPDATE tool_approvals
1514
+ SET status = ?, scope = ?, resolved_at = ?
1515
+ WHERE id = ? AND user_id = ? AND status = 'pending'
1516
+ AND (expires_at IS NULL OR expires_at > ?)`)
1517
+ .run(status, scope, resolvedAt, approvalId, userId, resolvedAt).changes;
1518
+ if (changed === 0)
1519
+ return null;
1520
+ return this.findByOwner(approvalId, userId);
1521
+ }
1522
+ async listPendingByOwner(userId, maximum) {
1523
+ return this.database
1524
+ .prepare(`SELECT * FROM tool_approvals
1525
+ WHERE user_id = ? AND status = 'pending'
1526
+ ORDER BY created_at ASC
1527
+ LIMIT ?`)
1528
+ .all(userId, maximum);
1529
+ }
1530
+ async listStandingByOwner(userId, maximum) {
1531
+ return this.database
1532
+ .prepare(`SELECT * FROM tool_approvals
1533
+ WHERE user_id = ? AND status = 'approved'
1534
+ AND scope IN ('session', 'always')
1535
+ ORDER BY resolved_at DESC
1536
+ LIMIT ?`)
1537
+ .all(userId, maximum);
1538
+ }
1539
+ async expirePending(now) {
1540
+ return this.database
1541
+ .prepare(`UPDATE tool_approvals
1542
+ SET status = 'expired', resolved_at = ?
1543
+ WHERE status = 'pending' AND expires_at IS NOT NULL AND expires_at <= ?`)
1544
+ .run(now, now).changes;
1545
+ }
1546
+ async deleteByOwner(approvalId, userId) {
1547
+ return (this.database
1548
+ .prepare('DELETE FROM tool_approvals WHERE id = ? AND user_id = ?')
1549
+ .run(approvalId, userId).changes > 0);
1550
+ }
1551
+ }
1552
+ class SQLitePromptRepository {
1553
+ constructor(database) {
1554
+ this.database = database;
1555
+ }
1556
+ async listByOwner(userId, maximum) {
1557
+ return this.database
1558
+ .prepare(`SELECT * FROM prompts
1559
+ WHERE user_id = ?
1560
+ ORDER BY updated_at DESC
1561
+ LIMIT ?`)
1562
+ .all(userId, maximum);
1563
+ }
1564
+ async findByOwner(promptId, userId) {
1565
+ return (this.database
1566
+ .prepare('SELECT * FROM prompts WHERE id = ? AND user_id = ?')
1567
+ .get(promptId, userId) ?? null);
1568
+ }
1569
+ async findById(promptId) {
1570
+ return (this.database
1571
+ .prepare('SELECT * FROM prompts WHERE id = ?')
1572
+ .get(promptId) ?? null);
1573
+ }
1574
+ async findBySlug(userId, slug) {
1575
+ return (this.database
1576
+ .prepare('SELECT * FROM prompts WHERE user_id = ? AND slug = ?')
1577
+ .get(userId, slug) ?? null);
1578
+ }
1579
+ async replaceWithLimit(prompt, maximum, archivedVersion) {
1580
+ const replace = this.database.transaction(() => {
1581
+ const exists = ensureSameOwner(this.database, 'prompts', prompt.id, prompt.user_id);
1582
+ if (!exists) {
1583
+ const row = this.database
1584
+ .prepare('SELECT COUNT(*) AS count FROM prompts WHERE user_id = ?')
1585
+ .get(prompt.user_id);
1586
+ if (row.count >= maximum) {
1587
+ throw new PersistenceResourceLimitError('prompt', maximum);
1588
+ }
1589
+ }
1590
+ this.database
1591
+ .prepare(`INSERT INTO prompts
1592
+ (id, user_id, slug, title, description, content, variables,
1593
+ tags, version, created_at, updated_at)
1594
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1595
+ ON CONFLICT(id) DO UPDATE SET
1596
+ slug = excluded.slug,
1597
+ title = excluded.title,
1598
+ description = excluded.description,
1599
+ content = excluded.content,
1600
+ variables = excluded.variables,
1601
+ tags = excluded.tags,
1602
+ version = excluded.version,
1603
+ updated_at = excluded.updated_at
1604
+ WHERE prompts.user_id = excluded.user_id`)
1605
+ .run(prompt.id, prompt.user_id, prompt.slug, prompt.title, prompt.description, prompt.content, prompt.variables, prompt.tags, prompt.version, prompt.created_at, prompt.updated_at);
1606
+ if (archivedVersion) {
1607
+ this.database
1608
+ .prepare(`INSERT INTO prompt_versions
1609
+ (id, prompt_id, version, content, variables, created_at)
1610
+ VALUES (?, ?, ?, ?, ?, ?)
1611
+ ON CONFLICT(prompt_id, version) DO NOTHING`)
1612
+ .run(archivedVersion.id, archivedVersion.prompt_id, archivedVersion.version, archivedVersion.content, archivedVersion.variables, archivedVersion.created_at);
1613
+ }
1614
+ });
1615
+ replace();
1616
+ }
1617
+ async listVersions(promptId, maximum) {
1618
+ return this.database
1619
+ .prepare(`SELECT * FROM prompt_versions
1620
+ WHERE prompt_id = ?
1621
+ ORDER BY version DESC
1622
+ LIMIT ?`)
1623
+ .all(promptId, maximum);
1624
+ }
1625
+ async findVersion(promptId, version) {
1626
+ return (this.database
1627
+ .prepare('SELECT * FROM prompt_versions WHERE prompt_id = ? AND version = ?')
1628
+ .get(promptId, version) ??
1629
+ null);
1630
+ }
1631
+ async deleteByOwner(promptId, userId) {
327
1632
  return (this.database
328
- .prepare('DELETE FROM calendar_events WHERE id = ? AND user_id = ?')
329
- .run(eventId, userId).changes > 0);
1633
+ .prepare('DELETE FROM prompts WHERE id = ? AND user_id = ?')
1634
+ .run(promptId, userId).changes > 0);
330
1635
  }
331
1636
  }
332
- class SQLiteAutomationRepository {
1637
+ class SQLiteSkillRepository {
333
1638
  constructor(database) {
334
1639
  this.database = database;
335
1640
  }
336
1641
  async listByOwner(userId, maximum) {
337
1642
  return this.database
338
- .prepare(`SELECT * FROM automations
1643
+ .prepare(`SELECT * FROM skills
339
1644
  WHERE user_id = ?
340
1645
  ORDER BY updated_at DESC
341
1646
  LIMIT ?`)
342
1647
  .all(userId, maximum);
343
1648
  }
344
- async findByOwner(automationId, userId) {
1649
+ async findByOwner(skillId, userId) {
345
1650
  return (this.database
346
- .prepare('SELECT * FROM automations WHERE id = ? AND user_id = ?')
347
- .get(automationId, userId) ??
348
- null);
1651
+ .prepare('SELECT * FROM skills WHERE id = ? AND user_id = ?')
1652
+ .get(skillId, userId) ?? null);
349
1653
  }
350
- async findById(automationId) {
1654
+ async findById(skillId) {
351
1655
  return (this.database
352
- .prepare('SELECT * FROM automations WHERE id = ?')
353
- .get(automationId) ?? null);
1656
+ .prepare('SELECT * FROM skills WHERE id = ?')
1657
+ .get(skillId) ?? null);
354
1658
  }
355
- async replaceWithLimit(automation, maximum) {
1659
+ async findBySlug(userId, slug) {
1660
+ return (this.database
1661
+ .prepare('SELECT * FROM skills WHERE user_id = ? AND slug = ?')
1662
+ .get(userId, slug) ?? null);
1663
+ }
1664
+ async replaceWithLimit(skill, maximum, archivedVersion) {
356
1665
  const replace = this.database.transaction(() => {
357
- const exists = ensureSameOwner(this.database, 'automations', automation.id, automation.user_id);
1666
+ const exists = ensureSameOwner(this.database, 'skills', skill.id, skill.user_id);
358
1667
  if (!exists) {
359
1668
  const row = this.database
360
- .prepare('SELECT COUNT(*) AS count FROM automations WHERE user_id = ?')
361
- .get(automation.user_id);
1669
+ .prepare('SELECT COUNT(*) AS count FROM skills WHERE user_id = ?')
1670
+ .get(skill.user_id);
362
1671
  if (row.count >= maximum) {
363
- throw new PersistenceResourceLimitError('automation', maximum);
1672
+ throw new PersistenceResourceLimitError('skill', maximum);
364
1673
  }
365
1674
  }
366
1675
  this.database
367
- .prepare(`INSERT INTO automations
368
- (id, user_id, name, instructions, triggers, provider, model,
369
- notify, status, next_run_at, last_run_at, created_at, updated_at)
370
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1676
+ .prepare(`INSERT INTO skills
1677
+ (id, user_id, slug, name, description, instructions, enabled,
1678
+ version, created_at, updated_at)
1679
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
371
1680
  ON CONFLICT(id) DO UPDATE SET
1681
+ slug = excluded.slug,
372
1682
  name = excluded.name,
1683
+ description = excluded.description,
373
1684
  instructions = excluded.instructions,
374
- triggers = excluded.triggers,
375
- provider = excluded.provider,
376
- model = excluded.model,
377
- notify = excluded.notify,
378
- status = excluded.status,
379
- next_run_at = excluded.next_run_at,
380
- last_run_at = excluded.last_run_at,
1685
+ enabled = excluded.enabled,
1686
+ version = excluded.version,
381
1687
  updated_at = excluded.updated_at
382
- WHERE automations.user_id = excluded.user_id`)
383
- .run(automation.id, automation.user_id, automation.name, automation.instructions, automation.triggers, automation.provider, automation.model, automation.notify, automation.status, automation.next_run_at, automation.last_run_at, automation.created_at, automation.updated_at);
1688
+ WHERE skills.user_id = excluded.user_id`)
1689
+ .run(skill.id, skill.user_id, skill.slug, skill.name, skill.description, skill.instructions, skill.enabled, skill.version, skill.created_at, skill.updated_at);
1690
+ if (archivedVersion) {
1691
+ this.database
1692
+ .prepare(`INSERT INTO skill_versions
1693
+ (id, skill_id, version, instructions, created_at)
1694
+ VALUES (?, ?, ?, ?, ?)
1695
+ ON CONFLICT(skill_id, version) DO NOTHING`)
1696
+ .run(archivedVersion.id, archivedVersion.skill_id, archivedVersion.version, archivedVersion.instructions, archivedVersion.created_at);
1697
+ }
384
1698
  });
385
1699
  replace();
386
1700
  }
387
- async listDue(now, maximum) {
1701
+ async listVersions(skillId, maximum) {
388
1702
  return this.database
389
- .prepare(`SELECT * FROM automations
390
- WHERE status = 'active'
391
- AND next_run_at IS NOT NULL
392
- AND next_run_at <= ?
393
- ORDER BY next_run_at ASC
1703
+ .prepare(`SELECT * FROM skill_versions
1704
+ WHERE skill_id = ?
1705
+ ORDER BY version DESC
394
1706
  LIMIT ?`)
395
- .all(now, maximum);
396
- }
397
- async advanceNextRun(automationId, observedNextRunAt, nextRunAt, lastRunAt) {
398
- return (this.database
399
- .prepare(`UPDATE automations
400
- SET next_run_at = ?, last_run_at = ?
401
- WHERE id = ? AND next_run_at = ?`)
402
- .run(nextRunAt, lastRunAt, automationId, observedNextRunAt).changes > 0);
1707
+ .all(skillId, maximum);
403
1708
  }
404
- async setStatus(automationId, userId, status, nextRunAt, updatedAt) {
1709
+ async findVersion(skillId, version) {
405
1710
  return (this.database
406
- .prepare(`UPDATE automations
407
- SET status = ?, next_run_at = ?, updated_at = ?
408
- WHERE id = ? AND user_id = ?`)
409
- .run(status, nextRunAt, updatedAt, automationId, userId).changes > 0);
1711
+ .prepare('SELECT * FROM skill_versions WHERE skill_id = ? AND version = ?')
1712
+ .get(skillId, version) ?? null);
410
1713
  }
411
- async deleteByOwner(automationId, userId) {
1714
+ async deleteByOwner(skillId, userId) {
412
1715
  return (this.database
413
- .prepare('DELETE FROM automations WHERE id = ? AND user_id = ?')
414
- .run(automationId, userId).changes > 0);
1716
+ .prepare('DELETE FROM skills WHERE id = ? AND user_id = ?')
1717
+ .run(skillId, userId).changes > 0);
415
1718
  }
416
1719
  }
417
- class SQLiteAutomationRunRepository {
1720
+ class SQLiteSkillFileRepository {
418
1721
  constructor(database) {
419
1722
  this.database = database;
420
1723
  }
421
- async insert(run) {
422
- this.database
423
- .prepare(`INSERT INTO automation_runs
424
- (id, automation_id, user_id, scheduled_for, started_at,
425
- finished_at, status, session_id, assistant_message_id, error,
426
- seen_at, created_at)
427
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
428
- .run(run.id, run.automation_id, run.user_id, run.scheduled_for, run.started_at, run.finished_at, run.status, run.session_id, run.assistant_message_id, run.error, run.seen_at, run.created_at);
429
- }
430
- async findByOwner(runId, userId) {
431
- return (this.database
432
- .prepare('SELECT * FROM automation_runs WHERE id = ? AND user_id = ?')
433
- .get(runId, userId) ?? null);
434
- }
435
- async listByOwner(userId, options) {
436
- const clauses = ['user_id = ?'];
437
- const values = [userId];
438
- if (options.automationId !== undefined) {
439
- clauses.push('automation_id = ?');
440
- values.push(options.automationId);
441
- }
442
- if (options.from !== undefined) {
443
- clauses.push('scheduled_for >= ?');
444
- values.push(options.from);
445
- }
446
- if (options.to !== undefined) {
447
- clauses.push('scheduled_for < ?');
448
- values.push(options.to);
449
- }
450
- values.push(options.maximum);
451
- return this.database
452
- .prepare(`SELECT * FROM automation_runs
453
- WHERE ${clauses.join(' AND ')}
454
- ORDER BY scheduled_for DESC
455
- LIMIT ?`)
456
- .all(...values);
457
- }
458
- async listUnfinished(maximum) {
1724
+ async listBySkill(skillId) {
459
1725
  return this.database
460
- .prepare(`SELECT * FROM automation_runs
461
- WHERE status IN ('queued', 'running')
462
- ORDER BY scheduled_for ASC
463
- LIMIT ?`)
464
- .all(maximum);
465
- }
466
- async markStarted(runId, sessionId, assistantMessageId, startedAt) {
467
- return (this.database
468
- .prepare(`UPDATE automation_runs
469
- SET session_id = ?, assistant_message_id = ?, started_at = ?,
470
- status = 'running'
471
- WHERE id = ? AND status = 'queued'`)
472
- .run(sessionId, assistantMessageId, startedAt, runId).changes > 0);
1726
+ .prepare(`SELECT * FROM skill_files
1727
+ WHERE skill_id = ?
1728
+ ORDER BY path ASC`)
1729
+ .all(skillId);
473
1730
  }
474
- async finalize(runId, status, finishedAt, error) {
1731
+ async find(skillId, path) {
475
1732
  return (this.database
476
- .prepare(`UPDATE automation_runs
477
- SET status = ?, finished_at = ?, error = ?
478
- WHERE id = ? AND status IN ('queued', 'running')`)
479
- .run(status, finishedAt, error, runId).changes > 0);
1733
+ .prepare('SELECT * FROM skill_files WHERE skill_id = ? AND path = ?')
1734
+ .get(skillId, path) ?? null);
1735
+ }
1736
+ async upsert(file, maximumPerSkill) {
1737
+ const upsert = this.database.transaction(() => {
1738
+ const exists = this.database
1739
+ .prepare('SELECT id FROM skill_files WHERE skill_id = ? AND path = ?')
1740
+ .get(file.skill_id, file.path);
1741
+ if (!exists) {
1742
+ const row = this.database
1743
+ .prepare('SELECT COUNT(*) AS count FROM skill_files WHERE skill_id = ?')
1744
+ .get(file.skill_id);
1745
+ if (row.count >= maximumPerSkill) {
1746
+ throw new PersistenceResourceLimitError('skill-file', maximumPerSkill);
1747
+ }
1748
+ }
1749
+ this.database
1750
+ .prepare(`INSERT INTO skill_files
1751
+ (id, skill_id, path, content, size, created_at, updated_at)
1752
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1753
+ ON CONFLICT(skill_id, path) DO UPDATE SET
1754
+ content = excluded.content,
1755
+ size = excluded.size,
1756
+ updated_at = excluded.updated_at`)
1757
+ .run(file.id, file.skill_id, file.path, file.content, file.size, file.created_at, file.updated_at);
1758
+ });
1759
+ upsert();
480
1760
  }
481
- async countUnseenFinished(userId) {
482
- const row = this.database
483
- .prepare(`SELECT COUNT(*) AS count FROM automation_runs
484
- WHERE user_id = ?
485
- AND status IN ('succeeded', 'failed')
486
- AND seen_at IS NULL`)
487
- .get(userId);
488
- return row.count;
1761
+ async replaceAllForSkill(skillId, files) {
1762
+ const replace = this.database.transaction(() => {
1763
+ this.database
1764
+ .prepare('DELETE FROM skill_files WHERE skill_id = ?')
1765
+ .run(skillId);
1766
+ const insert = this.database.prepare(`INSERT INTO skill_files
1767
+ (id, skill_id, path, content, size, created_at, updated_at)
1768
+ VALUES (?, ?, ?, ?, ?, ?, ?)`);
1769
+ for (const file of files) {
1770
+ if (file.skill_id !== skillId) {
1771
+ throw new Error('A skill file does not belong to its skill');
1772
+ }
1773
+ insert.run(file.id, file.skill_id, file.path, file.content, file.size, file.created_at, file.updated_at);
1774
+ }
1775
+ });
1776
+ replace();
489
1777
  }
490
- async markSeenBefore(userId, seenAt) {
491
- return this.database
492
- .prepare(`UPDATE automation_runs
493
- SET seen_at = ?
494
- WHERE user_id = ?
495
- AND status IN ('succeeded', 'failed')
496
- AND seen_at IS NULL
497
- AND finished_at IS NOT NULL
498
- AND finished_at <= ?`)
499
- .run(seenAt, userId, seenAt).changes;
1778
+ async delete(skillId, path) {
1779
+ return (this.database
1780
+ .prepare('DELETE FROM skill_files WHERE skill_id = ? AND path = ?')
1781
+ .run(skillId, path).changes > 0);
500
1782
  }
501
1783
  }
502
1784
  class SQLiteSessionFolderRepository {
@@ -670,6 +1952,10 @@ const ARCHIVE_TABLES = {
670
1952
  'knowledge-collection': 'knowledge_collections',
671
1953
  document: 'documents',
672
1954
  persona: 'personas',
1955
+ prompt: 'prompts',
1956
+ skill: 'skills',
1957
+ 'tool-server': 'tool_servers',
1958
+ calendar: 'calendars',
673
1959
  };
674
1960
  class SQLiteDataArchiveRepository {
675
1961
  constructor(database) {
@@ -881,16 +2167,253 @@ class SQLiteDataArchiveRepository {
881
2167
  apply.immediate();
882
2168
  }
883
2169
  }
2170
+ class SQLiteModelTariffRepository {
2171
+ constructor(database) {
2172
+ this.database = database;
2173
+ }
2174
+ async listAll(maximum) {
2175
+ return this.database
2176
+ .prepare(`SELECT * FROM model_tariffs
2177
+ ORDER BY plugin_id ASC, model ASC, effective_from DESC
2178
+ LIMIT ?`)
2179
+ .all(maximum);
2180
+ }
2181
+ async insert(tariff) {
2182
+ this.database
2183
+ .prepare(`INSERT INTO model_tariffs
2184
+ (id, plugin_id, model, input_per_million, output_per_million,
2185
+ unit_price, currency, effective_from, created_by, created_at)
2186
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
2187
+ .run(tariff.id, tariff.plugin_id, tariff.model, tariff.input_per_million, tariff.output_per_million, tariff.unit_price, tariff.currency, tariff.effective_from, tariff.created_by, tariff.created_at);
2188
+ }
2189
+ async deleteById(tariffId) {
2190
+ return (this.database
2191
+ .prepare('DELETE FROM model_tariffs WHERE id = ?')
2192
+ .run(tariffId).changes > 0);
2193
+ }
2194
+ }
2195
+ class SQLiteUsageBudgetRepository {
2196
+ constructor(database) {
2197
+ this.database = database;
2198
+ }
2199
+ async listAll(maximum) {
2200
+ return this.database
2201
+ .prepare(`SELECT * FROM usage_budgets
2202
+ ORDER BY created_at ASC, id ASC
2203
+ LIMIT ?`)
2204
+ .all(maximum);
2205
+ }
2206
+ async findById(budgetId) {
2207
+ return (this.database
2208
+ .prepare('SELECT * FROM usage_budgets WHERE id = ?')
2209
+ .get(budgetId) ?? null);
2210
+ }
2211
+ async replace(budget) {
2212
+ this.database
2213
+ .prepare(`INSERT INTO usage_budgets
2214
+ (id, name, principal_type, principal_id, period, amount_usd,
2215
+ mode, created_by, created_at, updated_at)
2216
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2217
+ ON CONFLICT(id) DO UPDATE SET
2218
+ name = excluded.name,
2219
+ principal_type = excluded.principal_type,
2220
+ principal_id = excluded.principal_id,
2221
+ period = excluded.period,
2222
+ amount_usd = excluded.amount_usd,
2223
+ mode = excluded.mode,
2224
+ updated_at = excluded.updated_at`)
2225
+ .run(budget.id, budget.name, budget.principal_type, budget.principal_id, budget.period, budget.amount_usd, budget.mode, budget.created_by, budget.created_at, budget.updated_at);
2226
+ }
2227
+ async deleteById(budgetId) {
2228
+ return (this.database
2229
+ .prepare('DELETE FROM usage_budgets WHERE id = ?')
2230
+ .run(budgetId).changes > 0);
2231
+ }
2232
+ }
2233
+ class SQLiteMessageFeedbackRepository {
2234
+ constructor(database) {
2235
+ this.database = database;
2236
+ }
2237
+ async upsertByMessage(feedback) {
2238
+ this.database
2239
+ .prepare(`INSERT INTO message_feedback
2240
+ (id, user_id, session_id, message_id, rating, tags, comment,
2241
+ model, plugin_id, snapshot, created_at, updated_at)
2242
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2243
+ ON CONFLICT(user_id, message_id) DO UPDATE SET
2244
+ rating = excluded.rating,
2245
+ tags = excluded.tags,
2246
+ comment = excluded.comment,
2247
+ model = excluded.model,
2248
+ plugin_id = excluded.plugin_id,
2249
+ snapshot = excluded.snapshot,
2250
+ updated_at = excluded.updated_at`)
2251
+ .run(feedback.id, feedback.user_id, feedback.session_id, feedback.message_id, feedback.rating, feedback.tags, feedback.comment, feedback.model, feedback.plugin_id, feedback.snapshot, feedback.created_at, feedback.updated_at);
2252
+ }
2253
+ async deleteByMessage(userId, messageId) {
2254
+ return (this.database
2255
+ .prepare('DELETE FROM message_feedback WHERE user_id = ? AND message_id = ?')
2256
+ .run(userId, messageId).changes > 0);
2257
+ }
2258
+ async listByOwner(userId, maximum) {
2259
+ return this.database
2260
+ .prepare(`SELECT * FROM message_feedback
2261
+ WHERE user_id = ?
2262
+ ORDER BY created_at DESC, id DESC
2263
+ LIMIT ?`)
2264
+ .all(userId, maximum);
2265
+ }
2266
+ async listAll(maximum) {
2267
+ return this.database
2268
+ .prepare(`SELECT * FROM message_feedback
2269
+ ORDER BY created_at DESC, id DESC
2270
+ LIMIT ?`)
2271
+ .all(maximum);
2272
+ }
2273
+ }
2274
+ class SQLiteArenaVoteRepository {
2275
+ constructor(database) {
2276
+ this.database = database;
2277
+ }
2278
+ async insertOnce(vote) {
2279
+ return (this.database
2280
+ .prepare(`INSERT INTO arena_votes
2281
+ (id, user_id, compare_group, model_a, model_b, winner, created_at)
2282
+ VALUES (?, ?, ?, ?, ?, ?, ?)
2283
+ ON CONFLICT(user_id, compare_group) DO NOTHING`)
2284
+ .run(vote.id, vote.user_id, vote.compare_group, vote.model_a, vote.model_b, vote.winner, vote.created_at).changes > 0);
2285
+ }
2286
+ async listAllOrdered(maximum) {
2287
+ return this.database
2288
+ .prepare(`SELECT * FROM arena_votes
2289
+ ORDER BY created_at ASC, id ASC
2290
+ LIMIT ?`)
2291
+ .all(maximum);
2292
+ }
2293
+ }
2294
+ class SQLiteEvalSetRepository {
2295
+ constructor(database) {
2296
+ this.database = database;
2297
+ }
2298
+ async listByOwner(userId, maximum) {
2299
+ return this.database
2300
+ .prepare(`SELECT * FROM eval_sets
2301
+ WHERE user_id = ?
2302
+ ORDER BY updated_at DESC, id DESC
2303
+ LIMIT ?`)
2304
+ .all(userId, maximum);
2305
+ }
2306
+ async findByOwner(setId, userId) {
2307
+ return (this.database
2308
+ .prepare('SELECT * FROM eval_sets WHERE id = ? AND user_id = ?')
2309
+ .get(setId, userId) ?? null);
2310
+ }
2311
+ async replaceWithLimit(set, maximum) {
2312
+ const replace = this.database.transaction(() => {
2313
+ const exists = ensureSameOwner(this.database, 'eval_sets', set.id, set.user_id);
2314
+ if (!exists) {
2315
+ const row = this.database
2316
+ .prepare('SELECT COUNT(*) AS count FROM eval_sets WHERE user_id = ?')
2317
+ .get(set.user_id);
2318
+ if (row.count >= maximum) {
2319
+ throw new PersistenceResourceLimitError('eval-set', maximum);
2320
+ }
2321
+ }
2322
+ this.database
2323
+ .prepare(`INSERT INTO eval_sets
2324
+ (id, user_id, name, description, items, created_at, updated_at)
2325
+ VALUES (?, ?, ?, ?, ?, ?, ?)
2326
+ ON CONFLICT(id) DO UPDATE SET
2327
+ name = excluded.name,
2328
+ description = excluded.description,
2329
+ items = excluded.items,
2330
+ updated_at = excluded.updated_at
2331
+ WHERE eval_sets.user_id = excluded.user_id`)
2332
+ .run(set.id, set.user_id, set.name, set.description, set.items, set.created_at, set.updated_at);
2333
+ });
2334
+ replace();
2335
+ }
2336
+ async deleteByOwner(setId, userId) {
2337
+ return (this.database
2338
+ .prepare('DELETE FROM eval_sets WHERE id = ? AND user_id = ?')
2339
+ .run(setId, userId).changes > 0);
2340
+ }
2341
+ }
2342
+ class SQLiteEvalRunRepository {
2343
+ constructor(database) {
2344
+ this.database = database;
2345
+ }
2346
+ async listByOwner(userId, maximum) {
2347
+ return this.database
2348
+ .prepare(`SELECT * FROM eval_runs
2349
+ WHERE user_id = ?
2350
+ ORDER BY created_at DESC, id DESC
2351
+ LIMIT ?`)
2352
+ .all(userId, maximum);
2353
+ }
2354
+ async listBySet(setId, userId, maximum) {
2355
+ return this.database
2356
+ .prepare(`SELECT * FROM eval_runs
2357
+ WHERE set_id = ? AND user_id = ?
2358
+ ORDER BY created_at DESC, id DESC
2359
+ LIMIT ?`)
2360
+ .all(setId, userId, maximum);
2361
+ }
2362
+ async findByOwner(runId, userId) {
2363
+ return (this.database
2364
+ .prepare('SELECT * FROM eval_runs WHERE id = ? AND user_id = ?')
2365
+ .get(runId, userId) ?? null);
2366
+ }
2367
+ async insert(run) {
2368
+ this.database
2369
+ .prepare(`INSERT INTO eval_runs
2370
+ (id, set_id, user_id, label, plugin_id, model, status, results,
2371
+ error, created_at, updated_at, completed_at)
2372
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
2373
+ .run(run.id, run.set_id, run.user_id, run.label, run.plugin_id, run.model, run.status, run.results, run.error, run.created_at, run.updated_at, run.completed_at);
2374
+ }
2375
+ async update(runId, userId, changes) {
2376
+ return (this.database
2377
+ .prepare(`UPDATE eval_runs SET
2378
+ status = ?,
2379
+ results = COALESCE(?, results),
2380
+ error = ?,
2381
+ updated_at = ?,
2382
+ completed_at = COALESCE(?, completed_at)
2383
+ WHERE id = ? AND user_id = ?`)
2384
+ .run(changes.status, changes.results ?? null, changes.error ?? null, changes.updated_at, changes.completed_at ?? null, runId, userId).changes > 0);
2385
+ }
2386
+ }
884
2387
  export const createSQLiteResourceRepositories = (database) => ({
885
2388
  chatSessions: new SQLiteChatSessionRepository(database),
886
2389
  knowledgeCollections: new SQLiteKnowledgeCollectionRepository(database),
887
2390
  notes: new SQLiteNoteRepository(database),
2391
+ calendars: new SQLiteCalendarRepository(database),
888
2392
  calendarEvents: new SQLiteCalendarEventRepository(database),
2393
+ channels: new SQLiteChannelRepository(database),
2394
+ channelMessages: new SQLiteChannelMessageRepository(database),
2395
+ notifications: new SQLiteNotificationRepository(database),
2396
+ webhookTargets: new SQLiteWebhookTargetRepository(database),
2397
+ pushSubscriptions: new SQLitePushSubscriptionRepository(database),
2398
+ recoveryDrills: new SQLiteRecoveryDrillRepository(database),
889
2399
  automations: new SQLiteAutomationRepository(database),
890
2400
  automationRuns: new SQLiteAutomationRunRepository(database),
2401
+ toolServers: new SQLiteToolServerRepository(database),
2402
+ toolServerTools: new SQLiteToolServerToolRepository(database),
2403
+ toolServerCredentials: new SQLiteToolServerCredentialRepository(database),
2404
+ toolApprovals: new SQLiteToolApprovalRepository(database),
2405
+ prompts: new SQLitePromptRepository(database),
2406
+ skills: new SQLiteSkillRepository(database),
2407
+ skillFiles: new SQLiteSkillFileRepository(database),
891
2408
  sessionFolders: new SQLiteSessionFolderRepository(database),
892
2409
  preferences: new SQLitePreferenceRepository(database),
893
2410
  systemSettings: new SQLiteSystemSettingRepository(database),
894
2411
  archive: new SQLiteDataArchiveRepository(database),
2412
+ modelTariffs: new SQLiteModelTariffRepository(database),
2413
+ usageBudgets: new SQLiteUsageBudgetRepository(database),
2414
+ messageFeedback: new SQLiteMessageFeedbackRepository(database),
2415
+ arenaVotes: new SQLiteArenaVoteRepository(database),
2416
+ evalSets: new SQLiteEvalSetRepository(database),
2417
+ evalRuns: new SQLiteEvalRunRepository(database),
895
2418
  });
896
2419
  //# sourceMappingURL=sqliteResourceRepositories.js.map