@lobehub/lobehub 2.0.0-next.344 → 2.0.0-next.345

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 (168) hide show
  1. package/.cursor/rules/i18n.mdc +1 -1
  2. package/.cursor/rules/modal-imperative.mdc +162 -0
  3. package/.cursor/rules/rules-index.mdc +1 -0
  4. package/.env.example +0 -14
  5. package/.eslintrc.js +8 -1
  6. package/CHANGELOG.md +41 -0
  7. package/Dockerfile +3 -13
  8. package/README.md +3 -5
  9. package/README.zh-CN.md +3 -5
  10. package/changelog/v1.json +15 -0
  11. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx +11 -42
  12. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx +10 -41
  13. package/e2e/src/support/webServer.ts +2 -0
  14. package/locales/ar/error.json +0 -4
  15. package/locales/bg-BG/error.json +0 -4
  16. package/locales/de-DE/error.json +0 -4
  17. package/locales/en-US/error.json +0 -4
  18. package/locales/es-ES/error.json +0 -4
  19. package/locales/fa-IR/error.json +0 -4
  20. package/locales/fr-FR/error.json +0 -4
  21. package/locales/it-IT/error.json +0 -4
  22. package/locales/ja-JP/error.json +0 -4
  23. package/locales/ko-KR/error.json +0 -4
  24. package/locales/nl-NL/error.json +0 -4
  25. package/locales/pl-PL/error.json +0 -4
  26. package/locales/pt-BR/error.json +0 -4
  27. package/locales/ru-RU/error.json +0 -4
  28. package/locales/tr-TR/error.json +0 -4
  29. package/locales/vi-VN/error.json +0 -4
  30. package/locales/zh-CN/error.json +0 -4
  31. package/locales/zh-TW/error.json +0 -4
  32. package/package.json +7 -9
  33. package/packages/builtin-agents/package.json +2 -0
  34. package/packages/builtin-agents/src/agents/agent-builder/index.ts +4 -2
  35. package/packages/builtin-agents/src/agents/group-agent-builder/index.ts +4 -2
  36. package/packages/builtin-agents/src/agents/page-agent/index.ts +5 -2
  37. package/packages/context-engine/src/engine/messages/MessagesEngine.ts +9 -9
  38. package/packages/context-engine/src/providers/GroupContextInjector.ts +19 -33
  39. package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +79 -43
  40. package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +5 -15
  41. package/packages/database/src/repositories/userMemory/__tests__/UserMemoryTopicRepository.test.ts +24 -3
  42. package/packages/model-bank/src/modelProviders/comfyui.ts +0 -1
  43. package/packages/model-bank/src/modelProviders/fal.ts +0 -1
  44. package/packages/types/src/fetch.ts +1 -2
  45. package/packages/utils/src/server/__tests__/auth.test.ts +0 -47
  46. package/packages/utils/src/server/auth.ts +1 -9
  47. package/scripts/_shared/checkDeprecatedClerkEnv.js +42 -0
  48. package/scripts/changelogWorkflow/buildStaticChangelog.ts +2 -1
  49. package/scripts/clerk-to-betterauth/_internal/types.ts +53 -20
  50. package/scripts/clerk-to-betterauth/export-clerk-users-with-api.ts +43 -36
  51. package/scripts/countEnWord.ts +1 -1
  52. package/scripts/electronWorkflow/modifiers/appCode.mts +2 -131
  53. package/scripts/i18nWorkflow/protectedPatterns.ts +1 -2
  54. package/scripts/prebuild.mts +10 -8
  55. package/scripts/serverLauncher/startServer.js +23 -5
  56. package/src/app/(backend)/middleware/auth/index.test.ts +8 -4
  57. package/src/app/(backend)/middleware/auth/index.ts +0 -15
  58. package/src/app/(backend)/middleware/auth/utils.test.ts +0 -28
  59. package/src/app/(backend)/middleware/auth/utils.ts +2 -17
  60. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +3 -51
  61. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -4
  62. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +7 -6
  63. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -16
  64. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/index.tsx +1 -1
  65. package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +13 -13
  66. package/src/app/[variants]/(main)/home/features/RecentPage/Item.tsx +2 -2
  67. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
  68. package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -2
  69. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -22
  70. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +12 -14
  71. package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +8 -14
  72. package/src/app/[variants]/(main)/settings/skill/index.tsx +7 -5
  73. package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +2 -35
  74. package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -20
  75. package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -2
  76. package/src/app/[variants]/(mobile)/me/profile/features/Category.tsx +3 -13
  77. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +2 -3
  78. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
  79. package/src/app/[variants]/share/t/[id]/index.tsx +1 -1
  80. package/src/app/robots.tsx +1 -1
  81. package/src/envs/auth.ts +2 -27
  82. package/src/envs/llm.ts +2 -2
  83. package/src/features/AgentSetting/AgentPlugin/index.tsx +9 -12
  84. package/src/features/ChatInput/ActionBar/Tools/index.tsx +7 -5
  85. package/src/features/ChatMiniMap/utils.ts +1 -1
  86. package/src/features/CommandMenu/SearchResults.tsx +1 -1
  87. package/src/features/Conversation/ChatList/components/AutoScroll/DebugInspector.tsx +166 -0
  88. package/src/features/Conversation/ChatList/components/AutoScroll/index.tsx +86 -0
  89. package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +11 -17
  90. package/src/features/Conversation/Messages/AgentCouncil/components/AutoScrollShadow.tsx +25 -14
  91. package/src/features/Conversation/Messages/AgentCouncil/components/CouncilMember.tsx +1 -1
  92. package/src/features/IntegrationDetailModal/IntegrationDetailContent.tsx +305 -0
  93. package/src/features/IntegrationDetailModal/index.tsx +21 -283
  94. package/src/features/MCPPluginDetail/Deployment/index.tsx +1 -1
  95. package/src/features/MCPPluginDetail/Schema/Prompts.tsx +1 -1
  96. package/src/features/MCPPluginDetail/Schema/Tools.tsx +1 -1
  97. package/src/features/ProfileEditor/AgentTool.tsx +14 -20
  98. package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/NoteFileItem.tsx +1 -1
  99. package/src/features/SkillStore/LobeHubList/index.tsx +50 -87
  100. package/src/features/SkillStore/Search/index.tsx +1 -1
  101. package/src/features/SkillStore/{Content.tsx → SkillStoreContent.tsx} +3 -8
  102. package/src/features/SkillStore/index.tsx +15 -33
  103. package/src/features/User/UserPanel/PanelContent.tsx +0 -8
  104. package/src/features/User/__tests__/PanelContent.test.tsx +1 -35
  105. package/src/features/User/__tests__/UserAvatar.test.tsx +30 -57
  106. package/src/features/User/__tests__/useMenu.test.tsx +2 -43
  107. package/src/layout/AuthProvider/index.tsx +0 -5
  108. package/src/libs/next/config/define-config.ts +6 -0
  109. package/src/libs/next/proxy/createRouteMatcher.test.ts +121 -0
  110. package/src/libs/next/proxy/createRouteMatcher.ts +18 -0
  111. package/src/libs/next/proxy/define-config.ts +4 -53
  112. package/src/libs/next-auth/adapter/index.ts +1 -2
  113. package/src/libs/oidc-provider/provider.test.ts +5 -316
  114. package/src/libs/trpc/lambda/context.test.ts +0 -13
  115. package/src/libs/trpc/lambda/context.ts +3 -22
  116. package/src/libs/trpc/middleware/userAuth.ts +2 -4
  117. package/src/libs/trusted-client/getSessionUser.ts +2 -17
  118. package/src/locales/default/error.ts +0 -6
  119. package/src/locales/default/index.ts +0 -2
  120. package/src/proxy.ts +0 -1
  121. package/src/server/routers/lambda/__tests__/user.test.ts +0 -71
  122. package/src/server/routers/lambda/user.ts +6 -63
  123. package/src/server/services/changelog/index.test.ts +3 -2
  124. package/src/server/services/changelog/index.ts +1 -1
  125. package/src/server/services/user/index.ts +0 -83
  126. package/src/services/chat/index.ts +1 -2
  127. package/src/services/chat/mecha/agentConfigResolver.test.ts +43 -0
  128. package/src/services/chat/mecha/agentConfigResolver.ts +3 -1
  129. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +58 -14
  130. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +10 -2
  131. package/src/store/user/slices/auth/action.test.ts +1 -81
  132. package/src/store/user/slices/auth/action.ts +3 -28
  133. package/src/store/user/slices/auth/initialState.ts +1 -18
  134. package/src/store/user/slices/auth/selectors.test.ts +2 -127
  135. package/src/store/user/slices/auth/selectors.ts +1 -21
  136. package/src/utils/errorResponse.ts +1 -4
  137. package/src/utils/markdownToTxt.ts +20 -0
  138. package/locales/ar/clerk.json +0 -545
  139. package/locales/bg-BG/clerk.json +0 -545
  140. package/locales/de-DE/clerk.json +0 -545
  141. package/locales/en-US/clerk.json +0 -545
  142. package/locales/es-ES/clerk.json +0 -545
  143. package/locales/fa-IR/clerk.json +0 -545
  144. package/locales/fr-FR/clerk.json +0 -545
  145. package/locales/it-IT/clerk.json +0 -545
  146. package/locales/ja-JP/clerk.json +0 -545
  147. package/locales/ko-KR/clerk.json +0 -545
  148. package/locales/nl-NL/clerk.json +0 -545
  149. package/locales/pl-PL/clerk.json +0 -545
  150. package/locales/pt-BR/clerk.json +0 -545
  151. package/locales/ru-RU/clerk.json +0 -545
  152. package/locales/tr-TR/clerk.json +0 -545
  153. package/locales/vi-VN/clerk.json +0 -545
  154. package/locales/zh-CN/clerk.json +0 -545
  155. package/locales/zh-TW/clerk.json +0 -545
  156. package/src/app/(backend)/api/webhooks/clerk/__tests__/fixtures/createUser.json +0 -73
  157. package/src/app/(backend)/api/webhooks/clerk/route.ts +0 -95
  158. package/src/app/(backend)/api/webhooks/clerk/validateRequest.ts +0 -22
  159. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +0 -27
  160. package/src/app/[variants]/(main)/settings/security/features/ClerkProfile.tsx +0 -67
  161. package/src/features/Conversation/ChatList/components/AutoScroll.tsx +0 -25
  162. package/src/layout/AuthProvider/Clerk/UserUpdater.tsx +0 -40
  163. package/src/layout/AuthProvider/Clerk/index.tsx +0 -54
  164. package/src/layout/AuthProvider/Clerk/useAppearance.ts +0 -133
  165. package/src/libs/clerk-auth/index.test.ts +0 -216
  166. package/src/libs/clerk-auth/index.ts +0 -80
  167. package/src/locales/default/clerk.ts +0 -677
  168. package/src/server/services/user/index.test.ts +0 -220
@@ -12,7 +12,7 @@ describe('GroupContextInjector', () => {
12
12
  });
13
13
 
14
14
  describe('Basic Scenarios', () => {
15
- it('should inject group context into system message', async () => {
15
+ it('should inject group context before first user message', async () => {
16
16
  const injector = new GroupContextInjector({
17
17
  currentAgentId: 'agt_editor',
18
18
  currentAgentName: 'Editor',
@@ -35,31 +35,39 @@ describe('GroupContextInjector', () => {
35
35
  const context = createContext(input);
36
36
  const result = await injector.process(context);
37
37
 
38
- const systemContent = result.messages[0].content;
38
+ // System message should be unchanged
39
+ expect(result.messages[0].content).toBe('You are a helpful editor.');
40
+
41
+ // Should have 3 messages now (system, injected, user)
42
+ expect(result.messages).toHaveLength(3);
39
43
 
40
- // Original content should be preserved
41
- expect(systemContent).toContain('You are a helpful editor.');
44
+ // Check injected message (second message)
45
+ const injectedContent = result.messages[1].content;
46
+ expect(result.messages[1].role).toBe('user');
42
47
 
43
48
  // Agent identity (plain text, no wrapper)
44
- expect(systemContent).toContain('You are "Editor"');
45
- expect(systemContent).toContain('acting as a participant');
46
- expect(systemContent).toContain('"Writing Team"');
47
- expect(systemContent).toContain('agt_editor');
48
- expect(systemContent).not.toContain('<agent_identity>');
49
+ expect(injectedContent).toContain('You are "Editor"');
50
+ expect(injectedContent).toContain('acting as a participant');
51
+ expect(injectedContent).toContain('"Writing Team"');
52
+ expect(injectedContent).toContain('agt_editor');
53
+ expect(injectedContent).not.toContain('<agent_identity>');
49
54
 
50
55
  // Group context section with system prompt
51
- expect(systemContent).toContain('<group_context>');
52
- expect(systemContent).toContain('A team for collaborative writing');
56
+ expect(injectedContent).toContain('<group_context>');
57
+ expect(injectedContent).toContain('A team for collaborative writing');
53
58
 
54
59
  // Participants section with XML format
55
- expect(systemContent).toContain('<group_participants>');
56
- expect(systemContent).toContain('<member name="Supervisor" id="agt_supervisor" />');
57
- expect(systemContent).toContain('<member name="Writer" id="agt_writer" />');
58
- expect(systemContent).toContain('<member name="Editor" id="agt_editor" you="true" />');
60
+ expect(injectedContent).toContain('<group_participants>');
61
+ expect(injectedContent).toContain('<member name="Supervisor" id="agt_supervisor" />');
62
+ expect(injectedContent).toContain('<member name="Writer" id="agt_writer" />');
63
+ expect(injectedContent).toContain('<member name="Editor" id="agt_editor" you="true" />');
59
64
 
60
65
  // Identity rules
61
- expect(systemContent).toContain('<identity_rules>');
62
- expect(systemContent).toContain('NEVER expose or display agent IDs');
66
+ expect(injectedContent).toContain('<identity_rules>');
67
+ expect(injectedContent).toContain('NEVER expose or display agent IDs');
68
+
69
+ // Original user message should be third
70
+ expect(result.messages[2].content).toBe('Please review this.');
63
71
 
64
72
  // Metadata should be updated
65
73
  expect(result.metadata.groupContextInjected).toBe(true);
@@ -72,35 +80,37 @@ describe('GroupContextInjector', () => {
72
80
  enabled: false, // Disabled
73
81
  });
74
82
 
75
- const input: any[] = [{ role: 'system', content: 'You are a helpful editor.' }];
83
+ const input: any[] = [
84
+ { role: 'system', content: 'You are a helpful editor.' },
85
+ { role: 'user', content: 'Hello' },
86
+ ];
76
87
 
77
88
  const context = createContext(input);
78
89
  const result = await injector.process(context);
79
90
 
80
- // Should be unchanged
91
+ // Should be unchanged - no injection
92
+ expect(result.messages).toHaveLength(2);
81
93
  expect(result.messages[0].content).toBe('You are a helpful editor.');
94
+ expect(result.messages[1].content).toBe('Hello');
82
95
  expect(result.metadata.groupContextInjected).toBeUndefined();
83
96
  });
84
97
 
85
- it('should skip injection when no system message exists', async () => {
98
+ it('should skip injection when no user message exists', async () => {
86
99
  const injector = new GroupContextInjector({
87
100
  currentAgentId: 'agt_editor',
88
101
  currentAgentName: 'Editor',
89
102
  enabled: true,
90
103
  });
91
104
 
92
- const input: any[] = [
93
- { role: 'user', content: 'Hello' },
94
- { role: 'assistant', content: 'Hi there!' },
95
- ];
105
+ const input: any[] = [{ role: 'system', content: 'You are a helpful editor.' }];
96
106
 
97
107
  const context = createContext(input);
98
108
  const result = await injector.process(context);
99
109
 
100
- // Messages should be unchanged
101
- expect(result.messages[0].content).toBe('Hello');
102
- expect(result.messages[1].content).toBe('Hi there!');
103
- expect(result.metadata.groupContextInjected).toBeUndefined();
110
+ // Messages should be unchanged - no user message to inject before
111
+ expect(result.messages).toHaveLength(1);
112
+ expect(result.messages[0].content).toBe('You are a helpful editor.');
113
+ expect(result.metadata.groupContextInjected).toBe(true);
104
114
  });
105
115
  });
106
116
 
@@ -113,12 +123,16 @@ describe('GroupContextInjector', () => {
113
123
  enabled: true,
114
124
  });
115
125
 
116
- const input: any[] = [{ content: 'You are an editor.', role: 'system' }];
126
+ const input: any[] = [
127
+ { content: 'You are an editor.', role: 'system' },
128
+ { content: 'Hello', role: 'user' },
129
+ ];
117
130
 
118
131
  const context = createContext(input);
119
132
  const result = await injector.process(context);
120
133
 
121
- expect(result.messages[0].content).toMatchSnapshot();
134
+ // Check injected message content
135
+ expect(result.messages[1].content).toMatchSnapshot();
122
136
  });
123
137
 
124
138
  it('should handle config with only group info', async () => {
@@ -129,12 +143,16 @@ describe('GroupContextInjector', () => {
129
143
  systemPrompt: 'Test group description',
130
144
  });
131
145
 
132
- const input: any[] = [{ content: 'System prompt.', role: 'system' }];
146
+ const input: any[] = [
147
+ { content: 'System prompt.', role: 'system' },
148
+ { content: 'Hello', role: 'user' },
149
+ ];
133
150
 
134
151
  const context = createContext(input);
135
152
  const result = await injector.process(context);
136
153
 
137
- expect(result.messages[0].content).toMatchSnapshot();
154
+ // Check injected message content
155
+ expect(result.messages[1].content).toMatchSnapshot();
138
156
  });
139
157
 
140
158
  it('should handle empty config', async () => {
@@ -142,12 +160,16 @@ describe('GroupContextInjector', () => {
142
160
  enabled: true,
143
161
  });
144
162
 
145
- const input: any[] = [{ content: 'Base prompt.', role: 'system' }];
163
+ const input: any[] = [
164
+ { content: 'Base prompt.', role: 'system' },
165
+ { content: 'Hello', role: 'user' },
166
+ ];
146
167
 
147
168
  const context = createContext(input);
148
169
  const result = await injector.process(context);
149
170
 
150
- expect(result.messages[0].content).toMatchSnapshot();
171
+ // Check injected message content
172
+ expect(result.messages[1].content).toMatchSnapshot();
151
173
  });
152
174
  });
153
175
 
@@ -158,15 +180,19 @@ describe('GroupContextInjector', () => {
158
180
  // Minimal config
159
181
  });
160
182
 
161
- const input: any[] = [{ role: 'system', content: 'Base prompt.' }];
183
+ const input: any[] = [
184
+ { role: 'system', content: 'Base prompt.' },
185
+ { role: 'user', content: 'Hello' },
186
+ ];
162
187
 
163
188
  const context = createContext(input);
164
189
  const result = await injector.process(context);
165
190
 
166
- const systemContent = result.messages[0].content;
191
+ // Check injected message content
192
+ const injectedContent = result.messages[1].content;
167
193
 
168
194
  // Even with minimal config, identity rules should be present
169
- expect(systemContent).toMatchSnapshot();
195
+ expect(injectedContent).toMatchSnapshot();
170
196
  });
171
197
  });
172
198
 
@@ -179,12 +205,16 @@ describe('GroupContextInjector', () => {
179
205
  systemPrompt: 'Empty group description',
180
206
  });
181
207
 
182
- const input: any[] = [{ content: 'Prompt.', role: 'system' }];
208
+ const input: any[] = [
209
+ { content: 'Prompt.', role: 'system' },
210
+ { content: 'Hello', role: 'user' },
211
+ ];
183
212
 
184
213
  const context = createContext(input);
185
214
  const result = await injector.process(context);
186
215
 
187
- expect(result.messages[0].content).toMatchSnapshot();
216
+ // Check injected message content
217
+ expect(result.messages[1].content).toMatchSnapshot();
188
218
  });
189
219
 
190
220
  it('should preserve other messages unchanged', async () => {
@@ -204,10 +234,16 @@ describe('GroupContextInjector', () => {
204
234
  const context = createContext(input);
205
235
  const result = await injector.process(context);
206
236
 
207
- // Only system message should be modified
208
- expect(result.messages[0].content).toContain('<group_context>');
209
- expect(result.messages[1].content).toBe('User message.');
210
- expect(result.messages[2].content).toBe('Assistant response.');
237
+ // System message should be unchanged
238
+ expect(result.messages[0].content).toBe('System prompt.');
239
+
240
+ // Injected message should be second
241
+ expect(result.messages[1].role).toBe('user');
242
+ expect(result.messages[1].content).toContain('<group_context>');
243
+
244
+ // Original messages should be preserved
245
+ expect(result.messages[2].content).toBe('User message.');
246
+ expect(result.messages[3].content).toBe('Assistant response.');
211
247
  });
212
248
  });
213
249
  });
@@ -1,9 +1,7 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`GroupContextInjector > Edge Cases > should handle empty members array 1`] = `
4
- "Prompt.
5
-
6
- <group_context>
4
+ "<group_context>
7
5
  You are "", acting as a in the multi-agent group "Empty Group".
8
6
  Your internal agent ID is (for system use only, never expose to users).
9
7
 
@@ -24,9 +22,7 @@ Empty group description
24
22
  `;
25
23
 
26
24
  exports[`GroupContextInjector > Identity Rules Section > should always include identity rules 1`] = `
27
- "Base prompt.
28
-
29
- <group_context>
25
+ "<group_context>
30
26
  You are "", acting as a in the multi-agent group "".
31
27
  Your internal agent ID is (for system use only, never expose to users).
32
28
 
@@ -47,9 +43,7 @@ Your internal agent ID is (for system use only, never expose to users).
47
43
  `;
48
44
 
49
45
  exports[`GroupContextInjector > Variable Replacement > should handle config with only group info 1`] = `
50
- "System prompt.
51
-
52
- <group_context>
46
+ "<group_context>
53
47
  You are "", acting as a in the multi-agent group "Test Group".
54
48
  Your internal agent ID is (for system use only, never expose to users).
55
49
 
@@ -70,9 +64,7 @@ Test group description
70
64
  `;
71
65
 
72
66
  exports[`GroupContextInjector > Variable Replacement > should handle config with only identity info 1`] = `
73
- "You are an editor.
74
-
75
- <group_context>
67
+ "<group_context>
76
68
  You are "Editor", acting as a participant in the multi-agent group "".
77
69
  Your internal agent ID is agt_editor (for system use only, never expose to users).
78
70
 
@@ -93,9 +85,7 @@ Your internal agent ID is agt_editor (for system use only, never expose to users
93
85
  `;
94
86
 
95
87
  exports[`GroupContextInjector > Variable Replacement > should handle empty config 1`] = `
96
- "Base prompt.
97
-
98
- <group_context>
88
+ "<group_context>
99
89
  You are "", acting as a in the multi-agent group "".
100
90
  Your internal agent ID is (for system use only, never expose to users).
101
91
 
@@ -50,9 +50,30 @@ describe('UserMemoryTopicRepository', () => {
50
50
 
51
51
  it('should return concatenated user message content', async () => {
52
52
  await serverDB.insert(messages).values([
53
- { id: 'msg-1', content: 'Hello', role: 'user', topicId, userId },
54
- { id: 'msg-2', content: 'Hi there!', role: 'assistant', topicId, userId },
55
- { id: 'msg-3', content: 'How are you?', role: 'user', topicId, userId },
53
+ {
54
+ id: 'msg-1',
55
+ content: 'Hello',
56
+ role: 'user',
57
+ topicId,
58
+ userId,
59
+ createdAt: new Date('2024-01-01'),
60
+ },
61
+ {
62
+ id: 'msg-2',
63
+ content: 'Hi there!',
64
+ role: 'assistant',
65
+ topicId,
66
+ userId,
67
+ createdAt: new Date('2024-01-02'),
68
+ },
69
+ {
70
+ id: 'msg-3',
71
+ content: 'How are you?',
72
+ role: 'user',
73
+ topicId,
74
+ userId,
75
+ createdAt: new Date('2024-01-03'),
76
+ },
56
77
  ]);
57
78
 
58
79
  const result = await repo.getUserMessagesQueryForTopic(topicId);
@@ -12,7 +12,6 @@ const ComfyUI: ModelProviderCard = {
12
12
  chatModels: [],
13
13
  description:
14
14
  'A powerful open-source workflow engine for image, video, and audio generation, supporting models like SD, FLUX, Qwen, Hunyuan, and WAN with node-based editing and private deployment.',
15
- enabled: true,
16
15
  id: 'comfyui',
17
16
  name: 'ComfyUI',
18
17
  settings: {
@@ -6,7 +6,6 @@ import { type ModelProviderCard } from '@/types/llm';
6
6
  const Fal: ModelProviderCard = {
7
7
  chatModels: [],
8
8
  description: 'A generative media platform built for developers.',
9
- enabled: true,
10
9
  id: 'fal',
11
10
  name: 'Fal',
12
11
  settings: {
@@ -5,8 +5,7 @@ export const ChatErrorType = {
5
5
  // ******* Business Error Semantics ******* //
6
6
 
7
7
  InvalidAccessCode: 'InvalidAccessCode', // is in valid password
8
- InvalidClerkUser: 'InvalidClerkUser', // is not Clerk User
9
- FreePlanLimit: 'FreePlanLimit', // is not Clerk User
8
+ FreePlanLimit: 'FreePlanLimit', // Free plan usage limit
10
9
  SubscriptionPlanLimit: 'SubscriptionPlanLimit', // Subscription user limit exceeded
11
10
  SubscriptionKeyMismatch: 'SubscriptionKeyMismatch', // Subscription key mismatch
12
11
 
@@ -4,34 +4,17 @@ import { extractBearerToken, getUserAuth } from '../auth';
4
4
 
5
5
  // Mock auth constants
6
6
  let mockEnableBetterAuth = false;
7
- let mockEnableClerk = false;
8
7
  let mockEnableNextAuth = false;
9
8
 
10
9
  vi.mock('@/envs/auth', () => ({
11
10
  get enableBetterAuth() {
12
11
  return mockEnableBetterAuth;
13
12
  },
14
- get enableClerk() {
15
- return mockEnableClerk;
16
- },
17
13
  get enableNextAuth() {
18
14
  return mockEnableNextAuth;
19
15
  },
20
16
  }));
21
17
 
22
- vi.mock('@/libs/clerk-auth', () => ({
23
- ClerkAuth: class {
24
- async getAuth() {
25
- return {
26
- clerkAuth: {
27
- redirectToSignIn: vi.fn(),
28
- },
29
- userId: 'clerk-user-id',
30
- };
31
- }
32
- },
33
- }));
34
-
35
18
  vi.mock('@/libs/next-auth', () => ({
36
19
  default: {
37
20
  auth: vi.fn().mockResolvedValue({
@@ -62,7 +45,6 @@ describe('getUserAuth', () => {
62
45
  beforeEach(() => {
63
46
  vi.clearAllMocks();
64
47
  mockEnableBetterAuth = false;
65
- mockEnableClerk = false;
66
48
  mockEnableNextAuth = false;
67
49
  });
68
50
 
@@ -70,22 +52,7 @@ describe('getUserAuth', () => {
70
52
  await expect(getUserAuth()).rejects.toThrow('Auth method is not enabled');
71
53
  });
72
54
 
73
- it('should return clerk auth when clerk is enabled', async () => {
74
- mockEnableClerk = true;
75
- mockEnableNextAuth = false;
76
-
77
- const auth = await getUserAuth();
78
-
79
- expect(auth).toEqual({
80
- clerkAuth: {
81
- redirectToSignIn: expect.any(Function),
82
- },
83
- userId: 'clerk-user-id',
84
- });
85
- });
86
-
87
55
  it('should return next auth when next auth is enabled', async () => {
88
- mockEnableClerk = false;
89
56
  mockEnableNextAuth = true;
90
57
 
91
58
  const auth = await getUserAuth();
@@ -100,20 +67,6 @@ describe('getUserAuth', () => {
100
67
  });
101
68
  });
102
69
 
103
- it('should prioritize clerk auth over next auth when both are enabled', async () => {
104
- mockEnableClerk = true;
105
- mockEnableNextAuth = true;
106
-
107
- const auth = await getUserAuth();
108
-
109
- expect(auth).toEqual({
110
- clerkAuth: {
111
- redirectToSignIn: expect.any(Function),
112
- },
113
- userId: 'clerk-user-id',
114
- });
115
- });
116
-
117
70
  it('should return better auth when better auth is enabled', async () => {
118
71
  mockEnableBetterAuth = true;
119
72
 
@@ -1,16 +1,8 @@
1
1
  import { headers } from 'next/headers';
2
2
 
3
- import { enableBetterAuth, enableClerk, enableNextAuth } from '@/envs/auth';
3
+ import { enableBetterAuth, enableNextAuth } from '@/envs/auth';
4
4
 
5
5
  export const getUserAuth = async () => {
6
- if (enableClerk) {
7
- const { ClerkAuth } = await import('@/libs/clerk-auth');
8
-
9
- const clerkAuth = new ClerkAuth();
10
-
11
- return await clerkAuth.getAuth();
12
- }
13
-
14
6
  if (enableBetterAuth) {
15
7
  const { auth: betterAuth } = await import('@/auth');
16
8
 
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shared utility to check for deprecated Clerk environment variables.
3
+ * Used by both prebuild.mts (build time) and startServer.js (Docker runtime).
4
+ *
5
+ * IMPORTANT: Keep this file as CommonJS (.js) for compatibility with startServer.js
6
+ */
7
+
8
+ const CLERK_MIGRATION_DOC_URL =
9
+ 'https://lobehub.com/docs/self-hosting/advanced/auth/clerk-to-betterauth';
10
+
11
+ const CLERK_ENV_VARS = [
12
+ 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
13
+ 'CLERK_SECRET_KEY',
14
+ 'CLERK_WEBHOOK_SECRET',
15
+ ];
16
+
17
+ /**
18
+ * Check for deprecated Clerk environment variables and exit if found
19
+ * @param {object} options
20
+ * @param {string} [options.action='redeploy'] - Action hint in error message ('redeploy' or 'restart')
21
+ */
22
+ function checkDeprecatedClerkEnv(options = {}) {
23
+ const { action = 'redeploy' } = options;
24
+ const foundClerkEnvVars = CLERK_ENV_VARS.filter((envVar) => process.env[envVar]);
25
+
26
+ if (foundClerkEnvVars.length > 0) {
27
+ console.error('\n' + '═'.repeat(70));
28
+ console.error('❌ ERROR: Clerk authentication is no longer supported!');
29
+ console.error('═'.repeat(70));
30
+ console.error('\nDetected deprecated Clerk environment variables:');
31
+ for (const envVar of foundClerkEnvVars) {
32
+ console.error(` • ${envVar}`);
33
+ }
34
+ console.error('\nClerk has been removed from LobeChat. Please migrate to Better Auth.');
35
+ console.error(`\n📖 Migration guide: ${CLERK_MIGRATION_DOC_URL}`);
36
+ console.error(`\nAfter migration, remove the Clerk environment variables and ${action}.`);
37
+ console.error('═'.repeat(70) + '\n');
38
+ process.exit(1);
39
+ }
40
+ }
41
+
42
+ module.exports = { checkDeprecatedClerkEnv };
@@ -1,10 +1,11 @@
1
1
  import { consola } from 'consola';
2
2
  import { readJsonSync, writeJSONSync } from 'fs-extra';
3
- import { markdownToTxt } from 'markdown-to-txt';
4
3
  import { existsSync, readFileSync } from 'node:fs';
5
4
  import { resolve } from 'node:path';
6
5
  import semver from 'semver';
7
6
 
7
+ import { markdownToTxt } from '@/utils/markdownToTxt';
8
+
8
9
  import { CHANGELOG_DIR, CHANGELOG_FILE } from './const';
9
10
 
10
11
  export interface ChangelogStaticItem {
@@ -1,5 +1,3 @@
1
- import type { ExternalAccountJSON, UserJSON } from '@clerk/backend';
2
-
3
1
  export type ClerkToBetterAuthMode = 'test' | 'prod';
4
2
  export type DatabaseDriver = 'neon' | 'node';
5
3
 
@@ -19,27 +17,62 @@ export type CSVUserRow = {
19
17
  verified_phone_numbers: string;
20
18
  };
21
19
 
22
- export type ClerkExternalAccount = Pick<
23
- ExternalAccountJSON,
24
- 'id' | 'provider' | 'provider_user_id' | 'approved_scopes'
25
- > & {
20
+ // Clerk API response types (no SDK dependency)
21
+ export interface ClerkApiExternalAccount {
22
+ approved_scopes: string;
23
+ created_at?: number;
24
+ id: string;
25
+ provider: string;
26
+ provider_user_id: string;
27
+ updated_at?: number;
28
+ verification?: { status: string };
29
+ }
30
+
31
+ export interface ClerkApiEmailAddress {
32
+ email_address: string;
33
+ id: string;
34
+ }
35
+
36
+ export interface ClerkApiUser {
37
+ banned: boolean;
38
+ created_at: number;
39
+ email_addresses?: ClerkApiEmailAddress[];
40
+ external_accounts?: ClerkApiExternalAccount[];
41
+ id: string;
42
+ image_url: string;
43
+ lockout_expires_in_seconds: number | null;
44
+ password_enabled: boolean;
45
+ password_last_updated_at: number | null;
46
+ primary_email_address_id: string | null;
47
+ two_factor_enabled: boolean;
48
+ updated_at: number;
49
+ }
50
+
51
+ export interface ClerkApiUserListResponse {
52
+ data: ClerkApiUser[];
53
+ total_count: number;
54
+ }
55
+
56
+ export interface ClerkExternalAccount {
57
+ approved_scopes: string;
26
58
  created_at?: number;
59
+ id: string;
60
+ provider: string;
61
+ provider_user_id: string;
27
62
  updated_at?: number;
28
63
  verificationStatus?: boolean;
29
- };
64
+ }
30
65
 
31
- export type ClerkUser = Pick<
32
- UserJSON,
33
- | 'id'
34
- | 'image_url'
35
- | 'created_at'
36
- | 'updated_at'
37
- | 'password_last_updated_at'
38
- | 'password_enabled'
39
- | 'banned'
40
- | 'two_factor_enabled'
41
- | 'lockout_expires_in_seconds'
42
- > & {
66
+ export interface ClerkUser {
67
+ banned: boolean;
68
+ created_at: number;
43
69
  external_accounts: ClerkExternalAccount[];
70
+ id: string;
71
+ image_url: string;
72
+ lockout_expires_in_seconds: number | null;
73
+ password_enabled: boolean;
74
+ password_last_updated_at: number | null;
44
75
  primaryEmail?: string;
45
- };
76
+ two_factor_enabled: boolean;
77
+ updated_at: number;
78
+ }