ruflo 3.5.2 → 3.5.3

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 (521) hide show
  1. package/dist/rvf.manifest.json +295 -0
  2. package/package.json +16 -2
  3. package/src/chat-ui/Dockerfile +25 -0
  4. package/src/chat-ui/patch-mcp-url-safety.sh +28 -0
  5. package/src/chat-ui/static/chatui/icon-144x144.png +0 -0
  6. package/src/chat-ui/static/chatui/omni-welcome.gif +0 -0
  7. package/src/config/config.example.json +76 -0
  8. package/src/mcp-bridge/Dockerfile +45 -0
  9. package/src/mcp-bridge/index.js +1668 -0
  10. package/src/mcp-bridge/mcp-stdio-kernel.js +159 -0
  11. package/src/mcp-bridge/package.json +17 -0
  12. package/src/mcp-bridge/test-harness.js +470 -0
  13. package/src/nginx/Dockerfile +10 -0
  14. package/src/nginx/nginx.conf +67 -0
  15. package/src/nginx/static/favicon-dark.svg +4 -0
  16. package/src/nginx/static/favicon.svg +4 -0
  17. package/src/nginx/static/icon.svg +5 -0
  18. package/src/nginx/static/logo.svg +9 -0
  19. package/src/nginx/static/manifest.json +22 -0
  20. package/src/nginx/static/welcome.js +184 -0
  21. package/src/ruvocal/.claude/skills/add-model-descriptions/SKILL.md +73 -0
  22. package/src/ruvocal/.devcontainer/Dockerfile +9 -0
  23. package/src/ruvocal/.devcontainer/devcontainer.json +36 -0
  24. package/src/ruvocal/.dockerignore +13 -0
  25. package/src/ruvocal/.env +194 -0
  26. package/src/ruvocal/.env.ci +1 -0
  27. package/src/ruvocal/.eslintignore +13 -0
  28. package/src/ruvocal/.eslintrc.cjs +45 -0
  29. package/src/ruvocal/.github/ISSUE_TEMPLATE/bug-report--chat-ui-.md +43 -0
  30. package/src/ruvocal/.github/ISSUE_TEMPLATE/config-support.md +9 -0
  31. package/src/ruvocal/.github/ISSUE_TEMPLATE/feature-request--chat-ui-.md +17 -0
  32. package/src/ruvocal/.github/ISSUE_TEMPLATE/huggingchat.md +11 -0
  33. package/src/ruvocal/.github/release.yml +16 -0
  34. package/src/ruvocal/.github/workflows/build-docs.yml +18 -0
  35. package/src/ruvocal/.github/workflows/build-image.yml +142 -0
  36. package/src/ruvocal/.github/workflows/build-pr-docs.yml +20 -0
  37. package/src/ruvocal/.github/workflows/deploy-dev.yml +63 -0
  38. package/src/ruvocal/.github/workflows/deploy-prod.yml +78 -0
  39. package/src/ruvocal/.github/workflows/lint-and-test.yml +84 -0
  40. package/src/ruvocal/.github/workflows/slugify.yaml +72 -0
  41. package/src/ruvocal/.github/workflows/trufflehog.yml +17 -0
  42. package/src/ruvocal/.github/workflows/upload-pr-documentation.yml +16 -0
  43. package/src/ruvocal/.husky/lint-stage-config.js +4 -0
  44. package/src/ruvocal/.husky/pre-commit +2 -0
  45. package/src/ruvocal/.prettierignore +14 -0
  46. package/src/ruvocal/.prettierrc +7 -0
  47. package/src/ruvocal/.vscode/launch.json +11 -0
  48. package/src/ruvocal/.vscode/settings.json +14 -0
  49. package/src/ruvocal/CLAUDE.md +126 -0
  50. package/src/ruvocal/Dockerfile +93 -0
  51. package/src/ruvocal/LICENSE +203 -0
  52. package/src/ruvocal/PRIVACY.md +41 -0
  53. package/src/ruvocal/README.md +190 -0
  54. package/src/ruvocal/chart/Chart.yaml +5 -0
  55. package/src/ruvocal/chart/env/dev.yaml +260 -0
  56. package/src/ruvocal/chart/env/prod.yaml +273 -0
  57. package/src/ruvocal/chart/templates/_helpers.tpl +22 -0
  58. package/src/ruvocal/chart/templates/config.yaml +10 -0
  59. package/src/ruvocal/chart/templates/deployment.yaml +81 -0
  60. package/src/ruvocal/chart/templates/hpa.yaml +45 -0
  61. package/src/ruvocal/chart/templates/infisical.yaml +24 -0
  62. package/src/ruvocal/chart/templates/ingress-internal.yaml +32 -0
  63. package/src/ruvocal/chart/templates/ingress.yaml +32 -0
  64. package/src/ruvocal/chart/templates/network-policy.yaml +36 -0
  65. package/src/ruvocal/chart/templates/service-account.yaml +13 -0
  66. package/src/ruvocal/chart/templates/service-monitor.yaml +17 -0
  67. package/src/ruvocal/chart/templates/service.yaml +21 -0
  68. package/src/ruvocal/chart/values.yaml +73 -0
  69. package/src/ruvocal/docker-compose.yml +21 -0
  70. package/src/ruvocal/docs/adr/ADR-029-HUGGINGFACE-CHAT-UI-CLOUD-RUN.md +1236 -0
  71. package/src/ruvocal/docs/adr/ADR-033-RUVECTOR-RUFLO-MCP-INTEGRATION.md +111 -0
  72. package/src/ruvocal/docs/adr/ADR-034-OPTIONAL-MCP-BACKENDS.md +117 -0
  73. package/src/ruvocal/docs/adr/ADR-035-MCP-TOOL-GROUPS.md +186 -0
  74. package/src/ruvocal/docs/adr/ADR-037-AUTOPILOT-CHAT-MODE.md +1500 -0
  75. package/src/ruvocal/docs/adr/ADR-038-RUVOCAL-FORK.md +286 -0
  76. package/src/ruvocal/docs/source/_toctree.yml +30 -0
  77. package/src/ruvocal/docs/source/configuration/common-issues.md +38 -0
  78. package/src/ruvocal/docs/source/configuration/llm-router.md +105 -0
  79. package/src/ruvocal/docs/source/configuration/mcp-tools.md +84 -0
  80. package/src/ruvocal/docs/source/configuration/metrics.md +9 -0
  81. package/src/ruvocal/docs/source/configuration/open-id.md +57 -0
  82. package/src/ruvocal/docs/source/configuration/overview.md +89 -0
  83. package/src/ruvocal/docs/source/configuration/theming.md +20 -0
  84. package/src/ruvocal/docs/source/developing/architecture.md +48 -0
  85. package/src/ruvocal/docs/source/index.md +53 -0
  86. package/src/ruvocal/docs/source/installation/docker.md +43 -0
  87. package/src/ruvocal/docs/source/installation/helm.md +43 -0
  88. package/src/ruvocal/docs/source/installation/local.md +62 -0
  89. package/src/ruvocal/entrypoint.sh +19 -0
  90. package/src/ruvocal/mcp-bridge/.claude-flow/agents/store.json +27 -0
  91. package/src/ruvocal/mcp-bridge/.claude-flow/daemon-state.json +130 -0
  92. package/src/ruvocal/mcp-bridge/.claude-flow/daemon.log +0 -0
  93. package/src/ruvocal/mcp-bridge/.claude-flow/daemon.pid +1 -0
  94. package/src/ruvocal/mcp-bridge/.claude-flow/tasks/store.json +21 -0
  95. package/src/ruvocal/mcp-bridge/.swarm/hnsw.index +0 -0
  96. package/src/ruvocal/mcp-bridge/.swarm/hnsw.metadata.json +1 -0
  97. package/src/ruvocal/mcp-bridge/.swarm/memory.db +0 -0
  98. package/src/ruvocal/mcp-bridge/.swarm/model-router-state.json +14 -0
  99. package/src/ruvocal/mcp-bridge/.swarm/schema.sql +305 -0
  100. package/src/ruvocal/mcp-bridge/Dockerfile +45 -0
  101. package/src/ruvocal/mcp-bridge/cloudbuild.yaml +49 -0
  102. package/src/ruvocal/mcp-bridge/index.js +1864 -0
  103. package/src/ruvocal/mcp-bridge/mcp-stdio-kernel.js +159 -0
  104. package/src/ruvocal/mcp-bridge/package-lock.json +762 -0
  105. package/src/ruvocal/mcp-bridge/package.json +17 -0
  106. package/src/ruvocal/mcp-bridge/test-harness.js +470 -0
  107. package/src/ruvocal/models/add-your-models-here.txt +1 -0
  108. package/src/ruvocal/package-lock.json +11741 -0
  109. package/src/ruvocal/package.json +121 -0
  110. package/src/ruvocal/postcss.config.js +6 -0
  111. package/src/ruvocal/rvf.manifest.json +204 -0
  112. package/src/ruvocal/scripts/config.ts +64 -0
  113. package/src/ruvocal/scripts/generate-welcome.mjs +181 -0
  114. package/src/ruvocal/scripts/populate.ts +288 -0
  115. package/src/ruvocal/scripts/samples.txt +194 -0
  116. package/src/ruvocal/scripts/setups/vitest-setup-client.ts +0 -0
  117. package/src/ruvocal/scripts/setups/vitest-setup-server.ts +44 -0
  118. package/src/ruvocal/scripts/updateLocalEnv.ts +48 -0
  119. package/src/ruvocal/src/ambient.d.ts +7 -0
  120. package/src/ruvocal/src/app.d.ts +29 -0
  121. package/src/ruvocal/src/app.html +53 -0
  122. package/src/ruvocal/src/hooks.server.ts +32 -0
  123. package/src/ruvocal/src/hooks.ts +6 -0
  124. package/src/ruvocal/src/lib/APIClient.ts +148 -0
  125. package/src/ruvocal/src/lib/actions/clickOutside.ts +18 -0
  126. package/src/ruvocal/src/lib/actions/snapScrollToBottom.ts +346 -0
  127. package/src/ruvocal/src/lib/buildPrompt.ts +33 -0
  128. package/src/ruvocal/src/lib/components/AnnouncementBanner.svelte +20 -0
  129. package/src/ruvocal/src/lib/components/BackgroundGenerationPoller.svelte +168 -0
  130. package/src/ruvocal/src/lib/components/CodeBlock.svelte +73 -0
  131. package/src/ruvocal/src/lib/components/CopyToClipBoardBtn.svelte +92 -0
  132. package/src/ruvocal/src/lib/components/DeleteConversationModal.svelte +75 -0
  133. package/src/ruvocal/src/lib/components/EditConversationModal.svelte +100 -0
  134. package/src/ruvocal/src/lib/components/ExpandNavigation.svelte +22 -0
  135. package/src/ruvocal/src/lib/components/HoverTooltip.svelte +44 -0
  136. package/src/ruvocal/src/lib/components/HtmlPreviewModal.svelte +143 -0
  137. package/src/ruvocal/src/lib/components/InfiniteScroll.svelte +50 -0
  138. package/src/ruvocal/src/lib/components/MobileNav.svelte +300 -0
  139. package/src/ruvocal/src/lib/components/Modal.svelte +115 -0
  140. package/src/ruvocal/src/lib/components/ModelCardMetadata.svelte +71 -0
  141. package/src/ruvocal/src/lib/components/NavConversationItem.svelte +151 -0
  142. package/src/ruvocal/src/lib/components/NavMenu.svelte +295 -0
  143. package/src/ruvocal/src/lib/components/Pagination.svelte +97 -0
  144. package/src/ruvocal/src/lib/components/PaginationArrow.svelte +27 -0
  145. package/src/ruvocal/src/lib/components/Portal.svelte +24 -0
  146. package/src/ruvocal/src/lib/components/RetryBtn.svelte +18 -0
  147. package/src/ruvocal/src/lib/components/RuFloUniverse.svelte +185 -0
  148. package/src/ruvocal/src/lib/components/ScrollToBottomBtn.svelte +47 -0
  149. package/src/ruvocal/src/lib/components/ScrollToPreviousBtn.svelte +77 -0
  150. package/src/ruvocal/src/lib/components/ShareConversationModal.svelte +182 -0
  151. package/src/ruvocal/src/lib/components/StopGeneratingBtn.svelte +69 -0
  152. package/src/ruvocal/src/lib/components/SubscribeModal.svelte +87 -0
  153. package/src/ruvocal/src/lib/components/Switch.svelte +36 -0
  154. package/src/ruvocal/src/lib/components/SystemPromptModal.svelte +44 -0
  155. package/src/ruvocal/src/lib/components/Toast.svelte +27 -0
  156. package/src/ruvocal/src/lib/components/Tooltip.svelte +30 -0
  157. package/src/ruvocal/src/lib/components/WelcomeModal.svelte +46 -0
  158. package/src/ruvocal/src/lib/components/chat/Alternatives.svelte +77 -0
  159. package/src/ruvocal/src/lib/components/chat/BlockWrapper.svelte +72 -0
  160. package/src/ruvocal/src/lib/components/chat/ChatInput.svelte +490 -0
  161. package/src/ruvocal/src/lib/components/chat/ChatIntroduction.svelte +123 -0
  162. package/src/ruvocal/src/lib/components/chat/ChatMessage.svelte +548 -0
  163. package/src/ruvocal/src/lib/components/chat/ChatWindow.svelte +939 -0
  164. package/src/ruvocal/src/lib/components/chat/FileDropzone.svelte +92 -0
  165. package/src/ruvocal/src/lib/components/chat/ImageLightbox.svelte +66 -0
  166. package/src/ruvocal/src/lib/components/chat/MarkdownBlock.svelte +23 -0
  167. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte +69 -0
  168. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte.test.ts +58 -0
  169. package/src/ruvocal/src/lib/components/chat/MessageAvatar.svelte +103 -0
  170. package/src/ruvocal/src/lib/components/chat/ModelSwitch.svelte +64 -0
  171. package/src/ruvocal/src/lib/components/chat/OpenReasoningResults.svelte +81 -0
  172. package/src/ruvocal/src/lib/components/chat/TaskGroup.svelte +88 -0
  173. package/src/ruvocal/src/lib/components/chat/ToolUpdate.svelte +273 -0
  174. package/src/ruvocal/src/lib/components/chat/UploadedFile.svelte +253 -0
  175. package/src/ruvocal/src/lib/components/chat/UrlFetchModal.svelte +203 -0
  176. package/src/ruvocal/src/lib/components/chat/VoiceRecorder.svelte +214 -0
  177. package/src/ruvocal/src/lib/components/icons/IconBurger.svelte +20 -0
  178. package/src/ruvocal/src/lib/components/icons/IconCheap.svelte +20 -0
  179. package/src/ruvocal/src/lib/components/icons/IconChevron.svelte +24 -0
  180. package/src/ruvocal/src/lib/components/icons/IconDazzled.svelte +40 -0
  181. package/src/ruvocal/src/lib/components/icons/IconFast.svelte +20 -0
  182. package/src/ruvocal/src/lib/components/icons/IconLoading.svelte +22 -0
  183. package/src/ruvocal/src/lib/components/icons/IconMCP.svelte +28 -0
  184. package/src/ruvocal/src/lib/components/icons/IconMoon.svelte +21 -0
  185. package/src/ruvocal/src/lib/components/icons/IconNew.svelte +20 -0
  186. package/src/ruvocal/src/lib/components/icons/IconOmni.svelte +90 -0
  187. package/src/ruvocal/src/lib/components/icons/IconPaperclip.svelte +24 -0
  188. package/src/ruvocal/src/lib/components/icons/IconPro.svelte +37 -0
  189. package/src/ruvocal/src/lib/components/icons/IconShare.svelte +21 -0
  190. package/src/ruvocal/src/lib/components/icons/IconSun.svelte +93 -0
  191. package/src/ruvocal/src/lib/components/icons/Logo.svelte +68 -0
  192. package/src/ruvocal/src/lib/components/icons/LogoHuggingFaceBorderless.svelte +54 -0
  193. package/src/ruvocal/src/lib/components/mcp/AddServerForm.svelte +250 -0
  194. package/src/ruvocal/src/lib/components/mcp/MCPServerManager.svelte +185 -0
  195. package/src/ruvocal/src/lib/components/mcp/ServerCard.svelte +203 -0
  196. package/src/ruvocal/src/lib/components/players/AudioPlayer.svelte +82 -0
  197. package/src/ruvocal/src/lib/components/voice/AudioWaveform.svelte +96 -0
  198. package/src/ruvocal/src/lib/constants/mcpExamples.ts +135 -0
  199. package/src/ruvocal/src/lib/constants/mime.ts +11 -0
  200. package/src/ruvocal/src/lib/constants/pagination.ts +1 -0
  201. package/src/ruvocal/src/lib/constants/publicSepToken.ts +1 -0
  202. package/src/ruvocal/src/lib/constants/routerExamples.ts +209 -0
  203. package/src/ruvocal/src/lib/createShareLink.ts +27 -0
  204. package/src/ruvocal/src/lib/jobs/refresh-conversation-stats.ts +297 -0
  205. package/src/ruvocal/src/lib/migrations/lock.ts +56 -0
  206. package/src/ruvocal/src/lib/migrations/migrations.spec.ts +74 -0
  207. package/src/ruvocal/src/lib/migrations/migrations.ts +109 -0
  208. package/src/ruvocal/src/lib/migrations/routines/01-update-search-assistants.ts +50 -0
  209. package/src/ruvocal/src/lib/migrations/routines/02-update-assistants-models.ts +48 -0
  210. package/src/ruvocal/src/lib/migrations/routines/04-update-message-updates.ts +151 -0
  211. package/src/ruvocal/src/lib/migrations/routines/05-update-message-files.ts +56 -0
  212. package/src/ruvocal/src/lib/migrations/routines/06-trim-message-updates.ts +56 -0
  213. package/src/ruvocal/src/lib/migrations/routines/08-update-featured-to-review.ts +32 -0
  214. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.spec.ts +214 -0
  215. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.ts +88 -0
  216. package/src/ruvocal/src/lib/migrations/routines/10-update-reports-assistantid.ts +29 -0
  217. package/src/ruvocal/src/lib/migrations/routines/index.ts +15 -0
  218. package/src/ruvocal/src/lib/server/__tests__/conversation-stop-generating.spec.ts +103 -0
  219. package/src/ruvocal/src/lib/server/abortRegistry.ts +57 -0
  220. package/src/ruvocal/src/lib/server/abortedGenerations.ts +43 -0
  221. package/src/ruvocal/src/lib/server/adminToken.ts +62 -0
  222. package/src/ruvocal/src/lib/server/api/__tests__/conversations-id.spec.ts +296 -0
  223. package/src/ruvocal/src/lib/server/api/__tests__/conversations-message.spec.ts +216 -0
  224. package/src/ruvocal/src/lib/server/api/__tests__/conversations.spec.ts +235 -0
  225. package/src/ruvocal/src/lib/server/api/__tests__/misc.spec.ts +72 -0
  226. package/src/ruvocal/src/lib/server/api/__tests__/testHelpers.ts +86 -0
  227. package/src/ruvocal/src/lib/server/api/__tests__/user-reports.spec.ts +78 -0
  228. package/src/ruvocal/src/lib/server/api/__tests__/user.spec.ts +239 -0
  229. package/src/ruvocal/src/lib/server/api/types.ts +37 -0
  230. package/src/ruvocal/src/lib/server/api/utils/requireAuth.ts +22 -0
  231. package/src/ruvocal/src/lib/server/api/utils/resolveConversation.ts +69 -0
  232. package/src/ruvocal/src/lib/server/api/utils/resolveModel.ts +27 -0
  233. package/src/ruvocal/src/lib/server/api/utils/superjsonResponse.ts +15 -0
  234. package/src/ruvocal/src/lib/server/apiToken.ts +11 -0
  235. package/src/ruvocal/src/lib/server/auth.ts +554 -0
  236. package/src/ruvocal/src/lib/server/config.ts +187 -0
  237. package/src/ruvocal/src/lib/server/conversation.ts +83 -0
  238. package/src/ruvocal/src/lib/server/database/__tests__/rvf.spec.ts +709 -0
  239. package/src/ruvocal/src/lib/server/database/postgres.ts +700 -0
  240. package/src/ruvocal/src/lib/server/database/rvf.ts +1078 -0
  241. package/src/ruvocal/src/lib/server/database.ts +145 -0
  242. package/src/ruvocal/src/lib/server/endpoints/document.ts +68 -0
  243. package/src/ruvocal/src/lib/server/endpoints/endpoints.ts +43 -0
  244. package/src/ruvocal/src/lib/server/endpoints/images.ts +211 -0
  245. package/src/ruvocal/src/lib/server/endpoints/openai/endpointOai.ts +266 -0
  246. package/src/ruvocal/src/lib/server/endpoints/openai/openAIChatToTextGenerationStream.ts +212 -0
  247. package/src/ruvocal/src/lib/server/endpoints/openai/openAICompletionToTextGenerationStream.ts +32 -0
  248. package/src/ruvocal/src/lib/server/endpoints/preprocessMessages.ts +61 -0
  249. package/src/ruvocal/src/lib/server/exitHandler.ts +59 -0
  250. package/src/ruvocal/src/lib/server/files/downloadFile.ts +34 -0
  251. package/src/ruvocal/src/lib/server/files/uploadFile.ts +29 -0
  252. package/src/ruvocal/src/lib/server/findRepoRoot.ts +13 -0
  253. package/src/ruvocal/src/lib/server/fonts/Inter-Black.ttf +0 -0
  254. package/src/ruvocal/src/lib/server/fonts/Inter-Bold.ttf +0 -0
  255. package/src/ruvocal/src/lib/server/fonts/Inter-ExtraBold.ttf +0 -0
  256. package/src/ruvocal/src/lib/server/fonts/Inter-ExtraLight.ttf +0 -0
  257. package/src/ruvocal/src/lib/server/fonts/Inter-Light.ttf +0 -0
  258. package/src/ruvocal/src/lib/server/fonts/Inter-Medium.ttf +0 -0
  259. package/src/ruvocal/src/lib/server/fonts/Inter-Regular.ttf +0 -0
  260. package/src/ruvocal/src/lib/server/fonts/Inter-SemiBold.ttf +0 -0
  261. package/src/ruvocal/src/lib/server/fonts/Inter-Thin.ttf +0 -0
  262. package/src/ruvocal/src/lib/server/generateFromDefaultEndpoint.ts +46 -0
  263. package/src/ruvocal/src/lib/server/hooks/error.ts +37 -0
  264. package/src/ruvocal/src/lib/server/hooks/fetch.ts +22 -0
  265. package/src/ruvocal/src/lib/server/hooks/handle.ts +250 -0
  266. package/src/ruvocal/src/lib/server/hooks/init.ts +51 -0
  267. package/src/ruvocal/src/lib/server/isURLLocal.spec.ts +31 -0
  268. package/src/ruvocal/src/lib/server/isURLLocal.ts +74 -0
  269. package/src/ruvocal/src/lib/server/logger.ts +42 -0
  270. package/src/ruvocal/src/lib/server/mcp/clientPool.ts +70 -0
  271. package/src/ruvocal/src/lib/server/mcp/hf.ts +32 -0
  272. package/src/ruvocal/src/lib/server/mcp/httpClient.ts +122 -0
  273. package/src/ruvocal/src/lib/server/mcp/registry.ts +76 -0
  274. package/src/ruvocal/src/lib/server/mcp/tools.ts +196 -0
  275. package/src/ruvocal/src/lib/server/metrics.ts +255 -0
  276. package/src/ruvocal/src/lib/server/models.ts +518 -0
  277. package/src/ruvocal/src/lib/server/requestContext.ts +55 -0
  278. package/src/ruvocal/src/lib/server/router/arch.ts +230 -0
  279. package/src/ruvocal/src/lib/server/router/endpoint.ts +316 -0
  280. package/src/ruvocal/src/lib/server/router/multimodal.ts +28 -0
  281. package/src/ruvocal/src/lib/server/router/policy.ts +49 -0
  282. package/src/ruvocal/src/lib/server/router/toolsRoute.ts +51 -0
  283. package/src/ruvocal/src/lib/server/router/types.ts +21 -0
  284. package/src/ruvocal/src/lib/server/sendSlack.ts +23 -0
  285. package/src/ruvocal/src/lib/server/textGeneration/generate.ts +258 -0
  286. package/src/ruvocal/src/lib/server/textGeneration/index.ts +95 -0
  287. package/src/ruvocal/src/lib/server/textGeneration/mcp/fileRefs.ts +155 -0
  288. package/src/ruvocal/src/lib/server/textGeneration/mcp/routerResolution.ts +108 -0
  289. package/src/ruvocal/src/lib/server/textGeneration/mcp/runMcpFlow.ts +822 -0
  290. package/src/ruvocal/src/lib/server/textGeneration/mcp/toolInvocation.ts +349 -0
  291. package/src/ruvocal/src/lib/server/textGeneration/reasoning.ts +23 -0
  292. package/src/ruvocal/src/lib/server/textGeneration/title.ts +83 -0
  293. package/src/ruvocal/src/lib/server/textGeneration/types.ts +26 -0
  294. package/src/ruvocal/src/lib/server/textGeneration/utils/prepareFiles.ts +88 -0
  295. package/src/ruvocal/src/lib/server/textGeneration/utils/routing.ts +21 -0
  296. package/src/ruvocal/src/lib/server/textGeneration/utils/toolPrompt.ts +49 -0
  297. package/src/ruvocal/src/lib/server/urlSafety.ts +72 -0
  298. package/src/ruvocal/src/lib/server/usageLimits.ts +30 -0
  299. package/src/ruvocal/src/lib/stores/autopilotStore.svelte.ts +175 -0
  300. package/src/ruvocal/src/lib/stores/backgroundGenerations.svelte.ts +32 -0
  301. package/src/ruvocal/src/lib/stores/backgroundGenerations.ts +1 -0
  302. package/src/ruvocal/src/lib/stores/errors.ts +9 -0
  303. package/src/ruvocal/src/lib/stores/isAborted.ts +3 -0
  304. package/src/ruvocal/src/lib/stores/isPro.ts +4 -0
  305. package/src/ruvocal/src/lib/stores/loading.ts +3 -0
  306. package/src/ruvocal/src/lib/stores/mcpServers.ts +345 -0
  307. package/src/ruvocal/src/lib/stores/pendingChatInput.ts +3 -0
  308. package/src/ruvocal/src/lib/stores/pendingMessage.ts +9 -0
  309. package/src/ruvocal/src/lib/stores/settings.ts +182 -0
  310. package/src/ruvocal/src/lib/stores/shareModal.ts +13 -0
  311. package/src/ruvocal/src/lib/stores/titleUpdate.ts +8 -0
  312. package/src/ruvocal/src/lib/switchTheme.ts +124 -0
  313. package/src/ruvocal/src/lib/types/AbortedGeneration.ts +8 -0
  314. package/src/ruvocal/src/lib/types/Assistant.ts +31 -0
  315. package/src/ruvocal/src/lib/types/AssistantStats.ts +11 -0
  316. package/src/ruvocal/src/lib/types/ConfigKey.ts +4 -0
  317. package/src/ruvocal/src/lib/types/ConvSidebar.ts +9 -0
  318. package/src/ruvocal/src/lib/types/Conversation.ts +27 -0
  319. package/src/ruvocal/src/lib/types/ConversationStats.ts +13 -0
  320. package/src/ruvocal/src/lib/types/Message.ts +41 -0
  321. package/src/ruvocal/src/lib/types/MessageEvent.ts +10 -0
  322. package/src/ruvocal/src/lib/types/MessageUpdate.ts +139 -0
  323. package/src/ruvocal/src/lib/types/MigrationResult.ts +7 -0
  324. package/src/ruvocal/src/lib/types/Model.ts +23 -0
  325. package/src/ruvocal/src/lib/types/Report.ts +12 -0
  326. package/src/ruvocal/src/lib/types/Review.ts +6 -0
  327. package/src/ruvocal/src/lib/types/Semaphore.ts +19 -0
  328. package/src/ruvocal/src/lib/types/Session.ts +22 -0
  329. package/src/ruvocal/src/lib/types/Settings.ts +86 -0
  330. package/src/ruvocal/src/lib/types/SharedConversation.ts +9 -0
  331. package/src/ruvocal/src/lib/types/Template.ts +6 -0
  332. package/src/ruvocal/src/lib/types/Timestamps.ts +4 -0
  333. package/src/ruvocal/src/lib/types/TokenCache.ts +6 -0
  334. package/src/ruvocal/src/lib/types/Tool.ts +74 -0
  335. package/src/ruvocal/src/lib/types/UrlDependency.ts +5 -0
  336. package/src/ruvocal/src/lib/types/User.ts +14 -0
  337. package/src/ruvocal/src/lib/utils/PublicConfig.svelte.ts +75 -0
  338. package/src/ruvocal/src/lib/utils/auth.ts +17 -0
  339. package/src/ruvocal/src/lib/utils/chunk.ts +33 -0
  340. package/src/ruvocal/src/lib/utils/cookiesAreEnabled.ts +13 -0
  341. package/src/ruvocal/src/lib/utils/debounce.ts +17 -0
  342. package/src/ruvocal/src/lib/utils/deepestChild.ts +6 -0
  343. package/src/ruvocal/src/lib/utils/favicon.ts +21 -0
  344. package/src/ruvocal/src/lib/utils/fetchJSON.ts +23 -0
  345. package/src/ruvocal/src/lib/utils/file2base64.ts +14 -0
  346. package/src/ruvocal/src/lib/utils/formatUserCount.ts +37 -0
  347. package/src/ruvocal/src/lib/utils/generationState.spec.ts +75 -0
  348. package/src/ruvocal/src/lib/utils/generationState.ts +26 -0
  349. package/src/ruvocal/src/lib/utils/getHref.ts +41 -0
  350. package/src/ruvocal/src/lib/utils/getReturnFromGenerator.ts +7 -0
  351. package/src/ruvocal/src/lib/utils/haptics.ts +64 -0
  352. package/src/ruvocal/src/lib/utils/hashConv.ts +12 -0
  353. package/src/ruvocal/src/lib/utils/hf.ts +17 -0
  354. package/src/ruvocal/src/lib/utils/isDesktop.ts +7 -0
  355. package/src/ruvocal/src/lib/utils/isUrl.ts +8 -0
  356. package/src/ruvocal/src/lib/utils/isVirtualKeyboard.ts +16 -0
  357. package/src/ruvocal/src/lib/utils/loadAttachmentsFromUrls.ts +115 -0
  358. package/src/ruvocal/src/lib/utils/marked.spec.ts +96 -0
  359. package/src/ruvocal/src/lib/utils/marked.ts +531 -0
  360. package/src/ruvocal/src/lib/utils/mcpValidation.ts +147 -0
  361. package/src/ruvocal/src/lib/utils/mergeAsyncGenerators.ts +38 -0
  362. package/src/ruvocal/src/lib/utils/messageUpdates.spec.ts +262 -0
  363. package/src/ruvocal/src/lib/utils/messageUpdates.ts +324 -0
  364. package/src/ruvocal/src/lib/utils/mime.ts +56 -0
  365. package/src/ruvocal/src/lib/utils/models.ts +14 -0
  366. package/src/ruvocal/src/lib/utils/parseBlocks.ts +120 -0
  367. package/src/ruvocal/src/lib/utils/parseIncompleteMarkdown.ts +644 -0
  368. package/src/ruvocal/src/lib/utils/parseStringToList.ts +10 -0
  369. package/src/ruvocal/src/lib/utils/randomUuid.ts +14 -0
  370. package/src/ruvocal/src/lib/utils/searchTokens.ts +33 -0
  371. package/src/ruvocal/src/lib/utils/sha256.ts +7 -0
  372. package/src/ruvocal/src/lib/utils/stringifyError.ts +12 -0
  373. package/src/ruvocal/src/lib/utils/sum.ts +3 -0
  374. package/src/ruvocal/src/lib/utils/template.spec.ts +59 -0
  375. package/src/ruvocal/src/lib/utils/template.ts +53 -0
  376. package/src/ruvocal/src/lib/utils/timeout.ts +9 -0
  377. package/src/ruvocal/src/lib/utils/toolProgress.spec.ts +46 -0
  378. package/src/ruvocal/src/lib/utils/toolProgress.ts +11 -0
  379. package/src/ruvocal/src/lib/utils/tree/addChildren.spec.ts +102 -0
  380. package/src/ruvocal/src/lib/utils/tree/addChildren.ts +48 -0
  381. package/src/ruvocal/src/lib/utils/tree/addSibling.spec.ts +81 -0
  382. package/src/ruvocal/src/lib/utils/tree/addSibling.ts +41 -0
  383. package/src/ruvocal/src/lib/utils/tree/buildSubtree.spec.ts +110 -0
  384. package/src/ruvocal/src/lib/utils/tree/buildSubtree.ts +24 -0
  385. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.spec.ts +31 -0
  386. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.ts +36 -0
  387. package/src/ruvocal/src/lib/utils/tree/isMessageId.spec.ts +15 -0
  388. package/src/ruvocal/src/lib/utils/tree/isMessageId.ts +5 -0
  389. package/src/ruvocal/src/lib/utils/tree/tree.d.ts +14 -0
  390. package/src/ruvocal/src/lib/utils/tree/treeHelpers.spec.ts +167 -0
  391. package/src/ruvocal/src/lib/utils/updates.ts +39 -0
  392. package/src/ruvocal/src/lib/utils/urlParams.ts +13 -0
  393. package/src/ruvocal/src/lib/workers/autopilotWorker.ts +221 -0
  394. package/src/ruvocal/src/lib/workers/detailFetchWorker.ts +100 -0
  395. package/src/ruvocal/src/lib/workers/markdownWorker.ts +61 -0
  396. package/src/ruvocal/src/routes/+error.svelte +20 -0
  397. package/src/ruvocal/src/routes/+layout.svelte +324 -0
  398. package/src/ruvocal/src/routes/+layout.ts +91 -0
  399. package/src/ruvocal/src/routes/+page.svelte +168 -0
  400. package/src/ruvocal/src/routes/.well-known/oauth-cimd/+server.ts +37 -0
  401. package/src/ruvocal/src/routes/__debug/openai/+server.ts +21 -0
  402. package/src/ruvocal/src/routes/admin/export/+server.ts +159 -0
  403. package/src/ruvocal/src/routes/admin/stats/compute/+server.ts +16 -0
  404. package/src/ruvocal/src/routes/api/conversation/[id]/+server.ts +40 -0
  405. package/src/ruvocal/src/routes/api/conversation/[id]/message/[messageId]/+server.ts +42 -0
  406. package/src/ruvocal/src/routes/api/conversations/+server.ts +48 -0
  407. package/src/ruvocal/src/routes/api/fetch-url/+server.ts +147 -0
  408. package/src/ruvocal/src/routes/api/mcp/health/+server.ts +292 -0
  409. package/src/ruvocal/src/routes/api/mcp/servers/+server.ts +32 -0
  410. package/src/ruvocal/src/routes/api/models/+server.ts +25 -0
  411. package/src/ruvocal/src/routes/api/transcribe/+server.ts +104 -0
  412. package/src/ruvocal/src/routes/api/user/+server.ts +15 -0
  413. package/src/ruvocal/src/routes/api/user/validate-token/+server.ts +20 -0
  414. package/src/ruvocal/src/routes/api/v2/conversations/+server.ts +48 -0
  415. package/src/ruvocal/src/routes/api/v2/conversations/[id]/+server.ts +94 -0
  416. package/src/ruvocal/src/routes/api/v2/conversations/[id]/message/[messageId]/+server.ts +43 -0
  417. package/src/ruvocal/src/routes/api/v2/conversations/import-share/+server.ts +23 -0
  418. package/src/ruvocal/src/routes/api/v2/debug/config/+server.ts +16 -0
  419. package/src/ruvocal/src/routes/api/v2/debug/refresh/+server.ts +30 -0
  420. package/src/ruvocal/src/routes/api/v2/export/+server.ts +196 -0
  421. package/src/ruvocal/src/routes/api/v2/feature-flags/+server.ts +14 -0
  422. package/src/ruvocal/src/routes/api/v2/models/+server.ts +38 -0
  423. package/src/ruvocal/src/routes/api/v2/models/[namespace]/+server.ts +8 -0
  424. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/+server.ts +8 -0
  425. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/subscribe/+server.ts +28 -0
  426. package/src/ruvocal/src/routes/api/v2/models/[namespace]/subscribe/+server.ts +28 -0
  427. package/src/ruvocal/src/routes/api/v2/models/old/+server.ts +7 -0
  428. package/src/ruvocal/src/routes/api/v2/models/refresh/+server.ts +33 -0
  429. package/src/ruvocal/src/routes/api/v2/public-config/+server.ts +7 -0
  430. package/src/ruvocal/src/routes/api/v2/user/+server.ts +17 -0
  431. package/src/ruvocal/src/routes/api/v2/user/billing-orgs/+server.ts +73 -0
  432. package/src/ruvocal/src/routes/api/v2/user/reports/+server.ts +17 -0
  433. package/src/ruvocal/src/routes/api/v2/user/settings/+server.ts +103 -0
  434. package/src/ruvocal/src/routes/conversation/+server.ts +115 -0
  435. package/src/ruvocal/src/routes/conversation/[id]/+page.svelte +582 -0
  436. package/src/ruvocal/src/routes/conversation/[id]/+page.ts +60 -0
  437. package/src/ruvocal/src/routes/conversation/[id]/+server.ts +736 -0
  438. package/src/ruvocal/src/routes/conversation/[id]/message/[messageId]/prompt/+server.ts +66 -0
  439. package/src/ruvocal/src/routes/conversation/[id]/output/[sha256]/+server.ts +58 -0
  440. package/src/ruvocal/src/routes/conversation/[id]/share/+server.ts +69 -0
  441. package/src/ruvocal/src/routes/conversation/[id]/stop-generating/+server.ts +35 -0
  442. package/src/ruvocal/src/routes/healthcheck/+server.ts +3 -0
  443. package/src/ruvocal/src/routes/login/+server.ts +5 -0
  444. package/src/ruvocal/src/routes/login/callback/+server.ts +103 -0
  445. package/src/ruvocal/src/routes/login/callback/updateUser.spec.ts +157 -0
  446. package/src/ruvocal/src/routes/login/callback/updateUser.ts +215 -0
  447. package/src/ruvocal/src/routes/logout/+server.ts +18 -0
  448. package/src/ruvocal/src/routes/metrics/+server.ts +18 -0
  449. package/src/ruvocal/src/routes/models/+page.svelte +233 -0
  450. package/src/ruvocal/src/routes/models/[...model]/+page.svelte +161 -0
  451. package/src/ruvocal/src/routes/models/[...model]/+page.ts +14 -0
  452. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/+server.ts +64 -0
  453. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte +28 -0
  454. package/src/ruvocal/src/routes/privacy/+page.svelte +11 -0
  455. package/src/ruvocal/src/routes/r/[id]/+page.ts +34 -0
  456. package/src/ruvocal/src/routes/settings/(nav)/+layout.svelte +282 -0
  457. package/src/ruvocal/src/routes/settings/(nav)/+layout.ts +1 -0
  458. package/src/ruvocal/src/routes/settings/(nav)/+page.svelte +0 -0
  459. package/src/ruvocal/src/routes/settings/(nav)/+server.ts +53 -0
  460. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.svelte +464 -0
  461. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.ts +14 -0
  462. package/src/ruvocal/src/routes/settings/(nav)/application/+page.svelte +362 -0
  463. package/src/ruvocal/src/routes/settings/+layout.svelte +40 -0
  464. package/src/ruvocal/src/styles/highlight-js.css +195 -0
  465. package/src/ruvocal/src/styles/main.css +144 -0
  466. package/src/ruvocal/static/chatui/apple-touch-icon.png +0 -0
  467. package/src/ruvocal/static/chatui/favicon-dark.svg +3 -0
  468. package/src/ruvocal/static/chatui/favicon-dev.svg +3 -0
  469. package/src/ruvocal/static/chatui/favicon.ico +0 -0
  470. package/src/ruvocal/static/chatui/favicon.svg +3 -0
  471. package/src/ruvocal/static/chatui/icon-128x128.png +0 -0
  472. package/src/ruvocal/static/chatui/icon-144x144.png +0 -0
  473. package/src/ruvocal/static/chatui/icon-192x192.png +0 -0
  474. package/src/ruvocal/static/chatui/icon-256x256.png +0 -0
  475. package/src/ruvocal/static/chatui/icon-36x36.png +0 -0
  476. package/src/ruvocal/static/chatui/icon-48x48.png +0 -0
  477. package/src/ruvocal/static/chatui/icon-512x512.png +0 -0
  478. package/src/ruvocal/static/chatui/icon-72x72.png +0 -0
  479. package/src/ruvocal/static/chatui/icon-96x96.png +0 -0
  480. package/src/ruvocal/static/chatui/icon.svg +3 -0
  481. package/src/ruvocal/static/chatui/logo.svg +7 -0
  482. package/src/ruvocal/static/chatui/manifest.json +54 -0
  483. package/src/ruvocal/static/chatui/omni-welcome.gif +0 -0
  484. package/src/ruvocal/static/chatui/omni-welcome.png +0 -0
  485. package/src/ruvocal/static/chatui/welcome.js +184 -0
  486. package/src/ruvocal/static/chatui/welcome.svg +1 -0
  487. package/src/ruvocal/static/huggingchat/apple-touch-icon.png +0 -0
  488. package/src/ruvocal/static/huggingchat/assistants-thumbnail.png +0 -0
  489. package/src/ruvocal/static/huggingchat/castle-example.jpg +0 -0
  490. package/src/ruvocal/static/huggingchat/favicon-dark.svg +4 -0
  491. package/src/ruvocal/static/huggingchat/favicon-dev.svg +4 -0
  492. package/src/ruvocal/static/huggingchat/favicon.ico +0 -0
  493. package/src/ruvocal/static/huggingchat/favicon.svg +4 -0
  494. package/src/ruvocal/static/huggingchat/fulltext-logo.svg +2 -0
  495. package/src/ruvocal/static/huggingchat/icon-128x128.png +0 -0
  496. package/src/ruvocal/static/huggingchat/icon-144x144.png +0 -0
  497. package/src/ruvocal/static/huggingchat/icon-192x192.png +0 -0
  498. package/src/ruvocal/static/huggingchat/icon-256x256.png +0 -0
  499. package/src/ruvocal/static/huggingchat/icon-36x36.png +0 -0
  500. package/src/ruvocal/static/huggingchat/icon-48x48.png +0 -0
  501. package/src/ruvocal/static/huggingchat/icon-512x512.png +0 -0
  502. package/src/ruvocal/static/huggingchat/icon-72x72.png +0 -0
  503. package/src/ruvocal/static/huggingchat/icon-96x96.png +0 -0
  504. package/src/ruvocal/static/huggingchat/icon.svg +4 -0
  505. package/src/ruvocal/static/huggingchat/logo.svg +4 -0
  506. package/src/ruvocal/static/huggingchat/manifest.json +54 -0
  507. package/src/ruvocal/static/huggingchat/omni-welcome.gif +0 -0
  508. package/src/ruvocal/static/huggingchat/routes.chat.json +226 -0
  509. package/src/ruvocal/static/huggingchat/thumbnail.png +0 -0
  510. package/src/ruvocal/static/huggingchat/tools-thumbnail.png +0 -0
  511. package/src/ruvocal/static/robots.txt +10 -0
  512. package/src/ruvocal/stub/@reflink/reflink/index.js +0 -0
  513. package/src/ruvocal/stub/@reflink/reflink/package.json +5 -0
  514. package/src/ruvocal/svelte.config.js +53 -0
  515. package/src/ruvocal/tailwind.config.cjs +30 -0
  516. package/src/ruvocal/tsconfig.json +19 -0
  517. package/src/ruvocal/vite.config.ts +87 -0
  518. package/src/scripts/deploy.sh +116 -0
  519. package/src/scripts/generate-config.js +245 -0
  520. package/src/scripts/generate-welcome.js +187 -0
  521. package/src/scripts/package-rvf.sh +116 -0
@@ -0,0 +1,214 @@
1
+ import type { Session } from "$lib/types/Session";
2
+ import type { User } from "$lib/types/User";
3
+ import type { Conversation } from "$lib/types/Conversation";
4
+ import { ObjectId } from "mongodb";
5
+ import { deleteConversations } from "./09-delete-empty-conversations";
6
+ import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
7
+ import { collections } from "$lib/server/database";
8
+
9
+ type Message = Conversation["messages"][number];
10
+
11
+ const userData = {
12
+ _id: new ObjectId(),
13
+ createdAt: new Date(),
14
+ updatedAt: new Date(),
15
+ username: "new-username",
16
+ name: "name",
17
+ avatarUrl: "https://example.com/avatar.png",
18
+ hfUserId: "9999999999",
19
+ } satisfies User;
20
+ Object.freeze(userData);
21
+
22
+ const sessionForUser = {
23
+ _id: new ObjectId(),
24
+ createdAt: new Date(),
25
+ updatedAt: new Date(),
26
+ userId: userData._id,
27
+ sessionId: "session-id-9999999999",
28
+ expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24),
29
+ } satisfies Session;
30
+ Object.freeze(sessionForUser);
31
+
32
+ const userMessage = {
33
+ from: "user",
34
+ id: "user-message-id",
35
+ content: "Hello, how are you?",
36
+ } satisfies Message;
37
+
38
+ const assistantMessage = {
39
+ from: "assistant",
40
+ id: "assistant-message-id",
41
+ content: "I'm fine, thank you!",
42
+ } satisfies Message;
43
+
44
+ const systemMessage = {
45
+ from: "system",
46
+ id: "system-message-id",
47
+ content: "This is a system message",
48
+ } satisfies Message;
49
+
50
+ const conversationBase = {
51
+ _id: new ObjectId(),
52
+ createdAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
53
+ updatedAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
54
+ model: "model-id",
55
+
56
+ title: "title",
57
+ messages: [],
58
+ } satisfies Conversation;
59
+
60
+ describe.sequential("Deleting discarded conversations", async () => {
61
+ test("a conversation with no messages should get deleted", async () => {
62
+ await collections.conversations.insertOne({
63
+ ...conversationBase,
64
+ sessionId: sessionForUser.sessionId,
65
+ });
66
+
67
+ const result = await deleteConversations(collections);
68
+
69
+ expect(result).toBe(1);
70
+ });
71
+ test("a conversation with no messages that is less than 1 hour old should not get deleted", async () => {
72
+ await collections.conversations.insertOne({
73
+ ...conversationBase,
74
+ sessionId: sessionForUser.sessionId,
75
+ createdAt: new Date(Date.now() - 30 * 60 * 1000),
76
+ });
77
+
78
+ const result = await deleteConversations(collections);
79
+
80
+ expect(result).toBe(0);
81
+ });
82
+ test("a conversation with only system messages should get deleted", async () => {
83
+ await collections.conversations.insertOne({
84
+ ...conversationBase,
85
+ sessionId: sessionForUser.sessionId,
86
+ messages: [systemMessage],
87
+ });
88
+
89
+ const result = await deleteConversations(collections);
90
+
91
+ expect(result).toBe(1);
92
+ });
93
+ test("a conversation with a user message should not get deleted", async () => {
94
+ await collections.conversations.insertOne({
95
+ ...conversationBase,
96
+ sessionId: sessionForUser.sessionId,
97
+ messages: [userMessage],
98
+ });
99
+
100
+ const result = await deleteConversations(collections);
101
+
102
+ expect(result).toBe(0);
103
+ });
104
+ test("a conversation with an assistant message should not get deleted", async () => {
105
+ await collections.conversations.insertOne({
106
+ ...conversationBase,
107
+ sessionId: sessionForUser.sessionId,
108
+ messages: [assistantMessage],
109
+ });
110
+
111
+ const result = await deleteConversations(collections);
112
+
113
+ expect(result).toBe(0);
114
+ });
115
+ test("a conversation with a mix of messages should not get deleted", async () => {
116
+ await collections.conversations.insertOne({
117
+ ...conversationBase,
118
+ sessionId: sessionForUser.sessionId,
119
+ messages: [systemMessage, userMessage, assistantMessage, userMessage, assistantMessage],
120
+ });
121
+
122
+ const result = await deleteConversations(collections);
123
+
124
+ expect(result).toBe(0);
125
+ });
126
+ test("a conversation with a userId and no sessionId should not get deleted", async () => {
127
+ await collections.conversations.insertOne({
128
+ ...conversationBase,
129
+ messages: [userMessage, assistantMessage],
130
+ userId: userData._id,
131
+ });
132
+
133
+ const result = await deleteConversations(collections);
134
+
135
+ expect(result).toBe(0);
136
+ });
137
+ test("a conversation with no userId or sessionId should get deleted", async () => {
138
+ await collections.conversations.insertOne({
139
+ ...conversationBase,
140
+ messages: [userMessage, assistantMessage],
141
+ });
142
+
143
+ const result = await deleteConversations(collections);
144
+
145
+ expect(result).toBe(1);
146
+ });
147
+ test("a conversation with a sessionId that exists should not get deleted", async () => {
148
+ await collections.conversations.insertOne({
149
+ ...conversationBase,
150
+ messages: [userMessage, assistantMessage],
151
+ sessionId: sessionForUser.sessionId,
152
+ });
153
+
154
+ const result = await deleteConversations(collections);
155
+
156
+ expect(result).toBe(0);
157
+ });
158
+ test("a conversation with a userId and a sessionId that doesn't exist should NOT get deleted", async () => {
159
+ await collections.conversations.insertOne({
160
+ ...conversationBase,
161
+ userId: userData._id,
162
+ messages: [userMessage, assistantMessage],
163
+ sessionId: new ObjectId().toString(),
164
+ });
165
+
166
+ const result = await deleteConversations(collections);
167
+
168
+ expect(result).toBe(0);
169
+ });
170
+ test("a conversation with only a sessionId that doesn't exist, should get deleted", async () => {
171
+ await collections.conversations.insertOne({
172
+ ...conversationBase,
173
+ messages: [userMessage, assistantMessage],
174
+ sessionId: new ObjectId().toString(),
175
+ });
176
+
177
+ const result = await deleteConversations(collections);
178
+
179
+ expect(result).toBe(1);
180
+ });
181
+ test("many conversations should get deleted", async () => {
182
+ const conversations = Array.from({ length: 10010 }, () => ({
183
+ ...conversationBase,
184
+ _id: new ObjectId(),
185
+ }));
186
+
187
+ await collections.conversations.insertMany(conversations);
188
+
189
+ const result = await deleteConversations(collections);
190
+
191
+ expect(result).toBe(10010);
192
+ });
193
+ });
194
+
195
+ beforeAll(async () => {
196
+ await collections.users.insertOne(userData);
197
+ await collections.sessions.insertOne(sessionForUser);
198
+ }, 20000);
199
+
200
+ afterAll(async () => {
201
+ await collections.users.deleteOne({
202
+ _id: userData._id,
203
+ });
204
+ await collections.sessions.deleteOne({
205
+ _id: sessionForUser._id,
206
+ });
207
+ await collections.conversations.deleteMany({});
208
+ });
209
+
210
+ afterEach(async () => {
211
+ await collections.conversations.deleteMany({
212
+ _id: { $in: [conversationBase._id] },
213
+ });
214
+ });
@@ -0,0 +1,88 @@
1
+ import type { Migration } from ".";
2
+ import { collections } from "$lib/server/database";
3
+ import { Collection, FindCursor, ObjectId } from "mongodb";
4
+ import { logger } from "$lib/server/logger";
5
+ import type { Conversation } from "$lib/types/Conversation";
6
+
7
+ const BATCH_SIZE = 1000;
8
+ const DELETE_THRESHOLD_MS = 60 * 60 * 1000;
9
+
10
+ async function deleteBatch(conversations: Collection<Conversation>, ids: ObjectId[]) {
11
+ if (ids.length === 0) return 0;
12
+ const deleteResult = await conversations.deleteMany({ _id: { $in: ids } });
13
+ return deleteResult.deletedCount;
14
+ }
15
+
16
+ async function processCursor<T>(
17
+ cursor: FindCursor<T>,
18
+ processBatchFn: (batch: T[]) => Promise<void>
19
+ ) {
20
+ let batch = [];
21
+ while (await cursor.hasNext()) {
22
+ const doc = await cursor.next();
23
+ if (doc) {
24
+ batch.push(doc);
25
+ }
26
+ if (batch.length >= BATCH_SIZE) {
27
+ await processBatchFn(batch);
28
+ batch = [];
29
+ }
30
+ }
31
+ if (batch.length > 0) {
32
+ await processBatchFn(batch);
33
+ }
34
+ }
35
+
36
+ export async function deleteConversations(
37
+ collections: typeof import("$lib/server/database").collections
38
+ ) {
39
+ let deleteCount = 0;
40
+ const { conversations, sessions } = collections;
41
+
42
+ // First criteria: Delete conversations with no user/assistant messages older than 1 hour
43
+ const emptyConvCursor = conversations
44
+ .find({
45
+ "messages.from": { $not: { $in: ["user", "assistant"] } },
46
+ createdAt: { $lt: new Date(Date.now() - DELETE_THRESHOLD_MS) },
47
+ })
48
+ .batchSize(BATCH_SIZE);
49
+
50
+ await processCursor(emptyConvCursor, async (batch) => {
51
+ const ids = batch.map((doc) => doc._id);
52
+ deleteCount += await deleteBatch(conversations, ids);
53
+ });
54
+
55
+ // Second criteria: Process conversations without users in batches and check sessions
56
+ const noUserCursor = conversations.find({ userId: { $exists: false } }).batchSize(BATCH_SIZE);
57
+
58
+ await processCursor(noUserCursor, async (batch) => {
59
+ const sessionIds = [
60
+ ...new Set(batch.map((conv) => conv.sessionId).filter((id): id is string => !!id)),
61
+ ];
62
+
63
+ const existingSessions = await sessions.find({ sessionId: { $in: sessionIds } }).toArray();
64
+ const validSessionIds = new Set(existingSessions.map((s) => s.sessionId));
65
+
66
+ const invalidConvs = batch.filter(
67
+ (conv) => !conv.sessionId || !validSessionIds.has(conv.sessionId)
68
+ );
69
+ const idsToDelete = invalidConvs.map((conv) => conv._id);
70
+ deleteCount += await deleteBatch(conversations, idsToDelete);
71
+ });
72
+
73
+ logger.info(`[MIGRATIONS] Deleted ${deleteCount} conversations in total.`);
74
+ return deleteCount;
75
+ }
76
+
77
+ const deleteEmptyConversations: Migration = {
78
+ _id: new ObjectId("000000000000000000000009"),
79
+ name: "Delete conversations with no user or assistant messages or valid sessions",
80
+ up: async () => {
81
+ await deleteConversations(collections);
82
+ return true;
83
+ },
84
+ runEveryTime: false,
85
+ runForHuggingChat: "only",
86
+ };
87
+
88
+ export default deleteEmptyConversations;
@@ -0,0 +1,29 @@
1
+ import { collections } from "$lib/server/database";
2
+ import type { Migration } from ".";
3
+ import { ObjectId } from "mongodb";
4
+
5
+ const migration: Migration = {
6
+ _id: new ObjectId("000000000000000000000010"),
7
+ name: "Update reports with assistantId to use contentId",
8
+ up: async () => {
9
+ await collections.reports.updateMany(
10
+ {
11
+ assistantId: { $exists: true, $ne: null },
12
+ },
13
+ [
14
+ {
15
+ $set: {
16
+ object: "assistant",
17
+ contentId: "$assistantId",
18
+ },
19
+ },
20
+ {
21
+ $unset: "assistantId",
22
+ },
23
+ ]
24
+ );
25
+ return true;
26
+ },
27
+ };
28
+
29
+ export default migration;
@@ -0,0 +1,15 @@
1
+ import type { ObjectId } from "mongodb";
2
+
3
+ import type { Database } from "$lib/server/database";
4
+
5
+ export interface Migration {
6
+ _id: ObjectId;
7
+ name: string;
8
+ up: (client: Database) => Promise<boolean>;
9
+ down?: (client: Database) => Promise<boolean>;
10
+ runForFreshInstall?: "only" | "never"; // leave unspecified to run for both
11
+ runForHuggingChat?: "only" | "never"; // leave unspecified to run for both
12
+ runEveryTime?: boolean;
13
+ }
14
+
15
+ export const migrations: Migration[] = [];
@@ -0,0 +1,103 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { ObjectId } from "mongodb";
3
+
4
+ import { collections } from "$lib/server/database";
5
+ import { AbortRegistry } from "$lib/server/abortRegistry";
6
+ import {
7
+ cleanupTestData,
8
+ createTestConversation,
9
+ createTestLocals,
10
+ createTestUser,
11
+ } from "$lib/server/api/__tests__/testHelpers";
12
+ import { POST } from "../../../routes/conversation/[id]/stop-generating/+server";
13
+
14
+ describe.sequential("POST /conversation/[id]/stop-generating", () => {
15
+ afterEach(async () => {
16
+ vi.restoreAllMocks();
17
+ await cleanupTestData();
18
+ });
19
+
20
+ it(
21
+ "creates abort marker and aborts active registry controllers",
22
+ { timeout: 30000 },
23
+ async () => {
24
+ const { locals } = await createTestUser();
25
+ const conversation = await createTestConversation(locals);
26
+ const abortSpy = vi.spyOn(AbortRegistry.getInstance(), "abort");
27
+
28
+ const response = await POST({
29
+ params: { id: conversation._id.toString() },
30
+ locals,
31
+ } as never);
32
+
33
+ expect(response.status).toBe(200);
34
+ expect(abortSpy).toHaveBeenCalledWith(conversation._id.toString());
35
+
36
+ const marker = await collections.abortedGenerations.findOne({
37
+ conversationId: conversation._id,
38
+ });
39
+ expect(marker).not.toBeNull();
40
+ expect(marker?.createdAt).toBeInstanceOf(Date);
41
+ expect(marker?.updatedAt).toBeInstanceOf(Date);
42
+ }
43
+ );
44
+
45
+ it("updates updatedAt while preserving createdAt on repeated stop", async () => {
46
+ const { locals } = await createTestUser();
47
+ const conversation = await createTestConversation(locals);
48
+
49
+ await POST({
50
+ params: { id: conversation._id.toString() },
51
+ locals,
52
+ } as never);
53
+ const firstMarker = await collections.abortedGenerations.findOne({
54
+ conversationId: conversation._id,
55
+ });
56
+
57
+ await new Promise((resolve) => setTimeout(resolve, 5));
58
+
59
+ await POST({
60
+ params: { id: conversation._id.toString() },
61
+ locals,
62
+ } as never);
63
+ const secondMarker = await collections.abortedGenerations.findOne({
64
+ conversationId: conversation._id,
65
+ });
66
+
67
+ expect(firstMarker).not.toBeNull();
68
+ expect(secondMarker).not.toBeNull();
69
+ expect(secondMarker?.createdAt.getTime()).toBe(firstMarker?.createdAt.getTime());
70
+ expect(secondMarker?.updatedAt.getTime()).toBeGreaterThan(
71
+ firstMarker?.updatedAt.getTime() ?? 0
72
+ );
73
+ });
74
+
75
+ it("throws 404 when conversation is not found", async () => {
76
+ const { locals } = await createTestUser();
77
+ const missingId = new ObjectId().toString();
78
+
79
+ try {
80
+ await POST({
81
+ params: { id: missingId },
82
+ locals,
83
+ } as never);
84
+ expect.fail("Expected 404 error");
85
+ } catch (e: unknown) {
86
+ expect((e as { status: number }).status).toBe(404);
87
+ }
88
+ });
89
+
90
+ it("throws 401 for unauthenticated requests", async () => {
91
+ const locals = createTestLocals({ user: undefined, sessionId: undefined });
92
+
93
+ try {
94
+ await POST({
95
+ params: { id: new ObjectId().toString() },
96
+ locals,
97
+ } as never);
98
+ expect.fail("Expected 401 error");
99
+ } catch (e: unknown) {
100
+ expect((e as { status: number }).status).toBe(401);
101
+ }
102
+ });
103
+ });
@@ -0,0 +1,57 @@
1
+ import { logger } from "$lib/server/logger";
2
+
3
+ /**
4
+ * Tracks active upstream generation requests so they can be cancelled on demand.
5
+ * Multiple controllers can be registered per conversation (for threaded/background runs).
6
+ */
7
+ export class AbortRegistry {
8
+ private static instance: AbortRegistry;
9
+
10
+ private controllers = new Map<string, Set<AbortController>>();
11
+
12
+ public static getInstance(): AbortRegistry {
13
+ if (!AbortRegistry.instance) {
14
+ AbortRegistry.instance = new AbortRegistry();
15
+ }
16
+ return AbortRegistry.instance;
17
+ }
18
+
19
+ public register(conversationId: string, controller: AbortController) {
20
+ const key = conversationId.toString();
21
+ let set = this.controllers.get(key);
22
+ if (!set) {
23
+ set = new Set();
24
+ this.controllers.set(key, set);
25
+ }
26
+ set.add(controller);
27
+ controller.signal.addEventListener(
28
+ "abort",
29
+ () => {
30
+ this.unregister(key, controller);
31
+ },
32
+ { once: true }
33
+ );
34
+ }
35
+
36
+ public abort(conversationId: string) {
37
+ const set = this.controllers.get(conversationId);
38
+ if (!set?.size) return;
39
+
40
+ logger.debug({ conversationId }, "Aborting active generation via AbortRegistry");
41
+ for (const controller of set) {
42
+ if (!controller.signal.aborted) {
43
+ controller.abort();
44
+ }
45
+ }
46
+ this.controllers.delete(conversationId);
47
+ }
48
+
49
+ public unregister(conversationId: string, controller: AbortController) {
50
+ const set = this.controllers.get(conversationId);
51
+ if (!set) return;
52
+ set.delete(controller);
53
+ if (set.size === 0) {
54
+ this.controllers.delete(conversationId);
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,43 @@
1
+ // Shouldn't be needed if we dove into sveltekit internals, see https://github.com/huggingface/chat-ui/pull/88#issuecomment-1523173850
2
+
3
+ import { logger } from "$lib/server/logger";
4
+ import { collections } from "$lib/server/database";
5
+ import { onExit } from "./exitHandler";
6
+
7
+ export class AbortedGenerations {
8
+ private static instance: AbortedGenerations;
9
+
10
+ private abortedGenerations: Record<string, Date> = {};
11
+
12
+ private constructor() {
13
+ // Poll every 500ms for faster abort detection (reduced from 1000ms)
14
+ const interval = setInterval(() => this.updateList(), 500);
15
+ onExit(() => clearInterval(interval));
16
+
17
+ this.updateList();
18
+ }
19
+
20
+ public static getInstance(): AbortedGenerations {
21
+ if (!AbortedGenerations.instance) {
22
+ AbortedGenerations.instance = new AbortedGenerations();
23
+ }
24
+
25
+ return AbortedGenerations.instance;
26
+ }
27
+
28
+ public getAbortTime(conversationId: string): Date | undefined {
29
+ return this.abortedGenerations[conversationId];
30
+ }
31
+
32
+ private async updateList() {
33
+ try {
34
+ const aborts = await collections.abortedGenerations.find({}).sort({ createdAt: 1 }).toArray();
35
+
36
+ this.abortedGenerations = Object.fromEntries(
37
+ aborts.map((abort) => [abort.conversationId.toString(), abort.updatedAt ?? abort.createdAt])
38
+ );
39
+ } catch (err) {
40
+ logger.error(err, "Error updating aborted generations list");
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,62 @@
1
+ import { config } from "$lib/server/config";
2
+ import type { Session } from "$lib/types/Session";
3
+ import { logger } from "./logger";
4
+ import { v4 } from "uuid";
5
+
6
+ class AdminTokenManager {
7
+ private token = config.ADMIN_TOKEN || v4();
8
+ // contains all session ids that are currently admin sessions
9
+ private adminSessions: Array<Session["sessionId"]> = [];
10
+
11
+ public get enabled() {
12
+ // if open id is configured, disable the feature
13
+ return config.ADMIN_CLI_LOGIN === "true";
14
+ }
15
+ public isAdmin(sessionId: Session["sessionId"]) {
16
+ if (!this.enabled) return false;
17
+ return this.adminSessions.includes(sessionId);
18
+ }
19
+
20
+ public checkToken(token: string, sessionId: Session["sessionId"]) {
21
+ if (!this.enabled) return false;
22
+ if (token === this.token) {
23
+ logger.info(`[ADMIN] Token validated`);
24
+ this.adminSessions.push(sessionId);
25
+ this.token = config.ADMIN_TOKEN || v4();
26
+ return true;
27
+ }
28
+
29
+ return false;
30
+ }
31
+
32
+ public removeSession(sessionId: Session["sessionId"]) {
33
+ this.adminSessions = this.adminSessions.filter((id) => id !== sessionId);
34
+ }
35
+
36
+ public displayToken() {
37
+ // if admin token is set, don't display it
38
+ if (!this.enabled || config.ADMIN_TOKEN) return;
39
+
40
+ let port = process.env.PORT
41
+ ? parseInt(process.env.PORT)
42
+ : process.argv.includes("--port")
43
+ ? parseInt(process.argv[process.argv.indexOf("--port") + 1])
44
+ : undefined;
45
+
46
+ if (!port) {
47
+ const mode = process.argv.find((arg) => arg === "preview" || arg === "dev");
48
+ if (mode === "preview") {
49
+ port = 4173;
50
+ } else if (mode === "dev") {
51
+ port = 5173;
52
+ } else {
53
+ port = 3000;
54
+ }
55
+ }
56
+
57
+ const url = (config.PUBLIC_ORIGIN || `http://localhost:${port}`) + "?token=";
58
+ logger.info(`[ADMIN] You can login with ${url + this.token}`);
59
+ }
60
+ }
61
+
62
+ export const adminTokenManager = new AdminTokenManager();