@lobehub/lobehub 2.0.0-next.72 → 2.0.0-next.74

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 (358) hide show
  1. package/.github/workflows/desktop-pr-build.yml +7 -3
  2. package/CHANGELOG.md +50 -0
  3. package/apps/desktop/package.json +1 -0
  4. package/apps/desktop/src/main/controllers/LocalFileCtr.ts +55 -11
  5. package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +153 -0
  6. package/changelog/v1.json +18 -0
  7. package/locales/en-US/tool.json +12 -1
  8. package/locales/zh-CN/tool.json +12 -1
  9. package/next.config.ts +5 -5
  10. package/package.json +2 -1
  11. package/packages/electron-client-ipc/src/types/localSystem.ts +4 -0
  12. package/scripts/prebuild.mts +16 -6
  13. package/src/app/(backend)/trpc/desktop/[trpc]/route.ts +6 -6
  14. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +7 -4
  15. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +4 -4
  16. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/UserBanner.tsx +5 -6
  17. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/useCategory.tsx +5 -8
  18. package/src/app/[variants]/(main)/(mobile)/me/(home)/index.tsx +25 -0
  19. package/src/app/[variants]/(main)/(mobile)/me/(home)/layout.tsx +13 -16
  20. package/src/app/[variants]/(main)/(mobile)/me/profile/features/Category.tsx +6 -6
  21. package/src/app/[variants]/(main)/(mobile)/me/profile/features/Header.tsx +3 -3
  22. package/src/app/[variants]/(main)/(mobile)/me/profile/index.tsx +16 -0
  23. package/src/app/[variants]/(main)/(mobile)/me/profile/layout.tsx +9 -11
  24. package/src/app/[variants]/(main)/(mobile)/me/settings/features/Header.tsx +3 -3
  25. package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +3 -4
  26. package/src/app/[variants]/(main)/(mobile)/me/settings/index.tsx +16 -0
  27. package/src/app/[variants]/(main)/(mobile)/me/settings/layout.tsx +15 -13
  28. package/src/app/[variants]/(main)/changelog/_layout/Desktop/index.tsx +6 -5
  29. package/src/app/[variants]/(main)/changelog/_layout/Mobile/Header.tsx +3 -3
  30. package/src/app/[variants]/(main)/changelog/_layout/Mobile/index.tsx +5 -5
  31. package/src/app/[variants]/(main)/changelog/features/Post.tsx +7 -4
  32. package/src/app/[variants]/(main)/changelog/index.tsx +55 -0
  33. package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +6 -7
  34. package/src/app/[variants]/(main)/chat/_layout/Mobile.tsx +3 -3
  35. package/src/app/[variants]/(main)/chat/components/WorkspaceLayout.tsx +1 -14
  36. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatHydration/index.tsx +2 -2
  37. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +11 -13
  38. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/index.tsx +0 -1
  39. package/src/app/[variants]/(main)/chat/components/conversation/features/ThreadHydration.tsx +2 -2
  40. package/src/app/[variants]/(main)/chat/components/topic/features/Topic/TopicListContent/TopicItem/TopicContent.tsx +20 -11
  41. package/src/app/[variants]/(main)/chat/features/PageTitle/index.tsx +1 -2
  42. package/src/app/[variants]/(main)/chat/index.tsx +29 -0
  43. package/src/app/[variants]/(main)/chat/session/features/SessionHydration.tsx +8 -6
  44. package/src/app/[variants]/(main)/chat/session/features/SessionListContent/Inbox/index.tsx +2 -4
  45. package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/index.tsx +1 -1
  46. package/src/app/[variants]/(main)/chat/session/layout/Mobile/SessionHeader.tsx +3 -3
  47. package/src/app/[variants]/(main)/chat/settings/_layout/Desktop/Header.tsx +3 -3
  48. package/src/app/[variants]/(main)/chat/settings/_layout/Mobile/Header.tsx +3 -3
  49. package/src/app/[variants]/(main)/chat/settings/features/PublishResultModal/index.tsx +3 -3
  50. package/src/app/[variants]/(main)/chat/{components/SettingsPage.tsx → settings/index.tsx} +6 -35
  51. package/src/app/[variants]/(main)/components/Link.tsx +21 -0
  52. package/src/app/[variants]/(main)/discover/(detail)/_layout/{Desktop.tsx → Desktop/index.tsx} +15 -7
  53. package/src/app/[variants]/(main)/discover/(detail)/_layout/Mobile/index.tsx +3 -3
  54. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/Block.tsx +1 -1
  55. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Overview/index.tsx +1 -1
  56. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Related/index.tsx +2 -2
  57. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/SystemRole/index.tsx +3 -3
  58. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Versions/index.tsx +4 -4
  59. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/index.tsx +1 -1
  60. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Header.tsx +2 -2
  61. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/ActionButton/AddAgent.tsx +3 -3
  62. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/ActionButton/index.tsx +1 -1
  63. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/Related/index.tsx +1 -1
  64. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/TocList/index.tsx +2 -2
  65. package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/StatusPage/index.tsx +3 -3
  66. package/src/app/[variants]/(main)/discover/(detail)/assistant/{AssistantDetailPage.tsx → index.tsx} +18 -11
  67. package/src/app/[variants]/(main)/discover/(detail)/features/Breadcrumb.tsx +1 -1
  68. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Details/Related/index.tsx +2 -2
  69. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Details/Versions/index.tsx +5 -5
  70. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Details/index.tsx +1 -1
  71. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/Related/index.tsx +2 -2
  72. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/ServerConfig.tsx +4 -4
  73. package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/TocList/index.tsx +2 -2
  74. package/src/app/[variants]/(main)/discover/(detail)/mcp/{McpDetailPage.tsx → index.tsx} +15 -7
  75. package/src/app/[variants]/(main)/discover/(detail)/mcp/loading.tsx +1 -0
  76. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Overview/index.tsx +1 -1
  77. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Parameter/ParameterItem.tsx +1 -1
  78. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Related/index.tsx +2 -2
  79. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/index.tsx +1 -1
  80. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Header.tsx +1 -1
  81. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/ActionButton/ChatWithModel.tsx +3 -3
  82. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/ActionButton/index.tsx +1 -1
  83. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/Related/index.tsx +2 -2
  84. package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/RelatedProviders/index.tsx +1 -1
  85. package/src/app/[variants]/(main)/discover/(detail)/model/{ModelDetailPage.tsx → index.tsx} +17 -10
  86. package/src/app/[variants]/(main)/discover/(detail)/model/loading.tsx +1 -0
  87. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Related/index.tsx +2 -2
  88. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/index.tsx +1 -1
  89. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/ActionButton/ProviderConfig.tsx +5 -4
  90. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/ActionButton/index.tsx +1 -1
  91. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/Related/index.tsx +2 -2
  92. package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/RelatedModels/index.tsx +1 -1
  93. package/src/app/[variants]/(main)/discover/(detail)/provider/{ProviderDetailPage.tsx → index.tsx} +17 -10
  94. package/src/app/[variants]/(main)/discover/(detail)/provider/loading.tsx +1 -0
  95. package/src/app/[variants]/(main)/discover/(list)/(home)/{HomePage.tsx → index.tsx} +14 -0
  96. package/src/app/[variants]/(main)/discover/(list)/_layout/Desktop/Nav.tsx +2 -3
  97. package/src/app/[variants]/(main)/discover/(list)/_layout/Desktop/index.tsx +4 -3
  98. package/src/app/[variants]/(main)/discover/(list)/_layout/Mobile/Nav.tsx +3 -4
  99. package/src/app/[variants]/(main)/discover/(list)/_layout/Mobile/index.tsx +4 -3
  100. package/src/app/[variants]/(main)/discover/(list)/assistant/_layout/Desktop.tsx +3 -4
  101. package/src/app/[variants]/(main)/discover/(list)/assistant/_layout/Mobile.tsx +3 -3
  102. package/src/app/[variants]/(main)/discover/(list)/assistant/features/List/Item.tsx +2 -2
  103. package/src/app/[variants]/(main)/discover/(list)/assistant/{AssistantPage.tsx → index.tsx} +18 -4
  104. package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +2 -2
  105. package/src/app/[variants]/(main)/discover/(list)/mcp/_layout/Desktop.tsx +3 -3
  106. package/src/app/[variants]/(main)/discover/(list)/mcp/_layout/Mobile.tsx +3 -3
  107. package/src/app/[variants]/(main)/discover/(list)/mcp/features/List/Item.tsx +1 -1
  108. package/src/app/[variants]/(main)/discover/(list)/mcp/{McpPage.tsx → index.tsx} +9 -2
  109. package/src/app/[variants]/(main)/discover/(list)/model/_layout/Desktop.tsx +3 -3
  110. package/src/app/[variants]/(main)/discover/(list)/model/_layout/Mobile.tsx +3 -3
  111. package/src/app/[variants]/(main)/discover/(list)/model/features/List/Item.tsx +1 -1
  112. package/src/app/[variants]/(main)/discover/(list)/model/{ModelPage.tsx → index.tsx} +9 -2
  113. package/src/app/[variants]/(main)/discover/(list)/provider/features/List/Item.tsx +1 -1
  114. package/src/app/[variants]/(main)/discover/(list)/provider/{Client.tsx → index.tsx} +9 -2
  115. package/src/app/[variants]/(main)/discover/_layout/Desktop/index.tsx +4 -4
  116. package/src/app/[variants]/(main)/discover/_layout/Mobile/index.tsx +3 -3
  117. package/src/app/[variants]/(main)/discover/components/Title.tsx +1 -1
  118. package/src/app/[variants]/(main)/discover/features/Search.tsx +2 -2
  119. package/src/app/[variants]/(main)/discover/features/useNav.tsx +11 -12
  120. package/src/app/[variants]/(main)/hooks/useActiveTabKey.ts +40 -0
  121. package/src/app/[variants]/(main)/hooks/usePathname.ts +10 -0
  122. package/src/app/[variants]/(main)/hooks/useQuery.ts +12 -0
  123. package/src/app/[variants]/(main)/hooks/useRouter.ts +22 -0
  124. package/src/app/[variants]/(main)/hooks/useSearchParams.ts +11 -0
  125. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ModelSelect/index.tsx +5 -5
  126. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicUrlSync.tsx +1 -1
  127. package/src/app/[variants]/(main)/image/ComingSoon.tsx +15 -0
  128. package/src/app/[variants]/(main)/image/_layout/Desktop/index.tsx +5 -2
  129. package/src/app/[variants]/(main)/image/_layout/DesktopWrapper.tsx +15 -0
  130. package/src/app/[variants]/(main)/image/_layout/Mobile/index.tsx +3 -1
  131. package/src/app/[variants]/(main)/image/features/ImageWorkspace/index.tsx +1 -2
  132. package/src/app/[variants]/(main)/image/index.tsx +18 -0
  133. package/src/app/[variants]/(main)/knowledge/_layout/Desktop.tsx +17 -0
  134. package/src/app/[variants]/(main)/knowledge/_layout/Mobile.tsx +17 -0
  135. package/src/app/[variants]/(main)/knowledge/components/KnowledgeBaseItem/index.tsx +1 -1
  136. package/src/app/[variants]/(main)/knowledge/components/modal/ModalPageClient.tsx +4 -4
  137. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/index.tsx +5 -8
  138. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/menu/Head.tsx +1 -1
  139. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/CategoryMenu.tsx +2 -2
  140. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/KnowledgeBase.tsx +1 -1
  141. package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/DesktopLayoutContainer.tsx +3 -2
  142. package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/BottomActions.tsx +4 -4
  143. package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/TopActions.test.tsx +9 -9
  144. package/src/app/[variants]/(main)/layouts/desktop/SideBar/TopActions.tsx +117 -0
  145. package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/index.tsx +9 -4
  146. package/src/app/[variants]/(main)/layouts/index.tsx +11 -0
  147. package/src/app/[variants]/(main)/{_layout/Mobile → layouts/mobile}/NavBar.tsx +5 -5
  148. package/src/app/[variants]/(main)/{_layout/Mobile → layouts/mobile}/index.tsx +10 -7
  149. package/src/app/[variants]/(main)/profile/(home)/desktop.tsx +26 -0
  150. package/src/app/[variants]/(main)/profile/(home)/index.tsx +26 -0
  151. package/src/app/[variants]/(main)/profile/@category/features/CategoryContent.tsx +5 -7
  152. package/src/app/[variants]/(main)/profile/_layout/Desktop/index.tsx +3 -2
  153. package/src/app/[variants]/(main)/profile/_layout/DesktopWrapper.tsx +14 -0
  154. package/src/app/[variants]/(main)/profile/_layout/Mobile/Header.tsx +3 -3
  155. package/src/app/[variants]/(main)/profile/_layout/Mobile/index.tsx +4 -3
  156. package/src/app/[variants]/(main)/profile/apikey/index.tsx +18 -0
  157. package/src/app/[variants]/(main)/profile/hooks/useCategory.tsx +6 -6
  158. package/src/app/[variants]/(main)/profile/security/index.tsx +29 -0
  159. package/src/app/[variants]/(main)/profile/stats/features/AssistantsRank.tsx +4 -4
  160. package/src/app/[variants]/(main)/profile/stats/features/TopicsRank.tsx +4 -4
  161. package/src/app/[variants]/(main)/profile/stats/index.tsx +20 -0
  162. package/src/app/[variants]/(main)/profile/usage/features/UsageTable.tsx +7 -9
  163. package/src/app/[variants]/(main)/profile/usage/index.tsx +13 -0
  164. package/src/app/[variants]/(main)/settings/_layout/Desktop/Header.tsx +2 -2
  165. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +2 -2
  166. package/src/app/[variants]/(main)/settings/_layout/DesktopWrapper.tsx +23 -0
  167. package/src/app/[variants]/(main)/settings/_layout/Mobile/Header.tsx +8 -7
  168. package/src/app/[variants]/(main)/settings/_layout/Mobile/index.tsx +2 -5
  169. package/src/app/[variants]/(main)/settings/_layout/MobileWrapper.tsx +23 -0
  170. package/src/app/[variants]/(main)/settings/agent/AgentMenu/Menu.tsx +2 -4
  171. package/src/app/[variants]/(main)/settings/agent/index.tsx +2 -4
  172. package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +2 -3
  173. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/All.tsx +2 -2
  174. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/Item.tsx +38 -37
  175. package/src/app/[variants]/(main)/settings/provider/_layout/Mobile.tsx +2 -2
  176. package/src/app/[variants]/(main)/settings/provider/features/CreateNewProvider/index.tsx +3 -3
  177. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/UpdateProviderInfo/SettingModal.tsx +3 -3
  178. package/src/app/[variants]/DesktopRouter.tsx +40 -0
  179. package/src/app/[variants]/MobileRouter.tsx +41 -0
  180. package/src/app/[variants]/desktopRouter.config.tsx +364 -0
  181. package/src/app/[variants]/layout.tsx +1 -3
  182. package/src/app/[variants]/loaders/routeParams.ts +45 -0
  183. package/src/app/[variants]/loading/Server/Redirect.tsx +1 -1
  184. package/src/app/[variants]/mobileRouter.config.tsx +396 -0
  185. package/src/app/[variants]/page.tsx +17 -6
  186. package/src/app/desktop/devtools/page.tsx +1 -1
  187. package/src/app/desktop/layout.tsx +1 -1
  188. package/src/components/BootErrorBoundary/index.tsx +129 -0
  189. package/src/components/mdx/constants.ts +1 -0
  190. package/src/features/ChangelogModal/index.tsx +3 -3
  191. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +23 -4
  192. package/src/features/KnowledgeManager/FileExplorer/index.tsx +4 -4
  193. package/src/features/KnowledgeManager/Header/FilesSearchBar.tsx +1 -1
  194. package/src/features/User/UserPanel/PanelContent.tsx +4 -3
  195. package/src/features/User/UserPanel/useMenu.tsx +20 -14
  196. package/src/features/User/__tests__/PanelContent.test.tsx +13 -7
  197. package/src/hooks/useDiscoverTab.ts +2 -2
  198. package/src/hooks/useInterceptingRoutes.test.ts +23 -26
  199. package/src/hooks/useInterceptingRoutes.ts +5 -4
  200. package/src/hooks/usePinnedAgentState.ts +6 -6
  201. package/src/hooks/useQuery.ts +5 -0
  202. package/src/hooks/useQueryParam.ts +322 -0
  203. package/src/hooks/useQueryRoute.test.ts +2 -12
  204. package/src/hooks/useQueryRoute.ts +5 -5
  205. package/src/hooks/useShowMobileWorkspace.ts +1 -1
  206. package/src/hooks/useSwitchSession.ts +4 -3
  207. package/src/layout/GlobalProvider/ImportSettings.tsx +22 -9
  208. package/src/layout/GlobalProvider/StoreInitialization.tsx +9 -1
  209. package/src/locales/default/tool.ts +11 -0
  210. package/src/proxy.ts +13 -1
  211. package/src/services/message/index.ts +11 -2
  212. package/src/store/chat/agents/createAgentExecutors.ts +31 -16
  213. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +218 -0
  214. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +4 -0
  215. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +9 -3
  216. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +142 -61
  217. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +5 -6
  218. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +204 -10
  219. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +45 -182
  220. package/src/store/chat/slices/builtinTool/actions/search.ts +51 -26
  221. package/src/store/chat/slices/message/action.test.ts +182 -33
  222. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +79 -36
  223. package/src/store/chat/slices/message/actions/query.ts +7 -5
  224. package/src/store/chat/slices/message/selectors/dbMessage.ts +11 -4
  225. package/src/store/chat/slices/plugin/action.test.ts +257 -54
  226. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +63 -26
  227. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +52 -19
  228. package/src/store/chat/slices/plugin/actions/publicApi.ts +6 -1
  229. package/src/store/chat/slices/plugin/actions/workflow.ts +17 -6
  230. package/src/store/chat/slices/thread/action.ts +2 -0
  231. package/src/store/global/action.test.ts +3 -3
  232. package/src/store/global/actions/workspacePane.ts +2 -1
  233. package/src/store/global/initialState.ts +10 -2
  234. package/src/store/user/slices/common/action.ts +4 -0
  235. package/src/tools/executionRuntimes.ts +3 -0
  236. package/src/tools/local-system/ExecutionRuntime/index.ts +407 -0
  237. package/src/tools/local-system/Intervention/EditLocalFile/index.tsx +89 -0
  238. package/src/tools/local-system/Intervention/WriteFile/index.tsx +72 -0
  239. package/src/tools/local-system/Intervention/index.ts +4 -0
  240. package/src/tools/local-system/Render/EditLocalFile/index.tsx +67 -0
  241. package/src/tools/local-system/Render/ReadLocalFile/ReadFileView.tsx +53 -78
  242. package/src/tools/local-system/Render/index.ts +2 -0
  243. package/src/tools/local-system/index.ts +1 -0
  244. package/src/tools/local-system/type.ts +4 -3
  245. package/src/app/[variants]/(main)/(mobile)/me/(home)/loading.tsx +0 -38
  246. package/src/app/[variants]/(main)/(mobile)/me/(home)/page.tsx +0 -40
  247. package/src/app/[variants]/(main)/(mobile)/me/profile/loading.tsx +0 -5
  248. package/src/app/[variants]/(main)/(mobile)/me/profile/page.tsx +0 -30
  249. package/src/app/[variants]/(main)/(mobile)/me/settings/loading.tsx +0 -5
  250. package/src/app/[variants]/(main)/(mobile)/me/settings/page.tsx +0 -30
  251. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +0 -106
  252. package/src/app/[variants]/(main)/changelog/layout.tsx +0 -10
  253. package/src/app/[variants]/(main)/changelog/modal/page.tsx +0 -23
  254. package/src/app/[variants]/(main)/changelog/page.tsx +0 -78
  255. package/src/app/[variants]/(main)/chat/ChatRouter.tsx +0 -83
  256. package/src/app/[variants]/(main)/chat/_layout/ChatLayout.tsx +0 -22
  257. package/src/app/[variants]/(main)/chat/components/MainChatPage.tsx +0 -25
  258. package/src/app/[variants]/(main)/chat/error.tsx +0 -3
  259. package/src/app/[variants]/(main)/chat/layout.tsx +0 -10
  260. package/src/app/[variants]/(main)/chat/loading.tsx +0 -3
  261. package/src/app/[variants]/(main)/chat/not-found.tsx +0 -1
  262. package/src/app/[variants]/(main)/chat/page.tsx +0 -12
  263. package/src/app/[variants]/(main)/chat/settings/error.tsx +0 -3
  264. package/src/app/[variants]/(main)/chat/settings/loading.tsx +0 -3
  265. package/src/app/[variants]/(main)/chat/settings/not-found.tsx +0 -1
  266. package/src/app/[variants]/(main)/discover/(detail)/_layout/DetailLayout.tsx +0 -22
  267. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/Client.tsx +0 -51
  268. package/src/app/[variants]/(main)/discover/(detail)/mcp/[slug]/Client.tsx +0 -43
  269. package/src/app/[variants]/(main)/discover/(detail)/mcp/[slug]/loading.tsx +0 -1
  270. package/src/app/[variants]/(main)/discover/(detail)/model/[...slugs]/Client.tsx +0 -40
  271. package/src/app/[variants]/(main)/discover/(detail)/model/[...slugs]/loading.tsx +0 -1
  272. package/src/app/[variants]/(main)/discover/(detail)/provider/[...slugs]/Client.tsx +0 -40
  273. package/src/app/[variants]/(main)/discover/(detail)/provider/[...slugs]/loading.tsx +0 -1
  274. package/src/app/[variants]/(main)/discover/(list)/_layout/ListLayout.tsx +0 -22
  275. package/src/app/[variants]/(main)/discover/(list)/assistant/AssistantLayout.tsx +0 -21
  276. package/src/app/[variants]/(main)/discover/(list)/mcp/Client.tsx +0 -44
  277. package/src/app/[variants]/(main)/discover/(list)/mcp/McpLayout.tsx +0 -21
  278. package/src/app/[variants]/(main)/discover/(list)/model/Client.tsx +0 -44
  279. package/src/app/[variants]/(main)/discover/(list)/model/ModelLayout.tsx +0 -21
  280. package/src/app/[variants]/(main)/discover/(list)/provider/ProviderPage.tsx +0 -43
  281. package/src/app/[variants]/(main)/discover/DiscoverRouter.tsx +0 -170
  282. package/src/app/[variants]/(main)/discover/[[...path]]/page.tsx +0 -12
  283. package/src/app/[variants]/(main)/discover/_layout/DiscoverLayout.tsx +0 -22
  284. package/src/app/[variants]/(main)/discover/error.tsx +0 -3
  285. package/src/app/[variants]/(main)/discover/not-found.tsx +0 -1
  286. package/src/app/[variants]/(main)/error.tsx +0 -3
  287. package/src/app/[variants]/(main)/image/layout.tsx +0 -15
  288. package/src/app/[variants]/(main)/image/page.tsx +0 -45
  289. package/src/app/[variants]/(main)/knowledge/KnowledgeRouter.tsx +0 -74
  290. package/src/app/[variants]/(main)/knowledge/[[...path]]/page.tsx +0 -12
  291. package/src/app/[variants]/(main)/knowledge/components/modal/page.tsx +0 -13
  292. package/src/app/[variants]/(main)/knowledge/layout.tsx +0 -12
  293. package/src/app/[variants]/(main)/layout.tsx +0 -10
  294. package/src/app/[variants]/(main)/not-found.tsx +0 -1
  295. package/src/app/[variants]/(main)/profile/apikey/page.tsx +0 -32
  296. package/src/app/[variants]/(main)/profile/error.tsx +0 -3
  297. package/src/app/[variants]/(main)/profile/layout.tsx +0 -11
  298. package/src/app/[variants]/(main)/profile/loading.tsx +0 -3
  299. package/src/app/[variants]/(main)/profile/not-found.tsx +0 -1
  300. package/src/app/[variants]/(main)/profile/security/page.tsx +0 -28
  301. package/src/app/[variants]/(main)/profile/stats/page.tsx +0 -23
  302. package/src/app/[variants]/(main)/profile/usage/page.tsx +0 -23
  303. package/src/app/[variants]/@modal/(.)changelog/modal/features/Cover.tsx +0 -48
  304. package/src/app/[variants]/@modal/(.)changelog/modal/features/Hero.tsx +0 -29
  305. package/src/app/[variants]/@modal/(.)changelog/modal/features/Post.tsx +0 -57
  306. package/src/app/[variants]/@modal/(.)changelog/modal/features/PublishedTime.tsx +0 -50
  307. package/src/app/[variants]/@modal/(.)changelog/modal/features/ReadDetail.tsx +0 -72
  308. package/src/app/[variants]/@modal/(.)changelog/modal/features/VersionTag.tsx +0 -26
  309. package/src/app/[variants]/@modal/(.)changelog/modal/layout.tsx +0 -41
  310. package/src/app/[variants]/@modal/(.)changelog/modal/loading.tsx +0 -10
  311. package/src/app/[variants]/@modal/(.)changelog/modal/page.tsx +0 -38
  312. package/src/app/[variants]/@modal/_layout/ModalLayout.tsx +0 -63
  313. package/src/app/[variants]/@modal/_layout/SettingModalLayout.tsx +0 -71
  314. package/src/app/[variants]/@modal/default.tsx +0 -3
  315. package/src/app/[variants]/@modal/error.tsx +0 -3
  316. package/src/app/[variants]/@modal/layout.tsx +0 -7
  317. package/src/app/[variants]/@modal/loading.tsx +0 -5
  318. /package/src/app/[variants]/{@modal/(.)changelog/modal → (main)/changelog}/features/Pagination.tsx +0 -0
  319. /package/src/app/[variants]/{@modal/(.)changelog/modal → (main)/changelog}/features/UpdateChangelogStatus.tsx +0 -0
  320. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/DetailProvider.tsx +0 -0
  321. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/Knowledge.tsx +0 -0
  322. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/KnowledgeItem.tsx +0 -0
  323. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/PluginItem.tsx +0 -0
  324. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/Plugins.tsx +0 -0
  325. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Capabilities/index.tsx +0 -0
  326. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Nav.tsx +0 -0
  327. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/Overview/TagList.tsx +0 -0
  328. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Details/SystemRole/TagList.tsx +0 -0
  329. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/Related/Item.tsx +0 -0
  330. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/Summary/index.tsx +0 -0
  331. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/features → features}/Sidebar/index.tsx +0 -0
  332. /package/src/app/[variants]/(main)/discover/(detail)/assistant/{[...slugs]/loading.tsx → loading.tsx} +0 -0
  333. /package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/ActionButton/index.tsx +0 -0
  334. /package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/ConnectionTypeAlert.tsx +0 -0
  335. /package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/Related/Item.tsx +0 -0
  336. /package/src/app/[variants]/(main)/discover/(detail)/mcp/{[slug]/features → features}/Sidebar/index.tsx +0 -0
  337. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/DetailProvider.tsx +0 -0
  338. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Nav.tsx +0 -0
  339. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Overview/ProviderList/index.tsx +0 -0
  340. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Details/Parameter/index.tsx +0 -0
  341. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/Related/Item.tsx +0 -0
  342. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/RelatedProviders/Item.tsx +0 -0
  343. /package/src/app/[variants]/(main)/discover/(detail)/model/{[...slugs]/features → features}/Sidebar/index.tsx +0 -0
  344. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/DetailProvider.tsx +0 -0
  345. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Guide/index.tsx +0 -0
  346. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Nav.tsx +0 -0
  347. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Overview/ModelList/index.tsx +0 -0
  348. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Details/Overview/index.tsx +0 -0
  349. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Header.tsx +0 -0
  350. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/Related/Item.tsx +0 -0
  351. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/RelatedModels/Item.tsx +0 -0
  352. /package/src/app/[variants]/(main)/discover/(detail)/provider/{[...slugs]/features → features}/Sidebar/index.tsx +0 -0
  353. /package/src/app/[variants]/(main)/labs/{page.tsx → index.tsx} +0 -0
  354. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/RegisterHotkeys.tsx +0 -0
  355. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/Avatar.test.tsx +0 -0
  356. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/Avatar.tsx +0 -0
  357. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/PinList/index.tsx +0 -0
  358. /package/src/app/[variants]/(main)/{_layout/Desktop → layouts/desktop}/SideBar/index.tsx +0 -0
@@ -50,18 +50,17 @@ describe('localFileSlice', () => {
50
50
 
51
51
  describe('internal_triggerLocalFileToolCalling', () => {
52
52
  it('should handle successful calling', async () => {
53
- const mockContent = { foo: 'bar' };
53
+ const mockContent = 'result content';
54
54
  const mockState = { state: 'test' };
55
- const mockService = vi.fn().mockResolvedValue({ content: mockContent, state: mockState });
55
+ const mockService = vi
56
+ .fn()
57
+ .mockResolvedValue({ content: mockContent, state: mockState, success: true });
56
58
 
57
59
  await store.internal_triggerLocalFileToolCalling('test-id', mockService);
58
60
 
59
61
  expect(mockStore.toggleLocalFileLoading).toBeCalledWith('test-id', true);
60
62
  expect(mockStore.optimisticUpdatePluginState).toBeCalledWith('test-id', mockState);
61
- expect(mockStore.optimisticUpdateMessageContent).toBeCalledWith(
62
- 'test-id',
63
- JSON.stringify(mockContent),
64
- );
63
+ expect(mockStore.optimisticUpdateMessageContent).toBeCalledWith('test-id', mockContent);
65
64
  expect(mockStore.toggleLocalFileLoading).toBeCalledWith('test-id', false);
66
65
  });
67
66
 
@@ -6,7 +6,7 @@ import { Mock, beforeEach, describe, expect, it, vi } from 'vitest';
6
6
 
7
7
  import { searchService } from '@/services/search';
8
8
  import { useChatStore } from '@/store/chat';
9
- import { chatSelectors } from '@/store/chat/selectors';
9
+ import { dbMessageSelectors } from '@/store/chat/selectors';
10
10
  import { CRAWL_CONTENT_LIMITED_COUNT } from '@/tools/web-browsing/const';
11
11
 
12
12
  // Mock services
@@ -18,8 +18,8 @@ vi.mock('@/services/search', () => ({
18
18
  }));
19
19
 
20
20
  vi.mock('@/store/chat/selectors', () => ({
21
- chatSelectors: {
22
- getMessageById: vi.fn(),
21
+ dbMessageSelectors: {
22
+ getDbMessageById: vi.fn(),
23
23
  },
24
24
  }));
25
25
 
@@ -38,6 +38,9 @@ describe('search actions', () => {
38
38
  optimisticAddToolToAssistantMessage: vi.fn(),
39
39
  openToolUI: vi.fn(),
40
40
  });
41
+
42
+ // Default mock for dbMessageSelectors - returns undefined to use activeId/activeTopicId
43
+ vi.spyOn(dbMessageSelectors, 'getDbMessageById').mockImplementation(() => () => undefined);
41
44
  });
42
45
 
43
46
  describe('search', () => {
@@ -90,6 +93,8 @@ describe('search actions', () => {
90
93
  expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
91
94
  messageId,
92
95
  searchResultsPrompt(expectedContent),
96
+ undefined,
97
+ { sessionId: undefined, topicId: undefined },
93
98
  );
94
99
  });
95
100
 
@@ -126,6 +131,8 @@ describe('search actions', () => {
126
131
  expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
127
132
  messageId,
128
133
  searchResultsPrompt([]),
134
+ undefined,
135
+ { sessionId: undefined, topicId: undefined },
129
136
  );
130
137
  });
131
138
 
@@ -145,15 +152,21 @@ describe('search actions', () => {
145
152
  await search(messageId, query);
146
153
  });
147
154
 
148
- expect(result.current.optimisticUpdateMessagePluginError).toHaveBeenCalledWith(messageId, {
149
- body: error,
150
- message: 'Search failed',
151
- type: 'PluginServerError',
152
- });
155
+ expect(result.current.optimisticUpdateMessagePluginError).toHaveBeenCalledWith(
156
+ messageId,
157
+ {
158
+ body: error,
159
+ message: 'Search failed',
160
+ type: 'PluginServerError',
161
+ },
162
+ { sessionId: undefined, topicId: undefined },
163
+ );
153
164
  expect(result.current.searchLoading[messageId]).toBe(false);
154
165
  expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
155
166
  messageId,
156
167
  'Search failed',
168
+ undefined,
169
+ { sessionId: undefined, topicId: undefined },
157
170
  );
158
171
  });
159
172
  });
@@ -193,6 +206,8 @@ describe('search actions', () => {
193
206
  expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
194
207
  messageId,
195
208
  crawlResultsPrompt(expectedContent as any),
209
+ undefined,
210
+ { sessionId: undefined, topicId: undefined },
196
211
  );
197
212
  });
198
213
 
@@ -219,6 +234,8 @@ describe('search actions', () => {
219
234
  expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
220
235
  messageId,
221
236
  crawlResultsPrompt(mockResponse.results),
237
+ undefined,
238
+ { sessionId: undefined, topicId: undefined },
222
239
  );
223
240
  });
224
241
  });
@@ -250,6 +267,8 @@ describe('search actions', () => {
250
267
  const mockMessage: Partial<UIChatMessage> = {
251
268
  id: messageId,
252
269
  parentId,
270
+ sessionId: undefined,
271
+ topicId: undefined,
253
272
  content: 'test content',
254
273
  plugin: {
255
274
  identifier: 'search',
@@ -264,7 +283,7 @@ describe('search actions', () => {
264
283
  meta: {},
265
284
  };
266
285
 
267
- vi.spyOn(chatSelectors, 'getMessageById').mockImplementation(
286
+ vi.spyOn(dbMessageSelectors, 'getDbMessageById').mockImplementation(
268
287
  () => () => mockMessage as UIChatMessage,
269
288
  );
270
289
 
@@ -282,7 +301,10 @@ describe('search actions', () => {
282
301
  plugin: mockMessage.plugin,
283
302
  pluginState: mockMessage.pluginState,
284
303
  role: 'tool',
304
+ sessionId: 'session-id',
305
+ topicId: 'topic-id',
285
306
  }),
307
+ { sessionId: 'session-id', topicId: 'topic-id' },
286
308
  );
287
309
 
288
310
  expect(result.current.optimisticAddToolToAssistantMessage).toHaveBeenCalledWith(
@@ -291,11 +313,12 @@ describe('search actions', () => {
291
313
  identifier: 'search',
292
314
  type: 'default',
293
315
  }),
316
+ { sessionId: undefined, topicId: undefined },
294
317
  );
295
318
  });
296
319
 
297
320
  it('should not save if message not found', async () => {
298
- vi.spyOn(chatSelectors, 'getMessageById').mockImplementation(() => () => undefined);
321
+ vi.spyOn(dbMessageSelectors, 'getDbMessageById').mockImplementation(() => () => undefined);
299
322
 
300
323
  const { result } = renderHook(() => useChatStore());
301
324
  const { saveSearchResult } = result.current;
@@ -327,4 +350,175 @@ describe('search actions', () => {
327
350
  expect(result.current.searchLoading[messageId]).toBe(false);
328
351
  });
329
352
  });
353
+
354
+ describe('OptimisticUpdateContext isolation', () => {
355
+ it('search should pass context to optimistic methods', async () => {
356
+ const mockResponse: UniformSearchResponse = {
357
+ results: [
358
+ {
359
+ title: 'Test',
360
+ content: 'Content',
361
+ url: 'https://test.com',
362
+ category: 'general',
363
+ engines: ['google'],
364
+ parsedUrl: 'test.com',
365
+ score: 1,
366
+ },
367
+ ],
368
+ costTime: 1,
369
+ resultNumbers: 1,
370
+ query: 'test',
371
+ };
372
+
373
+ (searchService.webSearch as Mock).mockResolvedValue(mockResponse);
374
+
375
+ const messageId = 'test-message-id';
376
+ const contextSessionId = 'context-session-id';
377
+ const contextTopicId = 'context-topic-id';
378
+
379
+ const mockMessage: Partial<UIChatMessage> = {
380
+ id: messageId,
381
+ sessionId: contextSessionId,
382
+ topicId: contextTopicId,
383
+ role: 'tool',
384
+ content: '',
385
+ createdAt: Date.now(),
386
+ updatedAt: Date.now(),
387
+ meta: {},
388
+ };
389
+
390
+ vi.spyOn(dbMessageSelectors, 'getDbMessageById').mockImplementation(
391
+ () => () => mockMessage as UIChatMessage,
392
+ );
393
+
394
+ const { result } = renderHook(() => useChatStore());
395
+ const query: SearchQuery = { query: 'test' };
396
+
397
+ await act(async () => {
398
+ await result.current.search(messageId, query);
399
+ });
400
+
401
+ expect(result.current.optimisticUpdatePluginState).toHaveBeenCalledWith(
402
+ messageId,
403
+ expect.any(Object),
404
+ { sessionId: contextSessionId, topicId: contextTopicId },
405
+ );
406
+ expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
407
+ messageId,
408
+ expect.any(String),
409
+ undefined,
410
+ { sessionId: contextSessionId, topicId: contextTopicId },
411
+ );
412
+ });
413
+
414
+ it('crawlMultiPages should pass context to optimistic methods', async () => {
415
+ const mockResponse = {
416
+ results: [
417
+ {
418
+ data: {
419
+ content: 'Test content',
420
+ title: 'Test',
421
+ },
422
+ crawler: 'naive',
423
+ originalUrl: 'https://test.com',
424
+ },
425
+ ],
426
+ };
427
+
428
+ (searchService.crawlPages as Mock).mockResolvedValue(mockResponse);
429
+
430
+ const messageId = 'test-message-id';
431
+ const contextSessionId = 'context-session-id';
432
+ const contextTopicId = 'context-topic-id';
433
+
434
+ const mockMessage: Partial<UIChatMessage> = {
435
+ id: messageId,
436
+ sessionId: contextSessionId,
437
+ topicId: contextTopicId,
438
+ role: 'tool',
439
+ content: '',
440
+ createdAt: Date.now(),
441
+ updatedAt: Date.now(),
442
+ meta: {},
443
+ };
444
+
445
+ vi.spyOn(dbMessageSelectors, 'getDbMessageById').mockImplementation(
446
+ () => () => mockMessage as UIChatMessage,
447
+ );
448
+
449
+ const { result } = renderHook(() => useChatStore());
450
+
451
+ await act(async () => {
452
+ await result.current.crawlMultiPages(messageId, { urls: ['https://test.com'] });
453
+ });
454
+
455
+ expect(result.current.optimisticUpdateMessageContent).toHaveBeenCalledWith(
456
+ messageId,
457
+ expect.any(String),
458
+ undefined,
459
+ { sessionId: contextSessionId, topicId: contextTopicId },
460
+ );
461
+ expect(result.current.optimisticUpdatePluginState).toHaveBeenCalledWith(
462
+ messageId,
463
+ expect.any(Object),
464
+ { sessionId: contextSessionId, topicId: contextTopicId },
465
+ );
466
+ });
467
+
468
+ it('saveSearchResult should pass context to optimistic methods', async () => {
469
+ const messageId = 'test-message-id';
470
+ const parentId = 'parent-message-id';
471
+ const contextSessionId = 'context-session-id';
472
+ const contextTopicId = 'context-topic-id';
473
+
474
+ const mockMessage: Partial<UIChatMessage> = {
475
+ id: messageId,
476
+ parentId,
477
+ sessionId: contextSessionId,
478
+ topicId: contextTopicId,
479
+ content: 'test content',
480
+ plugin: {
481
+ identifier: 'search',
482
+ arguments: '{}',
483
+ apiName: 'search',
484
+ type: 'default',
485
+ },
486
+ pluginState: {},
487
+ role: 'tool',
488
+ createdAt: Date.now(),
489
+ updatedAt: Date.now(),
490
+ meta: {},
491
+ };
492
+
493
+ vi.spyOn(dbMessageSelectors, 'getDbMessageById').mockImplementation(
494
+ () => () => mockMessage as UIChatMessage,
495
+ );
496
+
497
+ (useChatStore.getState().optimisticCreateMessage as Mock).mockResolvedValue({
498
+ id: 'new-message-id',
499
+ });
500
+
501
+ const { result } = renderHook(() => useChatStore());
502
+
503
+ await act(async () => {
504
+ await result.current.saveSearchResult(messageId);
505
+ });
506
+
507
+ expect(result.current.optimisticAddToolToAssistantMessage).toHaveBeenCalledWith(
508
+ parentId,
509
+ expect.objectContaining({
510
+ identifier: 'search',
511
+ type: 'default',
512
+ }),
513
+ { sessionId: contextSessionId, topicId: contextTopicId },
514
+ );
515
+ expect(result.current.optimisticCreateMessage).toHaveBeenCalledWith(
516
+ expect.objectContaining({
517
+ sessionId: contextSessionId,
518
+ topicId: contextTopicId,
519
+ }),
520
+ { sessionId: contextSessionId, topicId: contextTopicId },
521
+ );
522
+ });
523
+ });
330
524
  });
@@ -5,7 +5,6 @@ import {
5
5
  GrepContentParams,
6
6
  KillCommandParams,
7
7
  ListLocalFileParams,
8
- LocalMoveFilesResultItem,
9
8
  LocalReadFileParams,
10
9
  LocalReadFilesParams,
11
10
  LocalSearchFilesParams,
@@ -16,28 +15,14 @@ import {
16
15
  } from '@lobechat/electron-client-ipc';
17
16
  import { StateCreator } from 'zustand/vanilla';
18
17
 
19
- import { localFileService } from '@/services/electron/localFileService';
20
18
  import { ChatStore } from '@/store/chat/store';
21
- import {
22
- EditLocalFileState,
23
- GetCommandOutputState,
24
- GlobFilesState,
25
- GrepContentState,
26
- KillCommandState,
27
- LocalFileListState,
28
- LocalFileSearchState,
29
- LocalMoveFilesState,
30
- LocalReadFileState,
31
- LocalReadFilesState,
32
- LocalRenameFileState,
33
- RunCommandState,
34
- } from '@/tools/local-system/type';
19
+ import { LocalSystemExecutionRuntime } from '@/tools/local-system/ExecutionRuntime';
35
20
 
36
21
  /* eslint-disable typescript-sort-keys/interface */
37
22
  export interface LocalFileAction {
38
- internal_triggerLocalFileToolCalling: <T = any>(
23
+ internal_triggerLocalFileToolCalling: (
39
24
  id: string,
40
- callingService: () => Promise<{ content: any; state?: T }>,
25
+ callingService: () => Promise<{ content: string; error?: any; state?: any; success: boolean }>,
41
26
  ) => Promise<boolean>;
42
27
 
43
28
  // File Operations
@@ -63,6 +48,8 @@ export interface LocalFileAction {
63
48
  }
64
49
  /* eslint-enable typescript-sort-keys/interface */
65
50
 
51
+ const runtime = new LocalSystemExecutionRuntime();
52
+
66
53
  /* eslint-disable sort-keys-fix/sort-keys-fix */
67
54
  export const localSystemSlice: StateCreator<
68
55
  ChatStore,
@@ -72,148 +59,49 @@ export const localSystemSlice: StateCreator<
72
59
  > = (set, get) => ({
73
60
  // ==================== File Editing ====================
74
61
  editLocalFile: async (id, params) => {
75
- return get().internal_triggerLocalFileToolCalling<EditLocalFileState>(id, async () => {
76
- const result = await localFileService.editLocalFile(params);
77
-
78
- const message = result.success
79
- ? `Successfully replaced ${result.replacements} occurrence(s) in ${params.file_path}`
80
- : `Edit failed: ${result.error}`;
81
-
82
- const state: EditLocalFileState = { message, result };
83
-
84
- return { content: result, state };
62
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
63
+ return await runtime.editLocalFile(params);
85
64
  });
86
65
  },
87
66
 
88
67
  writeLocalFile: async (id, params) => {
89
68
  return get().internal_triggerLocalFileToolCalling(id, async () => {
90
- const result = await localFileService.writeFile(params);
91
-
92
- let content: { message: string; success: boolean };
93
-
94
- if (result.success) {
95
- content = {
96
- message: `成功写入文件 ${params.path}`,
97
- success: true,
98
- };
99
- } else {
100
- const errorMessage = result.error;
101
-
102
- content = { message: errorMessage || '写入文件失败', success: false };
103
- }
104
- return { content };
69
+ return await runtime.writeLocalFile(params);
105
70
  });
106
71
  },
107
72
  moveLocalFiles: async (id, params) => {
108
- return get().internal_triggerLocalFileToolCalling<LocalMoveFilesState>(id, async () => {
109
- const results: LocalMoveFilesResultItem[] = await localFileService.moveLocalFiles(params);
110
-
111
- // 检查所有文件是否成功移动以更新消息内容
112
- const allSucceeded = results.every((r) => r.success);
113
- const someFailed = results.some((r) => !r.success);
114
- const successCount = results.filter((r) => r.success).length;
115
- const failedCount = results.length - successCount;
116
-
117
- let message = '';
118
-
119
- if (allSucceeded) {
120
- message = `Successfully moved ${results.length} item(s).`;
121
- } else if (someFailed) {
122
- message = `Moved ${successCount} item(s) successfully. Failed to move ${failedCount} item(s).`;
123
- } else {
124
- // 所有都失败了?
125
- message = `Failed to move all ${results.length} item(s).`;
126
- }
127
-
128
- const state: LocalMoveFilesState = { results, successCount, totalCount: results.length };
129
-
130
- return { content: { message, results }, state };
73
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
74
+ return await runtime.moveLocalFiles(params);
131
75
  });
132
76
  },
133
77
  renameLocalFile: async (id, params) => {
134
- return get().internal_triggerLocalFileToolCalling<LocalRenameFileState>(id, async () => {
135
- const { path: currentPath, newName } = params;
136
-
137
- // Basic validation for newName (can be done here or backend, maybe better in backend)
138
- if (
139
- !newName ||
140
- newName.includes('/') ||
141
- newName.includes('\\') ||
142
- newName === '.' ||
143
- newName === '..' ||
144
- /["*/:<>?\\|]/.test(newName)
145
- ) {
146
- throw new Error(
147
- 'Invalid new name provided. It cannot be empty, contain path separators, or invalid characters.',
148
- );
149
- }
150
-
151
- const result = await localFileService.renameLocalFile({ newName, path: currentPath }); // Call the specific service
152
-
153
- let state: LocalRenameFileState;
154
- let content: { message: string; success: boolean };
155
-
156
- if (result.success) {
157
- state = { newPath: result.newPath!, oldPath: currentPath, success: true };
158
- // Simplified message
159
- content = {
160
- message: `Successfully renamed file ${currentPath} to ${newName}.`,
161
- success: true,
162
- };
163
- } else {
164
- const errorMessage = result.error;
165
- state = {
166
- error: errorMessage,
167
- newPath: '',
168
- oldPath: params.path,
169
- success: false,
170
- };
171
- content = { message: errorMessage, success: false };
172
- }
173
- return { content, state };
78
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
79
+ return await runtime.renameLocalFile(params);
174
80
  });
175
81
  },
176
82
 
177
83
  // ==================== Search & Find ====================
178
84
  grepContent: async (id, params) => {
179
- return get().internal_triggerLocalFileToolCalling<GrepContentState>(id, async () => {
180
- const result = await localFileService.grepContent(params);
181
-
182
- const message = result.success
183
- ? `Found ${result.total_matches} matches in ${result.matches.length} locations`
184
- : 'Search failed';
185
-
186
- const state: GrepContentState = { message, result };
187
-
188
- return { content: result, state };
85
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
86
+ return await runtime.grepContent(params);
189
87
  });
190
88
  },
191
89
 
192
90
  globLocalFiles: async (id, params) => {
193
- return get().internal_triggerLocalFileToolCalling<GlobFilesState>(id, async () => {
194
- const result = await localFileService.globFiles(params);
195
-
196
- const message = result.success ? `Found ${result.total_files} files` : 'Glob search failed';
197
-
198
- const state: GlobFilesState = { message, result };
199
-
200
- return { content: result, state };
91
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
92
+ return await runtime.globLocalFiles(params);
201
93
  });
202
94
  },
203
95
 
204
96
  searchLocalFiles: async (id, params) => {
205
- return get().internal_triggerLocalFileToolCalling<LocalFileSearchState>(id, async () => {
206
- const result = await localFileService.searchLocalFiles(params);
207
- const state: LocalFileSearchState = { searchResults: result };
208
- return { content: result, state };
97
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
98
+ return await runtime.searchLocalFiles(params);
209
99
  });
210
100
  },
211
101
 
212
102
  listLocalFiles: async (id, params) => {
213
- return get().internal_triggerLocalFileToolCalling<LocalFileListState>(id, async () => {
214
- const result = await localFileService.listLocalFiles(params);
215
- const state: LocalFileListState = { listResults: result };
216
- return { content: result, state };
103
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
104
+ return await runtime.listLocalFiles(params);
217
105
  });
218
106
  },
219
107
 
@@ -226,67 +114,31 @@ export const localSystemSlice: StateCreator<
226
114
  },
227
115
 
228
116
  readLocalFile: async (id, params) => {
229
- return get().internal_triggerLocalFileToolCalling<LocalReadFileState>(id, async () => {
230
- const result = await localFileService.readLocalFile(params);
231
- const state: LocalReadFileState = { fileContent: result };
232
- return { content: result, state };
117
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
118
+ return await runtime.readLocalFile(params);
233
119
  });
234
120
  },
235
121
 
236
122
  readLocalFiles: async (id, params) => {
237
- return get().internal_triggerLocalFileToolCalling<LocalReadFilesState>(id, async () => {
238
- const results = await localFileService.readLocalFiles(params);
239
- const state: LocalReadFilesState = { filesContent: results };
240
- return { content: results, state };
123
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
124
+ return await runtime.readLocalFiles(params);
241
125
  });
242
126
  },
243
127
 
244
128
  // ==================== Shell Commands ====================
245
129
  runCommand: async (id, params) => {
246
- return get().internal_triggerLocalFileToolCalling<RunCommandState>(id, async () => {
247
- const result = await localFileService.runCommand(params);
248
-
249
- let message: string;
250
-
251
- if (result.success) {
252
- if (result.shell_id) {
253
- message = `Command started in background with shell_id: ${result.shell_id}`;
254
- } else {
255
- message = `Command completed successfully.`;
256
- }
257
- } else {
258
- message = `Command failed: ${result.error}`;
259
- }
260
-
261
- const state: RunCommandState = { message, result };
262
-
263
- return { content: result, state };
130
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
131
+ return await runtime.runCommand(params);
264
132
  });
265
133
  },
266
134
  killCommand: async (id, params) => {
267
- return get().internal_triggerLocalFileToolCalling<KillCommandState>(id, async () => {
268
- const result = await localFileService.killCommand(params);
269
-
270
- const message = result.success
271
- ? `Successfully killed shell: ${params.shell_id}`
272
- : `Failed to kill shell: ${result.error}`;
273
-
274
- const state: KillCommandState = { message, result };
275
-
276
- return { content: result, state };
135
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
136
+ return await runtime.killCommand(params);
277
137
  });
278
138
  },
279
139
  getCommandOutput: async (id, params) => {
280
- return get().internal_triggerLocalFileToolCalling<GetCommandOutputState>(id, async () => {
281
- const result = await localFileService.getCommandOutput(params);
282
-
283
- const message = result.success
284
- ? `Output retrieved. Running: ${result.running}`
285
- : `Failed: ${result.error}`;
286
-
287
- const state: GetCommandOutputState = { message, result };
288
-
289
- return { content: result, state };
140
+ return get().internal_triggerLocalFileToolCalling(id, async () => {
141
+ return await runtime.getCommandOutput(params);
290
142
  });
291
143
  },
292
144
 
@@ -305,11 +157,22 @@ export const localSystemSlice: StateCreator<
305
157
  internal_triggerLocalFileToolCalling: async (id, callingService) => {
306
158
  get().toggleLocalFileLoading(id, true);
307
159
  try {
308
- const { state, content } = await callingService();
309
- if (state) {
310
- await get().optimisticUpdatePluginState(id, state as any);
160
+ const { state, content, success, error } = await callingService();
161
+
162
+ if (success) {
163
+ if (state) {
164
+ await get().optimisticUpdatePluginState(id, state);
165
+ }
166
+ await get().optimisticUpdateMessageContent(id, content);
167
+ } else {
168
+ await get().optimisticUpdateMessagePluginError(id, {
169
+ body: error,
170
+ message: error?.message || 'Operation failed',
171
+ type: 'PluginServerError',
172
+ });
173
+ // Still update content even if failed, to show error message
174
+ await get().optimisticUpdateMessageContent(id, content);
311
175
  }
312
- await get().optimisticUpdateMessageContent(id, JSON.stringify(content));
313
176
  } catch (error) {
314
177
  await get().optimisticUpdateMessagePluginError(id, {
315
178
  body: error,