libre-webui 0.25.0 → 0.26.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 (510) hide show
  1. package/README.md +32 -22
  2. package/backend/dist/index.d.ts.map +1 -1
  3. package/backend/dist/index.js +26 -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/postgresMigrationRegistry.d.ts.map +1 -1
  44. package/backend/dist/persistence/postgresMigrationRegistry.js +10 -0
  45. package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
  46. package/backend/dist/persistence/postgresNotesV2Migration.d.ts +4 -0
  47. package/backend/dist/persistence/postgresNotesV2Migration.d.ts.map +1 -0
  48. package/backend/dist/persistence/postgresNotesV2Migration.js +57 -0
  49. package/backend/dist/persistence/postgresNotesV2Migration.js.map +1 -0
  50. package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
  51. package/backend/dist/persistence/postgresResourceRepositories.js +1646 -11
  52. package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
  53. package/backend/dist/persistence/postgresSkillFilesMigration.d.ts +9 -0
  54. package/backend/dist/persistence/postgresSkillFilesMigration.d.ts.map +1 -0
  55. package/backend/dist/persistence/postgresSkillFilesMigration.js +50 -0
  56. package/backend/dist/persistence/postgresSkillFilesMigration.js.map +1 -0
  57. package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts +10 -0
  58. package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts.map +1 -0
  59. package/backend/dist/persistence/postgresTeamCollaborationMigration.js +161 -0
  60. package/backend/dist/persistence/postgresTeamCollaborationMigration.js.map +1 -0
  61. package/backend/dist/persistence/resourceTypes.d.ts +541 -3
  62. package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
  63. package/backend/dist/persistence/resourceTypes.js.map +1 -1
  64. package/backend/dist/persistence/sqliteExtensionRepositories.d.ts.map +1 -1
  65. package/backend/dist/persistence/sqliteExtensionRepositories.js +26 -4
  66. package/backend/dist/persistence/sqliteExtensionRepositories.js.map +1 -1
  67. package/backend/dist/persistence/sqliteMigrations.d.ts +9 -0
  68. package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
  69. package/backend/dist/persistence/sqliteMigrations.js +1196 -29
  70. package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
  71. package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
  72. package/backend/dist/persistence/sqliteResourceRepositories.js +1523 -134
  73. package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
  74. package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
  75. package/backend/dist/persistence/sqliteToPostgresMigration.js +257 -3
  76. package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
  77. package/backend/dist/platform/jobs/domainJobContracts.d.ts +8 -0
  78. package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
  79. package/backend/dist/platform/jobs/domainJobContracts.js +8 -0
  80. package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
  81. package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
  82. package/backend/dist/platform/jobs/domainJobHandlers.js +310 -3
  83. package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
  84. package/backend/dist/platform/jobs/embeddedDurableJobWorker.d.ts.map +1 -1
  85. package/backend/dist/platform/jobs/embeddedDurableJobWorker.js +4 -2
  86. package/backend/dist/platform/jobs/embeddedDurableJobWorker.js.map +1 -1
  87. package/backend/dist/platform/jobs/postgresDurableJobWorker.d.ts.map +1 -1
  88. package/backend/dist/platform/jobs/postgresDurableJobWorker.js +4 -2
  89. package/backend/dist/platform/jobs/postgresDurableJobWorker.js.map +1 -1
  90. package/backend/dist/platform/storage/pgVectorStore.d.ts +2 -1
  91. package/backend/dist/platform/storage/pgVectorStore.d.ts.map +1 -1
  92. package/backend/dist/platform/storage/pgVectorStore.js +51 -0
  93. package/backend/dist/platform/storage/pgVectorStore.js.map +1 -1
  94. package/backend/dist/platform/storage/platformDomainRepositories.d.ts +8 -2
  95. package/backend/dist/platform/storage/platformDomainRepositories.d.ts.map +1 -1
  96. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.d.ts.map +1 -1
  97. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js +25 -4
  98. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js.map +1 -1
  99. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts +2 -1
  100. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts.map +1 -1
  101. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js +35 -0
  102. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js.map +1 -1
  103. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.d.ts.map +1 -1
  104. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js +26 -3
  105. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js.map +1 -1
  106. package/backend/dist/platform/storage/vectorStore.d.ts +14 -0
  107. package/backend/dist/platform/storage/vectorStore.d.ts.map +1 -1
  108. package/backend/dist/platform/storage/vectorStore.js.map +1 -1
  109. package/backend/dist/routes/access.d.ts.map +1 -1
  110. package/backend/dist/routes/access.js +84 -3
  111. package/backend/dist/routes/access.js.map +1 -1
  112. package/backend/dist/routes/calendar.d.ts.map +1 -1
  113. package/backend/dist/routes/calendar.js +125 -22
  114. package/backend/dist/routes/calendar.js.map +1 -1
  115. package/backend/dist/routes/channels.d.ts +3 -0
  116. package/backend/dist/routes/channels.d.ts.map +1 -0
  117. package/backend/dist/routes/channels.js +447 -0
  118. package/backend/dist/routes/channels.js.map +1 -0
  119. package/backend/dist/routes/chat.d.ts.map +1 -1
  120. package/backend/dist/routes/chat.js +195 -7
  121. package/backend/dist/routes/chat.js.map +1 -1
  122. package/backend/dist/routes/costs.d.ts +3 -0
  123. package/backend/dist/routes/costs.d.ts.map +1 -0
  124. package/backend/dist/routes/costs.js +191 -0
  125. package/backend/dist/routes/costs.js.map +1 -0
  126. package/backend/dist/routes/documents.d.ts.map +1 -1
  127. package/backend/dist/routes/documents.js +64 -21
  128. package/backend/dist/routes/documents.js.map +1 -1
  129. package/backend/dist/routes/evaluations.d.ts +3 -0
  130. package/backend/dist/routes/evaluations.d.ts.map +1 -0
  131. package/backend/dist/routes/evaluations.js +352 -0
  132. package/backend/dist/routes/evaluations.js.map +1 -0
  133. package/backend/dist/routes/imageGen.d.ts.map +1 -1
  134. package/backend/dist/routes/imageGen.js +2 -1
  135. package/backend/dist/routes/imageGen.js.map +1 -1
  136. package/backend/dist/routes/media.d.ts.map +1 -1
  137. package/backend/dist/routes/media.js +228 -7
  138. package/backend/dist/routes/media.js.map +1 -1
  139. package/backend/dist/routes/notes.d.ts.map +1 -1
  140. package/backend/dist/routes/notes.js +149 -36
  141. package/backend/dist/routes/notes.js.map +1 -1
  142. package/backend/dist/routes/notifications.d.ts +3 -0
  143. package/backend/dist/routes/notifications.d.ts.map +1 -0
  144. package/backend/dist/routes/notifications.js +213 -0
  145. package/backend/dist/routes/notifications.js.map +1 -0
  146. package/backend/dist/routes/openaiCompat.d.ts +3 -0
  147. package/backend/dist/routes/openaiCompat.d.ts.map +1 -0
  148. package/backend/dist/routes/openaiCompat.js +314 -0
  149. package/backend/dist/routes/openaiCompat.js.map +1 -0
  150. package/backend/dist/routes/personas.js +1 -1
  151. package/backend/dist/routes/personas.js.map +1 -1
  152. package/backend/dist/routes/prompts.d.ts +3 -0
  153. package/backend/dist/routes/prompts.d.ts.map +1 -0
  154. package/backend/dist/routes/prompts.js +173 -0
  155. package/backend/dist/routes/prompts.js.map +1 -0
  156. package/backend/dist/routes/search.d.ts.map +1 -1
  157. package/backend/dist/routes/search.js +29 -1
  158. package/backend/dist/routes/search.js.map +1 -1
  159. package/backend/dist/routes/skills.d.ts +3 -0
  160. package/backend/dist/routes/skills.d.ts.map +1 -0
  161. package/backend/dist/routes/skills.js +246 -0
  162. package/backend/dist/routes/skills.js.map +1 -0
  163. package/backend/dist/routes/stt.d.ts.map +1 -1
  164. package/backend/dist/routes/stt.js +2 -1
  165. package/backend/dist/routes/stt.js.map +1 -1
  166. package/backend/dist/routes/tools.d.ts +3 -0
  167. package/backend/dist/routes/tools.d.ts.map +1 -0
  168. package/backend/dist/routes/tools.js +359 -0
  169. package/backend/dist/routes/tools.js.map +1 -0
  170. package/backend/dist/routes/tts.d.ts.map +1 -1
  171. package/backend/dist/routes/tts.js +63 -8
  172. package/backend/dist/routes/tts.js.map +1 -1
  173. package/backend/dist/routes/voiceAccess.d.ts +3 -0
  174. package/backend/dist/routes/voiceAccess.d.ts.map +1 -0
  175. package/backend/dist/routes/voiceAccess.js +102 -0
  176. package/backend/dist/routes/voiceAccess.js.map +1 -0
  177. package/backend/dist/services/apiTokenService.d.ts +2 -2
  178. package/backend/dist/services/apiTokenService.d.ts.map +1 -1
  179. package/backend/dist/services/apiTokenService.js +7 -0
  180. package/backend/dist/services/apiTokenService.js.map +1 -1
  181. package/backend/dist/services/authorizationService.d.ts +8 -4
  182. package/backend/dist/services/authorizationService.d.ts.map +1 -1
  183. package/backend/dist/services/authorizationService.js +31 -1
  184. package/backend/dist/services/authorizationService.js.map +1 -1
  185. package/backend/dist/services/automationSchedulerService.d.ts.map +1 -1
  186. package/backend/dist/services/automationSchedulerService.js +33 -3
  187. package/backend/dist/services/automationSchedulerService.js.map +1 -1
  188. package/backend/dist/services/automationService.d.ts +4 -1
  189. package/backend/dist/services/automationService.d.ts.map +1 -1
  190. package/backend/dist/services/automationService.js +25 -2
  191. package/backend/dist/services/automationService.js.map +1 -1
  192. package/backend/dist/services/builtinToolsService.d.ts +20 -0
  193. package/backend/dist/services/builtinToolsService.d.ts.map +1 -0
  194. package/backend/dist/services/builtinToolsService.js +661 -0
  195. package/backend/dist/services/builtinToolsService.js.map +1 -0
  196. package/backend/dist/services/calendarService.d.ts +74 -0
  197. package/backend/dist/services/calendarService.d.ts.map +1 -0
  198. package/backend/dist/services/calendarService.js +339 -0
  199. package/backend/dist/services/calendarService.js.map +1 -0
  200. package/backend/dist/services/channelService.d.ts +195 -0
  201. package/backend/dist/services/channelService.d.ts.map +1 -0
  202. package/backend/dist/services/channelService.js +1057 -0
  203. package/backend/dist/services/channelService.js.map +1 -0
  204. package/backend/dist/services/chatRequestService.d.ts.map +1 -1
  205. package/backend/dist/services/chatRequestService.js +29 -1
  206. package/backend/dist/services/chatRequestService.js.map +1 -1
  207. package/backend/dist/services/chatService.d.ts +61 -1
  208. package/backend/dist/services/chatService.d.ts.map +1 -1
  209. package/backend/dist/services/chatService.js +218 -1
  210. package/backend/dist/services/chatService.js.map +1 -1
  211. package/backend/dist/services/chatToolRuntimeService.d.ts +55 -0
  212. package/backend/dist/services/chatToolRuntimeService.d.ts.map +1 -0
  213. package/backend/dist/services/chatToolRuntimeService.js +389 -0
  214. package/backend/dist/services/chatToolRuntimeService.js.map +1 -0
  215. package/backend/dist/services/costGovernanceService.d.ts +102 -0
  216. package/backend/dist/services/costGovernanceService.d.ts.map +1 -0
  217. package/backend/dist/services/costGovernanceService.js +463 -0
  218. package/backend/dist/services/costGovernanceService.js.map +1 -0
  219. package/backend/dist/services/dataArchiveService.d.ts +6 -0
  220. package/backend/dist/services/dataArchiveService.d.ts.map +1 -1
  221. package/backend/dist/services/dataArchiveService.js +23 -4
  222. package/backend/dist/services/dataArchiveService.js.map +1 -1
  223. package/backend/dist/services/documentService.d.ts +58 -1
  224. package/backend/dist/services/documentService.d.ts.map +1 -1
  225. package/backend/dist/services/documentService.js +289 -84
  226. package/backend/dist/services/documentService.js.map +1 -1
  227. package/backend/dist/services/durableChatGenerationService.d.ts +9 -0
  228. package/backend/dist/services/durableChatGenerationService.d.ts.map +1 -1
  229. package/backend/dist/services/durableChatGenerationService.js +152 -11
  230. package/backend/dist/services/durableChatGenerationService.js.map +1 -1
  231. package/backend/dist/services/evaluationService.d.ts +111 -0
  232. package/backend/dist/services/evaluationService.d.ts.map +1 -0
  233. package/backend/dist/services/evaluationService.js +403 -0
  234. package/backend/dist/services/evaluationService.js.map +1 -0
  235. package/backend/dist/services/galleryService.d.ts +6 -0
  236. package/backend/dist/services/galleryService.d.ts.map +1 -1
  237. package/backend/dist/services/galleryService.js +26 -0
  238. package/backend/dist/services/galleryService.js.map +1 -1
  239. package/backend/dist/services/mcpClientService.d.ts +29 -0
  240. package/backend/dist/services/mcpClientService.d.ts.map +1 -0
  241. package/backend/dist/services/mcpClientService.js +189 -0
  242. package/backend/dist/services/mcpClientService.js.map +1 -0
  243. package/backend/dist/services/noteService.d.ts +71 -0
  244. package/backend/dist/services/noteService.d.ts.map +1 -0
  245. package/backend/dist/services/noteService.js +397 -0
  246. package/backend/dist/services/noteService.js.map +1 -0
  247. package/backend/dist/services/notificationService.d.ts +55 -0
  248. package/backend/dist/services/notificationService.d.ts.map +1 -0
  249. package/backend/dist/services/notificationService.js +308 -0
  250. package/backend/dist/services/notificationService.js.map +1 -0
  251. package/backend/dist/services/openApiToolService.d.ts +38 -0
  252. package/backend/dist/services/openApiToolService.d.ts.map +1 -0
  253. package/backend/dist/services/openApiToolService.js +254 -0
  254. package/backend/dist/services/openApiToolService.js.map +1 -0
  255. package/backend/dist/services/personaService.d.ts +18 -2
  256. package/backend/dist/services/personaService.d.ts.map +1 -1
  257. package/backend/dist/services/personaService.js +113 -4
  258. package/backend/dist/services/personaService.js.map +1 -1
  259. package/backend/dist/services/pluginImageGenerationService.d.ts +19 -0
  260. package/backend/dist/services/pluginImageGenerationService.d.ts.map +1 -1
  261. package/backend/dist/services/pluginImageGenerationService.js +121 -0
  262. package/backend/dist/services/pluginImageGenerationService.js.map +1 -1
  263. package/backend/dist/services/pluginService.d.ts +7 -0
  264. package/backend/dist/services/pluginService.d.ts.map +1 -1
  265. package/backend/dist/services/pluginService.js +8 -2
  266. package/backend/dist/services/pluginService.js.map +1 -1
  267. package/backend/dist/services/promptService.d.ts +74 -0
  268. package/backend/dist/services/promptService.d.ts.map +1 -0
  269. package/backend/dist/services/promptService.js +513 -0
  270. package/backend/dist/services/promptService.js.map +1 -0
  271. package/backend/dist/services/resourceGrantService.d.ts.map +1 -1
  272. package/backend/dist/services/resourceGrantService.js +62 -17
  273. package/backend/dist/services/resourceGrantService.js.map +1 -1
  274. package/backend/dist/services/searchService.d.ts +29 -0
  275. package/backend/dist/services/searchService.d.ts.map +1 -0
  276. package/backend/dist/services/searchService.js +0 -0
  277. package/backend/dist/services/searchService.js.map +1 -0
  278. package/backend/dist/services/securityAuditService.d.ts.map +1 -1
  279. package/backend/dist/services/securityAuditService.js +2 -1
  280. package/backend/dist/services/securityAuditService.js.map +1 -1
  281. package/backend/dist/services/sharedResourceAccess.d.ts +25 -0
  282. package/backend/dist/services/sharedResourceAccess.d.ts.map +1 -0
  283. package/backend/dist/services/sharedResourceAccess.js +59 -0
  284. package/backend/dist/services/sharedResourceAccess.js.map +1 -0
  285. package/backend/dist/services/skillService.d.ts +137 -0
  286. package/backend/dist/services/skillService.d.ts.map +1 -0
  287. package/backend/dist/services/skillService.js +852 -0
  288. package/backend/dist/services/skillService.js.map +1 -0
  289. package/backend/dist/services/toolAccessService.d.ts +9 -0
  290. package/backend/dist/services/toolAccessService.d.ts.map +1 -0
  291. package/backend/dist/services/toolAccessService.js +60 -0
  292. package/backend/dist/services/toolAccessService.js.map +1 -0
  293. package/backend/dist/services/toolApprovalService.d.ts +34 -0
  294. package/backend/dist/services/toolApprovalService.d.ts.map +1 -0
  295. package/backend/dist/services/toolApprovalService.js +188 -0
  296. package/backend/dist/services/toolApprovalService.js.map +1 -0
  297. package/backend/dist/services/toolGatewayService.d.ts +53 -0
  298. package/backend/dist/services/toolGatewayService.d.ts.map +1 -0
  299. package/backend/dist/services/toolGatewayService.js +249 -0
  300. package/backend/dist/services/toolGatewayService.js.map +1 -0
  301. package/backend/dist/services/toolServerService.d.ts +57 -0
  302. package/backend/dist/services/toolServerService.d.ts.map +1 -0
  303. package/backend/dist/services/toolServerService.js +521 -0
  304. package/backend/dist/services/toolServerService.js.map +1 -0
  305. package/backend/dist/services/voiceAccessService.d.ts +10 -0
  306. package/backend/dist/services/voiceAccessService.d.ts.map +1 -0
  307. package/backend/dist/services/voiceAccessService.js +74 -0
  308. package/backend/dist/services/voiceAccessService.js.map +1 -0
  309. package/backend/dist/services/voiceProfileService.d.ts +20 -0
  310. package/backend/dist/services/voiceProfileService.d.ts.map +1 -1
  311. package/backend/dist/services/voiceProfileService.js +60 -0
  312. package/backend/dist/services/voiceProfileService.js.map +1 -1
  313. package/backend/dist/storage.d.ts +28 -1
  314. package/backend/dist/storage.d.ts.map +1 -1
  315. package/backend/dist/storage.js +100 -1
  316. package/backend/dist/storage.js.map +1 -1
  317. package/backend/dist/storageMappers.d.ts +2 -2
  318. package/backend/dist/storageMappers.d.ts.map +1 -1
  319. package/backend/dist/storageMappers.js.map +1 -1
  320. package/backend/dist/types/index.d.ts +197 -2
  321. package/backend/dist/types/index.d.ts.map +1 -1
  322. package/backend/dist/types/index.js.map +1 -1
  323. package/backend/dist/types/tools.d.ts +137 -0
  324. package/backend/dist/types/tools.d.ts.map +1 -0
  325. package/backend/dist/types/tools.js +25 -0
  326. package/backend/dist/types/tools.js.map +1 -0
  327. package/backend/dist/utils/chatDocumentContext.d.ts +26 -1
  328. package/backend/dist/utils/chatDocumentContext.d.ts.map +1 -1
  329. package/backend/dist/utils/chatDocumentContext.js +87 -18
  330. package/backend/dist/utils/chatDocumentContext.js.map +1 -1
  331. package/backend/dist/utils/documentExtraction.d.ts +69 -0
  332. package/backend/dist/utils/documentExtraction.d.ts.map +1 -0
  333. package/backend/dist/utils/documentExtraction.js +545 -0
  334. package/backend/dist/utils/documentExtraction.js.map +1 -0
  335. package/backend/dist/utils/hybridRetrieval.d.ts +34 -0
  336. package/backend/dist/utils/hybridRetrieval.d.ts.map +1 -0
  337. package/backend/dist/utils/hybridRetrieval.js +119 -0
  338. package/backend/dist/utils/hybridRetrieval.js.map +1 -0
  339. package/backend/dist/utils/icsCalendar.d.ts +27 -0
  340. package/backend/dist/utils/icsCalendar.d.ts.map +1 -0
  341. package/backend/dist/utils/icsCalendar.js +313 -0
  342. package/backend/dist/utils/icsCalendar.js.map +1 -0
  343. package/backend/dist/utils/imageEditUpload.d.ts +29 -0
  344. package/backend/dist/utils/imageEditUpload.d.ts.map +1 -0
  345. package/backend/dist/utils/imageEditUpload.js +82 -0
  346. package/backend/dist/utils/imageEditUpload.js.map +1 -0
  347. package/backend/dist/utils/logRedaction.d.ts +2 -0
  348. package/backend/dist/utils/logRedaction.d.ts.map +1 -0
  349. package/backend/dist/utils/logRedaction.js +66 -0
  350. package/backend/dist/utils/logRedaction.js.map +1 -0
  351. package/backend/dist/utils/logger.d.ts +3 -0
  352. package/backend/dist/utils/logger.d.ts.map +1 -1
  353. package/backend/dist/utils/logger.js +63 -20
  354. package/backend/dist/utils/logger.js.map +1 -1
  355. package/backend/dist/utils/pluginChatAdapter.d.ts +5 -1
  356. package/backend/dist/utils/pluginChatAdapter.d.ts.map +1 -1
  357. package/backend/dist/utils/pluginChatAdapter.js +83 -0
  358. package/backend/dist/utils/pluginChatAdapter.js.map +1 -1
  359. package/backend/dist/utils/pluginDefinitionTrust.js +1 -1
  360. package/backend/dist/utils/resourceLimits.d.ts +61 -2
  361. package/backend/dist/utils/resourceLimits.d.ts.map +1 -1
  362. package/backend/dist/utils/resourceLimits.js +59 -0
  363. package/backend/dist/utils/resourceLimits.js.map +1 -1
  364. package/backend/dist/utils/toolEgress.d.ts +22 -0
  365. package/backend/dist/utils/toolEgress.d.ts.map +1 -0
  366. package/backend/dist/utils/toolEgress.js +157 -0
  367. package/backend/dist/utils/toolEgress.js.map +1 -0
  368. package/backend/dist/utils/websocketMessages.d.ts +6 -0
  369. package/backend/dist/utils/websocketMessages.d.ts.map +1 -1
  370. package/backend/dist/utils/websocketMessages.js +8 -0
  371. package/backend/dist/utils/websocketMessages.js.map +1 -1
  372. package/backend/dist/utils/zipArchive.d.ts +32 -0
  373. package/backend/dist/utils/zipArchive.d.ts.map +1 -0
  374. package/backend/dist/utils/zipArchive.js +171 -0
  375. package/backend/dist/utils/zipArchive.js.map +1 -0
  376. package/backend/dist/websocketServer.d.ts.map +1 -1
  377. package/backend/dist/websocketServer.js +142 -35
  378. package/backend/dist/websocketServer.js.map +1 -1
  379. package/backend/package.json +1 -1
  380. package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
  381. package/frontend/dist/assets/index-BPsH9FII.js +80 -0
  382. package/frontend/dist/assets/index-BtXF0Oxa.css +1 -0
  383. package/frontend/dist/index.html +18 -18
  384. package/frontend/dist/js/{ArtifactContainer-CpUi6ekF.js → ArtifactContainer-CIoFoMv8.js} +1 -1
  385. package/frontend/dist/js/{ArtifactDemoPage-BeNs3gHp.js → ArtifactDemoPage-D0JEIAbZ.js} +1 -1
  386. package/frontend/dist/js/{ArtifactSandboxFrame-qsj_Y3LV.js → ArtifactSandboxFrame-REazyIrx.js} +2 -2
  387. package/frontend/dist/js/ArtifactSlideOutPanel-A-i94ghF.js +1 -0
  388. package/frontend/dist/js/AutomationsPage-Dow_lbIf.js +1 -0
  389. package/frontend/dist/js/{Button-BPf_pOxH.js → Button-C0r3J9G_.js} +1 -1
  390. package/frontend/dist/js/CalendarPage-DLjasZOd.js +1 -0
  391. package/frontend/dist/js/ChannelsPage-C8hvTD7J.js +1 -0
  392. package/frontend/dist/js/ChatPage-BAgBHUES.js +16 -0
  393. package/frontend/dist/js/EvaluationsPage-Bm8FRoeX.js +2 -0
  394. package/frontend/dist/js/{FirstTimeSetup-Df0YJni3.js → FirstTimeSetup-BxJapNul.js} +1 -1
  395. package/frontend/dist/js/GalleryPage-DHz9DkK7.js +1 -0
  396. package/frontend/dist/js/HomePage-BqNy-FWX.js +1 -0
  397. package/frontend/dist/js/LibreClawPage-BDzDILjY.js +1 -0
  398. package/frontend/dist/js/{ModelManagerModals-DcLGYN3l.js → ModelManagerModals-DX9eT5r2.js} +1 -1
  399. package/frontend/dist/js/ModelSelector-ol8mK2ja.js +1 -0
  400. package/frontend/dist/js/NotesPage-DzHEx2O4.js +3 -0
  401. package/frontend/dist/js/{OptimizedSyntaxHighlighter-Dz7D1LPB.js → OptimizedSyntaxHighlighter-CZNPwN0i.js} +1 -1
  402. package/frontend/dist/js/PersonasPage-M_WKH2a9.js +13 -0
  403. package/frontend/dist/js/PluginUsagePage-Ctoka6JZ.js +1 -0
  404. package/frontend/dist/js/RichMessageContent-BqgafH4p.js +3 -0
  405. package/frontend/dist/js/SettingsModal-R9EDadAV.js +19 -0
  406. package/frontend/dist/js/{SettingsToggle-woG_WebF.js → SettingsToggle-BIVyfGjl.js} +1 -1
  407. package/frontend/dist/js/SystemPage-aOkrSuY7.js +1 -0
  408. package/frontend/dist/js/UserManagementPage-BOuaX3ay.js +1 -0
  409. package/frontend/dist/js/WorkPage-DxZISerQ.js +6 -0
  410. package/frontend/dist/js/api-6bSMtSNh.js +14 -0
  411. package/frontend/dist/js/{appStore-0JnCUlEq.js → appStore-CdObhQBb.js} +2 -2
  412. package/frontend/dist/js/ar-DT_VLBO3.js +2 -0
  413. package/frontend/dist/js/{authStore-DbMdvNgJ.js → authStore-DbqqrfZv.js} +3 -3
  414. package/frontend/dist/js/{babel-BsYoxD-7.js → babel-DSqqk5kt.js} +1 -1
  415. package/frontend/dist/js/bn-BrvEqhGA.js +2 -0
  416. package/frontend/dist/js/chatStore-DbZPJNsu.js +3 -0
  417. package/frontend/dist/js/{config-DSOYQ9a3.js → config-8KdgjtEx.js} +1 -1
  418. package/frontend/dist/js/cs-BthnM_YI.js +2 -0
  419. package/frontend/dist/js/da-DCth-6_j.js +2 -0
  420. package/frontend/dist/js/de-BBWt0gJ_.js +2 -0
  421. package/frontend/dist/js/es-DuslOPqM.js +2 -0
  422. package/frontend/dist/js/{estree-BcnBcOAx.js → estree-BU5-LV_1.js} +3 -3
  423. package/frontend/dist/js/evaluationsApi-CwZJsgeG.js +1 -0
  424. package/frontend/dist/js/fr-DZAeg46S.js +2 -0
  425. package/frontend/dist/js/hi-ijDSiY77.js +2 -0
  426. package/frontend/dist/js/{html-DkY55Jys.js → html-DeoAy2bo.js} +2 -2
  427. package/frontend/dist/js/id-BS6qS0oq.js +2 -0
  428. package/frontend/dist/js/is-CO2PMDte.js +2 -0
  429. package/frontend/dist/js/it-ChhxT4hs.js +2 -0
  430. package/frontend/dist/js/ja-cyeqUeJN.js +2 -0
  431. package/frontend/dist/js/ko-CfqHTLXj.js +2 -0
  432. package/frontend/dist/js/{markdown-IZSI9DXw.js → markdown-DOLsYkBY.js} +3 -3
  433. package/frontend/dist/js/markdown-core-BK7WVOb7.js +32 -0
  434. package/frontend/dist/js/{markdown-math-DK846gXU.js → markdown-math-e1ByS3KY.js} +5 -5
  435. package/frontend/dist/js/{messageCodeStyles-BFNR9Wv6.js → messageCodeStyles-DhVgizF-.js} +2 -2
  436. package/frontend/dist/js/ms-DZ5pACe1.js +2 -0
  437. package/frontend/dist/js/nl-BsRsnOVp.js +2 -0
  438. package/frontend/dist/js/pl-DXSTAW4M.js +2 -0
  439. package/frontend/dist/js/{postcss-BJ0okz7n.js → postcss-Bg15EcFE.js} +1 -1
  440. package/frontend/dist/js/pt-DmDx0JxB.js +2 -0
  441. package/frontend/dist/js/{react-vendor-C5yShNlM.js → react-vendor-BG_HPV6X.js} +1 -1
  442. package/frontend/dist/js/router-vendor-CbexJIsv.js +1 -0
  443. package/frontend/dist/js/ru-d2g8EWfY.js +2 -0
  444. package/frontend/dist/js/{standalone-Csw3jfJ_.js → standalone-BO5pzI8_.js} +3 -3
  445. package/frontend/dist/js/sv-CIhYUTVQ.js +2 -0
  446. package/frontend/dist/js/{syntax-highlight-3EEX6CGL.js → syntax-highlight-DcR9Vhb7.js} +3 -3
  447. package/frontend/dist/js/th-B3wSxanB.js +2 -0
  448. package/frontend/dist/js/tr-R4UGgPOY.js +2 -0
  449. package/frontend/dist/js/{typescript-CnF4UVx7.js → typescript-6GJ8APmn.js} +1 -1
  450. package/frontend/dist/js/ui-CV0LYmkM.js +2 -0
  451. package/frontend/dist/js/ui-vendor-CWjIBW-z.js +177 -0
  452. package/frontend/dist/js/uk-BzUF4Vkt.js +2 -0
  453. package/frontend/dist/js/{useTranslation-Bho4HCyg.js → useTranslation-5b5UhAc9.js} +1 -1
  454. package/frontend/dist/js/{utils-D9W1Lnkn.js → utils-BGrdX9ie.js} +1 -1
  455. package/frontend/dist/js/{utils-vendor-DWh39_3x.js → utils-vendor-9bQ62WeD.js} +1 -1
  456. package/frontend/dist/js/vi-CKc6Fg4h.js +2 -0
  457. package/frontend/dist/js/{workApi-C3uy9EDT.js → workApi-BruWTlHM.js} +3 -3
  458. package/frontend/dist/js/{yaml-DzT5hBSH.js → yaml-DUeWcOOH.js} +2 -2
  459. package/frontend/dist/js/zh-CXL-kDe8.js +2 -0
  460. package/package.json +2 -2
  461. package/plugins/openai.json +52 -9
  462. package/frontend/dist/assets/index-6o-Hc2wS.js +0 -41
  463. package/frontend/dist/assets/index-D-pM957b.css +0 -1
  464. package/frontend/dist/js/ArtifactSlideOutPanel-CSFRluWA.js +0 -1
  465. package/frontend/dist/js/AutomationsPage-KUeWoZNv.js +0 -1
  466. package/frontend/dist/js/CalendarPage-D9I972w8.js +0 -1
  467. package/frontend/dist/js/ChatPage-CmBx4oAL.js +0 -16
  468. package/frontend/dist/js/GalleryPage-bLCrdJN_.js +0 -1
  469. package/frontend/dist/js/HomePage-Uc6PEg-U.js +0 -1
  470. package/frontend/dist/js/LibreClawPage-CG63DHg1.js +0 -1
  471. package/frontend/dist/js/ModelSelector-yxJC64rM.js +0 -1
  472. package/frontend/dist/js/ModelsPage-1R_TxgmJ.js +0 -2
  473. package/frontend/dist/js/NotesPage-L65j5HoW.js +0 -1
  474. package/frontend/dist/js/PersonasPage-CKeBpHIH.js +0 -13
  475. package/frontend/dist/js/PluginUsagePage-BX1SKMbW.js +0 -1
  476. package/frontend/dist/js/RichMessageContent-DiySVYGB.js +0 -3
  477. package/frontend/dist/js/SettingsModal-68SG1uZ2.js +0 -1
  478. package/frontend/dist/js/SystemPage-CytpZiQV.js +0 -1
  479. package/frontend/dist/js/UserManagementPage-BzJBvZiS.js +0 -1
  480. package/frontend/dist/js/WorkPage-C8gr0QJe.js +0 -6
  481. package/frontend/dist/js/api-BDtqBtqw.js +0 -14
  482. package/frontend/dist/js/ar-CSpi37Gr.js +0 -2
  483. package/frontend/dist/js/bn-CyH1BeDD.js +0 -2
  484. package/frontend/dist/js/chatStore-CHQ2uINS.js +0 -3
  485. package/frontend/dist/js/cs-Cv2NfRK5.js +0 -2
  486. package/frontend/dist/js/da-D6D6IvPK.js +0 -2
  487. package/frontend/dist/js/de-BNoJJTrT.js +0 -2
  488. package/frontend/dist/js/es-kzo9C7q_.js +0 -2
  489. package/frontend/dist/js/fr-CjHYvJRp.js +0 -2
  490. package/frontend/dist/js/hi-BU98AdCQ.js +0 -2
  491. package/frontend/dist/js/id-CpdedejX.js +0 -2
  492. package/frontend/dist/js/is-Cp5dDWBH.js +0 -2
  493. package/frontend/dist/js/it-CuH_cK4U.js +0 -2
  494. package/frontend/dist/js/ja-BWXXXiHn.js +0 -2
  495. package/frontend/dist/js/ko-DQgv2M1t.js +0 -2
  496. package/frontend/dist/js/markdown-core-BmPPZiuD.js +0 -31
  497. package/frontend/dist/js/ms-DCcRoIcX.js +0 -2
  498. package/frontend/dist/js/nl-CbKR1_EU.js +0 -2
  499. package/frontend/dist/js/pl-CRyJZccn.js +0 -2
  500. package/frontend/dist/js/pt-BXYmHFGH.js +0 -2
  501. package/frontend/dist/js/router-vendor-DGFGM3EP.js +0 -1
  502. package/frontend/dist/js/ru-M9SyZG4b.js +0 -2
  503. package/frontend/dist/js/sv-DJ486LTU.js +0 -2
  504. package/frontend/dist/js/th-Ch7gKtZv.js +0 -2
  505. package/frontend/dist/js/tr-ClehWhSC.js +0 -2
  506. package/frontend/dist/js/ui-CC-K19wO.js +0 -2
  507. package/frontend/dist/js/ui-vendor-B1UgO3vd.js +0 -177
  508. package/frontend/dist/js/uk-BqAd5zHF.js +0 -2
  509. package/frontend/dist/js/vi-D9gjlDVB.js +0 -2
  510. package/frontend/dist/js/zh-DKTyHkiR.js +0 -2
@@ -18,6 +18,10 @@ import crypto from 'node:crypto';
18
18
  import { Readable } from 'node:stream';
19
19
  import { v4 as uuidv4 } from 'uuid';
20
20
  import storageService from '../storage.js';
21
+ import { getPersistence } from '../persistence/index.js';
22
+ import { encryptionService } from './encryptionService.js';
23
+ import { listGrantsForActor } from './resourceGrantService.js';
24
+ import { grantedResourceIdsFor, sharedMetaFor, } from './sharedResourceAccess.js';
21
25
  import { configurePlatformStorageRuntime, getPlatformStorageRuntime, } from '../platform/storage/index.js';
22
26
  import { MAX_VECTOR_RECORDS_PER_UPSERT, MAX_VECTOR_RESOURCE_INDEX_ENTRIES, } from '../platform/storage/vectorStore.js';
23
27
  import { aggregateDocumentIndexRevision, createDocumentIndexMetadata, DOCUMENT_CHUNKER_VERSION, DOCUMENT_INDEX_METADATA_KEY, DOCUMENT_VECTOR_NAMESPACE, DOCUMENT_VECTOR_VERSION, documentChunkSourceRevision as sourceRevision, documentEmbeddingDimensions as embeddingDimensions, documentIndexMatchesSpec, embeddedDocumentChunks as embeddedChunks, readDocumentIndexMetadata as readDocumentIndexMetadataValue, } from '../platform/storage/documentVectorIndex.js';
@@ -29,6 +33,8 @@ import { transactionalResourceDeletionEnqueuer } from '../platform/jobs/resource
29
33
  import preferencesService from './preferencesService.js';
30
34
  import { createLogger } from '../utils/logger.js';
31
35
  import { throwIfChatGenerationCancelled } from '../utils/chatCancellation.js';
36
+ import { reciprocalRankFusion, scoreCandidatesBm25, } from '../utils/hybridRetrieval.js';
37
+ import { extractDocumentContentByType, readDocumentSegments, resolveDocumentFileType, resolveSegmentLabel, } from '../utils/documentExtraction.js';
32
38
  const logger = createLogger('documents');
33
39
  const DOCUMENT_BLOB_PURPOSE = 'document.source';
34
40
  const DOCUMENT_POST_VECTOR_FAULT_MARKER = 'LIBRE_DOCUMENT_POST_VECTOR_KILL';
@@ -248,31 +254,29 @@ const getPdfjsLib = async () => {
248
254
  }
249
255
  return pdfjsLib;
250
256
  };
251
- const extractDocumentContent = async (fileBuffer, mimeType, signal) => {
257
+ /**
258
+ * Segment maps above this length would bloat the encrypted metadata column;
259
+ * a source under the extraction size cap never legitimately reaches it.
260
+ */
261
+ const MAX_DOCUMENT_SEGMENTS = 5000;
262
+ const extractDocumentContent = async (fileName, fileBuffer, mimeType, signal) => {
252
263
  if (signal?.aborted)
253
264
  throw signal.reason;
254
- if (mimeType === 'text/plain') {
255
- return { content: fileBuffer.toString('utf8'), fileType: 'txt' };
256
- }
257
- if (mimeType !== 'application/pdf') {
265
+ const fileType = resolveDocumentFileType(fileName, mimeType);
266
+ if (!fileType) {
258
267
  throw new Error(`Unsupported file type: ${mimeType}`);
259
268
  }
260
- const pdfLib = await getPdfjsLib();
261
- const pdfDocument = await pdfLib.getDocument({
262
- data: new Uint8Array(fileBuffer),
263
- }).promise;
264
- let textContent = '';
265
- for (let pageNum = 1; pageNum <= pdfDocument.numPages; pageNum += 1) {
266
- if (signal?.aborted)
267
- throw signal.reason;
268
- const page = await pdfDocument.getPage(pageNum);
269
- const pageText = await page.getTextContent();
270
- textContent += `${pageText.items
271
- .map(item => ('str' in item ? item.str : ''))
272
- .join(' ')}\n\n`;
273
- }
274
- return { content: textContent.trim(), fileType: 'pdf' };
269
+ const extracted = await extractDocumentContentByType(fileBuffer, fileType, {
270
+ signal,
271
+ ...(fileType === 'pdf' ? { pdfLib: await getPdfjsLib() } : {}),
272
+ });
273
+ return {
274
+ content: extracted.content,
275
+ fileType,
276
+ segments: extracted.segments.slice(0, MAX_DOCUMENT_SEGMENTS),
277
+ };
275
278
  };
279
+ const documentContentSha256 = (fileBuffer) => crypto.createHash('sha256').update(fileBuffer).digest('hex');
276
280
  export class DocumentService {
277
281
  async captureEmbeddingExecutionSpec(userId) {
278
282
  const settings = (await preferencesService.getPreferences(userId))
@@ -333,23 +337,36 @@ export class DocumentService {
333
337
  * if the SQL transaction cannot commit.
334
338
  */
335
339
  async queueDocumentProcessing(fileName, fileBuffer, mimeType, userId, sessionId, signal) {
336
- if (mimeType !== 'text/plain' && mimeType !== 'application/pdf') {
340
+ const resolvedType = resolveDocumentFileType(fileName, mimeType);
341
+ if (!resolvedType) {
337
342
  throw new Error(`Unsupported file type: ${mimeType}`);
338
343
  }
339
344
  if (signal?.aborted)
340
345
  throw signal.reason;
346
+ const contentSha256 = documentContentSha256(fileBuffer);
347
+ const duplicate = await this.findDuplicateDocument(userId, sessionId, contentSha256);
348
+ if (duplicate) {
349
+ const existingJob = await getDurableJobRuntime().service.getByIdempotency(userId, DOCUMENT_INGEST_IDEMPOTENCY_SCOPE, duplicate.id);
350
+ if (existingJob) {
351
+ return {
352
+ document: duplicate,
353
+ jobId: existingJob.id,
354
+ deduplicated: true,
355
+ };
356
+ }
357
+ }
341
358
  const documentId = uuidv4();
342
359
  const now = Date.now();
343
360
  const document = {
344
361
  id: documentId,
345
362
  filename: fileName,
346
363
  content: '',
347
- fileType: mimeType === 'application/pdf' ? 'pdf' : 'txt',
364
+ fileType: resolvedType,
348
365
  size: fileBuffer.length,
349
366
  ...(sessionId ? { sessionId } : {}),
350
367
  uploadedAt: now,
351
368
  createdAt: now,
352
- metadata: { processingStatus: 'queued' },
369
+ metadata: { processingStatus: 'queued', contentSha256 },
353
370
  };
354
371
  const platform = getPlatformStorageRuntime();
355
372
  const sourceBlob = await platform.blobStore.put({
@@ -417,7 +434,7 @@ export class DocumentService {
417
434
  async processDocument(fileName, fileBuffer, mimeType, userId, sessionId) {
418
435
  const documentId = uuidv4();
419
436
  try {
420
- const { content, fileType } = await extractDocumentContent(fileBuffer, mimeType);
437
+ const { content, fileType, segments } = await extractDocumentContent(fileName, fileBuffer, mimeType);
421
438
  let document = {
422
439
  id: documentId,
423
440
  filename: fileName,
@@ -426,6 +443,10 @@ export class DocumentService {
426
443
  size: fileBuffer.length,
427
444
  sessionId,
428
445
  uploadedAt: Date.now(),
446
+ metadata: {
447
+ contentSha256: documentContentSha256(fileBuffer),
448
+ ...(segments.length > 0 ? { segments } : {}),
449
+ },
429
450
  };
430
451
  const spec = await this.captureEmbeddingExecutionSpec(userId);
431
452
  // Process the document into chunks under one immutable execution spec.
@@ -791,7 +812,8 @@ export class DocumentService {
791
812
  }
792
813
  buffers.push(buffer);
793
814
  }
794
- const extracted = await extractDocumentContent(Buffer.concat(buffers, size), source.descriptor.contentType, signal);
815
+ const sourceBuffer = Buffer.concat(buffers, size);
816
+ const extracted = await extractDocumentContent(source.descriptor.originalFilename ?? current.filename, sourceBuffer, source.descriptor.contentType, signal);
795
817
  let document = {
796
818
  ...current,
797
819
  content: extracted.content,
@@ -804,6 +826,10 @@ export class DocumentService {
804
826
  ...(document.metadata ?? {}),
805
827
  processingStatus: 'completed',
806
828
  processedAt: Date.now(),
829
+ contentSha256: documentContentSha256(sourceBuffer),
830
+ ...(extracted.segments.length > 0
831
+ ? { segments: extracted.segments }
832
+ : {}),
807
833
  };
808
834
  document = withDocumentIndexMetadata(document, embedded, spec);
809
835
  await assertSideEffectAllowed?.();
@@ -837,6 +863,31 @@ export class DocumentService {
837
863
  async getDocument(documentId, userId) {
838
864
  return storageService.getDocument(documentId, userId);
839
865
  }
866
+ /**
867
+ * Finds an existing live document with identical source bytes in the same
868
+ * scope (the same session, or both standing). Upload deduplication only —
869
+ * a match in another scope is not a duplicate because deleting one scope's
870
+ * copy must never remove another's.
871
+ */
872
+ async findDuplicateDocument(userId, sessionId, contentSha256) {
873
+ const scope = sessionId ?? null;
874
+ const platform = getPlatformStorageRuntime();
875
+ const documents = await storageService.getAllDocuments(userId);
876
+ for (const candidate of documents) {
877
+ if ((candidate.sessionId ?? null) !== scope)
878
+ continue;
879
+ if (candidate.metadata?.contentSha256 !== contentSha256)
880
+ continue;
881
+ const reserved = await platform.domains.resourceDeletions.isReserved('document', candidate.id);
882
+ if (!reserved)
883
+ return candidate;
884
+ }
885
+ return undefined;
886
+ }
887
+ /** Documents in a chat's searchable scope: session uploads, selected collections, and standing uploads. */
888
+ async getDocumentsInScope(userId, sessionId, collectionIds) {
889
+ return (await storageService.getAllDocuments(userId)).filter(document => this.documentInScope(document, sessionId, collectionIds));
890
+ }
840
891
  async getDocuments(userId, sessionId) {
841
892
  const allDocs = await storageService.getAllDocuments(userId);
842
893
  if (sessionId) {
@@ -892,6 +943,147 @@ export class DocumentService {
892
943
  document.collectionId &&
893
944
  collectionIds.includes(document.collectionId));
894
945
  }
946
+ /**
947
+ * Principals that may retrieve this document's vectors: everyone granted
948
+ * the document directly plus everyone granted its collection. Grants ride
949
+ * the ACL table, so revocation applies on the very next query without
950
+ * re-embedding anything.
951
+ */
952
+ async vectorGrantsForDocument(document) {
953
+ const security = getPersistence(encryptionService).repositories.security;
954
+ const rows = [
955
+ ...(await security.grants.listForResource('document', document.id)),
956
+ ];
957
+ if (document.collectionId) {
958
+ rows.push(...(await security.grants.listForResource('knowledge-collection', document.collectionId)));
959
+ }
960
+ const unique = new Map();
961
+ for (const row of rows) {
962
+ if (row.principal_type !== 'user' && row.principal_type !== 'group') {
963
+ continue;
964
+ }
965
+ unique.set(`${row.principal_type}${row.principal_id}`, {
966
+ type: row.principal_type,
967
+ id: row.principal_id,
968
+ });
969
+ }
970
+ return [...unique.values()];
971
+ }
972
+ /**
973
+ * Re-publishes the vector ACL for every document a share change affects.
974
+ * Called after a grant on a document or knowledge collection is created
975
+ * or revoked.
976
+ */
977
+ async syncShareGrants(resourceType, resourceId, ownerUserId) {
978
+ const platform = getPlatformStorageRuntime();
979
+ const documents = resourceType === 'document'
980
+ ? await storageService
981
+ .getDocument(resourceId, ownerUserId)
982
+ .then(document => (document ? [document] : []))
983
+ : (await storageService.getAllDocuments(ownerUserId)).filter(document => document.collectionId === resourceId);
984
+ for (const document of documents) {
985
+ const grants = await this.vectorGrantsForDocument(document);
986
+ await platform.vectorStore.replaceResourceGrants({
987
+ actor: { userId: ownerUserId },
988
+ namespace: DOCUMENT_VECTOR_NAMESPACE,
989
+ resourceIds: [document.id],
990
+ grants,
991
+ });
992
+ }
993
+ }
994
+ /**
995
+ * Documents living in knowledge collections shared with the actor. The
996
+ * grant rows themselves are the authorization: they are resolved fresh
997
+ * (including group membership) on every call.
998
+ */
999
+ async loadSharedScopeDocuments(userId, collectionIds) {
1000
+ const grants = await listGrantsForActor({ userId });
1001
+ const shared = [];
1002
+ const seenCollections = new Set();
1003
+ const seenDocuments = new Set();
1004
+ for (const grant of grants) {
1005
+ if (grant.resource_type !== 'knowledge-collection')
1006
+ continue;
1007
+ const collectionId = grant.resource_id;
1008
+ if (seenCollections.has(collectionId))
1009
+ continue;
1010
+ seenCollections.add(collectionId);
1011
+ if (grant.owner_user_id === userId)
1012
+ continue;
1013
+ if (collectionIds &&
1014
+ collectionIds.length > 0 &&
1015
+ !collectionIds.includes(collectionId)) {
1016
+ continue;
1017
+ }
1018
+ const documents = (await storageService.getAllDocuments(grant.owner_user_id)).filter(document => document.collectionId === collectionId);
1019
+ for (const document of documents) {
1020
+ if (seenDocuments.has(document.id))
1021
+ continue;
1022
+ seenDocuments.add(document.id);
1023
+ shared.push({ document, ownerUserId: grant.owner_user_id });
1024
+ }
1025
+ }
1026
+ return shared;
1027
+ }
1028
+ /**
1029
+ * Loads a document the actor may read: their own, or one reachable
1030
+ * through a document or collection grant. Shared results are read-only.
1031
+ */
1032
+ async getDocumentShared(documentId, actor) {
1033
+ const own = await storageService.getDocument(documentId, actor.userId);
1034
+ if (own)
1035
+ return { document: own, ownerUserId: actor.userId };
1036
+ const ownerUserId = await getPersistence(encryptionService).repositories.resources.archive.ownerOf('document', documentId);
1037
+ if (!ownerUserId || ownerUserId === actor.userId)
1038
+ return undefined;
1039
+ const document = await storageService.getDocument(documentId, ownerUserId);
1040
+ if (!document)
1041
+ return undefined;
1042
+ let shared = await sharedMetaFor(actor, 'document', documentId, ownerUserId);
1043
+ if (!shared && document.collectionId) {
1044
+ shared = await sharedMetaFor(actor, 'knowledge-collection', document.collectionId, ownerUserId);
1045
+ }
1046
+ if (!shared)
1047
+ return undefined;
1048
+ return { document, ownerUserId, shared };
1049
+ }
1050
+ /** Documents shared with the actor through collection grants. */
1051
+ async listSharedDocuments(actor) {
1052
+ const entries = await this.loadSharedScopeDocuments(actor.userId);
1053
+ const result = [];
1054
+ for (const { document, ownerUserId } of entries) {
1055
+ if (!document.collectionId)
1056
+ continue;
1057
+ const meta = await sharedMetaFor(actor, 'knowledge-collection', document.collectionId, ownerUserId);
1058
+ if (meta)
1059
+ result.push({ document, shared: meta });
1060
+ }
1061
+ return result;
1062
+ }
1063
+ /**
1064
+ * The actor's own collections followed by collections shared with them.
1065
+ */
1066
+ async listCollectionsWithShared(actor) {
1067
+ const own = await storageService.getKnowledgeCollections(actor.userId);
1068
+ const sharedIds = await grantedResourceIdsFor(actor, 'knowledge-collection', new Set(own.map(collection => collection.id)));
1069
+ const shared = [];
1070
+ for (const collectionId of sharedIds) {
1071
+ const collection = await storageService.getKnowledgeCollectionById(collectionId);
1072
+ if (!collection || collection.ownerUserId === actor.userId)
1073
+ continue;
1074
+ const meta = await sharedMetaFor(actor, 'knowledge-collection', collectionId, collection.ownerUserId);
1075
+ if (!meta)
1076
+ continue;
1077
+ shared.push({
1078
+ id: collection.id,
1079
+ name: collection.name,
1080
+ createdAt: collection.createdAt,
1081
+ updatedAt: collection.updatedAt,
1082
+ shared: meta,
1083
+ });
1084
+ }
1085
+ return [...own, ...shared];
1086
+ }
895
1087
  async indexDocumentChunks(document, chunks, userId, spec, attemptCount = 1, signal, assertSideEffectAllowed) {
896
1088
  const executionSpec = spec ?? (await this.captureEmbeddingExecutionSpec(userId));
897
1089
  const embedded = embeddedChunks(chunks);
@@ -924,6 +1116,7 @@ export class DocumentService {
924
1116
  mutated = true;
925
1117
  await assertSideEffectAllowed?.();
926
1118
  await this.assertDocumentIndexAuthoritative(document.id, userId, expectedRevision, expectedSource);
1119
+ const shareGrants = await this.vectorGrantsForDocument(document);
927
1120
  for (let offset = 0; offset < embedded.length; offset += MAX_VECTOR_RECORDS_PER_UPSERT) {
928
1121
  await assertSideEffectAllowed?.();
929
1122
  await this.assertDocumentIndexAuthoritative(document.id, userId, expectedRevision, expectedSource);
@@ -947,6 +1140,7 @@ export class DocumentService {
947
1140
  ? { collectionId: document.collectionId }
948
1141
  : {}),
949
1142
  },
1143
+ ...(shareGrants.length > 0 ? { grants: shareGrants } : {}),
950
1144
  })),
951
1145
  });
952
1146
  await assertSideEffectAllowed?.();
@@ -992,9 +1186,19 @@ export class DocumentService {
992
1186
  return this.keywordSearchDocuments(query, userId, sessionId, limit, collectionIds, signal);
993
1187
  }
994
1188
  const documents = (await storageService.getAllDocuments(userId)).filter(document => this.documentInScope(document, sessionId, collectionIds));
1189
+ // Shared-collection documents join the corpus read-only: their chunks
1190
+ // feed the lexical ranking directly, and their vector hits pass the
1191
+ // store's ACL predicate through the grants published at share time.
1192
+ const sharedDocuments = await this.loadSharedScopeDocuments(userId, collectionIds);
995
1193
  const platform = getPlatformStorageRuntime();
996
1194
  const chunksById = new Map();
997
- const unembedded = [];
1195
+ for (const { document } of sharedDocuments) {
1196
+ throwIfChatGenerationCancelled(signal);
1197
+ const documentChunks = await this.loadDocumentChunks(document.id);
1198
+ for (const chunk of documentChunks) {
1199
+ chunksById.set(chunk.id, { chunk, document });
1200
+ }
1201
+ }
998
1202
  for (const document of documents) {
999
1203
  throwIfChatGenerationCancelled(signal);
1000
1204
  let searchableDocument = document;
@@ -1026,7 +1230,6 @@ export class DocumentService {
1026
1230
  // and deletion cleanup through the shared document resource lease.
1027
1231
  // PostgreSQL/team reads never mutate PGVector.
1028
1232
  const publishedIndex = readDocumentIndexMetadata(searchableDocument);
1029
- const canProveCurrentEmbeddingModel = documentIndexMatchesSpec(publishedIndex, executionSpec);
1030
1233
  const localIndexProbe = platform.dialect === 'sqlite'
1031
1234
  ? exactLocalDocumentIndexProbe(searchableDocument, documentChunks, userId, executionSpec)
1032
1235
  : undefined;
@@ -1069,24 +1272,20 @@ export class DocumentService {
1069
1272
  for (const chunk of documentChunks) {
1070
1273
  // PostgreSQL deliberately keeps embeddings only in PGVector, so a
1071
1274
  // relational chunk without an `embedding` property can still be the
1072
- // authoritative content for a vector hit. Always make every current
1073
- // relational chunk available for hit hydration. Metadata tells us
1074
- // whether an absent inline embedding means "stored externally" or
1075
- // whether this chunk genuinely needs keyword fallback.
1275
+ // authoritative content for a vector hit. Every current relational
1276
+ // chunk joins the candidate set: vector hits hydrate from it and
1277
+ // the BM25 side of the hybrid ranking scores it, so chunks without
1278
+ // a current embedding stay reachable.
1076
1279
  chunksById.set(chunk.id, {
1077
1280
  chunk,
1078
1281
  document: searchableDocument,
1079
1282
  });
1080
- const indexedForSemanticQuery = canProveCurrentEmbeddingModel &&
1081
- publishedIndex !== undefined &&
1082
- publishedIndex.dimensions !== null &&
1083
- (platform.dialect === 'postgres' || localIndexReady);
1084
- if (!indexedForSemanticQuery) {
1085
- unembedded.push({ chunk, document: searchableDocument });
1086
- }
1087
1283
  }
1088
1284
  }
1089
- const resourceIds = documents.map(document => document.id);
1285
+ const resourceIds = [
1286
+ ...documents.map(document => document.id),
1287
+ ...sharedDocuments.map(entry => entry.document.id),
1288
+ ];
1090
1289
  const hits = [];
1091
1290
  for (let offset = 0; offset < resourceIds.length; offset += MAX_VECTOR_RESOURCE_FILTERS) {
1092
1291
  throwIfChatGenerationCancelled(signal);
@@ -1106,37 +1305,39 @@ export class DocumentService {
1106
1305
  hits.sort((left, right) => right.score - left.score ||
1107
1306
  left.ownerUserId.localeCompare(right.ownerUserId) ||
1108
1307
  left.id.localeCompare(right.id));
1109
- const top = [];
1110
- const selectedChunkIds = new Set();
1308
+ // Hybrid assembly: fuse the vector ranking with an in-process BM25
1309
+ // ranking over every in-scope chunk. The lexical side keeps chunks
1310
+ // without current embeddings reachable and lets an exact term match
1311
+ // outrank a semantically weak neighbor; fusion means neither ranking
1312
+ // silently dominates the other.
1313
+ const semanticRanked = [];
1314
+ const semanticScores = new Map();
1111
1315
  for (const hit of hits) {
1112
- if (top.length >= limit)
1113
- break;
1114
1316
  const match = chunksById.get(hit.id);
1115
1317
  if (!match ||
1116
1318
  match.document.id !== hit.resourceId ||
1117
1319
  sourceRevision(match.chunk) !== hit.sourceRevision ||
1118
- selectedChunkIds.has(match.chunk.id)) {
1320
+ semanticScores.has(match.chunk.id)) {
1119
1321
  continue;
1120
1322
  }
1121
- top.push({
1122
- ...match.chunk,
1123
- filename: match.document.filename,
1124
- });
1125
- selectedChunkIds.add(match.chunk.id);
1323
+ semanticRanked.push(match.chunk.id);
1324
+ semanticScores.set(match.chunk.id, hit.score);
1126
1325
  }
1127
- if (top.length < limit && unembedded.length > 0) {
1128
- const scored = this.scoreChunksByKeywords(query, unembedded);
1129
- for (const entry of scored) {
1130
- if (top.length >= limit)
1131
- break;
1132
- if (selectedChunkIds.has(entry.chunk.id))
1133
- continue;
1134
- top.push({
1135
- ...entry.chunk,
1136
- filename: entry.document.filename,
1137
- });
1138
- selectedChunkIds.add(entry.chunk.id);
1139
- }
1326
+ const lexicalScored = this.scoreChunksByKeywords(query, [
1327
+ ...chunksById.values(),
1328
+ ]);
1329
+ const fused = reciprocalRankFusion([
1330
+ semanticRanked,
1331
+ lexicalScored.map(entry => entry.chunk.id),
1332
+ ]);
1333
+ const top = [];
1334
+ for (const { id, score } of fused) {
1335
+ if (top.length >= limit)
1336
+ break;
1337
+ const match = chunksById.get(id);
1338
+ if (!match)
1339
+ continue;
1340
+ top.push(this.hydrateRetrievedChunk(match.chunk, match.document, score));
1140
1341
  }
1141
1342
  if (top.length > 0)
1142
1343
  return top;
@@ -1151,25 +1352,13 @@ export class DocumentService {
1151
1352
  return this.keywordSearchDocuments(query, userId, sessionId, limit, collectionIds, signal);
1152
1353
  }
1153
1354
  }
1154
- /** Term-frequency scores for a set of chunks, best first. */
1355
+ /** BM25 scores for a set of chunks, best first. */
1155
1356
  scoreChunksByKeywords(query, entries) {
1156
- const searchTerms = query
1157
- .toLowerCase()
1158
- .split(/\s+/)
1159
- .filter(term => term.length > 2);
1160
- const scored = [];
1161
- for (const { chunk, document } of entries) {
1162
- const chunkText = chunk.content.toLowerCase();
1163
- let score = 0;
1164
- for (const term of searchTerms) {
1165
- // Escape special regex characters to prevent injection
1166
- const escapedTerm = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1167
- score += (chunkText.match(new RegExp(escapedTerm, 'gi')) || []).length;
1168
- }
1169
- if (score > 0)
1170
- scored.push({ chunk, score, document });
1171
- }
1172
- return scored.sort((a, b) => b.score - a.score);
1357
+ const byId = new Map(entries.map(entry => [entry.chunk.id, entry]));
1358
+ return scoreCandidatesBm25(query, entries.map(entry => ({ id: entry.chunk.id, text: entry.chunk.content })), { requireQueryWordMatch: true }).map(({ id, score }) => {
1359
+ const entry = byId.get(id);
1360
+ return { chunk: entry.chunk, score, document: entry.document };
1361
+ });
1173
1362
  }
1174
1363
  async keywordSearchDocuments(query, userId, sessionId, limit = 5, collectionIds, signal) {
1175
1364
  const candidates = [];
@@ -1184,12 +1373,28 @@ export class DocumentService {
1184
1373
  candidates.push({ chunk, document });
1185
1374
  }
1186
1375
  }
1376
+ for (const { document } of await this.loadSharedScopeDocuments(userId, collectionIds)) {
1377
+ throwIfChatGenerationCancelled(signal);
1378
+ const documentChunks = await this.loadDocumentChunks(document.id);
1379
+ for (const chunk of documentChunks) {
1380
+ candidates.push({ chunk, document });
1381
+ }
1382
+ }
1187
1383
  throwIfChatGenerationCancelled(signal);
1188
1384
  const results = this.scoreChunksByKeywords(query, candidates);
1189
- return results.slice(0, limit).map(result => ({
1190
- ...result.chunk,
1191
- filename: result.document.filename, // Add filename for context
1192
- }));
1385
+ return results
1386
+ .slice(0, limit)
1387
+ .map(result => this.hydrateRetrievedChunk(result.chunk, result.document, result.score));
1388
+ }
1389
+ /** Attaches filename, score, and source location to a retrieval result. */
1390
+ hydrateRetrievedChunk(chunk, document, score) {
1391
+ const location = resolveSegmentLabel(readDocumentSegments(document.metadata), chunk.startChar, chunk.endChar);
1392
+ return {
1393
+ ...chunk,
1394
+ filename: document.filename,
1395
+ ...(score !== undefined ? { score } : {}),
1396
+ ...(location ? { location } : {}),
1397
+ };
1193
1398
  }
1194
1399
  // Get relevant context for RAG
1195
1400
  async getRelevantContext(query, userId, sessionId) {