@lobehub/lobehub 2.0.0-next.207 → 2.0.0-next.208

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.208](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.207...v2.0.0-next.208)
6
+
7
+ <sup>Released on **2026-01-04**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Auto jump to group.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Auto jump to group, closes [#11187](https://github.com/lobehub/lobe-chat/issues/11187) ([e43578a](https://github.com/lobehub/lobe-chat/commit/e43578a))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ## [Version 2.0.0-next.207](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.206...v2.0.0-next.207)
6
31
 
7
32
  <sup>Released on **2026-01-04**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Auto jump to group."
6
+ ]
7
+ },
8
+ "date": "2026-01-04",
9
+ "version": "2.0.0-next.208"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "fixes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.207",
3
+ "version": "2.0.0-next.208",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -4,7 +4,7 @@ import { AIBaseModelCard } from 'model-bank';
4
4
  import type { ModelProviderKey } from '../types';
5
5
 
6
6
  export interface ModelProcessorConfig {
7
- excludeKeywords?: readonly string[]; // 对符合的模型不添加标签
7
+ excludeKeywords?: readonly string[]; // Do not add tags to models that match
8
8
  functionCallKeywords?: readonly string[];
9
9
  imageOutputKeywords?: readonly string[];
10
10
  reasoningKeywords?: readonly string[];
@@ -13,10 +13,10 @@ export interface ModelProcessorConfig {
13
13
  visionKeywords?: readonly string[];
14
14
  }
15
15
 
16
- // 默认关键字:任意包含 -search 的模型 ID 视为支持联网搜索
16
+ // Default keyword: any model ID containing -search is considered to support internet search
17
17
  const DEFAULT_SEARCH_KEYWORDS = ['-search'] as const;
18
18
 
19
- // 模型能力标签关键词配置
19
+ // Model capability tag keyword configuration
20
20
  export const MODEL_LIST_CONFIGS = {
21
21
  anthropic: {
22
22
  functionCallKeywords: ['claude'],
@@ -136,7 +136,7 @@ export const MODEL_LIST_CONFIGS = {
136
136
  },
137
137
  } as const;
138
138
 
139
- // 模型所有者 (提供商) 关键词配置
139
+ // Model owner (provider) keyword configuration
140
140
  export const MODEL_OWNER_DETECTION_CONFIG = {
141
141
  anthropic: ['claude'],
142
142
  comfyui: ['comfyui/'], // ComfyUI models detection - all ComfyUI models have comfyui/ prefix
@@ -159,7 +159,7 @@ export const MODEL_OWNER_DETECTION_CONFIG = {
159
159
  zhipu: ['glm'],
160
160
  } as const;
161
161
 
162
- // 图像模型关键词配置
162
+ // Image model keyword configuration
163
163
  export const IMAGE_MODEL_KEYWORDS = [
164
164
  'dall-e',
165
165
  'dalle',
@@ -173,25 +173,25 @@ export const IMAGE_MODEL_KEYWORDS = [
173
173
  'wanxiang',
174
174
  'DESCRIBE',
175
175
  'UPSCALE',
176
- '!gemini', // 排除 gemini 模型,即使包含 -image 也是 chat 模型
176
+ '!gemini', // Exclude gemini models, they are chat models even if they contain -image
177
177
  '-image',
178
178
  '^V3',
179
179
  '^V_2',
180
180
  '^V_1',
181
181
  ] as const;
182
182
 
183
- // 嵌入模型关键词配置
183
+ // Embedding model keyword configuration
184
184
  export const EMBEDDING_MODEL_KEYWORDS = ['embedding', 'embed', 'bge', 'm3e'] as const;
185
185
 
186
186
  /**
187
- * 检测关键词列表是否匹配模型ID(支持多种匹配模式)
188
- * @param modelId 模型ID(小写)
189
- * @param keywords 关键词列表,支持以下前缀:
190
- * - ^ 开头:只在模型ID开头匹配
191
- * - ! 开头:排除匹配,优先级最高
192
- * - re: 开头:正则表达式匹配(支持 !re: 正则排除)
193
- * - 无前缀:包含匹配(默认行为)
194
- * @returns 是否匹配(排除逻辑优先)
187
+ * Detect whether a keyword list matches a model ID (supports multiple matching patterns)
188
+ * @param modelId Model ID (lowercase)
189
+ * @param keywords Keyword list, supports the following prefixes:
190
+ * - ^ prefix: match only at the start of model ID
191
+ * - ! prefix: exclude match, highest priority
192
+ * - re: prefix: regular expression match (supports !re: for regex exclusion)
193
+ * - no prefix: contains match (default behavior)
194
+ * @returns Whether it matches (exclusion logic takes priority)
195
195
  */
196
196
  const isKeywordListMatch = (modelId: string, keywords: readonly string[]): boolean => {
197
197
  const matchKeyword = (keyword: string): boolean => {
@@ -212,7 +212,7 @@ const isKeywordListMatch = (modelId: string, keywords: readonly string[]): boole
212
212
  return modelId.includes(rawKeyword);
213
213
  };
214
214
 
215
- // 先检查排除规则(感叹号开头,包括 !re:)
215
+ // First check exclusion rules (starting with exclamation mark, including !re:)
216
216
  const excludeKeywords = keywords.filter((keyword) => keyword.startsWith('!'));
217
217
  const includeKeywords = keywords.filter((keyword) => !keyword.startsWith('!'));
218
218
 
@@ -222,15 +222,15 @@ const isKeywordListMatch = (modelId: string, keywords: readonly string[]): boole
222
222
  }
223
223
  }
224
224
 
225
- // 检查包含规则
225
+ // Check inclusion rules
226
226
  return includeKeywords.some((keyword) => matchKeyword(keyword));
227
227
  };
228
228
 
229
229
  /**
230
- * 根据提供商类型查找对应的本地模型配置
231
- * @param modelId 模型ID
232
- * @param provider 提供商类型
233
- * @returns 匹配的本地模型配置
230
+ * Find the corresponding local model configuration based on provider type
231
+ * @param modelId Model ID
232
+ * @param provider Provider type
233
+ * @returns Matching local model configuration
234
234
  */
235
235
  const findKnownModelByProvider = async (
236
236
  modelId: string,
@@ -239,32 +239,32 @@ const findKnownModelByProvider = async (
239
239
  const lowerModelId = modelId.toLowerCase();
240
240
 
241
241
  try {
242
- // 尝试动态导入对应的配置文件
242
+ // Attempt to dynamically import the corresponding configuration file
243
243
  const modules = await import('model-bank');
244
244
 
245
- // 如果提供商配置文件不存在,跳过
245
+ // If provider configuration file doesn't exist, skip
246
246
  if (!(provider in modules)) {
247
247
  return null;
248
248
  }
249
249
 
250
250
  const providerModels = modules[provider as keyof typeof modules] as AIBaseModelCard[];
251
251
 
252
- // 如果导入成功且有数据,进行查找
252
+ // If import succeeds and has data, perform search
253
253
  if (Array.isArray(providerModels)) {
254
254
  return providerModels.find((m) => m.id.toLowerCase() === lowerModelId);
255
255
  }
256
256
 
257
257
  return null;
258
258
  } catch {
259
- // 如果导入失败(文件不存在或其他错误),返回 null
259
+ // If import fails (file doesn't exist or other error), return null
260
260
  return null;
261
261
  }
262
262
  };
263
263
 
264
264
  /**
265
- * 检测单个模型的提供商类型
266
- * @param modelId 模型ID
267
- * @returns 检测到的提供商配置键名,默认为 'openai'
265
+ * Detect the provider type of a single model
266
+ * @param modelId Model ID
267
+ * @returns Detected provider configuration key name, defaults to 'openai'
268
268
  */
269
269
  export const detectModelProvider = (modelId: string): keyof typeof MODEL_LIST_CONFIGS => {
270
270
  const lowerModelId = modelId.toLowerCase();
@@ -281,20 +281,20 @@ export const detectModelProvider = (modelId: string): keyof typeof MODEL_LIST_CO
281
281
  };
282
282
 
283
283
  /**
284
- * 将时间戳转换为日期字符串
285
- * @param timestamp 时间戳(秒)
286
- * @returns 格式化的日期字符串 (YYYY-MM-DD)
284
+ * Convert timestamp to date string
285
+ * @param timestamp Timestamp (seconds)
286
+ * @returns Formatted date string (YYYY-MM-DD)
287
287
  */
288
288
  const formatTimestampToDate = (timestamp: number): string | undefined => {
289
289
  if (timestamp === null || timestamp === undefined || Number.isNaN(timestamp)) return undefined;
290
290
 
291
- // 支持秒级或毫秒级时间戳:
292
- // - 如果是毫秒级(>= 1e12),直接当作毫秒;
293
- // - 否则视为秒,需要 *1000 转为毫秒
291
+ // Support both second-level and millisecond-level timestamps:
292
+ // - If millisecond-level (>= 1e12), use as milliseconds directly;
293
+ // - Otherwise treat as seconds, need to *1000 to convert to milliseconds
294
294
  const msTimestamp = timestamp > 1e12 ? timestamp : timestamp * 1000;
295
295
  const date = new Date(msTimestamp);
296
296
 
297
- // 验证解析结果和年份范围(只接受 4 位年份,避免超出 varchar(10) YYYY-MM-DD
297
+ // Validate parsing result and year range (only accept 4-digit years to avoid exceeding varchar(10) YYYY-MM-DD)
298
298
  const year = date.getUTCFullYear();
299
299
  if (year < 1000 || year > 9999) return undefined;
300
300
 
@@ -303,22 +303,22 @@ const formatTimestampToDate = (timestamp: number): string | undefined => {
303
303
  };
304
304
 
305
305
  /**
306
- * 处理 releasedAt 字段
307
- * @param model 模型对象
308
- * @param knownModel 已知模型配置
309
- * @returns 处理后的 releasedAt
306
+ * Process releasedAt field
307
+ * @param model Model object
308
+ * @param knownModel Known model configuration
309
+ * @returns Processed releasedAt value
310
310
  */
311
311
  const processReleasedAt = (model: any, knownModel?: any): string | undefined => {
312
- // 优先检查 model.created
312
+ // Check model.created first
313
313
  if (model.created !== undefined && model.created !== null) {
314
- // 检查是否为时间戳格式
314
+ // Check if it's in timestamp format
315
315
  if (typeof model.created === 'number' && model.created > 1_630_000_000) {
316
- // AiHubMix 错误时间戳为 1626777600
316
+ // AiHubMix incorrect timestamp is 1626777600
317
317
  return formatTimestampToDate(model.created);
318
318
  }
319
- // 如果 created 是字符串且已经是日期格式,直接返回
319
+ // If created is a string and already in date format, return directly
320
320
  if (typeof model.created === 'string') {
321
- // Anthropic:若为 '2025-02-19T00:00:00Z' 只取日期部分
321
+ // Anthropic: if it's '2025-02-19T00:00:00Z', only take the date part
322
322
  if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(model.created)) {
323
323
  return model.created.split('T')[0];
324
324
  }
@@ -326,17 +326,17 @@ const processReleasedAt = (model: any, knownModel?: any): string | undefined =>
326
326
  }
327
327
  }
328
328
 
329
- // 回退到原有逻辑
329
+ // Fall back to original logic
330
330
  return model.releasedAt ?? knownModel?.releasedAt ?? undefined;
331
331
  };
332
332
 
333
333
  /**
334
- * 处理模型显示名称
335
- * @param displayName 原始显示名称
336
- * @returns 处理后的显示名称
334
+ * Process model display name
335
+ * @param displayName Original display name
336
+ * @returns Processed display name
337
337
  */
338
338
  const processDisplayName = (displayName: string): string => {
339
- // 如果包含 "Gemini 2.5 Flash Image Preview",替换对应部分为 "Nano Banana"
339
+ // If it contains "Gemini 2.5 Flash Image Preview", replace the corresponding part with "Nano Banana"
340
340
  if (displayName.includes('Gemini 2.5 Flash Image Preview')) {
341
341
  return displayName.replace('Gemini 2.5 Flash Image Preview', 'Nano Banana');
342
342
  }
@@ -345,9 +345,9 @@ const processDisplayName = (displayName: string): string => {
345
345
  };
346
346
 
347
347
  /**
348
- * 获取模型提供商的本地配置
349
- * @param provider 模型提供商
350
- * @returns 模型提供商的本地配置
348
+ * Get the local configuration of the model provider
349
+ * @param provider Model provider
350
+ * @returns Local configuration of the model provider
351
351
  */
352
352
  const getProviderLocalConfig = async (provider?: ModelProviderKey): Promise<any[] | null> => {
353
353
  let providerLocalConfig: any[] | null = null;
@@ -357,7 +357,7 @@ const getProviderLocalConfig = async (provider?: ModelProviderKey): Promise<any[
357
357
 
358
358
  providerLocalConfig = modules[provider];
359
359
  } catch {
360
- // 如果配置文件不存在或导入失败,保持为 null
360
+ // If configuration file doesn't exist or import fails, keep as null
361
361
  providerLocalConfig = null;
362
362
  }
363
363
  }
@@ -365,16 +365,16 @@ const getProviderLocalConfig = async (provider?: ModelProviderKey): Promise<any[
365
365
  };
366
366
 
367
367
  /**
368
- * 获取模型本地配置
369
- * @param providerLocalConfig 模型提供商的本地配置
370
- * @param model 模型对象
371
- * @returns 模型本地配置
368
+ * Get model local configuration
369
+ * @param providerLocalConfig Local configuration of the model provider
370
+ * @param model Model object
371
+ * @returns Model local configuration
372
372
  */
373
373
  const getModelLocalEnableConfig = (
374
374
  providerLocalConfig: any[],
375
375
  model: { id: string },
376
376
  ): any | null => {
377
- // 如果提供了 providerid 且有本地配置,尝试从中获取模型的 enabled 状态
377
+ // If providerid is provided and has local configuration, try to get the model's enabled status from it
378
378
  let providerLocalModelConfig = null;
379
379
  if (providerLocalConfig && Array.isArray(providerLocalConfig)) {
380
380
  providerLocalModelConfig = providerLocalConfig.find((m) => m.id === model.id);
@@ -383,7 +383,7 @@ const getModelLocalEnableConfig = (
383
383
  };
384
384
 
385
385
  /**
386
- * 处理模型卡片的通用逻辑
386
+ * Common logic for processing model cards
387
387
  */
388
388
  const processModelCard = (
389
389
  model: { [key: string]: any; id: string },
@@ -522,11 +522,11 @@ const processModelCard = (
522
522
  };
523
523
 
524
524
  /**
525
- * 处理单一提供商的模型列表
526
- * @param modelList 模型列表
527
- * @param config 提供商配置
528
- * @param provider 提供商类型(可选,用于优先匹配对应的本地配置, 当提供了 provider 时,才会尝试从本地配置覆盖 enabled)
529
- * @returns 处理后的模型卡片列表
525
+ * Process model list for a single provider
526
+ * @param modelList Model list
527
+ * @param config Provider configuration
528
+ * @param provider Provider type (optional, used to prioritize matching corresponding local configuration, will only attempt to override enabled from local configuration when provider is provided)
529
+ * @returns Processed model card list
530
530
  */
531
531
  export const processModelList = async (
532
532
  modelList: Array<{ id: string }>,
@@ -535,19 +535,19 @@ export const processModelList = async (
535
535
  ): Promise<ChatModelCard[]> => {
536
536
  const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
537
537
 
538
- // 如果提供了 provider,尝试获取该提供商的本地配置
538
+ // If provider is provided, try to get the local configuration for that provider
539
539
  const providerLocalConfig = await getProviderLocalConfig(provider as ModelProviderKey);
540
540
 
541
541
  return Promise.all(
542
542
  modelList.map(async (model) => {
543
543
  let knownModel: any = null;
544
544
 
545
- // 如果提供了provider,优先使用提供商特定的配置
545
+ // If provider is provided, prioritize using provider-specific configuration
546
546
  if (provider) {
547
547
  knownModel = await findKnownModelByProvider(model.id, provider);
548
548
  }
549
549
 
550
- // 如果未找到,回退到全局配置
550
+ // If not found, fall back to global configuration
551
551
  if (!knownModel) {
552
552
  knownModel = LOBE_DEFAULT_MODEL_LIST.find(
553
553
  (m) => model.id.toLowerCase() === m.id.toLowerCase(),
@@ -556,13 +556,13 @@ export const processModelList = async (
556
556
 
557
557
  const processedModel = processModelCard(model, config, knownModel);
558
558
 
559
- // 如果提供了 provider 且有本地配置,尝试从中获取模型的 enabled 状态
559
+ // If provider is provided and has local configuration, try to get the model's enabled status from it
560
560
  const providerLocalModelConfig = getModelLocalEnableConfig(
561
561
  providerLocalConfig as any[],
562
562
  model,
563
563
  );
564
564
 
565
- // 如果找到了本地配置中的模型,使用其 enabled 状态
565
+ // If model is found in local configuration, use its enabled status
566
566
  if (
567
567
  processedModel &&
568
568
  providerLocalModelConfig &&
@@ -577,10 +577,10 @@ export const processModelList = async (
577
577
  };
578
578
 
579
579
  /**
580
- * 处理混合提供商的模型列表
581
- * @param modelList 模型列表
582
- * @param providerid 可选的提供商ID,用于获取其本地配置文件
583
- * @returns 处理后的模型卡片列表
580
+ * Process model list for mixed providers
581
+ * @param modelList Model list
582
+ * @param providerid Optional provider ID, used to get its local configuration file
583
+ * @returns Processed model card list
584
584
  */
585
585
  export const processMultiProviderModelList = async (
586
586
  modelList: Array<{ id: string }>,
@@ -588,7 +588,7 @@ export const processMultiProviderModelList = async (
588
588
  ): Promise<ChatModelCard[]> => {
589
589
  const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
590
590
 
591
- // 如果提供了 providerid,尝试获取该提供商的本地配置
591
+ // If providerid is provided, try to get the local configuration for that provider
592
592
  const providerLocalConfig = await getProviderLocalConfig(providerid);
593
593
 
594
594
  return Promise.all(
@@ -596,17 +596,17 @@ export const processMultiProviderModelList = async (
596
596
  const detectedProvider = detectModelProvider(model.id);
597
597
  const config = MODEL_LIST_CONFIGS[detectedProvider];
598
598
 
599
- // 优先使用提供商特定的配置
599
+ // Prioritize using provider-specific configuration
600
600
  let knownModel = await findKnownModelByProvider(model.id, detectedProvider);
601
601
 
602
- // 如果未找到,回退到全局配置
602
+ // If not found, fall back to global configuration
603
603
  if (!knownModel) {
604
604
  knownModel = LOBE_DEFAULT_MODEL_LIST.find(
605
605
  (m) => model.id.toLowerCase() === m.id.toLowerCase(),
606
606
  );
607
607
  }
608
608
 
609
- // 如果提供了 providerid 且有本地配置,尝试从中获取模型的 enabled 状态
609
+ // If providerid is provided and has local configuration, try to get the model's enabled status from it
610
610
  const providerLocalModelConfig = getModelLocalEnableConfig(
611
611
  providerLocalConfig as any[],
612
612
  model,
@@ -614,7 +614,7 @@ export const processMultiProviderModelList = async (
614
614
 
615
615
  const processedModel = processModelCard(model, config, knownModel);
616
616
 
617
- // 如果找到了本地配置中的模型,使用其 enabled 状态
617
+ // If model is found in local configuration, use its enabled status
618
618
  if (
619
619
  processedModel &&
620
620
  providerLocalModelConfig &&
@@ -5,20 +5,20 @@ interface UriParserResult {
5
5
  }
6
6
 
7
7
  export const parseDataUri = (dataUri: string): UriParserResult => {
8
- // 正则表达式匹配整个 Data URI 结构
8
+ // Regular expression to match the entire Data URI structure
9
9
  const dataUriMatch = dataUri.match(/^data:([^;]+);base64,(.+)$/);
10
10
 
11
11
  if (dataUriMatch) {
12
- // 如果是合法的 Data URI
12
+ // If it's a valid Data URI
13
13
  return { base64: dataUriMatch[2], mimeType: dataUriMatch[1], type: 'base64' };
14
14
  }
15
15
 
16
16
  try {
17
17
  new URL(dataUri);
18
- // 如果是合法的 URL
18
+ // If it's a valid URL
19
19
  return { base64: null, mimeType: null, type: 'url' };
20
20
  } catch {
21
- // 既不是 Data URI 也不是合法 URL
21
+ // Neither a Data URI nor a valid URL
22
22
  return { base64: null, mimeType: null, type: null };
23
23
  }
24
24
  };
@@ -18,7 +18,7 @@ const AddButton = memo(() => {
18
18
  createGroupChatMenuItem,
19
19
  createPageMenuItem,
20
20
  createAgent,
21
- isValidatingAgent,
21
+ isMutatingAgent,
22
22
  isCreatingGroup,
23
23
  } = useCreateMenuItems();
24
24
 
@@ -39,7 +39,7 @@ const AddButton = memo(() => {
39
39
  <Flexbox horizontal>
40
40
  <ActionIcon
41
41
  icon={CreateBotIcon}
42
- loading={isValidatingAgent || isCreatingGroup}
42
+ loading={isMutatingAgent || isCreatingGroup}
43
43
  onClick={handleMainIconClick}
44
44
  size={DESKTOP_HEADER_ICON_SIZE}
45
45
  title={tChat('newAgent')}
@@ -6,10 +6,10 @@ import { BotIcon, FileTextIcon, FolderCogIcon, FolderPlus } from 'lucide-react';
6
6
  import { useCallback, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { useNavigate } from 'react-router-dom';
9
+ import useSWRMutation from 'swr/mutation';
9
10
 
10
11
  import { useGroupTemplates } from '@/components/ChatGroupWizard/templates';
11
12
  import { DEFAULT_CHAT_GROUP_CHAT_CONFIG } from '@/const/settings';
12
- import { useActionSWR } from '@/libs/swr';
13
13
  import { type GroupMemberConfig, chatGroupService } from '@/services/chatGroup';
14
14
  import { useAgentStore } from '@/store/agent';
15
15
  import { useAgentGroupStore } from '@/store/agentGroup';
@@ -51,22 +51,22 @@ export const useCreateMenuItems = () => {
51
51
  const [isCreatingSessionGroup, setIsCreatingSessionGroup] = useState(false);
52
52
 
53
53
  // SWR-based agent creation with auto navigation to profile
54
- const { mutate: mutateAgent, isValidating: isValidatingAgent } = useActionSWR(
54
+ const { trigger: mutateAgent, isMutating: isMutatingAgent } = useSWRMutation(
55
55
  'agent.createAgent',
56
56
  async () => {
57
57
  const result = await storeCreateAgent({});
58
- navigate(`/agent/${result.agentId}/profile`);
59
58
  return result;
60
59
  },
61
60
  {
62
- onSuccess: async () => {
61
+ onSuccess: async (result) => {
62
+ navigate(`/agent/${result.agentId}/profile`);
63
63
  await refreshAgentList();
64
64
  },
65
65
  },
66
66
  );
67
67
 
68
68
  // SWR-based group creation with auto navigation to profile
69
- const { mutate: mutateGroup, isValidating: isValidatingGroup } = useActionSWR(
69
+ const { trigger: mutateGroup, isMutating: isMutatingGroup } = useSWRMutation(
70
70
  'group.createGroup',
71
71
  async () => {
72
72
  const groupId = await createGroup(
@@ -77,11 +77,11 @@ export const useCreateMenuItems = () => {
77
77
  [],
78
78
  true, // silent mode - don't switch session, we'll navigate instead
79
79
  );
80
- navigate(`/group/${groupId}/profile`);
81
80
  return groupId;
82
81
  },
83
82
  {
84
- onSuccess: async () => {
83
+ onSuccess: async (groupId) => {
84
+ navigate(`/group/${groupId}/profile`);
85
85
  await refreshAgentList();
86
86
  await loadGroups();
87
87
  },
@@ -329,7 +329,7 @@ export const useCreateMenuItems = () => {
329
329
  // Loading states
330
330
  isCreatingGroup,
331
331
  isCreatingSessionGroup,
332
- isLoading: isValidatingAgent || isValidatingGroup || isCreatingGroup || isCreatingSessionGroup,
333
- isValidatingAgent,
332
+ isLoading: isMutatingAgent || isMutatingGroup || isCreatingGroup || isCreatingSessionGroup,
333
+ isMutatingAgent,
334
334
  };
335
335
  };