@lobehub/lobehub 2.0.0-next.294 → 2.0.0-next.296

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 (249) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/apps/desktop/src/main/__mocks__/node-mac-permissions.ts +0 -1
  3. package/apps/desktop/src/main/__mocks__/setup.ts +0 -1
  4. package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +1 -4
  5. package/apps/desktop/tsconfig.json +4 -10
  6. package/changelog/v1.json +18 -0
  7. package/e2e/scripts/setup.ts +45 -32
  8. package/locales/en-US/plugin.json +4 -0
  9. package/locales/zh-CN/plugin.json +4 -0
  10. package/package.json +1 -1
  11. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +5 -5
  12. package/packages/agent-runtime/src/utils/stepContextComputer.test.ts +5 -5
  13. package/packages/builtin-tool-gtd/src/client/Inspector/index.ts +0 -4
  14. package/packages/builtin-tool-gtd/src/client/Intervention/AddTodo.tsx +1 -1
  15. package/packages/builtin-tool-gtd/src/client/Render/TodoList/index.tsx +39 -10
  16. package/packages/builtin-tool-gtd/src/client/Render/index.ts +0 -2
  17. package/packages/builtin-tool-gtd/src/client/components/SortableTodoList/TodoItemRow.tsx +26 -12
  18. package/packages/builtin-tool-gtd/src/client/components/SortableTodoList/store/actions.ts +5 -5
  19. package/packages/builtin-tool-gtd/src/client/components/SortableTodoList/store/store.test.ts +14 -8
  20. package/packages/builtin-tool-gtd/src/executor/index.test.ts +48 -227
  21. package/packages/builtin-tool-gtd/src/executor/index.ts +15 -158
  22. package/packages/builtin-tool-gtd/src/manifest.ts +12 -42
  23. package/packages/builtin-tool-gtd/src/systemRole.ts +14 -8
  24. package/packages/builtin-tool-gtd/src/types.ts +47 -41
  25. package/packages/builtin-tool-memory/package.json +8 -0
  26. package/packages/builtin-tool-memory/src/client/Inspector/AddContextMemory/index.tsx +60 -0
  27. package/packages/builtin-tool-memory/src/client/Inspector/AddExperienceMemory/index.tsx +60 -0
  28. package/packages/builtin-tool-memory/src/client/Inspector/AddIdentityMemory/index.tsx +60 -0
  29. package/packages/builtin-tool-memory/src/client/Inspector/AddPreferenceMemory/index.tsx +60 -0
  30. package/packages/builtin-tool-memory/src/client/Inspector/RemoveIdentityMemory/index.tsx +60 -0
  31. package/packages/builtin-tool-memory/src/client/Inspector/SearchUserMemory/index.tsx +67 -0
  32. package/packages/builtin-tool-memory/src/client/Inspector/UpdateIdentityMemory/index.tsx +60 -0
  33. package/packages/builtin-tool-memory/src/client/Inspector/index.ts +35 -0
  34. package/packages/builtin-tool-memory/src/client/Intervention/AddExperienceMemory/index.tsx +17 -0
  35. package/packages/builtin-tool-memory/src/client/Intervention/index.ts +13 -0
  36. package/packages/builtin-tool-memory/src/client/Render/AddExperienceMemory/index.tsx +17 -0
  37. package/packages/builtin-tool-memory/src/client/Render/SearchUserMemory/index.tsx +217 -0
  38. package/packages/builtin-tool-memory/src/client/Render/index.ts +15 -0
  39. package/packages/builtin-tool-memory/src/client/Streaming/AddExperienceMemory/index.tsx +17 -0
  40. package/packages/builtin-tool-memory/src/client/Streaming/index.ts +18 -0
  41. package/packages/builtin-tool-memory/src/client/components/ExperienceMemoryCard.tsx +231 -0
  42. package/packages/builtin-tool-memory/src/client/components/index.ts +1 -0
  43. package/packages/builtin-tool-memory/src/client/index.ts +27 -0
  44. package/packages/builtin-tool-memory/src/executor/index.ts +9 -1
  45. package/packages/builtin-tool-memory/src/types.ts +61 -0
  46. package/packages/context-engine/src/providers/GTDTodoInjector.ts +15 -7
  47. package/packages/conversation-flow/src/__tests__/fixtures/outputs/assistantGroup/tools-with-branches.json +4 -0
  48. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +1 -0
  49. package/packages/database/src/models/__tests__/knowledgeBase.test.ts +1 -1
  50. package/packages/database/src/repositories/knowledge/index.ts +1 -4
  51. package/packages/prompts/src/prompts/gtd/index.test.ts +32 -16
  52. package/packages/prompts/src/prompts/gtd/index.ts +9 -5
  53. package/packages/types/src/discover/assistants.ts +2 -2
  54. package/packages/types/src/stepContext.ts +4 -1
  55. package/scripts/migrate-spa-navigation.ts +129 -0
  56. package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-topics/route.ts +112 -109
  57. package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-user-topics/route.ts +125 -113
  58. package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-users/route.ts +74 -65
  59. package/src/app/[variants]/(auth)/auth-error/page.tsx +1 -1
  60. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -1
  61. package/src/app/[variants]/(auth)/next-auth/error/AuthErrorPage.tsx +1 -1
  62. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +1 -1
  63. package/src/app/[variants]/(auth)/oauth/callback/error/page.tsx +1 -1
  64. package/src/app/[variants]/(auth)/oauth/callback/success/page.tsx +1 -1
  65. package/src/app/[variants]/(auth)/oauth/consent/[uid]/page.tsx +1 -1
  66. package/src/app/[variants]/(auth)/reset-password/layout.tsx +1 -1
  67. package/src/app/[variants]/(auth)/reset-password/page.tsx +2 -2
  68. package/src/app/[variants]/(auth)/signin/layout.tsx +1 -1
  69. package/src/app/[variants]/(auth)/signin/useSignIn.ts +1 -1
  70. package/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx +2 -2
  71. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -1
  72. package/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.tsx +1 -1
  73. package/src/app/[variants]/(auth)/verify-email/layout.tsx +1 -1
  74. package/src/app/[variants]/(auth)/verify-email/page.tsx +2 -2
  75. package/src/app/[variants]/(main)/_layout/index.tsx +1 -1
  76. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/CronTopicGroup.tsx +1 -1
  77. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/AddTopicButon.tsx +1 -1
  78. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx +1 -1
  79. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/AllTopicsDrawer/index.tsx +1 -1
  80. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts +1 -1
  81. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts +1 -1
  82. package/src/app/[variants]/(main)/agent/features/TelemetryNotification.tsx +2 -3
  83. package/src/app/[variants]/(main)/community/(detail)/assistant/features/Details/Nav.tsx +9 -9
  84. package/src/app/[variants]/(main)/community/(detail)/assistant/features/Details/Versions/index.tsx +2 -3
  85. package/src/app/[variants]/(main)/community/(detail)/features/MakedownRender.tsx +1 -2
  86. package/src/app/[variants]/(main)/community/(detail)/features/ShareButton.tsx +2 -3
  87. package/src/app/[variants]/(main)/community/(detail)/features/Toc/Heading.tsx +2 -3
  88. package/src/app/[variants]/(main)/community/(detail)/mcp/features/Details/Versions/index.tsx +2 -2
  89. package/src/app/[variants]/(main)/community/(detail)/model/features/Details/Nav.tsx +12 -11
  90. package/src/app/[variants]/(main)/community/(detail)/model/features/Details/Parameter/ParameterItem.tsx +2 -3
  91. package/src/app/[variants]/(main)/community/(detail)/provider/features/Details/Nav.tsx +11 -10
  92. package/src/app/[variants]/(main)/community/(detail)/provider/features/Header.tsx +10 -9
  93. package/src/app/[variants]/(main)/community/(list)/(home)/index.tsx +1 -1
  94. package/src/app/[variants]/(main)/community/(list)/assistant/features/Category/useCategory.tsx +1 -1
  95. package/src/app/[variants]/(main)/community/(list)/features/SortButton/index.tsx +2 -3
  96. package/src/app/[variants]/(main)/community/_layout/Sidebar/Header/Nav.tsx +1 -1
  97. package/src/app/[variants]/(main)/community/features/CreateButton/Inner.tsx +1 -1
  98. package/src/app/[variants]/(main)/community/features/CreateButton/index.tsx +1 -1
  99. package/src/app/[variants]/(main)/community/features/Search.tsx +1 -2
  100. package/src/app/[variants]/(main)/community/features/Title.tsx +5 -5
  101. package/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx +1 -1
  102. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/AllTopicsDrawer/index.tsx +1 -1
  103. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts +1 -1
  104. package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +1 -1
  105. package/src/app/[variants]/(main)/group/features/TelemetryNotification.tsx +2 -3
  106. package/src/app/[variants]/(main)/home/_layout/Body/Agent/AllAgentsDrawer/index.tsx +1 -1
  107. package/src/app/[variants]/(main)/hooks/useActiveTabKey.ts +6 -11
  108. package/src/app/[variants]/(main)/image/NotSupportClient.tsx +4 -3
  109. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ImageUpload.tsx +1 -1
  110. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/MultiImagesUpload/ImageManageModal.tsx +1 -1
  111. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/MultiImagesUpload/index.tsx +1 -1
  112. package/src/app/[variants]/(main)/memory/(home)/features/RoleTagCloud/index.tsx +1 -1
  113. package/src/app/[variants]/(main)/memory/_layout/Sidebar/Header/Nav.tsx +1 -1
  114. package/src/app/[variants]/(main)/memory/features/SourceLink.tsx +1 -1
  115. package/src/app/[variants]/(main)/page/_layout/Body/AllPagesDrawer/index.tsx +1 -1
  116. package/src/app/[variants]/(main)/resource/features/DndContextWrapper.tsx +4 -2
  117. package/src/app/[variants]/(main)/resource/library/_layout/Header/LibraryHead.tsx +30 -35
  118. package/src/app/[variants]/(main)/resource/library/_layout/Header/index.tsx +9 -11
  119. package/src/app/[variants]/(main)/settings/about/features/ItemCard.tsx +2 -3
  120. package/src/app/[variants]/(main)/settings/about/features/ItemLink.tsx +2 -3
  121. package/src/app/[variants]/(main)/settings/about/features/Version.tsx +6 -7
  122. package/src/app/[variants]/(main)/settings/features/SettingsContent.tsx +1 -1
  123. package/src/app/[variants]/(main)/settings/features/UpgradeAlert.tsx +4 -4
  124. package/src/app/[variants]/(main)/settings/provider/(list)/Footer.tsx +2 -2
  125. package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +1 -1
  126. package/src/app/[variants]/(main)/settings/provider/detail/ollama/CheckError.tsx +1 -1
  127. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +12 -6
  128. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -1
  129. package/src/app/[variants]/(main)/settings/stats/features/overview/ShareButton/ShareModal.tsx +1 -1
  130. package/src/app/[variants]/(main)/settings/stats/features/rankings/AssistantsRank.tsx +1 -1
  131. package/src/app/[variants]/(main)/settings/stats/features/rankings/TopicsRank.tsx +1 -1
  132. package/src/app/[variants]/(mobile)/_layout/index.tsx +1 -1
  133. package/src/app/[variants]/(mobile)/chat/settings/features/AgentInfoDescription/index.tsx +1 -1
  134. package/src/app/[variants]/(mobile)/chat/settings/features/SettingButton.tsx +1 -1
  135. package/src/app/[variants]/(mobile)/router/index.tsx +1 -1
  136. package/src/app/[variants]/page.tsx +1 -1
  137. package/src/app/[variants]/router/index.tsx +1 -1
  138. package/src/components/404/index.tsx +4 -4
  139. package/src/components/Analytics/index.tsx +1 -1
  140. package/src/components/BrandWatermark/index.tsx +4 -4
  141. package/src/components/Branding/ProductLogo/Custom.tsx +1 -1
  142. package/src/components/Error/index.tsx +3 -4
  143. package/src/components/GoBack/index.tsx +2 -2
  144. package/src/components/LabsModal/LabCard.tsx +1 -1
  145. package/src/components/Link.tsx +25 -5
  146. package/src/components/OllamaSetupGuide/index.tsx +5 -4
  147. package/src/components/WebFavicon/index.tsx +1 -1
  148. package/src/components/client/ClientResponsiveContent/index.tsx +1 -1
  149. package/src/components/client/ClientResponsiveLayout.tsx +1 -1
  150. package/src/components/mdx/Image.tsx +1 -1
  151. package/src/components/mdx/Link.tsx +26 -9
  152. package/src/features/AlertBanner/CloudBanner.tsx +2 -3
  153. package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +8 -7
  154. package/src/features/ChatInput/ActionBar/Params/Controls.tsx +1 -3
  155. package/src/features/ChatInput/ActionBar/Token/index.tsx +1 -1
  156. package/src/features/ChatInput/Mobile/index.tsx +1 -1
  157. package/src/features/Conversation/ChatItem/components/MessageContent/index.tsx +1 -1
  158. package/src/features/Conversation/Error/OllamaBizError/index.tsx +1 -1
  159. package/src/features/Conversation/Error/OllamaSetupGuide/Desktop.tsx +1 -2
  160. package/src/features/Conversation/Error/index.tsx +1 -1
  161. package/src/features/Conversation/Messages/AssistantGroup/Tool/Actions/Settings.tsx +1 -1
  162. package/src/features/Conversation/Messages/AssistantGroup/Tool/Actions/index.tsx +11 -17
  163. package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/LoadingPlaceholder/index.tsx +13 -3
  164. package/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/CustomRender.tsx +43 -0
  165. package/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/FallbacktArgumentRender.tsx +59 -0
  166. package/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/index.tsx +46 -0
  167. package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/index.tsx +13 -19
  168. package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +18 -18
  169. package/src/features/Conversation/Messages/AssistantGroup/index.tsx +1 -1
  170. package/src/features/Conversation/Messages/Tool/Tool/index.tsx +11 -10
  171. package/src/features/Conversation/Messages/components/SearchGrounding.tsx +1 -1
  172. package/src/features/Conversation/TodoProgress/index.tsx +56 -23
  173. package/src/features/DataImporter/Error.tsx +3 -3
  174. package/src/features/DevPanel/CacheViewer/cacheProvider.tsx +2 -1
  175. package/src/features/DevPanel/MetadataViewer/Og.tsx +1 -1
  176. package/src/features/DevPanel/features/FloatPanel.tsx +1 -1
  177. package/src/features/DevPanel/features/Table/TooltipContent.tsx +3 -4
  178. package/src/features/DevPanel/index.tsx +1 -1
  179. package/src/features/EditorCanvas/InlineToolbar.tsx +1 -6
  180. package/src/features/FileViewer/NotSupport/index.tsx +2 -3
  181. package/src/features/Follow/index.tsx +8 -9
  182. package/src/features/LibraryModal/AddFilesToKnowledgeBase/SelectForm.tsx +2 -2
  183. package/src/features/MCP/Scores.tsx +1 -1
  184. package/src/features/MCPPluginDetail/Nav.tsx +8 -8
  185. package/src/features/MCPPluginDetail/Overview/TagList.tsx +1 -1
  186. package/src/features/MobileTabBar/index.tsx +2 -1
  187. package/src/features/OllamaSetupGuide/Desktop.tsx +1 -2
  188. package/src/features/PWAInstall/Install.tsx +1 -1
  189. package/src/features/PWAInstall/index.tsx +1 -1
  190. package/src/features/PluginStore/McpList/index.tsx +1 -1
  191. package/src/features/PluginStore/PluginList/Detail/Header.tsx +6 -6
  192. package/src/features/PluginsUI/Render/DefaultType/index.tsx +1 -1
  193. package/src/features/PluginsUI/Render/MCPType/index.tsx +1 -1
  194. package/src/features/Portal/Artifacts/Body/Renderer/index.tsx +1 -1
  195. package/src/features/ResourceManager/components/ChunkDrawer/index.tsx +1 -1
  196. package/src/features/ResourceManager/components/Explorer/Header/index.tsx +57 -4
  197. package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +6 -4
  198. package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +26 -26
  199. package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +16 -5
  200. package/src/features/ResourceManager/components/Explorer/ToolBar/BatchActionsDropdown.tsx +147 -149
  201. package/src/features/ResourceManager/components/LibraryHierarchy/styles.ts +5 -4
  202. package/src/features/ResourceManager/index.tsx +1 -1
  203. package/src/features/Setting/Footer.tsx +4 -5
  204. package/src/features/User/UserPanel/PanelContent.tsx +1 -1
  205. package/src/hooks/useActiveTabKey.ts +6 -4
  206. package/src/hooks/useIsSingleMode.test.ts +10 -24
  207. package/src/hooks/useIsSingleMode.ts +4 -2
  208. package/src/hooks/useIsSubSlug.ts +2 -1
  209. package/src/hooks/useQuery.ts +5 -5
  210. package/src/layout/GlobalProvider/AppTheme.tsx +2 -2
  211. package/src/layout/GlobalProvider/StyleRegistry.tsx +1 -1
  212. package/src/layout/GlobalProvider/useUserStateRedirect.ts +13 -25
  213. package/src/libs/next/Image.tsx +13 -0
  214. package/src/libs/next/Link.tsx +13 -0
  215. package/src/libs/next/dynamic.tsx +13 -0
  216. package/src/libs/next/index.ts +22 -0
  217. package/src/libs/next/navigation.ts +22 -0
  218. package/src/libs/router/Link.tsx +30 -0
  219. package/src/libs/router/index.ts +18 -0
  220. package/src/libs/router/navigation.ts +72 -0
  221. package/src/locales/default/plugin.ts +1 -0
  222. package/src/server/modules/AgentRuntime/AgentStateManager.ts +5 -1
  223. package/src/store/chat/slices/message/selectors/dbMessage.test.ts +11 -11
  224. package/src/store/chat/slices/portal/selectors.test.ts +5 -15
  225. package/src/store/file/slices/resource/action.ts +4 -2
  226. package/src/store/page/index.ts +1 -1
  227. package/src/store/page/slices/crud/index.ts +1 -1
  228. package/src/tools/inspectors.ts +2 -0
  229. package/src/tools/interventions.ts +2 -0
  230. package/src/tools/renders.ts +3 -1
  231. package/src/tools/streamings.ts +2 -0
  232. package/packages/builtin-tool-gtd/src/client/Inspector/CompleteTodos/index.tsx +0 -52
  233. package/packages/builtin-tool-gtd/src/client/Inspector/RemoveTodos/index.tsx +0 -52
  234. package/src/app/[variants]/(main)/hooks/usePathname.ts +0 -10
  235. package/src/app/[variants]/(main)/hooks/useQuery.ts +0 -12
  236. package/src/app/[variants]/(main)/hooks/useRouter.ts +0 -22
  237. package/src/app/[variants]/(main)/hooks/useSearchParams.ts +0 -11
  238. package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/CustomRender.tsx +0 -113
  239. package/src/features/Conversation/Messages/Tool/Tool/Render.tsx +0 -47
  240. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/AbortResponse.tsx +0 -0
  241. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Arguments/index.tsx +0 -0
  242. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/ErrorResponse.tsx +0 -0
  243. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/ApprovalActions.tsx +0 -0
  244. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/Fallback.tsx +0 -0
  245. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/KeyValueEditor.tsx +0 -0
  246. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/ModeSelector.tsx +0 -0
  247. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/index.tsx +0 -0
  248. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/PluginSettings.tsx +0 -0
  249. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/RejectedResponse.tsx +0 -0
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Script to migrate SPA internal components from @/libs/next/navigation
4
+ * to React Router version hooks.
5
+ *
6
+ * For files in (main) directory:
7
+ * - usePathname -> @/app/[variants]/(main)/hooks/usePathname
8
+ * - useSearchParams -> @/app/[variants]/(main)/hooks/useSearchParams
9
+ * - useRouter -> @/app/[variants]/(main)/hooks/useRouter
10
+ *
11
+ * @see RFC 147: LOBE-2850 - Phase 3
12
+ */
13
+
14
+ import { readFile, writeFile } from 'node:fs/promises';
15
+ import { dirname, join, relative } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const __dirname = dirname(fileURLToPath(import.meta.url));
19
+
20
+ // Files that should be migrated to React Router version
21
+ const SPA_FILES = [
22
+ // (main) directory files using @/libs/next/navigation
23
+ 'src/app/[variants]/(main)/community/_layout/Sidebar/Header/Nav.tsx',
24
+ 'src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx',
25
+ 'src/app/[variants]/(main)/group/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts',
26
+ 'src/app/[variants]/(main)/group/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts',
27
+ 'src/app/[variants]/(main)/chat/_layout/Sidebar/Header/AddTopicButon.tsx',
28
+ 'src/app/[variants]/(main)/chat/_layout/Sidebar/Header/Nav.tsx',
29
+ 'src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts',
30
+ 'src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts',
31
+ 'src/app/[variants]/(main)/memory/_layout/Sidebar/Header/Nav.tsx',
32
+ ];
33
+
34
+ interface MigrationResult {
35
+ changes: string[];
36
+ filePath: string;
37
+ }
38
+
39
+ async function migrateFile(relativePath: string): Promise<MigrationResult | null> {
40
+ const fullPath = join(__dirname, '..', relativePath);
41
+ const content = await readFile(fullPath, 'utf8');
42
+ let newContent = content;
43
+ const changes: string[] = [];
44
+
45
+ // Check what hooks are being imported from @/libs/next/navigation
46
+ const importMatch = content.match(
47
+ /import\s*\{([^}]+)\}\s*from\s*['"]@\/libs\/next\/navigation['"]/
48
+ );
49
+
50
+ if (!importMatch) {
51
+ console.log(`⏭️ ${relativePath} - No @/libs/next/navigation import found`);
52
+ return null;
53
+ }
54
+
55
+ const importedHooks = importMatch[1]
56
+ .split(',')
57
+ .map((s) => s.trim())
58
+ .filter(Boolean);
59
+
60
+ console.log(`📝 ${relativePath}`);
61
+ console.log(` Imported hooks: ${importedHooks.join(', ')}`);
62
+
63
+ // Build new imports
64
+ const newImports: string[] = [];
65
+
66
+ for (const hook of importedHooks) {
67
+ if (hook === 'usePathname') {
68
+ newImports.push(`import { usePathname } from '@/app/[variants]/(main)/hooks/usePathname';`);
69
+ changes.push('usePathname -> React Router version');
70
+ } else if (hook === 'useSearchParams') {
71
+ newImports.push(
72
+ `import { useSearchParams } from '@/app/[variants]/(main)/hooks/useSearchParams';`
73
+ );
74
+ changes.push('useSearchParams -> React Router version');
75
+ } else if (hook === 'useRouter') {
76
+ newImports.push(`import { useRouter } from '@/app/[variants]/(main)/hooks/useRouter';`);
77
+ changes.push('useRouter -> React Router version');
78
+ } else {
79
+ // Keep other imports (like notFound, redirect) from next/navigation
80
+ console.log(` ⚠️ Unknown hook "${hook}" - keeping original import`);
81
+ }
82
+ }
83
+
84
+ if (newImports.length === 0) {
85
+ console.log(` ⏭️ No hooks to migrate`);
86
+ return null;
87
+ }
88
+
89
+ // Replace the old import with new imports
90
+ newContent = newContent.replace(
91
+ /import\s*\{[^}]+\}\s*from\s*['"]@\/libs\/next\/navigation['"];?\n?/,
92
+ newImports.join('\n') + '\n'
93
+ );
94
+
95
+ if (newContent !== content) {
96
+ await writeFile(fullPath, newContent, 'utf8');
97
+ for (const change of changes) {
98
+ console.log(` ✅ ${change}`);
99
+ }
100
+ return { changes, filePath: relativePath };
101
+ }
102
+
103
+ return null;
104
+ }
105
+
106
+ async function main() {
107
+ console.log('🚀 Starting SPA navigation migration...\n');
108
+
109
+ const results: MigrationResult[] = [];
110
+
111
+ for (const file of SPA_FILES) {
112
+ try {
113
+ const result = await migrateFile(file);
114
+ if (result) {
115
+ results.push(result);
116
+ }
117
+ } catch (error) {
118
+ console.error(`❌ Error processing ${file}:`, error);
119
+ }
120
+ }
121
+
122
+ console.log('\n' + '='.repeat(60));
123
+ console.log(`📊 Migration Summary:`);
124
+ console.log(` - Files processed: ${SPA_FILES.length}`);
125
+ console.log(` - Files modified: ${results.length}`);
126
+ console.log('\n✨ SPA navigation migration complete!');
127
+ }
128
+
129
+ await main();
@@ -4,143 +4,145 @@ import {
4
4
  tracer as upstashWorkflowTracer,
5
5
  } from '@lobechat/observability-otel/modules/upstash-workflow';
6
6
  import { LayersEnum, MemorySourceType } from '@lobechat/types';
7
- import { serve } from '@upstash/workflow/nextjs';
7
+ import { Client } from '@upstash/qstash';
8
8
  import { WorkflowAbort } from '@upstash/workflow';
9
+ import { serve } from '@upstash/workflow/nextjs';
9
10
 
11
+ import { parseMemoryExtractionConfig } from '@/server/globalConfig/parseMemoryExtractionConfig';
10
12
  import {
11
13
  MemoryExtractionExecutor,
12
14
  type MemoryExtractionPayloadInput,
13
15
  normalizeMemoryExtractionPayload,
14
16
  } from '@/server/services/memory/userMemory/extract';
15
- import { Client } from '@upstash/qstash'
16
- import { parseMemoryExtractionConfig } from '@/server/globalConfig/parseMemoryExtractionConfig';
17
17
 
18
18
  const { upstashWorkflowExtraHeaders } = parseMemoryExtractionConfig();
19
19
 
20
20
  const CEP_LAYERS: LayersEnum[] = [LayersEnum.Context, LayersEnum.Experience, LayersEnum.Preference];
21
21
  const IDENTITY_LAYERS: LayersEnum[] = [LayersEnum.Identity];
22
22
 
23
- export const { POST } = serve<MemoryExtractionPayloadInput>((context) =>
24
- upstashWorkflowTracer.startActiveSpan(
25
- 'workflow:memory-user-memory:process-topics',
26
- async (span) => {
27
- const payload = normalizeMemoryExtractionPayload(context.requestPayload || {});
28
-
29
- span.setAttributes({
30
- ...buildUpstashWorkflowAttributes(context),
31
- 'workflow.memory_user_memory.force_all': payload.forceAll,
32
- 'workflow.memory_user_memory.force_topics': payload.forceTopics,
33
- 'workflow.memory_user_memory.layers': payload.layers.join(','),
34
- 'workflow.memory_user_memory.source': payload.sources.join(','),
35
- 'workflow.memory_user_memory.topic_count': payload.topicIds.length,
36
- 'workflow.memory_user_memory.user_count': payload.userIds.length,
37
- 'workflow.name': 'memory-user-memory:process-topics',
38
- });
39
-
40
- try {
41
- console.log('[chat-topic][batch] Starting batch topic processing workflow', {
42
- topicIds: payload.topicIds,
43
- userIds: payload.userIds,
23
+ export const { POST } = serve<MemoryExtractionPayloadInput>(
24
+ (context) =>
25
+ upstashWorkflowTracer.startActiveSpan(
26
+ 'workflow:memory-user-memory:process-topics',
27
+ async (span) => {
28
+ const payload = normalizeMemoryExtractionPayload(context.requestPayload || {});
29
+
30
+ span.setAttributes({
31
+ ...buildUpstashWorkflowAttributes(context),
32
+ 'workflow.memory_user_memory.force_all': payload.forceAll,
33
+ 'workflow.memory_user_memory.force_topics': payload.forceTopics,
34
+ 'workflow.memory_user_memory.layers': payload.layers.join(','),
35
+ 'workflow.memory_user_memory.source': payload.sources.join(','),
36
+ 'workflow.memory_user_memory.topic_count': payload.topicIds.length,
37
+ 'workflow.memory_user_memory.user_count': payload.userIds.length,
38
+ 'workflow.name': 'memory-user-memory:process-topics',
44
39
  });
45
40
 
46
- if (!payload.userIds.length) {
47
- span.setStatus({ code: SpanStatusCode.OK });
48
-
49
- return {
50
- message: 'No user id provided for topic batch.',
51
- processedTopics: 0,
52
- processedUsers: 0,
53
- };
54
- }
55
- if (!payload.topicIds.length) {
56
- span.setStatus({ code: SpanStatusCode.OK });
57
-
58
- return {
59
- message: 'No topic ids provided for extraction.',
60
- processedTopics: 0,
61
- processedUsers: 0,
62
- };
63
- }
64
- if (!payload.sources.includes(MemorySourceType.ChatTopic)) {
65
- span.setStatus({ code: SpanStatusCode.OK });
66
-
67
- return {
68
- message: 'Source not supported in topic batch.',
69
- processedTopics: 0,
70
- processedUsers: 0,
71
- };
72
- }
73
-
74
- const userId = payload.userIds[0];
75
- const executor = await MemoryExtractionExecutor.create();
41
+ try {
42
+ console.log('[chat-topic][batch] Starting batch topic processing workflow', {
43
+ topicIds: payload.topicIds,
44
+ userIds: payload.userIds,
45
+ });
46
+
47
+ if (!payload.userIds.length) {
48
+ span.setStatus({ code: SpanStatusCode.OK });
49
+
50
+ return {
51
+ message: 'No user id provided for topic batch.',
52
+ processedTopics: 0,
53
+ processedUsers: 0,
54
+ };
55
+ }
56
+ if (!payload.topicIds.length) {
57
+ span.setStatus({ code: SpanStatusCode.OK });
58
+
59
+ return {
60
+ message: 'No topic ids provided for extraction.',
61
+ processedTopics: 0,
62
+ processedUsers: 0,
63
+ };
64
+ }
65
+ if (!payload.sources.includes(MemorySourceType.ChatTopic)) {
66
+ span.setStatus({ code: SpanStatusCode.OK });
67
+
68
+ return {
69
+ message: 'Source not supported in topic batch.',
70
+ processedTopics: 0,
71
+ processedUsers: 0,
72
+ };
73
+ }
74
+
75
+ const userId = payload.userIds[0];
76
+ const executor = await MemoryExtractionExecutor.create();
77
+
78
+ // CEP: run in parallel across the batch
79
+ await Promise.all(
80
+ payload.topicIds.map((topicId, index) =>
81
+ context.run(
82
+ `memory:user-memory:extract:users:${userId}:topics:${topicId}:cep:${index}`,
83
+ () =>
84
+ executor.extractTopic({
85
+ forceAll: payload.forceAll,
86
+ forceTopics: payload.forceTopics,
87
+ from: payload.from,
88
+ layers: CEP_LAYERS,
89
+ source: MemorySourceType.ChatTopic,
90
+ to: payload.to,
91
+ topicId,
92
+ userId,
93
+ }),
94
+ ),
95
+ ),
96
+ );
76
97
 
77
- // CEP: run in parallel across the batch
78
- await Promise.all(
79
- payload.topicIds.map((topicId, index) =>
80
- context.run(
81
- `memory:user-memory:extract:users:${userId}:topics:${topicId}:cep:${index}`,
98
+ // Identity: run sequentially for the batch
99
+ for (const [index, topicId] of payload.topicIds.entries()) {
100
+ await context.run(
101
+ `memory:user-memory:extract:users:${userId}:topics:${topicId}:identity:${index}`,
82
102
  () =>
83
103
  executor.extractTopic({
84
104
  forceAll: payload.forceAll,
85
105
  forceTopics: payload.forceTopics,
86
106
  from: payload.from,
87
- layers: CEP_LAYERS,
107
+ layers: IDENTITY_LAYERS,
88
108
  source: MemorySourceType.ChatTopic,
89
109
  to: payload.to,
90
110
  topicId,
91
111
  userId,
92
112
  }),
93
- ),
94
- ),
95
- );
96
-
97
- // Identity: run sequentially for the batch
98
- for (const [index, topicId] of payload.topicIds.entries()) {
99
- await context.run(
100
- `memory:user-memory:extract:users:${userId}:topics:${topicId}:identity:${index}`,
101
- () =>
102
- executor.extractTopic({
103
- forceAll: payload.forceAll,
104
- forceTopics: payload.forceTopics,
105
- from: payload.from,
106
- layers: IDENTITY_LAYERS,
107
- source: MemorySourceType.ChatTopic,
108
- to: payload.to,
109
- topicId,
110
- userId,
111
- }),
112
- );
113
- }
113
+ );
114
+ }
114
115
 
115
- console.log('[chat-topic][batch] Batch topic processing workflow completed', {
116
- processedTopics: payload.topicIds.length,
117
- });
116
+ console.log('[chat-topic][batch] Batch topic processing workflow completed', {
117
+ processedTopics: payload.topicIds.length,
118
+ });
118
119
 
119
- span.setStatus({ code: SpanStatusCode.OK });
120
+ span.setStatus({ code: SpanStatusCode.OK });
121
+
122
+ return {
123
+ processedTopics: payload.topicIds.length,
124
+ processedUsers: payload.userIds.length,
125
+ };
126
+ } catch (error) {
127
+ // NOTICE: Let WorkflowAbort bubble up (used internally by Upstash); record others
128
+ if (error instanceof WorkflowAbort) {
129
+ console.warn('workflow aborted:', error.message);
130
+ throw error;
131
+ }
132
+
133
+ span.recordException(error as Error);
134
+ span.setStatus({
135
+ code: SpanStatusCode.ERROR,
136
+ message: error instanceof Error ? error.message : 'process-topics workflow failed',
137
+ });
120
138
 
121
- return {
122
- processedTopics: payload.topicIds.length,
123
- processedUsers: payload.userIds.length,
124
- };
125
- } catch (error) {
126
- // NOTICE: Let WorkflowAbort bubble up (used internally by Upstash); record others
127
- if (error instanceof WorkflowAbort) {
128
- console.warn('workflow aborted:', error.message);
129
139
  throw error;
140
+ } finally {
141
+ span.end();
130
142
  }
131
-
132
- span.recordException(error as Error);
133
- span.setStatus({
134
- code: SpanStatusCode.ERROR,
135
- message: error instanceof Error ? error.message : 'process-topics workflow failed',
136
- });
137
-
138
- throw error;
139
- } finally {
140
- span.end();
141
- }
142
- },
143
- ), {
143
+ },
144
+ ),
145
+ {
144
146
  // NOTICE(@nekomeowww): Here as scenarios like Vercel Deployment Protection,
145
147
  // intermediate context.run(...) won't offer customizable headers like context.trigger(...) / client.trigger(...)
146
148
  // for passing additional headers, we have to provide a custom QStash client with the required headers here.
@@ -151,6 +153,7 @@ export const { POST } = serve<MemoryExtractionPayloadInput>((context) =>
151
153
  headers: {
152
154
  ...upstashWorkflowExtraHeaders,
153
155
  },
154
- token: process.env.QSTASH_TOKEN!
155
- })
156
- });
156
+ token: process.env.QSTASH_TOKEN!,
157
+ }),
158
+ },
159
+ );
@@ -1,5 +1,9 @@
1
+ import { MemorySourceType } from '@lobechat/types';
2
+ import { Client } from '@upstash/qstash';
1
3
  import { serve } from '@upstash/workflow/nextjs';
4
+
2
5
  import type { ListTopicsForMemoryExtractorCursor } from '@/database/models/topic';
6
+ import { parseMemoryExtractionConfig } from '@/server/globalConfig/parseMemoryExtractionConfig';
3
7
  import {
4
8
  MemoryExtractionExecutor,
5
9
  type MemoryExtractionPayloadInput,
@@ -8,135 +12,143 @@ import {
8
12
  normalizeMemoryExtractionPayload,
9
13
  } from '@/server/services/memory/userMemory/extract';
10
14
  import { forEachBatchSequential } from '@/server/services/memory/userMemory/topicBatching';
11
- import { MemorySourceType } from '@lobechat/types';
12
- import { parseMemoryExtractionConfig } from '@/server/globalConfig/parseMemoryExtractionConfig';
13
- import { Client } from '@upstash/qstash'
14
15
 
15
16
  const TOPIC_PAGE_SIZE = 50;
16
17
  const TOPIC_BATCH_SIZE = 4;
17
18
 
18
19
  const { upstashWorkflowExtraHeaders } = parseMemoryExtractionConfig();
19
20
 
20
- export const { POST } = serve<MemoryExtractionPayloadInput>(async (context) => {
21
- const params = normalizeMemoryExtractionPayload(context.requestPayload || {});
22
- if (!params.userIds.length) {
23
- return { message: 'No user ids provided for topic processing.' };
24
- }
25
- if (!params.sources.includes(MemorySourceType.ChatTopic)) {
26
- return { message: 'No supported sources requested, skip topic processing.' };
27
- }
28
-
29
- const executor = await MemoryExtractionExecutor.create();
30
-
31
- const scheduleNextPage = async (userId: string, cursorCreatedAt: Date, cursorId: string) => {
32
- await MemoryExtractionWorkflowService.triggerProcessUserTopics({
33
- ...buildWorkflowPayloadInput({
34
- ...params,
35
- topicCursor: {
36
- createdAt: cursorCreatedAt.toISOString(),
37
- id: cursorId,
38
- userId,
39
- },
40
- topicIds: [],
41
- userId,
42
- userIds: [userId],
43
- }),
44
- }, { extraHeaders: upstashWorkflowExtraHeaders });
45
- };
46
-
47
- for (const userId of params.userIds) {
48
- const topicCursor =
49
- params.topicCursor && params.topicCursor.userId === userId
50
- ? {
51
- createdAt: new Date(params.topicCursor.createdAt),
52
- id: params.topicCursor.id,
53
- }
54
- : undefined;
21
+ export const { POST } = serve<MemoryExtractionPayloadInput>(
22
+ async (context) => {
23
+ const params = normalizeMemoryExtractionPayload(context.requestPayload || {});
24
+ if (!params.userIds.length) {
25
+ return { message: 'No user ids provided for topic processing.' };
26
+ }
27
+ if (!params.sources.includes(MemorySourceType.ChatTopic)) {
28
+ return { message: 'No supported sources requested, skip topic processing.' };
29
+ }
55
30
 
56
- const topicsFromPayload =
57
- params.topicIds && params.topicIds.length > 0
58
- ? await context.run(
59
- `memory:user-memory:extract:users:${userId}:filter-topic-ids`,
60
- async () => {
61
- const filtered = await executor.filterTopicIdsForUser(userId, params.topicIds);
62
- return filtered.length > 0 ? filtered : undefined;
63
- },
64
- )
65
- : undefined;
31
+ const executor = await MemoryExtractionExecutor.create();
66
32
 
67
- const topicBatch = await context.run<{
68
- cursor?: ListTopicsForMemoryExtractorCursor;
69
- ids: string[];
70
- }>(`memory:user-memory:extract:users:${userId}:list-topics:${topicCursor?.id || 'root'}`, () =>
71
- topicsFromPayload && topicsFromPayload.length > 0
72
- ? Promise.resolve({ ids: topicsFromPayload })
73
- : executor.getTopicsForUser(
74
- {
75
- cursor: topicCursor,
76
- forceAll: params.forceAll,
77
- forceTopics: params.forceTopics,
78
- from: params.from,
79
- to: params.to,
33
+ const scheduleNextPage = async (userId: string, cursorCreatedAt: Date, cursorId: string) => {
34
+ await MemoryExtractionWorkflowService.triggerProcessUserTopics(
35
+ {
36
+ ...buildWorkflowPayloadInput({
37
+ ...params,
38
+ topicCursor: {
39
+ createdAt: cursorCreatedAt.toISOString(),
40
+ id: cursorId,
80
41
  userId,
81
42
  },
82
- TOPIC_PAGE_SIZE,
83
- ),
84
- );
43
+ topicIds: [],
44
+ userId,
45
+ userIds: [userId],
46
+ }),
47
+ },
48
+ { extraHeaders: upstashWorkflowExtraHeaders },
49
+ );
50
+ };
85
51
 
86
- const ids = topicBatch.ids;
87
- if (!ids.length) {
88
- continue;
89
- }
52
+ for (const userId of params.userIds) {
53
+ const topicCursor =
54
+ params.topicCursor && params.topicCursor.userId === userId
55
+ ? {
56
+ createdAt: new Date(params.topicCursor.createdAt),
57
+ id: params.topicCursor.id,
58
+ }
59
+ : undefined;
90
60
 
91
- const cursor = 'cursor' in topicBatch ? topicBatch.cursor : undefined;
61
+ const topicsFromPayload =
62
+ params.topicIds && params.topicIds.length > 0
63
+ ? await context.run(
64
+ `memory:user-memory:extract:users:${userId}:filter-topic-ids`,
65
+ async () => {
66
+ const filtered = await executor.filterTopicIdsForUser(userId, params.topicIds);
67
+ return filtered.length > 0 ? filtered : undefined;
68
+ },
69
+ )
70
+ : undefined;
92
71
 
93
- await forEachBatchSequential(ids, TOPIC_BATCH_SIZE, async (topicIds, batchIndex) => {
94
- // NOTICE: We trigger via QStash instead of context.invoke because invoke only swaps the last path
95
- // segment with the workflowId. If we invoked directly from /process-user-topics, child workflow
96
- // URLs would inherit that base and lose the desired /process-topics/workflows prefix.
97
- await context.run(
98
- `memory:user-memory:extract:users:${userId}:process-topics-batch:${batchIndex}`,
72
+ const topicBatch = await context.run<{
73
+ cursor?: ListTopicsForMemoryExtractorCursor;
74
+ ids: string[];
75
+ }>(
76
+ `memory:user-memory:extract:users:${userId}:list-topics:${topicCursor?.id || 'root'}`,
99
77
  () =>
100
- MemoryExtractionWorkflowService.triggerProcessTopics({
101
- ...buildWorkflowPayloadInput(params),
102
- topicCursor: undefined,
103
- topicIds,
104
- userId,
105
- userIds: [userId],
106
- }, { extraHeaders: upstashWorkflowExtraHeaders }),
78
+ topicsFromPayload && topicsFromPayload.length > 0
79
+ ? Promise.resolve({ ids: topicsFromPayload })
80
+ : executor.getTopicsForUser(
81
+ {
82
+ cursor: topicCursor,
83
+ forceAll: params.forceAll,
84
+ forceTopics: params.forceTopics,
85
+ from: params.from,
86
+ to: params.to,
87
+ userId,
88
+ },
89
+ TOPIC_PAGE_SIZE,
90
+ ),
107
91
  );
108
- });
109
92
 
110
- if (!topicsFromPayload && cursor) {
111
- await context.run(
112
- `memory:user-memory:extract:users:${userId}:topics:${cursor.id}:schedule-next-batch`,
113
- () => {
114
- // NOTICE: Upstash Workflow only supports serializable data into plain JSON,
115
- // this causes the Date object to be converted into string when passed as parameter from
116
- // context to child workflow. So we need to convert it back to Date object here.
117
- const createdAt = new Date(cursor.createdAt);
118
- if (Number.isNaN(createdAt.getTime())) {
119
- throw new Error('Invalid cursor date when scheduling next topic page');
120
- }
93
+ const ids = topicBatch.ids;
94
+ if (!ids.length) {
95
+ continue;
96
+ }
121
97
 
122
- scheduleNextPage(userId, createdAt, cursor.id);
123
- },
124
- );
98
+ const cursor = 'cursor' in topicBatch ? topicBatch.cursor : undefined;
99
+
100
+ await forEachBatchSequential(ids, TOPIC_BATCH_SIZE, async (topicIds, batchIndex) => {
101
+ // NOTICE: We trigger via QStash instead of context.invoke because invoke only swaps the last path
102
+ // segment with the workflowId. If we invoked directly from /process-user-topics, child workflow
103
+ // URLs would inherit that base and lose the desired /process-topics/workflows prefix.
104
+ await context.run(
105
+ `memory:user-memory:extract:users:${userId}:process-topics-batch:${batchIndex}`,
106
+ () =>
107
+ MemoryExtractionWorkflowService.triggerProcessTopics(
108
+ {
109
+ ...buildWorkflowPayloadInput(params),
110
+ topicCursor: undefined,
111
+ topicIds,
112
+ userId,
113
+ userIds: [userId],
114
+ },
115
+ { extraHeaders: upstashWorkflowExtraHeaders },
116
+ ),
117
+ );
118
+ });
119
+
120
+ if (!topicsFromPayload && cursor) {
121
+ await context.run(
122
+ `memory:user-memory:extract:users:${userId}:topics:${cursor.id}:schedule-next-batch`,
123
+ () => {
124
+ // NOTICE: Upstash Workflow only supports serializable data into plain JSON,
125
+ // this causes the Date object to be converted into string when passed as parameter from
126
+ // context to child workflow. So we need to convert it back to Date object here.
127
+ const createdAt = new Date(cursor.createdAt);
128
+ if (Number.isNaN(createdAt.getTime())) {
129
+ throw new Error('Invalid cursor date when scheduling next topic page');
130
+ }
131
+
132
+ scheduleNextPage(userId, createdAt, cursor.id);
133
+ },
134
+ );
135
+ }
125
136
  }
126
- }
127
137
 
128
- return { processedUsers: params.userIds.length };
129
- }, {
130
- // NOTICE(@nekomeowww): Here as scenarios like Vercel Deployment Protection,
131
- // intermediate context.run(...) won't offer customizable headers like context.trigger(...) / client.trigger(...)
132
- // for passing additional headers, we have to provide a custom QStash client with the required headers here.
133
- //
134
- // Refer to the doc for more details:
135
- // https://upstash.com/docs/workflow/troubleshooting/vercel#step-2-pass-header-when-triggering
136
- qstashClient: new Client({
137
- headers: {
138
- ...upstashWorkflowExtraHeaders,
139
- },
140
- token: process.env.QSTASH_TOKEN!
141
- })
142
- });
138
+ return { processedUsers: params.userIds.length };
139
+ },
140
+ {
141
+ // NOTICE(@nekomeowww): Here as scenarios like Vercel Deployment Protection,
142
+ // intermediate context.run(...) won't offer customizable headers like context.trigger(...) / client.trigger(...)
143
+ // for passing additional headers, we have to provide a custom QStash client with the required headers here.
144
+ //
145
+ // Refer to the doc for more details:
146
+ // https://upstash.com/docs/workflow/troubleshooting/vercel#step-2-pass-header-when-triggering
147
+ qstashClient: new Client({
148
+ headers: {
149
+ ...upstashWorkflowExtraHeaders,
150
+ },
151
+ token: process.env.QSTASH_TOKEN!,
152
+ }),
153
+ },
154
+ );