@lobehub/lobehub 2.0.0-next.322 → 2.0.0-next.323

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 (106) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts +9 -76
  3. package/apps/desktop/src/main/core/infrastructure/__tests__/UpdaterManager.test.ts +0 -1
  4. package/apps/desktop/src/main/modules/updater/configs.ts +0 -4
  5. package/changelog/v1.json +15 -0
  6. package/e2e/src/mocks/llm/index.ts +3 -3
  7. package/locales/ar/common.json +5 -0
  8. package/locales/ar/error.json +10 -1
  9. package/locales/bg-BG/common.json +5 -0
  10. package/locales/bg-BG/error.json +10 -1
  11. package/locales/de-DE/common.json +5 -0
  12. package/locales/de-DE/error.json +10 -1
  13. package/locales/en-US/common.json +5 -0
  14. package/locales/es-ES/common.json +5 -0
  15. package/locales/es-ES/error.json +10 -1
  16. package/locales/fa-IR/common.json +5 -0
  17. package/locales/fa-IR/error.json +10 -1
  18. package/locales/fr-FR/common.json +5 -0
  19. package/locales/fr-FR/error.json +10 -1
  20. package/locales/it-IT/common.json +5 -0
  21. package/locales/it-IT/error.json +10 -1
  22. package/locales/ja-JP/common.json +5 -0
  23. package/locales/ja-JP/error.json +10 -1
  24. package/locales/ko-KR/common.json +5 -0
  25. package/locales/ko-KR/error.json +10 -1
  26. package/locales/nl-NL/common.json +5 -0
  27. package/locales/nl-NL/error.json +10 -1
  28. package/locales/pl-PL/common.json +5 -0
  29. package/locales/pl-PL/error.json +10 -1
  30. package/locales/pt-BR/common.json +5 -0
  31. package/locales/pt-BR/error.json +10 -1
  32. package/locales/ru-RU/common.json +5 -0
  33. package/locales/ru-RU/error.json +10 -1
  34. package/locales/tr-TR/common.json +5 -0
  35. package/locales/tr-TR/error.json +10 -1
  36. package/locales/vi-VN/common.json +5 -0
  37. package/locales/vi-VN/error.json +10 -1
  38. package/locales/zh-CN/common.json +5 -0
  39. package/locales/zh-TW/common.json +5 -0
  40. package/locales/zh-TW/error.json +10 -1
  41. package/package.json +2 -2
  42. package/packages/business/const/src/branding.ts +1 -0
  43. package/packages/business/const/src/llm.ts +2 -1
  44. package/packages/const/src/settings/llm.ts +2 -1
  45. package/packages/const/src/settings/systemAgent.ts +12 -7
  46. package/packages/database/src/models/agent.ts +18 -1
  47. package/packages/database/src/models/chatGroup.ts +18 -1
  48. package/packages/database/src/types/chatGroup.ts +1 -0
  49. package/packages/model-bank/package.json +1 -1
  50. package/packages/model-bank/src/aiModels/index.ts +2 -2
  51. package/packages/model-bank/src/aiModels/lobehub/chat/anthropic.ts +256 -0
  52. package/packages/model-bank/src/aiModels/lobehub/chat/deepseek.ts +45 -0
  53. package/packages/model-bank/src/aiModels/lobehub/chat/google.ts +267 -0
  54. package/packages/model-bank/src/aiModels/lobehub/chat/index.ts +26 -0
  55. package/packages/model-bank/src/aiModels/lobehub/chat/minimax.ts +75 -0
  56. package/packages/model-bank/src/aiModels/lobehub/chat/moonshot.ts +28 -0
  57. package/packages/model-bank/src/aiModels/lobehub/chat/openai.ts +345 -0
  58. package/packages/model-bank/src/aiModels/lobehub/chat/xai.ts +32 -0
  59. package/packages/model-bank/src/aiModels/lobehub/image.ts +240 -0
  60. package/packages/model-bank/src/aiModels/lobehub/index.ts +10 -0
  61. package/packages/model-bank/src/aiModels/lobehub/utils.ts +58 -0
  62. package/packages/model-bank/src/modelProviders/index.ts +10 -10
  63. package/packages/model-runtime/src/core/streams/qwen.test.ts +320 -0
  64. package/packages/model-runtime/src/core/streams/qwen.ts +19 -10
  65. package/packages/types/package.json +1 -1
  66. package/packages/types/src/agentGroup/index.ts +2 -0
  67. package/packages/types/src/discover/assistants.ts +9 -0
  68. package/packages/types/src/discover/fork.ts +163 -0
  69. package/packages/types/src/discover/groupAgents.ts +13 -4
  70. package/packages/types/src/discover/index.ts +9 -0
  71. package/src/app/[variants]/(auth)/_layout/index.tsx +2 -1
  72. package/src/app/[variants]/(auth)/auth-error/page.tsx +5 -5
  73. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/index.tsx +1 -2
  74. package/src/app/[variants]/(main)/community/(detail)/agent/features/Header.tsx +37 -0
  75. package/src/app/[variants]/(main)/community/(detail)/agent/features/Sidebar/ActionButton/ForkAndChat.tsx +133 -0
  76. package/src/app/[variants]/(main)/community/(detail)/agent/features/Sidebar/ActionButton/index.tsx +2 -2
  77. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/index.tsx +7 -10
  78. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/ForkGroupAndChat.tsx +208 -0
  79. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/index.tsx +2 -2
  80. package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +2 -0
  81. package/src/app/[variants]/(main)/community/(detail)/user/features/UserContent.tsx +7 -0
  82. package/src/app/[variants]/(main)/community/(detail)/user/features/UserForkedAgentGroups.tsx +63 -0
  83. package/src/app/[variants]/(main)/community/(detail)/user/features/UserForkedAgents.tsx +61 -0
  84. package/src/app/[variants]/(main)/community/(detail)/user/index.tsx +3 -1
  85. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/index.tsx +1 -2
  86. package/src/app/[variants]/(main)/settings/profile/index.tsx +92 -68
  87. package/src/app/[variants]/(mobile)/chat/features/Topic/index.tsx +2 -1
  88. package/src/features/CommandMenu/AskAgentCommands.tsx +105 -0
  89. package/src/features/CommandMenu/CommandMenuContext.tsx +57 -38
  90. package/src/features/CommandMenu/components/CommandInput.tsx +43 -9
  91. package/src/features/CommandMenu/index.tsx +89 -27
  92. package/src/features/CommandMenu/types.ts +6 -0
  93. package/src/features/CommandMenu/useCommandMenu.ts +62 -39
  94. package/src/locales/default/common.ts +5 -0
  95. package/src/locales/default/discover.ts +371 -0
  96. package/src/server/globalConfig/parseMemoryExtractionConfig.ts +7 -8
  97. package/src/server/routers/lambda/agent.ts +14 -0
  98. package/src/server/routers/lambda/agentGroup.ts +19 -3
  99. package/src/server/routers/lambda/market/agent.ts +234 -26
  100. package/src/server/routers/lambda/market/agentGroup.ts +204 -1
  101. package/src/server/services/discover/index.ts +52 -2
  102. package/src/services/agent.ts +8 -0
  103. package/src/services/chatGroup/index.ts +8 -0
  104. package/src/services/marketApi.ts +78 -0
  105. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +12 -12
  106. package/packages/model-bank/src/aiModels/lobehub.ts +0 -1315
@@ -1,4 +1,5 @@
1
- import { DEFAULT_USER_MEMORY_EMBEDDING_MODEL_ITEM } from '@lobechat/const';
1
+ import { DEFAULT_MINI_PROVIDER } from '@lobechat/business-const';
2
+ import { DEFAULT_MINI_MODEL, DEFAULT_USER_MEMORY_EMBEDDING_MODEL_ITEM } from '@lobechat/const';
2
3
 
3
4
  import {
4
5
  type GlobalMemoryExtractionConfig,
@@ -8,8 +9,6 @@ import {
8
9
  } from '@/types/serverConfig';
9
10
 
10
11
  const MEMORY_LAYERS: GlobalMemoryLayer[] = ['context', 'experience', 'identity', 'preference'];
11
- const DEFAULT_GATE_MODEL = 'gpt-5-mini';
12
- const DEFAULT_PROVIDER = 'openai';
13
12
 
14
13
  const parseTokenLimitEnv = (value?: string) => {
15
14
  if (value === undefined) return undefined;
@@ -62,8 +61,8 @@ export interface MemoryExtractionPrivateConfig {
62
61
  const parseGateKeeperAgent = (): MemoryAgentConfig => {
63
62
  const apiKey = process.env.MEMORY_USER_MEMORY_GATEKEEPER_API_KEY;
64
63
  const baseURL = process.env.MEMORY_USER_MEMORY_GATEKEEPER_BASE_URL;
65
- const model = process.env.MEMORY_USER_MEMORY_GATEKEEPER_MODEL || DEFAULT_GATE_MODEL;
66
- const provider = process.env.MEMORY_USER_MEMORY_GATEKEEPER_PROVIDER || DEFAULT_PROVIDER;
64
+ const model = process.env.MEMORY_USER_MEMORY_GATEKEEPER_MODEL || DEFAULT_MINI_MODEL;
65
+ const provider = process.env.MEMORY_USER_MEMORY_GATEKEEPER_PROVIDER || DEFAULT_MINI_PROVIDER;
67
66
  const language = process.env.MEMORY_USER_MEMORY_GATEKEEPER_LANGUAGE || 'English';
68
67
 
69
68
  return {
@@ -79,7 +78,7 @@ const parseLayerExtractorAgent = (fallbackModel: string): MemoryLayerExtractorCo
79
78
  const apiKey = process.env.MEMORY_USER_MEMORY_LAYER_EXTRACTOR_API_KEY;
80
79
  const baseURL = process.env.MEMORY_USER_MEMORY_LAYER_EXTRACTOR_BASE_URL;
81
80
  const model = process.env.MEMORY_USER_MEMORY_LAYER_EXTRACTOR_MODEL || fallbackModel;
82
- const provider = process.env.MEMORY_USER_MEMORY_LAYER_EXTRACTOR_PROVIDER || DEFAULT_PROVIDER;
81
+ const provider = process.env.MEMORY_USER_MEMORY_LAYER_EXTRACTOR_PROVIDER || DEFAULT_MINI_PROVIDER;
83
82
  const contextLimit = parseTokenLimitEnv(
84
83
  process.env.MEMORY_USER_MEMORY_LAYER_EXTRACTOR_CONTEXT_LIMIT,
85
84
  );
@@ -117,7 +116,7 @@ const parseEmbeddingAgent = (
117
116
  process.env.MEMORY_USER_MEMORY_EMBEDDING_PROVIDER ||
118
117
  fallbackProvider ||
119
118
  defaultProvider ||
120
- DEFAULT_PROVIDER;
119
+ DEFAULT_MINI_PROVIDER;
121
120
 
122
121
  return {
123
122
  apiKey: process.env.MEMORY_USER_MEMORY_EMBEDDING_API_KEY ?? fallbackApiKey,
@@ -174,7 +173,7 @@ export const parseMemoryExtractionConfig = (): MemoryExtractionPrivateConfig =>
174
173
  const agentLayerExtractor = parseLayerExtractorAgent(agentGateKeeper.model);
175
174
  const embedding = parseEmbeddingAgent(
176
175
  agentLayerExtractor.model,
177
- agentLayerExtractor.provider || DEFAULT_PROVIDER,
176
+ agentLayerExtractor.provider || DEFAULT_MINI_PROVIDER,
178
177
  agentGateKeeper.apiKey || agentLayerExtractor.apiKey,
179
178
  );
180
179
  const extractorObservabilityS3 = parseExtractorAgentObservabilityS3();
@@ -169,6 +169,20 @@ export const agentRouter = router({
169
169
  return ctx.agentModel.duplicate(input.agentId, input.newTitle);
170
170
  }),
171
171
 
172
+ /**
173
+ * Get an agent by forkedFromIdentifier stored in params
174
+ * @returns agent id if exists, null otherwise
175
+ */
176
+ getAgentByForkedFromIdentifier: agentProcedure
177
+ .input(
178
+ z.object({
179
+ forkedFromIdentifier: z.string(),
180
+ }),
181
+ )
182
+ .query(async ({ input, ctx }) => {
183
+ return ctx.agentModel.getAgentByForkedFromIdentifier(input.forkedFromIdentifier);
184
+ }),
185
+
172
186
  /**
173
187
  * Get an agent by marketIdentifier
174
188
  * @returns agent id if exists, null otherwise
@@ -165,12 +165,14 @@ export const agentGroupRouter = router({
165
165
  )
166
166
  : undefined;
167
167
 
168
+ const normalizedConfig = ctx.agentGroupService.normalizeGroupConfig(
169
+ input.groupConfig.config as ChatGroupConfig | null,
170
+ );
171
+
168
172
  const { group, supervisorAgentId } = await ctx.agentGroupRepo.createGroupWithSupervisor(
169
173
  {
170
174
  ...input.groupConfig,
171
- config: ctx.agentGroupService.normalizeGroupConfig(
172
- input.groupConfig.config as ChatGroupConfig | null,
173
- ),
175
+ config: normalizedConfig,
174
176
  },
175
177
  memberAgentIds,
176
178
  supervisorConfig as any,
@@ -213,6 +215,20 @@ export const agentGroupRouter = router({
213
215
  return ctx.chatGroupModel.getGroupAgents(input.groupId);
214
216
  }),
215
217
 
218
+ /**
219
+ * Get a group by forkedFromIdentifier stored in config
220
+ * @returns group id if exists, null otherwise
221
+ */
222
+ getGroupByForkedFromIdentifier: agentGroupProcedure
223
+ .input(
224
+ z.object({
225
+ forkedFromIdentifier: z.string(),
226
+ }),
227
+ )
228
+ .query(async ({ input, ctx }) => {
229
+ return ctx.chatGroupModel.getGroupByForkedFromIdentifier(input.forkedFromIdentifier);
230
+ }),
231
+
216
232
  getGroupDetail: agentGroupProcedure
217
233
  .input(z.object({ id: z.string() }))
218
234
  .query(async ({ input, ctx }) => {
@@ -163,31 +163,23 @@ const paginationSchema = z.object({
163
163
 
164
164
  // Schema for the unified publish/create flow
165
165
  const publishOrCreateSchema = z.object({
166
-
167
166
  // Version data
168
- avatar: z.string().optional(),
169
-
167
+ avatar: z.string().optional(),
170
168
 
171
- category: z.string().optional(),
169
+ category: z.string().optional(),
172
170
 
173
-
174
-
171
+ changelog: z.string().optional(),
175
172
 
176
- changelog: z.string().optional(),
177
-
173
+ config: z.record(z.any()).optional(),
178
174
 
179
- config: z.record(z.any()).optional(),
180
-
175
+ description: z.string().optional(),
181
176
 
182
- description: z.string().optional(),
183
-
177
+ editorData: z.record(z.any()).optional(),
184
178
 
185
- editorData: z.record(z.any()).optional(),
186
-
187
- // Agent basic info
188
- identifier: z.string().optional(),
179
+ // Agent basic info
180
+ identifier: z.string().optional(),
189
181
  // Optional - if not provided or not owned, will create new
190
- name: z.string(),
182
+ name: z.string(),
191
183
  tags: z.array(z.string()).optional(),
192
184
  tokenUsage: z.number().optional(),
193
185
  });
@@ -320,11 +312,86 @@ export const agentRouter = router({
320
312
  }
321
313
  }),
322
314
 
315
+
323
316
  /**
317
+ * Fork an agent
318
+ * POST /market/agent/:identifier/fork
319
+ */
320
+ forkAgent: agentProcedure
321
+ .input(
322
+ z.object({
323
+ identifier: z.string(),
324
+ name: z.string().optional(),
325
+ sourceIdentifier: z.string(),
326
+ status: z.enum(['published', 'unpublished', 'archived', 'deprecated']).optional(),
327
+ versionNumber: z.number().optional(),
328
+ visibility: z.enum(['public', 'private', 'internal']).optional(),
329
+ }),
330
+ )
331
+ .mutation(async ({ input, ctx }) => {
332
+ log('forkAgent input: %O', input);
333
+
334
+ try {
335
+ // Call Market API directly to fork agent
336
+ const forkUrl = `${MARKET_BASE_URL}/api/v1/agents/${input.sourceIdentifier}/fork`;
337
+
338
+ const headers: Record<string, string> = {
339
+ 'Content-Type': 'application/json',
340
+ };
341
+
342
+ // Use trustedClientToken or accessToken for authentication
343
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
344
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
345
+
346
+ if (userInfo) {
347
+ const trustedClientToken = generateTrustedClientToken(userInfo);
348
+ if (trustedClientToken) {
349
+ headers['x-lobe-trust-token'] = trustedClientToken;
350
+ }
351
+ }
352
+
353
+ if (!headers['x-lobe-trust-token'] && accessToken) {
354
+ headers['Authorization'] = `Bearer ${accessToken}`;
355
+ }
356
+
357
+ const response = await fetch(forkUrl, {
358
+ body: JSON.stringify({
359
+ identifier: input.identifier,
360
+ name: input.name,
361
+ status: input.status,
362
+ versionNumber: input.versionNumber,
363
+ visibility: input.visibility,
364
+ }),
365
+ headers,
366
+ method: 'POST',
367
+ });
368
+
369
+ if (!response.ok) {
370
+ const errorText = await response.text();
371
+ log('Fork agent failed: %s %s - %s', response.status, response.statusText, errorText);
372
+ throw new Error(`Failed to fork agent: ${response.statusText}`);
373
+ }
374
+
375
+ const result = await response.json();
376
+ log('Fork agent success: %O', result);
377
+ return result;
378
+ } catch (error) {
379
+ log('Error forking agent: %O', error);
380
+ throw new TRPCError({
381
+ cause: error,
382
+ code: 'INTERNAL_SERVER_ERROR',
383
+ message: error instanceof Error ? error.message : 'Failed to fork agent',
384
+ });
385
+ }
386
+ }),
387
+
388
+
389
+
390
+ /**
324
391
  * Get agent detail by identifier
325
392
  * GET /market/agent/:identifier
326
393
  */
327
- getAgentDetail: agentProcedure
394
+ getAgentDetail: agentProcedure
328
395
  .input(z.object({ identifier: z.string() }))
329
396
  .query(async ({ input, ctx }) => {
330
397
  log('getAgentDetail input: %O', input);
@@ -342,11 +409,141 @@ export const agentRouter = router({
342
409
  }
343
410
  }),
344
411
 
345
- /**
412
+
413
+
414
+
415
+ /**
416
+ * Get the fork source of an agent
417
+ * GET /market/agent/:identifier/fork-source
418
+ */
419
+ getAgentForkSource: agentProcedure
420
+ .input(z.object({ identifier: z.string() }))
421
+ .query(async ({ input, ctx }) => {
422
+ log('getAgentForkSource input: %O', input);
423
+
424
+ try {
425
+ const forkSourceUrl = `${MARKET_BASE_URL}/api/v1/agents/${input.identifier}/fork-source`;
426
+
427
+ const headers: Record<string, string> = {
428
+ 'Content-Type': 'application/json',
429
+ };
430
+
431
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
432
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
433
+
434
+ if (userInfo) {
435
+ const trustedClientToken = generateTrustedClientToken(userInfo);
436
+ if (trustedClientToken) {
437
+ headers['x-lobe-trust-token'] = trustedClientToken;
438
+ }
439
+ }
440
+
441
+ if (!headers['x-lobe-trust-token'] && accessToken) {
442
+ headers['Authorization'] = `Bearer ${accessToken}`;
443
+ }
444
+
445
+ const response = await fetch(forkSourceUrl, {
446
+ headers,
447
+ method: 'GET',
448
+ });
449
+
450
+ if (!response.ok) {
451
+ const errorText = await response.text();
452
+ log(
453
+ 'Get agent fork source failed: %s %s - %s',
454
+ response.status,
455
+ response.statusText,
456
+ errorText,
457
+ );
458
+ throw new Error(`Failed to get agent fork source: ${response.statusText}`);
459
+ }
460
+
461
+ const result = await response.json();
462
+ return result;
463
+ } catch (error) {
464
+ log('Error getting agent fork source: %O', error);
465
+ throw new TRPCError({
466
+ cause: error,
467
+ code: 'INTERNAL_SERVER_ERROR',
468
+ message: error instanceof Error ? error.message : 'Failed to get agent fork source',
469
+ });
470
+ }
471
+ }),
472
+
473
+
474
+
475
+
476
+
477
+
478
+ /**
479
+ * Get all forks of an agent
480
+ * GET /market/agent/:identifier/forks
481
+ */
482
+ getAgentForks: agentProcedure
483
+ .input(z.object({ identifier: z.string() }))
484
+ .query(async ({ input, ctx }) => {
485
+ log('getAgentForks input: %O', input);
486
+
487
+ try {
488
+ const forksUrl = `${MARKET_BASE_URL}/api/v1/agents/${input.identifier}/forks`;
489
+
490
+ const headers: Record<string, string> = {
491
+ 'Content-Type': 'application/json',
492
+ };
493
+
494
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
495
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
496
+
497
+ if (userInfo) {
498
+ const trustedClientToken = generateTrustedClientToken(userInfo);
499
+ if (trustedClientToken) {
500
+ headers['x-lobe-trust-token'] = trustedClientToken;
501
+ }
502
+ }
503
+
504
+ if (!headers['x-lobe-trust-token'] && accessToken) {
505
+ headers['Authorization'] = `Bearer ${accessToken}`;
506
+ }
507
+
508
+ const response = await fetch(forksUrl, {
509
+ headers,
510
+ method: 'GET',
511
+ });
512
+
513
+ if (!response.ok) {
514
+ const errorText = await response.text();
515
+ log(
516
+ 'Get agent forks failed: %s %s - %s',
517
+ response.status,
518
+ response.statusText,
519
+ errorText,
520
+ );
521
+ throw new Error(`Failed to get agent forks: ${response.statusText}`);
522
+ }
523
+
524
+ const result = await response.json();
525
+ return result;
526
+ } catch (error) {
527
+ log('Error getting agent forks: %O', error);
528
+ throw new TRPCError({
529
+ cause: error,
530
+ code: 'INTERNAL_SERVER_ERROR',
531
+ message: error instanceof Error ? error.message : 'Failed to get agent forks',
532
+ });
533
+ }
534
+ }),
535
+
536
+
537
+
538
+
539
+
540
+
541
+
542
+ /**
346
543
  * Get own agents (requires authentication)
347
544
  * GET /market/agent/own
348
545
  */
349
- getOwnAgents: agentProcedure.input(paginationSchema.optional()).query(async ({ input, ctx }) => {
546
+ getOwnAgents: agentProcedure.input(paginationSchema.optional()).query(async ({ input, ctx }) => {
350
547
  log('getOwnAgents input: %O', input);
351
548
 
352
549
  try {
@@ -365,11 +562,16 @@ export const agentRouter = router({
365
562
  }
366
563
  }),
367
564
 
368
- /**
565
+
566
+
567
+
568
+
569
+
570
+ /**
369
571
  * Publish an agent (make it visible in marketplace)
370
572
  * POST /market/agent/:identifier/publish
371
573
  */
372
- publishAgent: agentProcedure
574
+ publishAgent: agentProcedure
373
575
  .input(z.object({ identifier: z.string() }))
374
576
  .mutation(async ({ input, ctx }) => {
375
577
  log('publishAgent input: %O', input);
@@ -387,7 +589,11 @@ export const agentRouter = router({
387
589
  }
388
590
  }),
389
591
 
390
- /**
592
+
593
+
594
+
595
+
596
+ /**
391
597
  * Unified publish or create agent flow
392
598
  * This procedure handles the complete publish logic:
393
599
  * 1. Check if identifier exists and if current user is owner
@@ -396,7 +602,7 @@ export const agentRouter = router({
396
602
  *
397
603
  * Returns: { identifier, isNewAgent, success }
398
604
  */
399
- publishOrCreate: agentProcedure.input(publishOrCreateSchema).mutation(async ({ input, ctx }) => {
605
+ publishOrCreate: agentProcedure.input(publishOrCreateSchema).mutation(async ({ input, ctx }) => {
400
606
  log('publishOrCreate input: %O', input);
401
607
 
402
608
  const { identifier: inputIdentifier, name, ...versionData } = input;
@@ -478,11 +684,13 @@ export const agentRouter = router({
478
684
  }
479
685
  }),
480
686
 
481
- /**
687
+
688
+
689
+ /**
482
690
  * Unpublish an agent (hide from marketplace, can be republished)
483
691
  * POST /market/agent/:identifier/unpublish
484
692
  */
485
- unpublishAgent: agentProcedure
693
+ unpublishAgent: agentProcedure
486
694
  .input(z.object({ identifier: z.string() }))
487
695
  .mutation(async ({ input, ctx }) => {
488
696
  log('unpublishAgent input: %O', input);
@@ -200,13 +200,214 @@ export const agentGroupRouter = router({
200
200
  }
201
201
  }),
202
202
 
203
+
203
204
  /**
205
+ * Fork an agent group
206
+ * POST /market/agent-group/:identifier/fork
207
+ */
208
+ forkAgentGroup: agentGroupProcedure
209
+ .input(
210
+ z.object({
211
+ identifier: z.string(),
212
+ name: z.string().optional(),
213
+ sourceIdentifier: z.string(),
214
+ status: z.enum(['published', 'unpublished', 'archived', 'deprecated']).optional(),
215
+ versionNumber: z.number().optional(),
216
+ visibility: z.enum(['public', 'private', 'internal']).optional(),
217
+ }),
218
+ )
219
+ .mutation(async ({ input, ctx }) => {
220
+ log('forkAgentGroup input: %O', input);
221
+
222
+ try {
223
+ // Call Market API directly to fork agent group
224
+ const forkUrl = `${MARKET_BASE_URL}/api/v1/agent-groups/${input.sourceIdentifier}/fork`;
225
+
226
+ const headers: Record<string, string> = {
227
+ 'Content-Type': 'application/json',
228
+ };
229
+
230
+ // Use trustedClientToken or accessToken for authentication
231
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
232
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
233
+
234
+ if (userInfo) {
235
+ const trustedClientToken = generateTrustedClientToken(userInfo);
236
+ if (trustedClientToken) {
237
+ headers['x-lobe-trust-token'] = trustedClientToken;
238
+ }
239
+ }
240
+
241
+ if (!headers['x-lobe-trust-token'] && accessToken) {
242
+ headers['Authorization'] = `Bearer ${accessToken}`;
243
+ }
244
+
245
+ const response = await fetch(forkUrl, {
246
+ body: JSON.stringify({
247
+ identifier: input.identifier,
248
+ name: input.name,
249
+ status: input.status,
250
+ versionNumber: input.versionNumber,
251
+ visibility: input.visibility,
252
+ }),
253
+ headers,
254
+ method: 'POST',
255
+ });
256
+
257
+ if (!response.ok) {
258
+ const errorText = await response.text();
259
+ log(
260
+ 'Fork agent group failed: %s %s - %s',
261
+ response.status,
262
+ response.statusText,
263
+ errorText,
264
+ );
265
+ throw new Error(`Failed to fork agent group: ${response.statusText}`);
266
+ }
267
+
268
+ const result = await response.json();
269
+ log('Fork agent group success: %O', result);
270
+ return result;
271
+ } catch (error) {
272
+ log('Error forking agent group: %O', error);
273
+ throw new TRPCError({
274
+ cause: error,
275
+ code: 'INTERNAL_SERVER_ERROR',
276
+ message: error instanceof Error ? error.message : 'Failed to fork agent group',
277
+ });
278
+ }
279
+ }),
280
+
281
+
282
+
283
+ /**
284
+ * Get the fork source of an agent group
285
+ * GET /market/agent-group/:identifier/fork-source
286
+ */
287
+ getAgentGroupForkSource: agentGroupProcedure
288
+ .input(z.object({ identifier: z.string() }))
289
+ .query(async ({ input, ctx }) => {
290
+ log('getAgentGroupForkSource input: %O', input);
291
+
292
+ try {
293
+ const forkSourceUrl = `${MARKET_BASE_URL}/api/v1/agent-groups/${input.identifier}/fork-source`;
294
+
295
+ const headers: Record<string, string> = {
296
+ 'Content-Type': 'application/json',
297
+ };
298
+
299
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
300
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
301
+
302
+ if (userInfo) {
303
+ const trustedClientToken = generateTrustedClientToken(userInfo);
304
+ if (trustedClientToken) {
305
+ headers['x-lobe-trust-token'] = trustedClientToken;
306
+ }
307
+ }
308
+
309
+ if (!headers['x-lobe-trust-token'] && accessToken) {
310
+ headers['Authorization'] = `Bearer ${accessToken}`;
311
+ }
312
+
313
+ const response = await fetch(forkSourceUrl, {
314
+ headers,
315
+ method: 'GET',
316
+ });
317
+
318
+ if (!response.ok) {
319
+ const errorText = await response.text();
320
+ log(
321
+ 'Get agent group fork source failed: %s %s - %s',
322
+ response.status,
323
+ response.statusText,
324
+ errorText,
325
+ );
326
+ throw new Error(`Failed to get agent group fork source: ${response.statusText}`);
327
+ }
328
+
329
+ const result = await response.json();
330
+ return result;
331
+ } catch (error) {
332
+ log('Error getting agent group fork source: %O', error);
333
+ throw new TRPCError({
334
+ cause: error,
335
+ code: 'INTERNAL_SERVER_ERROR',
336
+ message: error instanceof Error ? error.message : 'Failed to get agent group fork source',
337
+ });
338
+ }
339
+ }),
340
+
341
+
342
+
343
+
344
+ /**
345
+ * Get all forks of an agent group
346
+ * GET /market/agent-group/:identifier/forks
347
+ */
348
+ getAgentGroupForks: agentGroupProcedure
349
+ .input(z.object({ identifier: z.string() }))
350
+ .query(async ({ input, ctx }) => {
351
+ log('getAgentGroupForks input: %O', input);
352
+
353
+ try {
354
+ const forksUrl = `${MARKET_BASE_URL}/api/v1/agent-groups/${input.identifier}/forks`;
355
+
356
+ const headers: Record<string, string> = {
357
+ 'Content-Type': 'application/json',
358
+ };
359
+
360
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
361
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
362
+
363
+ if (userInfo) {
364
+ const trustedClientToken = generateTrustedClientToken(userInfo);
365
+ if (trustedClientToken) {
366
+ headers['x-lobe-trust-token'] = trustedClientToken;
367
+ }
368
+ }
369
+
370
+ if (!headers['x-lobe-trust-token'] && accessToken) {
371
+ headers['Authorization'] = `Bearer ${accessToken}`;
372
+ }
373
+
374
+ const response = await fetch(forksUrl, {
375
+ headers,
376
+ method: 'GET',
377
+ });
378
+
379
+ if (!response.ok) {
380
+ const errorText = await response.text();
381
+ log(
382
+ 'Get agent group forks failed: %s %s - %s',
383
+ response.status,
384
+ response.statusText,
385
+ errorText,
386
+ );
387
+ throw new Error(`Failed to get agent group forks: ${response.statusText}`);
388
+ }
389
+
390
+ const result = await response.json();
391
+ return result;
392
+ } catch (error) {
393
+ log('Error getting agent group forks: %O', error);
394
+ throw new TRPCError({
395
+ cause: error,
396
+ code: 'INTERNAL_SERVER_ERROR',
397
+ message: error instanceof Error ? error.message : 'Failed to get agent group forks',
398
+ });
399
+ }
400
+ }),
401
+
402
+
403
+
404
+ /**
204
405
  * Unified publish or create agent group flow
205
406
  * 1. Check if identifier exists and if current user is owner
206
407
  * 2. If not owner or no identifier, create new group
207
408
  * 3. Create new version for the group if updating
208
409
  */
209
- publishOrCreate: agentGroupProcedure
410
+ publishOrCreate: agentGroupProcedure
210
411
  .input(publishOrCreateGroupSchema)
211
412
  .mutation(async ({ input, ctx }) => {
212
413
  log('publishOrCreate input: %O', input);
@@ -294,3 +495,5 @@ export const agentGroupRouter = router({
294
495
  }
295
496
  }),
296
497
  });
498
+
499
+ export type AgentGroupRouter = typeof agentGroupRouter;