@lobehub/lobehub 2.0.0-next.71 → 2.0.0-next.73

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 (338) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/next.config.ts +5 -5
  4. package/package.json +1 -1
  5. package/packages/model-bank/src/aiModels/qiniu.ts +126 -0
  6. package/scripts/prebuild.mts +1 -1
  7. package/src/app/(backend)/trpc/desktop/[trpc]/route.ts +6 -6
  8. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +7 -4
  9. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +4 -4
  10. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/UserBanner.tsx +5 -6
  11. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/useCategory.tsx +5 -8
  12. package/src/app/[variants]/(main)/(mobile)/me/(home)/index.tsx +25 -0
  13. package/src/app/[variants]/(main)/(mobile)/me/(home)/layout.tsx +13 -16
  14. package/src/app/[variants]/(main)/(mobile)/me/profile/features/Category.tsx +6 -6
  15. package/src/app/[variants]/(main)/(mobile)/me/profile/features/Header.tsx +3 -3
  16. package/src/app/[variants]/(main)/(mobile)/me/profile/index.tsx +16 -0
  17. package/src/app/[variants]/(main)/(mobile)/me/profile/layout.tsx +9 -11
  18. package/src/app/[variants]/(main)/(mobile)/me/settings/features/Header.tsx +3 -3
  19. package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +3 -4
  20. package/src/app/[variants]/(main)/(mobile)/me/settings/index.tsx +16 -0
  21. package/src/app/[variants]/(main)/(mobile)/me/settings/layout.tsx +15 -13
  22. package/src/app/[variants]/(main)/changelog/_layout/Desktop/index.tsx +6 -5
  23. package/src/app/[variants]/(main)/changelog/_layout/Mobile/Header.tsx +3 -3
  24. package/src/app/[variants]/(main)/changelog/_layout/Mobile/index.tsx +5 -5
  25. package/src/app/[variants]/(main)/changelog/features/Post.tsx +7 -4
  26. package/src/app/[variants]/(main)/changelog/index.tsx +55 -0
  27. package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +6 -7
  28. package/src/app/[variants]/(main)/chat/_layout/Mobile.tsx +3 -3
  29. package/src/app/[variants]/(main)/chat/components/WorkspaceLayout.tsx +1 -14
  30. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatHydration/index.tsx +2 -2
  31. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +11 -13
  32. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/index.tsx +0 -1
  33. package/src/app/[variants]/(main)/chat/components/conversation/features/ThreadHydration.tsx +2 -2
  34. package/src/app/[variants]/(main)/chat/components/topic/features/Topic/TopicListContent/TopicItem/TopicContent.tsx +20 -11
  35. package/src/app/[variants]/(main)/chat/features/PageTitle/index.tsx +1 -2
  36. package/src/app/[variants]/(main)/chat/index.tsx +29 -0
  37. package/src/app/[variants]/(main)/chat/session/features/SessionHydration.tsx +8 -6
  38. package/src/app/[variants]/(main)/chat/session/features/SessionListContent/Inbox/index.tsx +2 -4
  39. package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/index.tsx +1 -1
  40. package/src/app/[variants]/(main)/chat/session/layout/Mobile/SessionHeader.tsx +3 -3
  41. package/src/app/[variants]/(main)/chat/settings/_layout/Desktop/Header.tsx +3 -3
  42. package/src/app/[variants]/(main)/chat/settings/_layout/Mobile/Header.tsx +3 -3
  43. package/src/app/[variants]/(main)/chat/settings/features/PublishResultModal/index.tsx +3 -3
  44. package/src/app/[variants]/(main)/chat/{components/SettingsPage.tsx → settings/index.tsx} +6 -35
  45. package/src/app/[variants]/(main)/components/Link.tsx +21 -0
  46. package/src/app/[variants]/(main)/discover/(detail)/_layout/{Desktop.tsx → Desktop/index.tsx} +15 -7
  47. package/src/app/[variants]/(main)/discover/(detail)/_layout/Mobile/index.tsx +3 -3
  48. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/Block.tsx +1 -1
  49. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Overview/index.tsx +1 -1
  50. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Related/index.tsx +2 -2
  51. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/SystemRole/index.tsx +3 -3
  52. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Versions/index.tsx +4 -4
  53. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/index.tsx +1 -1
  54. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Header.tsx +2 -2
  55. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/ActionButton/AddAgent.tsx +3 -3
  56. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/ActionButton/index.tsx +1 -1
  57. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/Related/index.tsx +1 -1
  58. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/TocList/index.tsx +2 -2
  59. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/StatusPage/index.tsx +3 -3
  60. package/src/app/[variants]/(main)/discover/(detail)/assistant/{AssistantDetailPage.tsx → index.tsx} +18 -11
  61. package/src/app/[variants]/(main)/discover/(detail)/features/Breadcrumb.tsx +1 -1
  62. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Details/Related/index.tsx +2 -2
  63. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Details/Versions/index.tsx +5 -5
  64. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Details/index.tsx +1 -1
  65. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/Related/index.tsx +2 -2
  66. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/ServerConfig.tsx +4 -4
  67. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/TocList/index.tsx +2 -2
  68. package/src/app/[variants]/(main)/discover/(detail)/mcp/{McpDetailPage.tsx → index.tsx} +15 -7
  69. package/src/app/[variants]/(main)/discover/(detail)/mcp/loading.tsx +1 -0
  70. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Overview/index.tsx +1 -1
  71. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Parameter/ParameterItem.tsx +1 -1
  72. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Related/index.tsx +2 -2
  73. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/index.tsx +1 -1
  74. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Header.tsx +1 -1
  75. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/ActionButton/ChatWithModel.tsx +3 -3
  76. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/ActionButton/index.tsx +1 -1
  77. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/Related/index.tsx +2 -2
  78. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/RelatedProviders/index.tsx +1 -1
  79. package/src/app/[variants]/(main)/discover/(detail)/model/{ModelDetailPage.tsx → index.tsx} +17 -10
  80. package/src/app/[variants]/(main)/discover/(detail)/model/loading.tsx +1 -0
  81. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Related/index.tsx +2 -2
  82. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/index.tsx +1 -1
  83. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/ActionButton/ProviderConfig.tsx +5 -4
  84. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/ActionButton/index.tsx +1 -1
  85. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/Related/index.tsx +2 -2
  86. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/RelatedModels/index.tsx +1 -1
  87. package/src/app/[variants]/(main)/discover/(detail)/provider/{ProviderDetailPage.tsx → index.tsx} +17 -10
  88. package/src/app/[variants]/(main)/discover/(detail)/provider/loading.tsx +1 -0
  89. package/src/app/[variants]/(main)/discover/(list)/(home)/{HomePage.tsx → index.tsx} +14 -0
  90. package/src/app/[variants]/(main)/discover/(list)/_layout/Desktop/Nav.tsx +2 -3
  91. package/src/app/[variants]/(main)/discover/(list)/_layout/Desktop/index.tsx +4 -3
  92. package/src/app/[variants]/(main)/discover/(list)/_layout/Mobile/Nav.tsx +3 -4
  93. package/src/app/[variants]/(main)/discover/(list)/_layout/Mobile/index.tsx +4 -3
  94. package/src/app/[variants]/(main)/discover/(list)/assistant/_layout/Desktop.tsx +3 -4
  95. package/src/app/[variants]/(main)/discover/(list)/assistant/_layout/Mobile.tsx +3 -3
  96. package/src/app/[variants]/(main)/discover/(list)/assistant/features/List/Item.tsx +2 -2
  97. package/src/app/[variants]/(main)/discover/(list)/assistant/{AssistantPage.tsx → index.tsx} +18 -4
  98. package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +2 -2
  99. package/src/app/[variants]/(main)/discover/(list)/mcp/_layout/Desktop.tsx +3 -3
  100. package/src/app/[variants]/(main)/discover/(list)/mcp/_layout/Mobile.tsx +3 -3
  101. package/src/app/[variants]/(main)/discover/(list)/mcp/features/List/Item.tsx +1 -1
  102. package/src/app/[variants]/(main)/discover/(list)/mcp/{McpPage.tsx → index.tsx} +9 -2
  103. package/src/app/[variants]/(main)/discover/(list)/model/_layout/Desktop.tsx +3 -3
  104. package/src/app/[variants]/(main)/discover/(list)/model/_layout/Mobile.tsx +3 -3
  105. package/src/app/[variants]/(main)/discover/(list)/model/features/List/Item.tsx +1 -1
  106. package/src/app/[variants]/(main)/discover/(list)/model/{ModelPage.tsx → index.tsx} +9 -2
  107. package/src/app/[variants]/(main)/discover/(list)/provider/features/List/Item.tsx +1 -1
  108. package/src/app/[variants]/(main)/discover/(list)/provider/{Client.tsx → index.tsx} +9 -2
  109. package/src/app/[variants]/(main)/discover/_layout/Desktop/index.tsx +4 -4
  110. package/src/app/[variants]/(main)/discover/_layout/Mobile/index.tsx +3 -3
  111. package/src/app/[variants]/(main)/discover/components/Title.tsx +1 -1
  112. package/src/app/[variants]/(main)/discover/features/Search.tsx +2 -2
  113. package/src/app/[variants]/(main)/discover/features/useNav.tsx +11 -12
  114. package/src/app/[variants]/(main)/hooks/useActiveTabKey.ts +40 -0
  115. package/src/app/[variants]/(main)/hooks/usePathname.ts +10 -0
  116. package/src/app/[variants]/(main)/hooks/useQuery.ts +12 -0
  117. package/src/app/[variants]/(main)/hooks/useRouter.ts +22 -0
  118. package/src/app/[variants]/(main)/hooks/useSearchParams.ts +11 -0
  119. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ModelSelect/index.tsx +5 -5
  120. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicUrlSync.tsx +1 -1
  121. package/src/app/[variants]/(main)/image/ComingSoon.tsx +15 -0
  122. package/src/app/[variants]/(main)/image/_layout/Desktop/index.tsx +5 -2
  123. package/src/app/[variants]/(main)/image/_layout/DesktopWrapper.tsx +15 -0
  124. package/src/app/[variants]/(main)/image/_layout/Mobile/index.tsx +3 -1
  125. package/src/app/[variants]/(main)/image/features/ImageWorkspace/index.tsx +1 -2
  126. package/src/app/[variants]/(main)/image/index.tsx +18 -0
  127. package/src/app/[variants]/(main)/knowledge/_layout/Desktop.tsx +17 -0
  128. package/src/app/[variants]/(main)/knowledge/_layout/Mobile.tsx +17 -0
  129. package/src/app/[variants]/(main)/knowledge/components/KnowledgeBaseItem/index.tsx +1 -1
  130. package/src/app/[variants]/(main)/knowledge/components/modal/ModalPageClient.tsx +4 -4
  131. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/index.tsx +5 -8
  132. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/menu/Head.tsx +1 -1
  133. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/CategoryMenu.tsx +2 -2
  134. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/KnowledgeBase.tsx +1 -1
  135. package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/DesktopLayoutContainer.tsx +3 -2
  136. package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/BottomActions.tsx +4 -4
  137. package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/TopActions.test.tsx +9 -9
  138. package/src/app/[variants]/(main)/layouts/desktop/SideBar/TopActions.tsx +117 -0
  139. package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/index.tsx +9 -4
  140. package/src/app/[variants]/(main)/layouts/index.tsx +11 -0
  141. package/src/app/[variants]/(main)/{_layout/Mobile → layouts/mobile}/NavBar.tsx +5 -5
  142. package/src/app/[variants]/(main)/{_layout/Mobile → layouts/mobile}/index.tsx +10 -7
  143. package/src/app/[variants]/(main)/profile/(home)/desktop.tsx +26 -0
  144. package/src/app/[variants]/(main)/profile/(home)/index.tsx +26 -0
  145. package/src/app/[variants]/(main)/profile/@category/features/CategoryContent.tsx +5 -7
  146. package/src/app/[variants]/(main)/profile/_layout/Desktop/index.tsx +3 -2
  147. package/src/app/[variants]/(main)/profile/_layout/DesktopWrapper.tsx +14 -0
  148. package/src/app/[variants]/(main)/profile/_layout/Mobile/Header.tsx +3 -3
  149. package/src/app/[variants]/(main)/profile/_layout/Mobile/index.tsx +4 -3
  150. package/src/app/[variants]/(main)/profile/apikey/index.tsx +18 -0
  151. package/src/app/[variants]/(main)/profile/hooks/useCategory.tsx +6 -6
  152. package/src/app/[variants]/(main)/profile/security/index.tsx +29 -0
  153. package/src/app/[variants]/(main)/profile/stats/features/AssistantsRank.tsx +4 -4
  154. package/src/app/[variants]/(main)/profile/stats/features/TopicsRank.tsx +4 -4
  155. package/src/app/[variants]/(main)/profile/stats/index.tsx +20 -0
  156. package/src/app/[variants]/(main)/profile/usage/features/UsageTable.tsx +7 -9
  157. package/src/app/[variants]/(main)/profile/usage/index.tsx +13 -0
  158. package/src/app/[variants]/(main)/settings/_layout/Desktop/Header.tsx +2 -2
  159. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +2 -2
  160. package/src/app/[variants]/(main)/settings/_layout/DesktopWrapper.tsx +23 -0
  161. package/src/app/[variants]/(main)/settings/_layout/Mobile/Header.tsx +8 -7
  162. package/src/app/[variants]/(main)/settings/_layout/Mobile/index.tsx +2 -5
  163. package/src/app/[variants]/(main)/settings/_layout/MobileWrapper.tsx +23 -0
  164. package/src/app/[variants]/(main)/settings/agent/AgentMenu/Menu.tsx +2 -4
  165. package/src/app/[variants]/(main)/settings/agent/index.tsx +2 -4
  166. package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +2 -3
  167. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/All.tsx +2 -2
  168. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/Item.tsx +38 -37
  169. package/src/app/[variants]/(main)/settings/provider/_layout/Mobile.tsx +2 -2
  170. package/src/app/[variants]/(main)/settings/provider/features/CreateNewProvider/index.tsx +3 -3
  171. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/UpdateProviderInfo/SettingModal.tsx +3 -3
  172. package/src/app/[variants]/DesktopRouter.tsx +40 -0
  173. package/src/app/[variants]/MobileRouter.tsx +41 -0
  174. package/src/app/[variants]/desktopRouter.config.tsx +381 -0
  175. package/src/app/[variants]/layout.tsx +1 -3
  176. package/src/app/[variants]/loaders/routeParams.ts +45 -0
  177. package/src/app/[variants]/loading/Server/Redirect.tsx +1 -1
  178. package/src/app/[variants]/mobileRouter.config.tsx +412 -0
  179. package/src/app/[variants]/page.tsx +16 -6
  180. package/src/app/desktop/devtools/page.tsx +1 -1
  181. package/src/app/desktop/layout.tsx +1 -1
  182. package/src/components/BootErrorBoundary/index.tsx +129 -0
  183. package/src/components/mdx/constants.ts +1 -0
  184. package/src/features/ChangelogModal/index.tsx +3 -3
  185. package/src/features/KnowledgeManager/FileExplorer/index.tsx +4 -4
  186. package/src/features/KnowledgeManager/Header/FilesSearchBar.tsx +1 -1
  187. package/src/features/User/UserPanel/PanelContent.tsx +4 -3
  188. package/src/features/User/UserPanel/useMenu.tsx +20 -14
  189. package/src/features/User/__tests__/PanelContent.test.tsx +13 -7
  190. package/src/hooks/useDiscoverTab.ts +2 -2
  191. package/src/hooks/useInterceptingRoutes.test.ts +23 -26
  192. package/src/hooks/useInterceptingRoutes.ts +5 -4
  193. package/src/hooks/usePinnedAgentState.ts +6 -6
  194. package/src/hooks/useQuery.ts +5 -0
  195. package/src/hooks/useQueryParam.ts +322 -0
  196. package/src/hooks/useQueryRoute.test.ts +2 -12
  197. package/src/hooks/useQueryRoute.ts +5 -5
  198. package/src/hooks/useShowMobileWorkspace.ts +1 -1
  199. package/src/hooks/useSwitchSession.ts +4 -3
  200. package/src/layout/GlobalProvider/ImportSettings.tsx +22 -9
  201. package/src/layout/GlobalProvider/StoreInitialization.tsx +9 -1
  202. package/src/proxy.ts +13 -1
  203. package/src/services/message/index.ts +11 -2
  204. package/src/store/chat/agents/createAgentExecutors.ts +31 -16
  205. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +218 -0
  206. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +4 -0
  207. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +9 -3
  208. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +142 -61
  209. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +204 -10
  210. package/src/store/chat/slices/builtinTool/actions/search.ts +51 -26
  211. package/src/store/chat/slices/message/action.test.ts +182 -33
  212. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +79 -36
  213. package/src/store/chat/slices/message/actions/query.ts +7 -5
  214. package/src/store/chat/slices/message/selectors/dbMessage.ts +11 -4
  215. package/src/store/chat/slices/plugin/action.test.ts +257 -54
  216. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +63 -26
  217. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +52 -19
  218. package/src/store/chat/slices/plugin/actions/publicApi.ts +6 -1
  219. package/src/store/chat/slices/plugin/actions/workflow.ts +17 -6
  220. package/src/store/chat/slices/thread/action.ts +2 -0
  221. package/src/store/global/action.test.ts +3 -3
  222. package/src/store/global/actions/workspacePane.ts +2 -1
  223. package/src/store/global/initialState.ts +10 -2
  224. package/src/store/user/slices/common/action.ts +4 -0
  225. package/src/app/[variants]/(main)/(mobile)/me/(home)/loading.tsx +0 -38
  226. package/src/app/[variants]/(main)/(mobile)/me/(home)/page.tsx +0 -40
  227. package/src/app/[variants]/(main)/(mobile)/me/profile/loading.tsx +0 -5
  228. package/src/app/[variants]/(main)/(mobile)/me/profile/page.tsx +0 -30
  229. package/src/app/[variants]/(main)/(mobile)/me/settings/loading.tsx +0 -5
  230. package/src/app/[variants]/(main)/(mobile)/me/settings/page.tsx +0 -30
  231. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +0 -106
  232. package/src/app/[variants]/(main)/changelog/layout.tsx +0 -10
  233. package/src/app/[variants]/(main)/changelog/modal/page.tsx +0 -23
  234. package/src/app/[variants]/(main)/changelog/page.tsx +0 -78
  235. package/src/app/[variants]/(main)/chat/ChatRouter.tsx +0 -83
  236. package/src/app/[variants]/(main)/chat/_layout/ChatLayout.tsx +0 -22
  237. package/src/app/[variants]/(main)/chat/components/MainChatPage.tsx +0 -25
  238. package/src/app/[variants]/(main)/chat/error.tsx +0 -3
  239. package/src/app/[variants]/(main)/chat/layout.tsx +0 -10
  240. package/src/app/[variants]/(main)/chat/loading.tsx +0 -3
  241. package/src/app/[variants]/(main)/chat/not-found.tsx +0 -1
  242. package/src/app/[variants]/(main)/chat/page.tsx +0 -12
  243. package/src/app/[variants]/(main)/chat/settings/error.tsx +0 -3
  244. package/src/app/[variants]/(main)/chat/settings/loading.tsx +0 -3
  245. package/src/app/[variants]/(main)/chat/settings/not-found.tsx +0 -1
  246. package/src/app/[variants]/(main)/discover/(detail)/_layout/DetailLayout.tsx +0 -22
  247. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/Client.tsx +0 -51
  248. package/src/app/[variants]/(main)/discover/(detail)/mcp/[slug]/Client.tsx +0 -43
  249. package/src/app/[variants]/(main)/discover/(detail)/mcp/[slug]/loading.tsx +0 -1
  250. package/src/app/[variants]/(main)/discover/(detail)/model/[...slugs]/Client.tsx +0 -40
  251. package/src/app/[variants]/(main)/discover/(detail)/model/[...slugs]/loading.tsx +0 -1
  252. package/src/app/[variants]/(main)/discover/(detail)/provider/[...slugs]/Client.tsx +0 -40
  253. package/src/app/[variants]/(main)/discover/(detail)/provider/[...slugs]/loading.tsx +0 -1
  254. package/src/app/[variants]/(main)/discover/(list)/_layout/ListLayout.tsx +0 -22
  255. package/src/app/[variants]/(main)/discover/(list)/assistant/AssistantLayout.tsx +0 -21
  256. package/src/app/[variants]/(main)/discover/(list)/mcp/Client.tsx +0 -44
  257. package/src/app/[variants]/(main)/discover/(list)/mcp/McpLayout.tsx +0 -21
  258. package/src/app/[variants]/(main)/discover/(list)/model/Client.tsx +0 -44
  259. package/src/app/[variants]/(main)/discover/(list)/model/ModelLayout.tsx +0 -21
  260. package/src/app/[variants]/(main)/discover/(list)/provider/ProviderPage.tsx +0 -43
  261. package/src/app/[variants]/(main)/discover/DiscoverRouter.tsx +0 -170
  262. package/src/app/[variants]/(main)/discover/[[...path]]/page.tsx +0 -12
  263. package/src/app/[variants]/(main)/discover/_layout/DiscoverLayout.tsx +0 -22
  264. package/src/app/[variants]/(main)/discover/error.tsx +0 -3
  265. package/src/app/[variants]/(main)/discover/not-found.tsx +0 -1
  266. package/src/app/[variants]/(main)/error.tsx +0 -3
  267. package/src/app/[variants]/(main)/image/layout.tsx +0 -15
  268. package/src/app/[variants]/(main)/image/page.tsx +0 -45
  269. package/src/app/[variants]/(main)/knowledge/KnowledgeRouter.tsx +0 -74
  270. package/src/app/[variants]/(main)/knowledge/[[...path]]/page.tsx +0 -12
  271. package/src/app/[variants]/(main)/knowledge/components/modal/page.tsx +0 -13
  272. package/src/app/[variants]/(main)/knowledge/layout.tsx +0 -12
  273. package/src/app/[variants]/(main)/layout.tsx +0 -10
  274. package/src/app/[variants]/(main)/not-found.tsx +0 -1
  275. package/src/app/[variants]/(main)/profile/apikey/page.tsx +0 -32
  276. package/src/app/[variants]/(main)/profile/error.tsx +0 -3
  277. package/src/app/[variants]/(main)/profile/layout.tsx +0 -11
  278. package/src/app/[variants]/(main)/profile/loading.tsx +0 -3
  279. package/src/app/[variants]/(main)/profile/not-found.tsx +0 -1
  280. package/src/app/[variants]/(main)/profile/security/page.tsx +0 -28
  281. package/src/app/[variants]/(main)/profile/stats/page.tsx +0 -23
  282. package/src/app/[variants]/(main)/profile/usage/page.tsx +0 -23
  283. package/src/app/[variants]/@modal/(.)changelog/modal/features/Cover.tsx +0 -48
  284. package/src/app/[variants]/@modal/(.)changelog/modal/features/Hero.tsx +0 -29
  285. package/src/app/[variants]/@modal/(.)changelog/modal/features/Post.tsx +0 -57
  286. package/src/app/[variants]/@modal/(.)changelog/modal/features/PublishedTime.tsx +0 -50
  287. package/src/app/[variants]/@modal/(.)changelog/modal/features/ReadDetail.tsx +0 -72
  288. package/src/app/[variants]/@modal/(.)changelog/modal/features/VersionTag.tsx +0 -26
  289. package/src/app/[variants]/@modal/(.)changelog/modal/layout.tsx +0 -41
  290. package/src/app/[variants]/@modal/(.)changelog/modal/loading.tsx +0 -10
  291. package/src/app/[variants]/@modal/(.)changelog/modal/page.tsx +0 -38
  292. package/src/app/[variants]/@modal/_layout/ModalLayout.tsx +0 -63
  293. package/src/app/[variants]/@modal/_layout/SettingModalLayout.tsx +0 -71
  294. package/src/app/[variants]/@modal/default.tsx +0 -3
  295. package/src/app/[variants]/@modal/error.tsx +0 -3
  296. package/src/app/[variants]/@modal/layout.tsx +0 -7
  297. package/src/app/[variants]/@modal/loading.tsx +0 -5
  298. /package/src/app/[variants]/{@modal/(.)changelog/modal → (main)/changelog}/features/Pagination.tsx +0 -0
  299. /package/src/app/[variants]/{@modal/(.)changelog/modal → (main)/changelog}/features/UpdateChangelogStatus.tsx +0 -0
  300. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/DetailProvider.tsx +0 -0
  301. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/Knowledge.tsx +0 -0
  302. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/KnowledgeItem.tsx +0 -0
  303. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/PluginItem.tsx +0 -0
  304. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/Plugins.tsx +0 -0
  305. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/index.tsx +0 -0
  306. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Nav.tsx +0 -0
  307. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Overview/TagList.tsx +0 -0
  308. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/SystemRole/TagList.tsx +0 -0
  309. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/Related/Item.tsx +0 -0
  310. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/Summary/index.tsx +0 -0
  311. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/index.tsx +0 -0
  312. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/loading.tsx → loading.tsx} +0 -0
  313. /package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/ActionButton/index.tsx +0 -0
  314. /package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/ConnectionTypeAlert.tsx +0 -0
  315. /package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/Related/Item.tsx +0 -0
  316. /package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/index.tsx +0 -0
  317. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/DetailProvider.tsx +0 -0
  318. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Nav.tsx +0 -0
  319. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Overview/ProviderList/index.tsx +0 -0
  320. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Parameter/index.tsx +0 -0
  321. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/Related/Item.tsx +0 -0
  322. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/RelatedProviders/Item.tsx +0 -0
  323. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/index.tsx +0 -0
  324. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/DetailProvider.tsx +0 -0
  325. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Guide/index.tsx +0 -0
  326. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Nav.tsx +0 -0
  327. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Overview/ModelList/index.tsx +0 -0
  328. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Overview/index.tsx +0 -0
  329. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Header.tsx +0 -0
  330. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/Related/Item.tsx +0 -0
  331. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/RelatedModels/Item.tsx +0 -0
  332. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/index.tsx +0 -0
  333. /package/src/app/[variants]/(main)/labs/{page.tsx → index.tsx} +0 -0
  334. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/RegisterHotkeys.tsx +0 -0
  335. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/Avatar.test.tsx +0 -0
  336. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/Avatar.tsx +0 -0
  337. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/PinList/index.tsx +0 -0
  338. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/index.tsx +0 -0
@@ -4,7 +4,7 @@ import { nanoid } from '@lobechat/utils';
4
4
  import { StateCreator } from 'zustand/vanilla';
5
5
 
6
6
  import { searchService } from '@/services/search';
7
- import { chatSelectors } from '@/store/chat/selectors';
7
+ import { dbMessageSelectors } from '@/store/chat/selectors';
8
8
  import { ChatStore } from '@/store/chat/store';
9
9
  import { WebBrowsingExecutionRuntime } from '@/tools/web-browsing/ExecutionRuntime';
10
10
 
@@ -45,15 +45,20 @@ export const searchSlice: StateCreator<
45
45
  crawlMultiPages: async (id, params, aiSummary = true) => {
46
46
  const { optimisticUpdateMessageContent } = get();
47
47
  get().toggleSearchLoading(id, true);
48
+
49
+ // Get message to extract sessionId/topicId
50
+ const message = dbMessageSelectors.getDbMessageById(id)(get());
51
+ const context = { sessionId: message?.sessionId, topicId: message?.topicId };
52
+
48
53
  try {
49
54
  const { content, success, error, state } = await runtime.crawlMultiPages(params);
50
55
 
51
- await optimisticUpdateMessageContent(id, content);
56
+ await optimisticUpdateMessageContent(id, content, undefined, context);
52
57
 
53
58
  if (success) {
54
- await get().optimisticUpdatePluginState(id, state);
59
+ await get().optimisticUpdatePluginState(id, state, context);
55
60
  } else {
56
- await get().optimisticUpdatePluginError(id, error);
61
+ await get().optimisticUpdatePluginError(id, error, context);
57
62
  }
58
63
  get().toggleSearchLoading(id, false);
59
64
 
@@ -67,7 +72,7 @@ export const searchSlice: StateCreator<
67
72
  const content = [{ errorMessage: err.message, errorType: err.name }];
68
73
 
69
74
  const xmlContent = crawlResultsPrompt(content);
70
- await optimisticUpdateMessageContent(id, xmlContent);
75
+ await optimisticUpdateMessageContent(id, xmlContent, undefined, context);
71
76
  }
72
77
  },
73
78
 
@@ -78,10 +83,13 @@ export const searchSlice: StateCreator<
78
83
  },
79
84
 
80
85
  saveSearchResult: async (id) => {
81
- const message = chatSelectors.getMessageById(id)(get());
86
+ const message = dbMessageSelectors.getDbMessageById(id)(get());
82
87
  if (!message || !message.plugin) return;
83
88
 
84
89
  const { optimisticAddToolToAssistantMessage, optimisticCreateMessage, openToolUI } = get();
90
+
91
+ const context = { sessionId: message.sessionId, topicId: message.topicId };
92
+
85
93
  // 1. 创建一个新的 tool call message
86
94
  const newToolCallId = `tool_call_${nanoid()}`;
87
95
 
@@ -92,23 +100,30 @@ export const searchSlice: StateCreator<
92
100
  plugin: message.plugin,
93
101
  pluginState: message.pluginState,
94
102
  role: 'tool',
95
- sessionId: get().activeId,
103
+ sessionId: message.sessionId ?? get().activeId,
96
104
  tool_call_id: newToolCallId,
97
- topicId: get().activeTopicId,
105
+ topicId: message.topicId !== undefined ? message.topicId : get().activeTopicId,
98
106
  };
99
107
 
100
108
  const addToolItem = async () => {
101
109
  if (!message.parentId || !message.plugin) return;
102
110
 
103
- await optimisticAddToolToAssistantMessage(message.parentId, {
104
- id: newToolCallId,
105
- ...message.plugin,
106
- });
111
+ await optimisticAddToolToAssistantMessage(
112
+ message.parentId,
113
+ {
114
+ id: newToolCallId,
115
+ ...message.plugin,
116
+ },
117
+ context,
118
+ );
107
119
  };
108
120
 
109
121
  const [result] = await Promise.all([
110
122
  // 1. 添加 tool message
111
- optimisticCreateMessage(toolMessage),
123
+ optimisticCreateMessage(toolMessage, {
124
+ sessionId: toolMessage.sessionId,
125
+ topicId: toolMessage.topicId,
126
+ }),
112
127
  // 2. 将这条 tool call message 插入到 ai 消息的 tools 中
113
128
  addToolItem(),
114
129
  ]);
@@ -121,31 +136,41 @@ export const searchSlice: StateCreator<
121
136
  search: async (id, params, aiSummary = true) => {
122
137
  get().toggleSearchLoading(id, true);
123
138
 
139
+ // Get message to extract sessionId/topicId
140
+ const message = dbMessageSelectors.getDbMessageById(id)(get());
141
+ const context = { sessionId: message?.sessionId, topicId: message?.topicId };
142
+
124
143
  const { content, success, error, state } = await runtime.search(params);
125
144
 
126
145
  if (success) {
127
- await get().optimisticUpdatePluginState(id, state);
146
+ await get().optimisticUpdatePluginState(id, state, context);
128
147
  } else {
129
148
  if ((error as Error).message === SEARCH_SEARXNG_NOT_CONFIG) {
130
- await get().optimisticUpdateMessagePluginError(id, {
131
- body: {
132
- provider: 'searxng',
149
+ await get().optimisticUpdateMessagePluginError(
150
+ id,
151
+ {
152
+ body: { provider: 'searxng' },
153
+ message: 'SearXNG is not configured',
154
+ type: 'PluginSettingsInvalid',
133
155
  },
134
- message: 'SearXNG is not configured',
135
- type: 'PluginSettingsInvalid',
136
- });
156
+ context,
157
+ );
137
158
  } else {
138
- await get().optimisticUpdateMessagePluginError(id, {
139
- body: error,
140
- message: (error as Error).message,
141
- type: 'PluginServerError',
142
- });
159
+ await get().optimisticUpdateMessagePluginError(
160
+ id,
161
+ {
162
+ body: error,
163
+ message: (error as Error).message,
164
+ type: 'PluginServerError',
165
+ },
166
+ context,
167
+ );
143
168
  }
144
169
  }
145
170
 
146
171
  get().toggleSearchLoading(id, false);
147
172
 
148
- await get().optimisticUpdateMessageContent(id, content);
173
+ await get().optimisticUpdateMessageContent(id, content, undefined, context);
149
174
 
150
175
  // 如果 aiSummary 为 true,则会自动触发总结
151
176
  return aiSummary;
@@ -22,11 +22,14 @@ vi.mock('@/services/message', () => ({
22
22
  messageService: {
23
23
  getMessages: vi.fn(),
24
24
  updateMessageError: vi.fn(),
25
- removeMessage: vi.fn(),
25
+ removeMessage: vi.fn(() => Promise.resolve({ success: true, messages: [] })),
26
26
  removeMessagesByAssistant: vi.fn(),
27
- removeMessages: vi.fn(() => Promise.resolve()),
27
+ removeMessages: vi.fn(() => Promise.resolve({ success: true, messages: [] })),
28
28
  createMessage: vi.fn(() => Promise.resolve({ id: 'new-message-id', messages: [] })),
29
- updateMessage: vi.fn(),
29
+ updateMessage: vi.fn(() => Promise.resolve({ success: true, messages: [] })),
30
+ updateMessageMetadata: vi.fn(() => Promise.resolve({ success: true, messages: [] })),
31
+ updateMessagePluginError: vi.fn(() => Promise.resolve({ success: true, messages: [] })),
32
+ updateMessageRAG: vi.fn(() => Promise.resolve({ success: true, messages: [] })),
30
33
  removeAllMessages: vi.fn(() => Promise.resolve()),
31
34
  },
32
35
  }));
@@ -224,7 +227,10 @@ describe('chatMessage actions', () => {
224
227
  });
225
228
 
226
229
  expect(deleteSpy).toHaveBeenCalledWith(messageId);
227
- expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
230
+ expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages, {
231
+ sessionId: 'session-id',
232
+ topicId: undefined,
233
+ });
228
234
  });
229
235
 
230
236
  it('deleteMessage should remove the message only', async () => {
@@ -266,7 +272,10 @@ describe('chatMessage actions', () => {
266
272
  sessionId: 'session-id',
267
273
  topicId: undefined,
268
274
  });
269
- expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
275
+ expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages, {
276
+ sessionId: 'session-id',
277
+ topicId: undefined,
278
+ });
270
279
  });
271
280
 
272
281
  it('deleteMessage should remove assistantGroup message with all children', async () => {
@@ -317,7 +326,10 @@ describe('chatMessage actions', () => {
317
326
  sessionId: 'session-id',
318
327
  topicId: undefined,
319
328
  });
320
- expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
329
+ expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages, {
330
+ sessionId: 'session-id',
331
+ topicId: undefined,
332
+ });
321
333
  });
322
334
 
323
335
  it('deleteMessage should remove group message with children that have tool calls', async () => {
@@ -381,7 +393,10 @@ describe('chatMessage actions', () => {
381
393
  topicId: undefined,
382
394
  },
383
395
  );
384
- expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages);
396
+ expect(replaceMessagesSpy).toHaveBeenCalledWith(mockMessages, {
397
+ sessionId: 'session-id',
398
+ topicId: undefined,
399
+ });
385
400
  });
386
401
  });
387
402
 
@@ -417,51 +432,84 @@ describe('chatMessage actions', () => {
417
432
 
418
433
  describe('deleteToolMessage', () => {
419
434
  it('deleteMessage should remove a message by id', async () => {
420
- const { result } = renderHook(() => useChatStore());
421
435
  const messageId = 'message-id';
422
- const updateMessageSpy = vi.spyOn(messageService, 'updateMessage');
423
- const removeMessageSpy = vi.spyOn(messageService, 'removeMessage');
436
+ const sessionId = 'session-id';
437
+ const topicId = null;
424
438
 
439
+ const rawMessages = [
440
+ {
441
+ id: messageId,
442
+ role: 'assistant',
443
+ tools: [{ id: 'tool1' }, { id: 'tool2' }],
444
+ } as UIChatMessage,
445
+ {
446
+ id: '2',
447
+ parentId: messageId,
448
+ tool_call_id: 'tool1',
449
+ role: 'tool',
450
+ } as UIChatMessage,
451
+ { id: '3', tool_call_id: 'tool2', role: 'tool' } as UIChatMessage,
452
+ ];
453
+
454
+ const key = messageMapKey(sessionId, topicId);
425
455
  act(() => {
426
- const rawMessages = [
427
- {
428
- id: messageId,
429
- role: 'assistant',
430
- tools: [{ id: 'tool1' }, { id: 'tool2' }],
431
- } as UIChatMessage,
432
- {
433
- id: '2',
434
- parentId: messageId,
435
- tool_call_id: 'tool1',
436
- role: 'tool',
437
- } as UIChatMessage,
438
- { id: '3', tool_call_id: 'tool2', role: 'tool' } as UIChatMessage,
439
- ];
440
-
441
456
  useChatStore.setState({
442
- activeId: 'session-id',
443
- activeTopicId: undefined,
457
+ activeId: sessionId,
458
+ activeTopicId: topicId as unknown as string,
444
459
  dbMessagesMap: {
445
- [messageMapKey('session-id')]: rawMessages,
460
+ [key]: rawMessages,
446
461
  },
447
462
  messagesMap: {
448
- [messageMapKey('session-id')]: rawMessages,
463
+ [key]: rawMessages,
449
464
  },
450
465
  });
451
466
  });
467
+
468
+ const { result } = renderHook(() => useChatStore());
469
+
470
+ // Mock removeMessage to return the remaining messages after deletion
471
+ // Note: tool1 is also removed from the assistant message's tools to reflect the concurrent update
472
+ const remainingAfterDelete = [
473
+ {
474
+ id: messageId,
475
+ role: 'assistant',
476
+ tools: [{ id: 'tool2' }],
477
+ } as UIChatMessage,
478
+ { id: '3', tool_call_id: 'tool2', role: 'tool' } as UIChatMessage,
479
+ ];
480
+
481
+ // Mock updateMessage to return updated messages after tool removal
482
+ const updatedMessages = [
483
+ {
484
+ id: messageId,
485
+ role: 'assistant',
486
+ tools: [{ id: 'tool2' }],
487
+ } as UIChatMessage,
488
+ { id: '3', tool_call_id: 'tool2', role: 'tool' } as UIChatMessage,
489
+ ];
490
+
491
+ const refreshToolsSpy = vi.spyOn(result.current, 'internal_refreshToUpdateMessageTools');
492
+ const updateMessageSpy = vi
493
+ .spyOn(messageService, 'updateMessage')
494
+ .mockResolvedValue({ success: true, messages: updatedMessages });
495
+ const removeMessageSpy = vi
496
+ .spyOn(messageService, 'removeMessage')
497
+ .mockResolvedValue({ success: true, messages: remainingAfterDelete });
498
+
452
499
  await act(async () => {
453
500
  await result.current.deleteToolMessage('2');
454
501
  });
455
502
 
456
503
  expect(removeMessageSpy).toHaveBeenCalled();
504
+ expect(refreshToolsSpy).toHaveBeenCalledWith('message-id', undefined);
457
505
  expect(updateMessageSpy).toHaveBeenCalledWith(
458
506
  'message-id',
459
507
  {
460
508
  tools: [{ id: 'tool2' }],
461
509
  },
462
510
  {
463
- sessionId: 'session-id',
464
- topicId: undefined,
511
+ sessionId,
512
+ topicId,
465
513
  },
466
514
  );
467
515
  });
@@ -647,16 +695,23 @@ describe('chatMessage actions', () => {
647
695
  });
648
696
  });
649
697
 
650
- it('should refresh messages after updating content', async () => {
698
+ it('should replace messages after updating content', async () => {
651
699
  const { result } = renderHook(() => useChatStore());
652
700
  const messageId = 'message-id';
653
701
  const newContent = 'Updated content';
702
+ const replaceMessagesSpy = vi.spyOn(result.current, 'replaceMessages');
654
703
 
655
704
  await act(async () => {
656
705
  await result.current.optimisticUpdateMessageContent(messageId, newContent);
657
706
  });
658
707
 
659
- expect(result.current.refreshMessages).toHaveBeenCalled();
708
+ expect(replaceMessagesSpy).toHaveBeenCalledWith(
709
+ [],
710
+ expect.objectContaining({
711
+ sessionId: 'session-id',
712
+ topicId: 'topic-id',
713
+ }),
714
+ );
660
715
  });
661
716
  });
662
717
 
@@ -802,4 +857,98 @@ describe('chatMessage actions', () => {
802
857
  });
803
858
  });
804
859
  });
860
+
861
+ describe('OptimisticUpdateContext isolation', () => {
862
+ beforeEach(() => {
863
+ vi.clearAllMocks();
864
+ });
865
+
866
+ it('optimisticUpdateMessageContent should use context sessionId/topicId', async () => {
867
+ const { result } = renderHook(() => useChatStore());
868
+ const messageId = 'message-id';
869
+ const content = 'Updated content';
870
+ const contextSessionId = 'context-session-id';
871
+ const contextTopicId = 'context-topic-id';
872
+
873
+ const updateMessageSpy = vi.spyOn(messageService, 'updateMessage');
874
+
875
+ await act(async () => {
876
+ await result.current.optimisticUpdateMessageContent(messageId, content, undefined, {
877
+ sessionId: contextSessionId,
878
+ topicId: contextTopicId,
879
+ });
880
+ });
881
+
882
+ expect(updateMessageSpy).toHaveBeenCalledWith(
883
+ messageId,
884
+ { content, tools: undefined },
885
+ { sessionId: contextSessionId, topicId: contextTopicId },
886
+ );
887
+ });
888
+
889
+ it('optimisticUpdateMessageError should use context sessionId/topicId', async () => {
890
+ const { result } = renderHook(() => useChatStore());
891
+ const messageId = 'message-id';
892
+ const error = { message: 'Error occurred', type: 'error' as any };
893
+ const contextSessionId = 'context-session';
894
+ const contextTopicId = 'context-topic';
895
+
896
+ const updateMessageSpy = vi.spyOn(messageService, 'updateMessage');
897
+
898
+ await act(async () => {
899
+ await result.current.optimisticUpdateMessageError(messageId, error, {
900
+ sessionId: contextSessionId,
901
+ topicId: contextTopicId,
902
+ });
903
+ });
904
+
905
+ expect(updateMessageSpy).toHaveBeenCalledWith(
906
+ messageId,
907
+ { error },
908
+ { sessionId: contextSessionId, topicId: contextTopicId },
909
+ );
910
+ });
911
+
912
+ it('optimisticDeleteMessage should use context sessionId/topicId', async () => {
913
+ const { result } = renderHook(() => useChatStore());
914
+ const messageId = 'message-id';
915
+ const contextSessionId = 'context-session';
916
+ const contextTopicId = 'context-topic';
917
+
918
+ const removeMessageSpy = vi.spyOn(messageService, 'removeMessage');
919
+
920
+ await act(async () => {
921
+ await result.current.optimisticDeleteMessage(messageId, {
922
+ sessionId: contextSessionId,
923
+ topicId: contextTopicId,
924
+ });
925
+ });
926
+
927
+ expect(removeMessageSpy).toHaveBeenCalledWith(messageId, {
928
+ sessionId: contextSessionId,
929
+ topicId: contextTopicId,
930
+ });
931
+ });
932
+
933
+ it('optimisticDeleteMessages should use context sessionId/topicId', async () => {
934
+ const { result } = renderHook(() => useChatStore());
935
+ const ids = ['id-1', 'id-2'];
936
+ const contextSessionId = 'context-session';
937
+ const contextTopicId = 'context-topic';
938
+
939
+ const removeMessagesSpy = vi.spyOn(messageService, 'removeMessages');
940
+
941
+ await act(async () => {
942
+ await result.current.optimisticDeleteMessages(ids, {
943
+ sessionId: contextSessionId,
944
+ topicId: contextTopicId,
945
+ });
946
+ });
947
+
948
+ expect(removeMessagesSpy).toHaveBeenCalledWith(ids, {
949
+ sessionId: contextSessionId,
950
+ topicId: contextTopicId,
951
+ });
952
+ });
953
+ });
805
954
  });
@@ -17,6 +17,14 @@ import { StateCreator } from 'zustand/vanilla';
17
17
  import { messageService } from '@/services/message';
18
18
  import { ChatStore } from '@/store/chat/store';
19
19
 
20
+ /**
21
+ * Context for optimistic updates to specify session/topic isolation
22
+ */
23
+ export interface OptimisticUpdateContext {
24
+ sessionId?: string;
25
+ topicId?: string | null;
26
+ }
27
+
20
28
  /**
21
29
  * Optimistic update operations
22
30
  * All methods follow the pattern: update frontend first, then persist to database
@@ -28,7 +36,13 @@ export interface MessageOptimisticUpdateAction {
28
36
  */
29
37
  optimisticCreateMessage: (
30
38
  params: CreateMessageParams,
31
- context?: { groupMessageId?: string; skipRefresh?: boolean; tempMessageId?: string },
39
+ context?: {
40
+ groupMessageId?: string;
41
+ sessionId?: string;
42
+ skipRefresh?: boolean;
43
+ tempMessageId?: string;
44
+ topicId?: string | null;
45
+ },
32
46
  ) => Promise<{ id: string; messages: UIChatMessage[] } | undefined>;
33
47
 
34
48
  /**
@@ -40,8 +54,8 @@ export interface MessageOptimisticUpdateAction {
40
54
  /**
41
55
  * delete the message content with optimistic update
42
56
  */
43
- optimisticDeleteMessage: (id: string) => Promise<void>;
44
- optimisticDeleteMessages: (ids: string[]) => Promise<void>;
57
+ optimisticDeleteMessage: (id: string, context?: OptimisticUpdateContext) => Promise<void>;
58
+ optimisticDeleteMessages: (ids: string[], context?: OptimisticUpdateContext) => Promise<void>;
45
59
 
46
60
  /**
47
61
  * update the message content with optimistic update
@@ -59,12 +73,17 @@ export interface MessageOptimisticUpdateAction {
59
73
  search?: GroundingSearch;
60
74
  toolCalls?: MessageToolCall[];
61
75
  },
76
+ context?: OptimisticUpdateContext,
62
77
  ) => Promise<void>;
63
78
 
64
79
  /**
65
80
  * update the message error with optimistic update
66
81
  */
67
- optimisticUpdateMessageError: (id: string, error: ChatMessageError | null) => Promise<void>;
82
+ optimisticUpdateMessageError: (
83
+ id: string,
84
+ error: ChatMessageError | null,
85
+ context?: OptimisticUpdateContext,
86
+ ) => Promise<void>;
68
87
 
69
88
  /**
70
89
  * update the message metadata with optimistic update
@@ -72,6 +91,7 @@ export interface MessageOptimisticUpdateAction {
72
91
  optimisticUpdateMessageMetadata: (
73
92
  id: string,
74
93
  metadata: Partial<MessageMetadata>,
94
+ context?: OptimisticUpdateContext,
75
95
  ) => Promise<void>;
76
96
 
77
97
  /**
@@ -80,12 +100,17 @@ export interface MessageOptimisticUpdateAction {
80
100
  optimisticUpdateMessagePluginError: (
81
101
  id: string,
82
102
  error: ChatMessagePluginError | null,
103
+ context?: OptimisticUpdateContext,
83
104
  ) => Promise<void>;
84
105
 
85
106
  /**
86
107
  * update message RAG with optimistic update
87
108
  */
88
- optimisticUpdateMessageRAG: (id: string, input: UpdateMessageRAGParams) => Promise<void>;
109
+ optimisticUpdateMessageRAG: (
110
+ id: string,
111
+ input: UpdateMessageRAGParams,
112
+ context?: OptimisticUpdateContext,
113
+ ) => Promise<void>;
89
114
  }
90
115
 
91
116
  export const messageOptimisticUpdate: StateCreator<
@@ -113,7 +138,9 @@ export const messageOptimisticUpdate: StateCreator<
113
138
 
114
139
  if (!context?.skipRefresh) {
115
140
  // Use the messages returned from createMessage (already grouped)
116
- replaceMessages(result.messages);
141
+ const sessionId = context?.sessionId ?? get().activeId;
142
+ const topicId = context?.topicId !== undefined ? context.topicId : get().activeTopicId;
143
+ replaceMessages(result.messages, { sessionId, topicId });
117
144
  }
118
145
 
119
146
  internal_toggleMessageLoading(false, tempId);
@@ -144,29 +171,33 @@ export const messageOptimisticUpdate: StateCreator<
144
171
  return tempId;
145
172
  },
146
173
 
147
- optimisticDeleteMessage: async (id: string) => {
174
+ optimisticDeleteMessage: async (id: string, context) => {
148
175
  get().internal_dispatchMessage({ id, type: 'deleteMessage' });
176
+ const sessionId = context?.sessionId ?? get().activeId;
177
+ const topicId = context?.topicId !== undefined ? context.topicId : get().activeTopicId;
149
178
  const result = await messageService.removeMessage(id, {
150
- sessionId: get().activeId,
151
- topicId: get().activeTopicId,
179
+ sessionId,
180
+ topicId,
152
181
  });
153
182
  if (result?.success && result.messages) {
154
- get().replaceMessages(result.messages);
183
+ get().replaceMessages(result.messages, { sessionId, topicId });
155
184
  }
156
185
  },
157
186
 
158
- optimisticDeleteMessages: async (ids) => {
187
+ optimisticDeleteMessages: async (ids, context) => {
159
188
  get().internal_dispatchMessage({ ids, type: 'deleteMessages' });
189
+ const sessionId = context?.sessionId ?? get().activeId;
190
+ const topicId = context?.topicId !== undefined ? context.topicId : get().activeTopicId;
160
191
  const result = await messageService.removeMessages(ids, {
161
- sessionId: get().activeId,
162
- topicId: get().activeTopicId,
192
+ sessionId,
193
+ topicId,
163
194
  });
164
195
  if (result?.success && result.messages) {
165
- get().replaceMessages(result.messages);
196
+ get().replaceMessages(result.messages, { sessionId, topicId });
166
197
  }
167
198
  },
168
199
 
169
- optimisticUpdateMessageContent: async (id, content, extra) => {
200
+ optimisticUpdateMessageContent: async (id, content, extra, context) => {
170
201
  const {
171
202
  internal_dispatchMessage,
172
203
  refreshMessages,
@@ -191,6 +222,9 @@ export const messageOptimisticUpdate: StateCreator<
191
222
  });
192
223
  }
193
224
 
225
+ const sessionId = context?.sessionId ?? get().activeId;
226
+ const topicId = context?.topicId !== undefined ? context.topicId : get().activeTopicId;
227
+
194
228
  const result = await messageService.updateMessage(
195
229
  id,
196
230
  {
@@ -203,31 +237,33 @@ export const messageOptimisticUpdate: StateCreator<
203
237
  search: extra?.search,
204
238
  tools: extra?.toolCalls ? internal_transformToolCalls(extra?.toolCalls) : undefined,
205
239
  },
206
- { sessionId: get().activeId, topicId: get().activeTopicId },
240
+ { sessionId, topicId },
207
241
  );
208
242
 
209
243
  if (result && result.success && result.messages) {
210
- replaceMessages(result.messages, { action: 'optimisticUpdateMessageContent' });
244
+ replaceMessages(result.messages, {
245
+ action: 'optimisticUpdateMessageContent',
246
+ sessionId,
247
+ topicId,
248
+ });
211
249
  } else {
212
250
  await refreshMessages();
213
251
  }
214
252
  },
215
253
 
216
- optimisticUpdateMessageError: async (id, error) => {
254
+ optimisticUpdateMessageError: async (id, error, context) => {
217
255
  get().internal_dispatchMessage({ id, type: 'updateMessage', value: { error } });
218
- const result = await messageService.updateMessage(
219
- id,
220
- { error },
221
- { sessionId: get().activeId, topicId: get().activeTopicId },
222
- );
256
+ const sessionId = context?.sessionId ?? get().activeId;
257
+ const topicId = context?.topicId !== undefined ? context.topicId : get().activeTopicId;
258
+ const result = await messageService.updateMessage(id, { error }, { sessionId, topicId });
223
259
  if (result?.success && result.messages) {
224
- get().replaceMessages(result.messages);
260
+ get().replaceMessages(result.messages, { sessionId, topicId });
225
261
  } else {
226
262
  await get().refreshMessages();
227
263
  }
228
264
  },
229
265
 
230
- optimisticUpdateMessageMetadata: async (id, metadata) => {
266
+ optimisticUpdateMessageMetadata: async (id, metadata, context) => {
231
267
  const { internal_dispatchMessage, refreshMessages, replaceMessages } = get();
232
268
 
233
269
  // Optimistic update: update the frontend immediately
@@ -237,36 +273,43 @@ export const messageOptimisticUpdate: StateCreator<
237
273
  value: metadata,
238
274
  });
239
275
 
276
+ const sessionId = context?.sessionId ?? get().activeId;
277
+ const topicId = context?.topicId !== undefined ? context.topicId : get().activeTopicId;
278
+
240
279
  // Persist to database
241
280
  const result = await messageService.updateMessageMetadata(id, metadata, {
242
- sessionId: get().activeId,
243
- topicId: get().activeTopicId,
281
+ sessionId,
282
+ topicId,
244
283
  });
245
284
 
246
285
  if (result?.success && result.messages) {
247
- replaceMessages(result.messages);
286
+ replaceMessages(result.messages, { sessionId, topicId });
248
287
  } else {
249
288
  await refreshMessages();
250
289
  }
251
290
  },
252
291
 
253
- optimisticUpdateMessagePluginError: async (id, error) => {
292
+ optimisticUpdateMessagePluginError: async (id, error, context) => {
293
+ const sessionId = context?.sessionId ?? get().activeId;
294
+ const topicId = context?.topicId !== undefined ? context.topicId : get().activeTopicId;
254
295
  const result = await messageService.updateMessagePluginError(id, error, {
255
- sessionId: get().activeId,
256
- topicId: get().activeTopicId,
296
+ sessionId,
297
+ topicId,
257
298
  });
258
299
  if (result?.success && result.messages) {
259
- get().replaceMessages(result.messages);
300
+ get().replaceMessages(result.messages, { sessionId, topicId });
260
301
  }
261
302
  },
262
303
 
263
- optimisticUpdateMessageRAG: async (id, data) => {
304
+ optimisticUpdateMessageRAG: async (id, data, context) => {
305
+ const sessionId = context?.sessionId ?? get().activeId;
306
+ const topicId = context?.topicId !== undefined ? context.topicId : get().activeTopicId;
264
307
  const result = await messageService.updateMessageRAG(id, data, {
265
- sessionId: get().activeId,
266
- topicId: get().activeTopicId,
308
+ sessionId,
309
+ topicId,
267
310
  });
268
311
  if (result?.success && result.messages) {
269
- get().replaceMessages(result.messages);
312
+ get().replaceMessages(result.messages, { sessionId, topicId });
270
313
  }
271
314
  },
272
315
  });