@lobehub/lobehub 2.0.0-next.284 → 2.0.0-next.286

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 (38) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/en-US/setting.json +1 -0
  4. package/locales/en-US/subscription.json +2 -0
  5. package/locales/zh-CN/setting.json +1 -0
  6. package/locales/zh-CN/subscription.json +2 -0
  7. package/package.json +1 -1
  8. package/packages/builtin-tool-agent-builder/src/ExecutionRuntime/index.ts +79 -2
  9. package/packages/builtin-tool-agent-builder/src/client/Intervention/InstallPlugin.tsx +66 -5
  10. package/packages/builtin-tool-agent-builder/src/client/Render/InstallPlugin.tsx +12 -4
  11. package/packages/builtin-tool-agent-builder/src/manifest.ts +3 -2
  12. package/packages/builtin-tool-agent-builder/src/systemRole.ts +8 -8
  13. package/packages/builtin-tool-agent-builder/src/types.ts +7 -3
  14. package/packages/business/const/src/index.ts +0 -3
  15. package/packages/context-engine/src/providers/AgentBuilderContextInjector.ts +20 -4
  16. package/packages/context-engine/src/providers/GroupAgentBuilderContextInjector.ts +18 -2
  17. package/packages/model-bank/src/aiModels/lobehub.ts +20 -1
  18. package/packages/model-runtime/src/providers/fal/index.test.ts +176 -1
  19. package/packages/model-runtime/src/providers/fal/index.ts +3 -1
  20. package/src/app/[variants]/(main)/agent/features/Conversation/AgentWelcome/AddButton.tsx +2 -1
  21. package/src/app/[variants]/(main)/agent/features/Conversation/Header/ShareButton/index.tsx +5 -3
  22. package/src/app/[variants]/(main)/agent/features/Conversation/MainChatInput/MessageFromUrl.tsx +57 -9
  23. package/src/app/[variants]/(main)/agent/profile/features/Header/AgentPublishButton/PublishButton.tsx +5 -8
  24. package/src/app/[variants]/(main)/agent/profile/features/Header/index.tsx +0 -2
  25. package/src/app/[variants]/(main)/agent/profile/features/ProfileEditor/index.tsx +2 -0
  26. package/src/app/[variants]/(main)/community/(detail)/provider/features/Details/Nav.tsx +27 -18
  27. package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +5 -3
  28. package/src/app/[variants]/(main)/group/features/Conversation/MainChatInput/MessageFromUrl.tsx +24 -10
  29. package/src/app/[variants]/onboarding/_layout/style.ts +10 -20
  30. package/src/features/ProfileEditor/AgentTool.tsx +68 -12
  31. package/src/features/ProfileEditor/PluginTag.tsx +56 -3
  32. package/src/locales/default/setting.ts +1 -0
  33. package/src/locales/default/subscription.ts +2 -0
  34. package/src/services/chat/index.ts +24 -1
  35. package/src/services/chat/mecha/contextEngineering.ts +28 -2
  36. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +11 -1
  37. package/src/store/user/slices/settings/action.test.ts +25 -0
  38. package/src/store/user/slices/settings/action.ts +11 -0
@@ -1,6 +1,11 @@
1
1
  'use client';
2
2
 
3
- import { KLAVIS_SERVER_TYPES, type KlavisServerType } from '@lobechat/const';
3
+ import {
4
+ KLAVIS_SERVER_TYPES,
5
+ type KlavisServerType,
6
+ LOBEHUB_SKILL_PROVIDERS,
7
+ type LobehubSkillProviderType,
8
+ } from '@lobechat/const';
4
9
  import { Avatar, Button, Flexbox, Icon, type ItemType, Segmented } from '@lobehub/ui';
5
10
  import { createStaticStyles, cssVar } from 'antd-style';
6
11
  import isEqual from 'fast-deep-equal';
@@ -10,6 +15,7 @@ import { useTranslation } from 'react-i18next';
10
15
 
11
16
  import PluginAvatar from '@/components/Plugins/PluginAvatar';
12
17
  import KlavisServerItem from '@/features/ChatInput/ActionBar/Tools/KlavisServerItem';
18
+ import LobehubSkillServerItem from '@/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem';
13
19
  import ToolItem from '@/features/ChatInput/ActionBar/Tools/ToolItem';
14
20
  import ActionDropdown from '@/features/ChatInput/ActionBar/components/ActionDropdown';
15
21
  import PluginStore from '@/features/PluginStore';
@@ -22,6 +28,7 @@ import { useToolStore } from '@/store/tool';
22
28
  import {
23
29
  builtinToolSelectors,
24
30
  klavisStoreSelectors,
31
+ lobehubSkillStoreSelectors,
25
32
  pluginSelectors,
26
33
  } from '@/store/tool/selectors';
27
34
  import { type LobeToolMetaWithAvailability } from '@/store/tool/slices/builtin/selectors';
@@ -81,6 +88,19 @@ const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label
81
88
  return <Icon className={styles.icon} fill={cssVar.colorText} icon={icon} size={18} />;
82
89
  });
83
90
 
91
+ /**
92
+ * LobeHub Skill Provider 图标组件
93
+ */
94
+ const LobehubSkillIcon = memo<Pick<LobehubSkillProviderType, 'icon' | 'label'>>(
95
+ ({ icon, label }) => {
96
+ if (typeof icon === 'string') {
97
+ return <img alt={label} className={styles.icon} height={18} src={icon} width={18} />;
98
+ }
99
+
100
+ return <Icon className={styles.icon} fill={cssVar.colorText} icon={icon} size={18} />;
101
+ },
102
+ );
103
+
84
104
  export interface AgentToolProps {
85
105
  /**
86
106
  * Optional agent ID to use instead of currentAgentConfig
@@ -125,12 +145,18 @@ const AgentTool = memo<AgentToolProps>(
125
145
  );
126
146
 
127
147
  // Web browsing uses searchMode instead of plugins array - use byId selector
128
- const isSearchEnabled = useAgentStore(chatConfigByIdSelectors.isEnableSearchById(effectiveAgentId));
148
+ const isSearchEnabled = useAgentStore(
149
+ chatConfigByIdSelectors.isEnableSearchById(effectiveAgentId),
150
+ );
129
151
 
130
152
  // Klavis 相关状态
131
153
  const allKlavisServers = useToolStore(klavisStoreSelectors.getServers, isEqual);
132
154
  const isKlavisEnabledInEnv = useServerConfigStore(serverConfigSelectors.enableKlavis);
133
155
 
156
+ // LobeHub Skill 相关状态
157
+ const allLobehubSkillServers = useToolStore(lobehubSkillStoreSelectors.getServers, isEqual);
158
+ const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
159
+
134
160
  // Plugin store modal state
135
161
  const [modalOpen, setModalOpen] = useState(false);
136
162
  const [updating, setUpdating] = useState(false);
@@ -140,10 +166,12 @@ const AgentTool = memo<AgentToolProps>(
140
166
  const isInitializedRef = useRef(false);
141
167
 
142
168
  // Fetch plugins
143
- const [useFetchPluginStore, useFetchUserKlavisServers] = useToolStore((s) => [
144
- s.useFetchPluginStore,
145
- s.useFetchUserKlavisServers,
146
- ]);
169
+ const [useFetchPluginStore, useFetchUserKlavisServers, useFetchLobehubSkillConnections] =
170
+ useToolStore((s) => [
171
+ s.useFetchPluginStore,
172
+ s.useFetchUserKlavisServers,
173
+ s.useFetchLobehubSkillConnections,
174
+ ]);
147
175
  useFetchPluginStore();
148
176
  useFetchInstalledPlugins();
149
177
  useCheckPluginsIsInstalled(plugins);
@@ -151,6 +179,9 @@ const AgentTool = memo<AgentToolProps>(
151
179
  // 使用 SWR 加载用户的 Klavis 集成(从数据库)
152
180
  useFetchUserKlavisServers(isKlavisEnabledInEnv);
153
181
 
182
+ // 使用 SWR 加载用户的 LobeHub Skill 连接
183
+ useFetchLobehubSkillConnections(isLobehubSkillEnabled);
184
+
154
185
  // Toggle web browsing via searchMode - use byId action
155
186
  const toggleWebBrowsing = useCallback(async () => {
156
187
  if (!effectiveAgentId) return;
@@ -270,6 +301,19 @@ const AgentTool = memo<AgentToolProps>(
270
301
  [isKlavisEnabledInEnv, allKlavisServers],
271
302
  );
272
303
 
304
+ // LobeHub Skill Provider 列表项
305
+ const lobehubSkillItems = useMemo(
306
+ () =>
307
+ isLobehubSkillEnabled
308
+ ? LOBEHUB_SKILL_PROVIDERS.map((provider) => ({
309
+ icon: <LobehubSkillIcon icon={provider.icon} label={provider.label} />,
310
+ key: provider.id, // 使用 provider.id 作为 key,与 pluginId 保持一致
311
+ label: <LobehubSkillServerItem label={provider.label} provider={provider.id} />,
312
+ }))
313
+ : [],
314
+ [isLobehubSkillEnabled, allLobehubSkillServers],
315
+ );
316
+
273
317
  // Handle plugin remove via Tag close - use byId actions
274
318
  const handleRemovePlugin =
275
319
  (
@@ -292,7 +336,7 @@ const AgentTool = memo<AgentToolProps>(
292
336
  (id) => !builtinList.some((b) => b.identifier === id),
293
337
  ).length;
294
338
 
295
- // 合并 builtin 工具和 Klavis 服务器
339
+ // 合并 builtin 工具、LobeHub Skill Providers 和 Klavis 服务器
296
340
  const builtinItems = useMemo(
297
341
  () => [
298
342
  // 原有的 builtin 工具
@@ -312,10 +356,12 @@ const AgentTool = memo<AgentToolProps>(
312
356
  />
313
357
  ),
314
358
  })),
359
+ // LobeHub Skill Providers
360
+ ...lobehubSkillItems,
315
361
  // Klavis 服务器
316
362
  ...klavisServerItems,
317
363
  ],
318
- [filteredBuiltinList, klavisServerItems, isToolEnabled, handleToggleTool],
364
+ [filteredBuiltinList, klavisServerItems, lobehubSkillItems, isToolEnabled, handleToggleTool],
319
365
  );
320
366
 
321
367
  // Plugin items for dropdown
@@ -413,8 +459,17 @@ const AgentTool = memo<AgentToolProps>(
413
459
  plugins.includes(item.key as string),
414
460
  );
415
461
 
416
- // 合并 builtin Klavis
417
- const allBuiltinItems = [...enabledBuiltinItems, ...connectedKlavisItems];
462
+ // 已连接的 LobeHub Skill Providers
463
+ const connectedLobehubSkillItems = lobehubSkillItems.filter((item) =>
464
+ plugins.includes(item.key as string),
465
+ );
466
+
467
+ // 合并 builtin、LobeHub Skill 和 Klavis
468
+ const allBuiltinItems = [
469
+ ...enabledBuiltinItems,
470
+ ...connectedKlavisItems,
471
+ ...connectedLobehubSkillItems,
472
+ ];
418
473
 
419
474
  if (allBuiltinItems.length > 0) {
420
475
  items.push({
@@ -462,6 +517,7 @@ const AgentTool = memo<AgentToolProps>(
462
517
  }, [
463
518
  filteredBuiltinList,
464
519
  klavisServerItems,
520
+ lobehubSkillItems,
465
521
  installedPluginList,
466
522
  plugins,
467
523
  isToolEnabled,
@@ -526,7 +582,7 @@ const AgentTool = memo<AgentToolProps>(
526
582
  overflowY: 'visible',
527
583
  },
528
584
  }}
529
- minHeight={isKlavisEnabledInEnv ? 500 : undefined}
585
+ minHeight={isKlavisEnabledInEnv || isLobehubSkillEnabled ? 500 : undefined}
530
586
  minWidth={400}
531
587
  placement={'bottomLeft'}
532
588
  popupRender={(menu) => (
@@ -554,7 +610,7 @@ const AgentTool = memo<AgentToolProps>(
554
610
  className={styles.scroller}
555
611
  style={{
556
612
  maxHeight: 500,
557
- minHeight: isKlavisEnabledInEnv ? 500 : undefined,
613
+ minHeight: isKlavisEnabledInEnv || isLobehubSkillEnabled ? 500 : undefined,
558
614
  }}
559
615
  >
560
616
  {menu}
@@ -1,6 +1,11 @@
1
1
  'use client';
2
2
 
3
- import { KLAVIS_SERVER_TYPES, type KlavisServerType } from '@lobechat/const';
3
+ import {
4
+ KLAVIS_SERVER_TYPES,
5
+ type KlavisServerType,
6
+ LOBEHUB_SKILL_PROVIDERS,
7
+ type LobehubSkillProviderType,
8
+ } from '@lobechat/const';
4
9
  import { Avatar, Icon, Tag } from '@lobehub/ui';
5
10
  import { createStaticStyles, cssVar } from 'antd-style';
6
11
  import isEqual from 'fast-deep-equal';
@@ -16,6 +21,7 @@ import { useToolStore } from '@/store/tool';
16
21
  import {
17
22
  builtinToolSelectors,
18
23
  klavisStoreSelectors,
24
+ lobehubSkillStoreSelectors,
19
25
  pluginSelectors,
20
26
  } from '@/store/tool/selectors';
21
27
  import { type LobeToolMetaWithAvailability } from '@/store/tool/slices/builtin/selectors';
@@ -31,6 +37,19 @@ const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label
31
37
  return <Icon fill={cssVar.colorText} icon={icon} size={16} />;
32
38
  });
33
39
 
40
+ /**
41
+ * LobeHub Skill Provider 图标组件
42
+ */
43
+ const LobehubSkillIcon = memo<Pick<LobehubSkillProviderType, 'icon' | 'label'>>(
44
+ ({ icon, label }) => {
45
+ if (typeof icon === 'string') {
46
+ return <img alt={label} height={16} src={icon} style={{ flexShrink: 0 }} width={16} />;
47
+ }
48
+
49
+ return <Icon fill={cssVar.colorText} icon={icon} size={16} />;
50
+ },
51
+ );
52
+
34
53
  const styles = createStaticStyles(({ css, cssVar }) => ({
35
54
  notInstalledTag: css`
36
55
  border-color: ${cssVar.colorWarningBorder};
@@ -80,10 +99,14 @@ const PluginTag = memo<PluginTagProps>(
80
99
  const allKlavisServers = useToolStore(klavisStoreSelectors.getServers, isEqual);
81
100
  const isKlavisEnabledInEnv = useServerConfigStore(serverConfigSelectors.enableKlavis);
82
101
 
102
+ // LobeHub Skill 相关状态
103
+ const allLobehubSkillServers = useToolStore(lobehubSkillStoreSelectors.getServers, isEqual);
104
+ const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
105
+
83
106
  // Check if plugin is installed
84
107
  const isInstalled = useToolStore(pluginSelectors.isPluginInstalled(identifier));
85
108
 
86
- // Try to find in local lists first (including Klavis)
109
+ // Try to find in local lists first (including Klavis and LobehubSkill)
87
110
  const localMeta = useMemo(() => {
88
111
  // Check if it's a Klavis server type
89
112
  if (isKlavisEnabledInEnv) {
@@ -102,6 +125,23 @@ const PluginTag = memo<PluginTagProps>(
102
125
  }
103
126
  }
104
127
 
128
+ // Check if it's a LobeHub Skill provider
129
+ if (isLobehubSkillEnabled) {
130
+ const lobehubSkillProvider = LOBEHUB_SKILL_PROVIDERS.find((p) => p.id === identifier);
131
+ if (lobehubSkillProvider) {
132
+ // Check if this LobehubSkill provider is connected
133
+ const connectedServer = allLobehubSkillServers.find((s) => s.identifier === identifier);
134
+ return {
135
+ availableInWeb: true,
136
+ icon: lobehubSkillProvider.icon,
137
+ isInstalled: !!connectedServer,
138
+ label: lobehubSkillProvider.label,
139
+ title: lobehubSkillProvider.label,
140
+ type: 'lobehub-skill' as const,
141
+ };
142
+ }
143
+ }
144
+
105
145
  const builtinMeta = builtinList.find((p) => p.identifier === identifier);
106
146
  if (builtinMeta) {
107
147
  // availableInWeb is only present when using allMetaList
@@ -130,7 +170,15 @@ const PluginTag = memo<PluginTagProps>(
130
170
  }
131
171
 
132
172
  return null;
133
- }, [identifier, builtinList, installedPluginList, isKlavisEnabledInEnv, allKlavisServers]);
173
+ }, [
174
+ identifier,
175
+ builtinList,
176
+ installedPluginList,
177
+ isKlavisEnabledInEnv,
178
+ allKlavisServers,
179
+ isLobehubSkillEnabled,
180
+ allLobehubSkillServers,
181
+ ]);
134
182
 
135
183
  // Fetch from remote if not found locally
136
184
  const usePluginDetail = useDiscoverStore((s) => s.usePluginDetail);
@@ -162,6 +210,11 @@ const PluginTag = memo<PluginTagProps>(
162
210
  return <KlavisIcon icon={meta.icon} label={meta.label} />;
163
211
  }
164
212
 
213
+ // LobeHub Skill type has icon property
214
+ if (meta.type === 'lobehub-skill' && 'icon' in meta && 'label' in meta) {
215
+ return <LobehubSkillIcon icon={meta.icon} label={meta.label} />;
216
+ }
217
+
165
218
  // Builtin type has avatar
166
219
  if (meta.type === 'builtin' && 'avatar' in meta && meta.avatar) {
167
220
  return <Avatar avatar={meta.avatar} shape={'square'} size={16} style={{ flexShrink: 0 }} />;
@@ -278,6 +278,7 @@ export default {
278
278
  'plugin.settings.title': '{{id}} Skill Configuration',
279
279
  'plugin.settings.tooltip': 'Skill Configuration',
280
280
  'plugin.store': 'Skill Store',
281
+ 'publishToCommunity': 'Publish to Community',
281
282
  'settingAgent.avatar.sizeExceeded': 'Image size exceeds 1MB limit, please choose a smaller image',
282
283
  'settingAgent.avatar.title': 'Avatar',
283
284
  'settingAgent.backgroundColor.title': 'Background Color',
@@ -62,6 +62,8 @@ export default {
62
62
  'funds.packages.expiresIn': 'Expires in {{days}} days',
63
63
  'funds.packages.expiresToday': 'Expires today',
64
64
  'funds.packages.expiringSoon': 'Expiring soon',
65
+ 'funds.packages.gift': 'Gift',
66
+ 'funds.packages.giftedOn': 'Gifted on {{date}}',
65
67
  'funds.packages.noPackages': 'No credit packages',
66
68
  'funds.packages.purchaseFirst': 'Purchase your first credit package',
67
69
  'funds.packages.purchasedOn': 'Purchased on {{date}}',
@@ -1,5 +1,5 @@
1
1
  import { AgentBuilderIdentifier } from '@lobechat/builtin-tool-agent-builder';
2
- import { KLAVIS_SERVER_TYPES } from '@lobechat/const';
2
+ import { KLAVIS_SERVER_TYPES, LOBEHUB_SKILL_PROVIDERS } from '@lobechat/const';
3
3
  import type { OfficialToolItem } from '@lobechat/context-engine';
4
4
  import {
5
5
  type FetchSSEOptions,
@@ -41,6 +41,7 @@ import { getToolStoreState } from '@/store/tool';
41
41
  import {
42
42
  builtinToolSelectors,
43
43
  klavisStoreSelectors,
44
+ lobehubSkillStoreSelectors,
44
45
  pluginSelectors,
45
46
  } from '@/store/tool/selectors';
46
47
  import { getUserStoreState, useUserStore } from '@/store/user';
@@ -221,6 +222,28 @@ class ChatService {
221
222
  }
222
223
  }
223
224
 
225
+ // Get LobehubSkill providers (if enabled)
226
+ const isLobehubSkillEnabled =
227
+ typeof window !== 'undefined' &&
228
+ window.global_serverConfigStore?.getState()?.serverConfig?.enableLobehubSkill;
229
+
230
+ if (isLobehubSkillEnabled) {
231
+ const allLobehubSkillServers = lobehubSkillStoreSelectors.getServers(toolState);
232
+
233
+ for (const provider of LOBEHUB_SKILL_PROVIDERS) {
234
+ const server = allLobehubSkillServers.find((s) => s.identifier === provider.id);
235
+
236
+ officialTools.push({
237
+ description: `LobeHub Skill Provider: ${provider.label}`,
238
+ enabled: enabledPlugins.includes(provider.id),
239
+ identifier: provider.id,
240
+ installed: !!server,
241
+ name: provider.label,
242
+ type: 'lobehub-skill',
243
+ });
244
+ }
245
+ }
246
+
224
247
  agentBuilderContext = {
225
248
  ...baseContext,
226
249
  officialTools,
@@ -1,7 +1,7 @@
1
1
  import { AgentBuilderIdentifier } from '@lobechat/builtin-tool-agent-builder';
2
2
  import { GroupAgentBuilderIdentifier } from '@lobechat/builtin-tool-group-agent-builder';
3
3
  import { GTDIdentifier } from '@lobechat/builtin-tool-gtd';
4
- import { KLAVIS_SERVER_TYPES, isDesktop } from '@lobechat/const';
4
+ import { KLAVIS_SERVER_TYPES, LOBEHUB_SKILL_PROVIDERS, isDesktop } from '@lobechat/const';
5
5
  import {
6
6
  type AgentBuilderContext,
7
7
  type AgentGroupConfig,
@@ -29,7 +29,11 @@ import { getChatGroupStoreState } from '@/store/agentGroup';
29
29
  import { agentGroupSelectors } from '@/store/agentGroup/selectors';
30
30
  import { getChatStoreState } from '@/store/chat';
31
31
  import { getToolStoreState } from '@/store/tool';
32
- import { builtinToolSelectors, klavisStoreSelectors } from '@/store/tool/selectors';
32
+ import {
33
+ builtinToolSelectors,
34
+ klavisStoreSelectors,
35
+ lobehubSkillStoreSelectors,
36
+ } from '@/store/tool/selectors';
33
37
 
34
38
  import { isCanUseVideo, isCanUseVision } from '../helper';
35
39
  import {
@@ -217,6 +221,28 @@ export const contextEngineering = async ({
217
221
  }
218
222
  }
219
223
 
224
+ // Get LobehubSkill providers (if enabled)
225
+ const isLobehubSkillEnabled =
226
+ typeof window !== 'undefined' &&
227
+ window.global_serverConfigStore?.getState()?.serverConfig?.enableLobehubSkill;
228
+
229
+ if (isLobehubSkillEnabled) {
230
+ const allLobehubSkillServers = lobehubSkillStoreSelectors.getServers(toolState);
231
+
232
+ for (const provider of LOBEHUB_SKILL_PROVIDERS) {
233
+ const server = allLobehubSkillServers.find((s) => s.identifier === provider.id);
234
+
235
+ officialTools.push({
236
+ description: `LobeHub Skill Provider: ${provider.label}`,
237
+ enabled: enabledPlugins.includes(provider.id),
238
+ identifier: provider.id,
239
+ installed: !!server,
240
+ name: provider.label,
241
+ type: 'lobehub-skill',
242
+ });
243
+ }
244
+ }
245
+
220
246
  groupAgentBuilderContext = {
221
247
  config: {
222
248
  openingMessage: activeGroupDetail.config?.openingMessage || undefined,
@@ -123,10 +123,20 @@ export const pluginTypes: StateCreator<
123
123
  const context = operationId ? { operationId } : undefined;
124
124
 
125
125
  // Get agent ID, group ID, and topic ID from operation context
126
- const agentId = operation?.context?.agentId;
126
+ let agentId = operation?.context?.agentId;
127
127
  let groupId = operation?.context?.groupId;
128
128
  const topicId = operation?.context?.topicId;
129
129
 
130
+ // For agent-builder tools, inject activeAgentId from store if not in context
131
+ // This is needed because AgentBuilderProvider uses a separate scope for messages
132
+ // but the tools need the correct agentId for execution
133
+ if (payload.identifier === 'lobe-agent-builder') {
134
+ const activeAgentId = get().activeAgentId;
135
+ if (activeAgentId) {
136
+ agentId = activeAgentId;
137
+ }
138
+ }
139
+
130
140
  // For group-agent-builder tools, inject activeGroupId from store if not in context
131
141
  // This is needed because AgentBuilderProvider uses a separate scope for messages
132
142
  // but still needs groupId for tool execution
@@ -84,6 +84,31 @@ describe('SettingsAction', () => {
84
84
  expect.any(AbortSignal),
85
85
  );
86
86
  });
87
+
88
+ it('should include field in diffs when user resets it to default value', async () => {
89
+ const { result } = renderHook(() => useUserStore());
90
+
91
+ // First, set memory.enabled to false (non-default value)
92
+ await act(async () => {
93
+ await result.current.setSettings({ memory: { enabled: false } });
94
+ });
95
+
96
+ expect(userService.updateUserSettings).toHaveBeenLastCalledWith(
97
+ expect.objectContaining({ memory: { enabled: false } }),
98
+ expect.any(AbortSignal),
99
+ );
100
+
101
+ // Then, reset memory.enabled back to true (default value)
102
+ // This should still include memory in the diffs to override the previously saved value
103
+ await act(async () => {
104
+ await result.current.setSettings({ memory: { enabled: true } });
105
+ });
106
+
107
+ expect(userService.updateUserSettings).toHaveBeenLastCalledWith(
108
+ expect.objectContaining({ memory: { enabled: true } }),
109
+ expect.any(AbortSignal),
110
+ );
111
+ });
87
112
  });
88
113
 
89
114
  describe('updateDefaultAgent', () => {
@@ -103,6 +103,17 @@ export const createSettingsSlice: StateCreator<
103
103
  if (isEqual(prevSetting, nextSettings)) return;
104
104
 
105
105
  const diffs = difference(nextSettings, defaultSettings);
106
+
107
+ // When user resets a field to default value, we need to explicitly include it in diffs
108
+ // to override the previously saved non-default value in the backend
109
+ const changedFields = difference(nextSettings, prevSetting);
110
+ for (const key of Object.keys(changedFields)) {
111
+ // Only handle fields that were previously set by user (exist in prevSetting)
112
+ if (key in prevSetting && !(key in diffs)) {
113
+ (diffs as any)[key] = (nextSettings as any)[key];
114
+ }
115
+ }
116
+
106
117
  set({ settings: diffs }, false, 'optimistic_updateSettings');
107
118
 
108
119
  const abortController = get().internal_createSignal();