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,235 @@
1
+ import { describe, expect, it, afterEach } from "vitest";
2
+ import superjson from "superjson";
3
+ import { collections } from "$lib/server/database";
4
+ import { CONV_NUM_PER_PAGE } from "$lib/constants/pagination";
5
+ import {
6
+ createTestLocals,
7
+ createTestUser,
8
+ createTestConversation,
9
+ cleanupTestData,
10
+ } from "./testHelpers";
11
+
12
+ import { GET, DELETE } from "../../../../routes/api/v2/conversations/+server";
13
+
14
+ async function parseResponse<T = unknown>(res: Response): Promise<T> {
15
+ return superjson.parse(await res.text()) as T;
16
+ }
17
+
18
+ function mockUrl(params?: Record<string, string>): URL {
19
+ const url = new URL("http://localhost:5173/api/v2/conversations");
20
+ if (params) {
21
+ for (const [key, value] of Object.entries(params)) {
22
+ url.searchParams.set(key, value);
23
+ }
24
+ }
25
+ return url;
26
+ }
27
+
28
+ describe.sequential("GET /api/v2/conversations", () => {
29
+ afterEach(async () => {
30
+ await cleanupTestData();
31
+ });
32
+
33
+ it("returns conversations for authenticated user", { timeout: 30000 }, async () => {
34
+ const { locals } = await createTestUser();
35
+ const conv = await createTestConversation(locals, { title: "My Chat" });
36
+
37
+ const res = await GET({
38
+ locals,
39
+ url: mockUrl(),
40
+ } as never);
41
+
42
+ expect(res.status).toBe(200);
43
+ const data = await parseResponse<{
44
+ conversations: Array<{ title: string; _id: { toString(): string } }>;
45
+ hasMore: boolean;
46
+ }>(res);
47
+ expect(data.conversations).toHaveLength(1);
48
+ expect(data.conversations[0].title).toBe("My Chat");
49
+ expect(data.conversations[0]._id.toString()).toBe(conv._id.toString());
50
+ expect(data.hasMore).toBe(false);
51
+ });
52
+
53
+ it("returns empty array for user with no conversations", async () => {
54
+ const { locals } = await createTestUser();
55
+
56
+ const res = await GET({
57
+ locals,
58
+ url: mockUrl(),
59
+ } as never);
60
+
61
+ expect(res.status).toBe(200);
62
+ const data = await parseResponse<{ conversations: unknown[]; hasMore: boolean }>(res);
63
+ expect(data.conversations).toHaveLength(0);
64
+ expect(data.hasMore).toBe(false);
65
+ });
66
+
67
+ it("supports pagination with p=0 and p=1", async () => {
68
+ const { locals } = await createTestUser();
69
+
70
+ // Create CONV_NUM_PER_PAGE + 5 conversations with distinct updatedAt values
71
+ for (let i = 0; i < CONV_NUM_PER_PAGE + 5; i++) {
72
+ await createTestConversation(locals, {
73
+ title: `Conv ${i}`,
74
+ updatedAt: new Date(Date.now() - (CONV_NUM_PER_PAGE + 5 - i) * 1000),
75
+ });
76
+ }
77
+
78
+ const resPage0 = await GET({
79
+ locals,
80
+ url: mockUrl({ p: "0" }),
81
+ } as never);
82
+
83
+ const dataPage0 = await parseResponse<{
84
+ conversations: Array<{ title: string }>;
85
+ hasMore: boolean;
86
+ }>(resPage0);
87
+ expect(dataPage0.conversations).toHaveLength(CONV_NUM_PER_PAGE);
88
+ expect(dataPage0.hasMore).toBe(true);
89
+
90
+ const resPage1 = await GET({
91
+ locals,
92
+ url: mockUrl({ p: "1" }),
93
+ } as never);
94
+
95
+ const dataPage1 = await parseResponse<{
96
+ conversations: Array<{ title: string }>;
97
+ hasMore: boolean;
98
+ }>(resPage1);
99
+ expect(dataPage1.conversations).toHaveLength(5);
100
+ expect(dataPage1.hasMore).toBe(false);
101
+ });
102
+
103
+ it("returns hasMore=true when more than CONV_NUM_PER_PAGE exist", async () => {
104
+ const { locals } = await createTestUser();
105
+
106
+ for (let i = 0; i < CONV_NUM_PER_PAGE + 1; i++) {
107
+ await createTestConversation(locals, {
108
+ title: `Conv ${i}`,
109
+ updatedAt: new Date(Date.now() - i * 1000),
110
+ });
111
+ }
112
+
113
+ const res = await GET({
114
+ locals,
115
+ url: mockUrl(),
116
+ } as never);
117
+
118
+ const data = await parseResponse<{ conversations: unknown[]; hasMore: boolean }>(res);
119
+ expect(data.conversations).toHaveLength(CONV_NUM_PER_PAGE);
120
+ expect(data.hasMore).toBe(true);
121
+ });
122
+
123
+ it("sorts by updatedAt descending", async () => {
124
+ const { locals } = await createTestUser();
125
+
126
+ await createTestConversation(locals, {
127
+ title: "Oldest",
128
+ updatedAt: new Date("2024-01-01"),
129
+ });
130
+ await createTestConversation(locals, {
131
+ title: "Newest",
132
+ updatedAt: new Date("2024-06-01"),
133
+ });
134
+ await createTestConversation(locals, {
135
+ title: "Middle",
136
+ updatedAt: new Date("2024-03-01"),
137
+ });
138
+
139
+ const res = await GET({
140
+ locals,
141
+ url: mockUrl(),
142
+ } as never);
143
+
144
+ const data = await parseResponse<{ conversations: Array<{ title: string }> }>(res);
145
+ expect(data.conversations[0].title).toBe("Newest");
146
+ expect(data.conversations[1].title).toBe("Middle");
147
+ expect(data.conversations[2].title).toBe("Oldest");
148
+ });
149
+
150
+ it("throws 401 for unauthenticated request", async () => {
151
+ const locals = createTestLocals({ sessionId: undefined, user: undefined });
152
+
153
+ try {
154
+ await GET({
155
+ locals,
156
+ url: mockUrl(),
157
+ } as never);
158
+ expect.fail("Should have thrown");
159
+ } catch (e: unknown) {
160
+ expect((e as { status: number }).status).toBe(401);
161
+ }
162
+ });
163
+
164
+ it("does not return other users' conversations", async () => {
165
+ const { locals: localsA } = await createTestUser();
166
+ const { locals: localsB } = await createTestUser();
167
+
168
+ await createTestConversation(localsA, { title: "User A Chat" });
169
+ await createTestConversation(localsB, { title: "User B Chat" });
170
+
171
+ const res = await GET({
172
+ locals: localsA,
173
+ url: mockUrl(),
174
+ } as never);
175
+
176
+ const data = await parseResponse<{ conversations: Array<{ title: string }> }>(res);
177
+ expect(data.conversations).toHaveLength(1);
178
+ expect(data.conversations[0].title).toBe("User A Chat");
179
+ });
180
+ });
181
+
182
+ describe.sequential("DELETE /api/v2/conversations", () => {
183
+ afterEach(async () => {
184
+ await cleanupTestData();
185
+ });
186
+
187
+ it("removes all conversations for authenticated user", async () => {
188
+ const { locals } = await createTestUser();
189
+
190
+ await createTestConversation(locals, { title: "Chat 1" });
191
+ await createTestConversation(locals, { title: "Chat 2" });
192
+ await createTestConversation(locals, { title: "Chat 3" });
193
+
194
+ const res = await DELETE({ locals } as never);
195
+ expect(res.status).toBe(200);
196
+
197
+ const data = await parseResponse<number>(res);
198
+ expect(data).toBe(3);
199
+
200
+ const remaining = await collections.conversations.countDocuments();
201
+ expect(remaining).toBe(0);
202
+ });
203
+
204
+ it("throws 401 for unauthenticated request", async () => {
205
+ const locals = createTestLocals({ sessionId: undefined, user: undefined });
206
+
207
+ try {
208
+ await DELETE({ locals } as never);
209
+ expect.fail("Should have thrown");
210
+ } catch (e: unknown) {
211
+ expect((e as { status: number }).status).toBe(401);
212
+ }
213
+ });
214
+
215
+ it("does not remove other users' conversations", async () => {
216
+ const { locals: localsA } = await createTestUser();
217
+ const { locals: localsB } = await createTestUser();
218
+
219
+ await createTestConversation(localsA, { title: "User A Chat" });
220
+ await createTestConversation(localsB, { title: "User B Chat" });
221
+
222
+ const res = await DELETE({ locals: localsA } as never);
223
+ const data = await parseResponse<number>(res);
224
+ expect(data).toBe(1);
225
+
226
+ const remaining = await collections.conversations.countDocuments();
227
+ expect(remaining).toBe(1);
228
+
229
+ const userBConvs = await collections.conversations
230
+ .find({ userId: localsB.user?._id })
231
+ .toArray();
232
+ expect(userBConvs).toHaveLength(1);
233
+ expect(userBConvs[0].title).toBe("User B Chat");
234
+ });
235
+ });
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import superjson from "superjson";
3
+ import { createTestLocals, createTestUser, cleanupTestData } from "./testHelpers";
4
+ import { GET as featureFlagsGET } from "../../../../routes/api/v2/feature-flags/+server";
5
+ import { GET as publicConfigGET } from "../../../../routes/api/v2/public-config/+server";
6
+ import type { FeatureFlags } from "$lib/server/api/types";
7
+
8
+ async function parseResponse<T = unknown>(res: Response): Promise<T> {
9
+ return superjson.parse(await res.text()) as T;
10
+ }
11
+
12
+ function mockRequestEvent(locals: App.Locals) {
13
+ return {
14
+ locals,
15
+ url: new URL("http://localhost"),
16
+ request: new Request("http://localhost"),
17
+ } as Parameters<typeof featureFlagsGET>[0];
18
+ }
19
+
20
+ describe("GET /api/v2/feature-flags", () => {
21
+ beforeEach(async () => {
22
+ await cleanupTestData();
23
+ }, 20000);
24
+
25
+ it("returns correct shape with expected fields", async () => {
26
+ const locals = createTestLocals();
27
+
28
+ const res = await featureFlagsGET(mockRequestEvent(locals));
29
+ const data = await parseResponse<FeatureFlags>(res);
30
+
31
+ expect(data).toHaveProperty("enableAssistants");
32
+ expect(data).toHaveProperty("loginEnabled");
33
+ expect(data).toHaveProperty("isAdmin");
34
+ expect(data).toHaveProperty("transcriptionEnabled");
35
+ expect(typeof data.enableAssistants).toBe("boolean");
36
+ expect(typeof data.loginEnabled).toBe("boolean");
37
+ expect(typeof data.isAdmin).toBe("boolean");
38
+ expect(typeof data.transcriptionEnabled).toBe("boolean");
39
+ });
40
+
41
+ it("reflects isAdmin from locals for non-admin user", async () => {
42
+ const locals = createTestLocals({ isAdmin: false });
43
+
44
+ const res = await featureFlagsGET(mockRequestEvent(locals));
45
+ const data = await parseResponse<FeatureFlags>(res);
46
+
47
+ expect(data.isAdmin).toBe(false);
48
+ });
49
+
50
+ it("reflects isAdmin from locals for admin user", async () => {
51
+ const { locals } = await createTestUser();
52
+ locals.isAdmin = true;
53
+
54
+ const res = await featureFlagsGET(mockRequestEvent(locals));
55
+ const data = await parseResponse<FeatureFlags>(res);
56
+
57
+ expect(data.isAdmin).toBe(true);
58
+ });
59
+ });
60
+
61
+ describe("GET /api/v2/public-config", () => {
62
+ it("returns an object", async () => {
63
+ const locals = createTestLocals();
64
+
65
+ const res = await publicConfigGET(mockRequestEvent(locals));
66
+ const data = await parseResponse<Record<string, unknown>>(res);
67
+
68
+ expect(data).toBeDefined();
69
+ expect(typeof data).toBe("object");
70
+ expect(data).not.toBeNull();
71
+ });
72
+ });
@@ -0,0 +1,86 @@
1
+ import { ObjectId } from "mongodb";
2
+ import { collections } from "$lib/server/database";
3
+ import type { User } from "$lib/types/User";
4
+ import type { Session } from "$lib/types/Session";
5
+ import type { Conversation } from "$lib/types/Conversation";
6
+
7
+ export function createTestLocals(overrides?: Partial<App.Locals>): App.Locals {
8
+ return {
9
+ sessionId: "test-session-id",
10
+ isAdmin: false,
11
+ user: undefined,
12
+ token: undefined,
13
+ ...overrides,
14
+ };
15
+ }
16
+
17
+ export async function createTestUser(): Promise<{
18
+ user: User;
19
+ session: Session;
20
+ locals: App.Locals;
21
+ }> {
22
+ const userId = new ObjectId();
23
+ const sessionId = `test-session-${userId.toString()}`;
24
+
25
+ const user: User = {
26
+ _id: userId,
27
+ createdAt: new Date(),
28
+ updatedAt: new Date(),
29
+ username: `user-${userId.toString().slice(0, 8)}`,
30
+ name: "Test User",
31
+ avatarUrl: "https://example.com/avatar.png",
32
+ hfUserId: `hf-${userId.toString()}`,
33
+ };
34
+
35
+ const session: Session = {
36
+ _id: new ObjectId(),
37
+ createdAt: new Date(),
38
+ updatedAt: new Date(),
39
+ userId,
40
+ sessionId,
41
+ expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24),
42
+ };
43
+
44
+ await collections.users.insertOne(user);
45
+ await collections.sessions.insertOne(session);
46
+
47
+ return {
48
+ user,
49
+ session,
50
+ locals: {
51
+ user,
52
+ sessionId,
53
+ isAdmin: false,
54
+ token: undefined,
55
+ },
56
+ };
57
+ }
58
+
59
+ export async function createTestConversation(
60
+ locals: App.Locals,
61
+ overrides?: Partial<Conversation>
62
+ ): Promise<Conversation> {
63
+ const conv: Conversation = {
64
+ _id: new ObjectId(),
65
+ title: "Test Conversation",
66
+ model: "test-model",
67
+ messages: [],
68
+ createdAt: new Date(),
69
+ updatedAt: new Date(),
70
+ ...(locals.user ? { userId: locals.user._id } : { sessionId: locals.sessionId }),
71
+ ...overrides,
72
+ };
73
+
74
+ await collections.conversations.insertOne(conv);
75
+ return conv;
76
+ }
77
+
78
+ export async function cleanupTestData() {
79
+ await collections.conversations.deleteMany({});
80
+ await collections.abortedGenerations.deleteMany({});
81
+ await collections.users.deleteMany({});
82
+ await collections.sessions.deleteMany({});
83
+ await collections.settings.deleteMany({});
84
+ await collections.sharedConversations.deleteMany({});
85
+ await collections.reports.deleteMany({});
86
+ }
@@ -0,0 +1,78 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { ObjectId } from "mongodb";
3
+ import superjson from "superjson";
4
+ import { collections } from "$lib/server/database";
5
+ import { createTestLocals, createTestUser, cleanupTestData } from "./testHelpers";
6
+ import { GET } from "../../../../routes/api/v2/user/reports/+server";
7
+ import type { Report } from "$lib/types/Report";
8
+
9
+ async function parseResponse<T = unknown>(res: Response): Promise<T> {
10
+ return superjson.parse(await res.text()) as T;
11
+ }
12
+
13
+ function mockRequestEvent(locals: App.Locals) {
14
+ return {
15
+ locals,
16
+ url: new URL("http://localhost"),
17
+ request: new Request("http://localhost"),
18
+ } as Parameters<typeof GET>[0];
19
+ }
20
+
21
+ describe("GET /api/v2/user/reports", () => {
22
+ beforeEach(async () => {
23
+ await cleanupTestData();
24
+ }, 20000);
25
+
26
+ it("returns empty array for unauthenticated user", async () => {
27
+ const locals = createTestLocals();
28
+
29
+ const res = await GET(mockRequestEvent(locals));
30
+ const data = await parseResponse<unknown[]>(res);
31
+
32
+ expect(data).toEqual([]);
33
+ });
34
+
35
+ it("returns reports for authenticated user", async () => {
36
+ const { user, locals } = await createTestUser();
37
+
38
+ const report1: Report = {
39
+ _id: new ObjectId(),
40
+ createdBy: user._id,
41
+ object: "assistant",
42
+ contentId: new ObjectId(),
43
+ reason: "Inappropriate content",
44
+ createdAt: new Date(),
45
+ updatedAt: new Date(),
46
+ };
47
+
48
+ const report2: Report = {
49
+ _id: new ObjectId(),
50
+ createdBy: user._id,
51
+ object: "tool",
52
+ contentId: new ObjectId(),
53
+ reason: "Broken tool",
54
+ createdAt: new Date(),
55
+ updatedAt: new Date(),
56
+ };
57
+
58
+ await collections.reports.insertMany([report1, report2]);
59
+
60
+ const res = await GET(mockRequestEvent(locals));
61
+ const data = await parseResponse<Report[]>(res);
62
+
63
+ expect(data).toHaveLength(2);
64
+ expect(data[0]._id.toString()).toBe(report1._id.toString());
65
+ expect(data[1]._id.toString()).toBe(report2._id.toString());
66
+ expect(data[0].reason).toBe("Inappropriate content");
67
+ expect(data[1].reason).toBe("Broken tool");
68
+ });
69
+
70
+ it("returns empty array when authenticated user has no reports", async () => {
71
+ const { locals } = await createTestUser();
72
+
73
+ const res = await GET(mockRequestEvent(locals));
74
+ const data = await parseResponse<unknown[]>(res);
75
+
76
+ expect(data).toEqual([]);
77
+ });
78
+ });
@@ -0,0 +1,239 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import superjson from "superjson";
3
+ import { collections } from "$lib/server/database";
4
+ import { createTestLocals, createTestUser, cleanupTestData } from "./testHelpers";
5
+ import { GET as userGET } from "../../../../routes/api/v2/user/+server";
6
+ import {
7
+ GET as settingsGET,
8
+ POST as settingsPOST,
9
+ } from "../../../../routes/api/v2/user/settings/+server";
10
+
11
+ async function parseResponse<T = unknown>(res: Response): Promise<T> {
12
+ return superjson.parse(await res.text()) as T;
13
+ }
14
+
15
+ function mockRequestEvent(locals: App.Locals, overrides?: Record<string, unknown>) {
16
+ return {
17
+ locals,
18
+ url: new URL("http://localhost"),
19
+ request: new Request("http://localhost"),
20
+ ...overrides,
21
+ } as Parameters<typeof userGET>[0];
22
+ }
23
+
24
+ describe("GET /api/v2/user", () => {
25
+ beforeEach(async () => {
26
+ await cleanupTestData();
27
+ }, 20000);
28
+
29
+ it("returns user info for authenticated user", async () => {
30
+ const { user, locals } = await createTestUser();
31
+
32
+ const res = await userGET(mockRequestEvent(locals));
33
+ const data = await parseResponse<Record<string, unknown>>(res);
34
+
35
+ expect(data).not.toBeNull();
36
+ expect(data).toMatchObject({
37
+ id: user._id.toString(),
38
+ username: user.username,
39
+ avatarUrl: user.avatarUrl,
40
+ isAdmin: false,
41
+ isEarlyAccess: false,
42
+ });
43
+ });
44
+
45
+ it("returns null for unauthenticated user", async () => {
46
+ const locals = createTestLocals();
47
+
48
+ const res = await userGET(mockRequestEvent(locals));
49
+ const data = await parseResponse(res);
50
+
51
+ expect(data).toBeNull();
52
+ });
53
+ });
54
+
55
+ describe("GET /api/v2/user/settings", () => {
56
+ beforeEach(async () => {
57
+ await cleanupTestData();
58
+ }, 20000);
59
+
60
+ it("returns default settings when none exist", async () => {
61
+ const { locals } = await createTestUser();
62
+
63
+ const res = await settingsGET(mockRequestEvent(locals));
64
+ const data = await parseResponse<Record<string, unknown>>(res);
65
+
66
+ expect(data).toMatchObject({
67
+ welcomeModalSeen: false,
68
+ welcomeModalSeenAt: null,
69
+ streamingMode: "smooth",
70
+ directPaste: false,
71
+ shareConversationsWithModelAuthors: true,
72
+ customPrompts: {},
73
+ multimodalOverrides: {},
74
+ toolsOverrides: {},
75
+ providerOverrides: {},
76
+ });
77
+ });
78
+
79
+ it("returns stored settings with canonical streaming mode", async () => {
80
+ const { user, locals } = await createTestUser();
81
+
82
+ await collections.settings.insertOne({
83
+ userId: user._id,
84
+ shareConversationsWithModelAuthors: false,
85
+ activeModel: "custom-model",
86
+ streamingMode: "raw",
87
+ directPaste: true,
88
+ hapticsEnabled: true,
89
+ customPrompts: { "my-model": "Be helpful" },
90
+ multimodalOverrides: {},
91
+ toolsOverrides: {},
92
+ hidePromptExamples: {},
93
+ providerOverrides: {},
94
+ welcomeModalSeenAt: new Date("2024-01-01"),
95
+ createdAt: new Date(),
96
+ updatedAt: new Date(),
97
+ });
98
+
99
+ const res = await settingsGET(mockRequestEvent(locals));
100
+ const data = await parseResponse<Record<string, unknown>>(res);
101
+
102
+ expect(data).toMatchObject({
103
+ welcomeModalSeen: true,
104
+ shareConversationsWithModelAuthors: false,
105
+ streamingMode: "raw",
106
+ directPaste: true,
107
+ customPrompts: { "my-model": "Be helpful" },
108
+ });
109
+ });
110
+
111
+ it("maps legacy stored streamingMode=final to smooth", async () => {
112
+ const { user, locals } = await createTestUser();
113
+
114
+ const legacySettingsWithFinal = {
115
+ userId: user._id,
116
+ shareConversationsWithModelAuthors: true,
117
+ activeModel: "custom-model",
118
+ streamingMode: "final",
119
+ directPaste: false,
120
+ customPrompts: {},
121
+ multimodalOverrides: {},
122
+ toolsOverrides: {},
123
+ hidePromptExamples: {},
124
+ providerOverrides: {},
125
+ createdAt: new Date(),
126
+ updatedAt: new Date(),
127
+ };
128
+
129
+ await collections.settings.insertOne(
130
+ legacySettingsWithFinal as unknown as Parameters<typeof collections.settings.insertOne>[0]
131
+ );
132
+
133
+ const res = await settingsGET(mockRequestEvent(locals));
134
+ const data = await parseResponse<Record<string, unknown>>(res);
135
+
136
+ expect(data).toMatchObject({
137
+ streamingMode: "smooth",
138
+ });
139
+ });
140
+ });
141
+
142
+ describe("POST /api/v2/user/settings", () => {
143
+ beforeEach(async () => {
144
+ await cleanupTestData();
145
+ }, 20000);
146
+
147
+ it("creates settings with upsert", async () => {
148
+ const { user, locals } = await createTestUser();
149
+
150
+ const body = {
151
+ shareConversationsWithModelAuthors: false,
152
+ activeModel: "test-model",
153
+ customPrompts: {},
154
+ multimodalOverrides: {},
155
+ toolsOverrides: {},
156
+ providerOverrides: {},
157
+ streamingMode: "raw",
158
+ directPaste: false,
159
+ hidePromptExamples: {},
160
+ };
161
+
162
+ const res = await settingsPOST(
163
+ mockRequestEvent(locals, {
164
+ request: new Request("http://localhost", {
165
+ method: "POST",
166
+ body: JSON.stringify(body),
167
+ headers: { "Content-Type": "application/json" },
168
+ }),
169
+ })
170
+ );
171
+
172
+ expect(res.status).toBe(200);
173
+
174
+ const stored = await collections.settings.findOne({ userId: user._id });
175
+ expect(stored).not.toBeNull();
176
+ expect(stored?.shareConversationsWithModelAuthors).toBe(false);
177
+ expect(stored?.streamingMode).toBe("raw");
178
+ expect(stored?.createdAt).toBeInstanceOf(Date);
179
+ expect(stored?.updatedAt).toBeInstanceOf(Date);
180
+ });
181
+
182
+ it("sets welcomeModalSeenAt when welcomeModalSeen is true", async () => {
183
+ const { user, locals } = await createTestUser();
184
+
185
+ const body = {
186
+ welcomeModalSeen: true,
187
+ shareConversationsWithModelAuthors: true,
188
+ activeModel: "test-model",
189
+ customPrompts: {},
190
+ multimodalOverrides: {},
191
+ toolsOverrides: {},
192
+ providerOverrides: {},
193
+ streamingMode: "smooth",
194
+ directPaste: false,
195
+ hidePromptExamples: {},
196
+ };
197
+
198
+ await settingsPOST(
199
+ mockRequestEvent(locals, {
200
+ request: new Request("http://localhost", {
201
+ method: "POST",
202
+ body: JSON.stringify(body),
203
+ headers: { "Content-Type": "application/json" },
204
+ }),
205
+ })
206
+ );
207
+
208
+ const stored = await collections.settings.findOne({ userId: user._id });
209
+ expect(stored).not.toBeNull();
210
+ expect(stored?.welcomeModalSeenAt).toBeInstanceOf(Date);
211
+ });
212
+
213
+ it("validates body with Zod and applies defaults for missing fields", async () => {
214
+ const { user, locals } = await createTestUser();
215
+
216
+ // POST with minimal body — Zod defaults should fill in the rest
217
+ const body = {};
218
+
219
+ const res = await settingsPOST(
220
+ mockRequestEvent(locals, {
221
+ request: new Request("http://localhost", {
222
+ method: "POST",
223
+ body: JSON.stringify(body),
224
+ headers: { "Content-Type": "application/json" },
225
+ }),
226
+ })
227
+ );
228
+
229
+ expect(res.status).toBe(200);
230
+
231
+ const stored = await collections.settings.findOne({ userId: user._id });
232
+ expect(stored).not.toBeNull();
233
+ // Zod defaults should be applied
234
+ expect(stored?.shareConversationsWithModelAuthors).toBe(true);
235
+ expect(stored?.streamingMode).toBe("smooth");
236
+ expect(stored?.directPaste).toBe(false);
237
+ expect(stored?.customPrompts).toEqual({});
238
+ });
239
+ });