@lobehub/lobehub 2.0.0-next.295 → 2.0.0-next.297

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 (132) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/en-US/plugin.json +4 -0
  4. package/locales/zh-CN/plugin.json +4 -0
  5. package/package.json +2 -2
  6. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +5 -5
  7. package/packages/agent-runtime/src/utils/stepContextComputer.test.ts +5 -5
  8. package/packages/builtin-tool-gtd/src/client/Inspector/index.ts +0 -4
  9. package/packages/builtin-tool-gtd/src/client/Intervention/AddTodo.tsx +1 -1
  10. package/packages/builtin-tool-gtd/src/client/Render/TodoList/index.tsx +39 -10
  11. package/packages/builtin-tool-gtd/src/client/Render/index.ts +0 -2
  12. package/packages/builtin-tool-gtd/src/client/components/SortableTodoList/TodoItemRow.tsx +26 -12
  13. package/packages/builtin-tool-gtd/src/client/components/SortableTodoList/store/actions.ts +5 -5
  14. package/packages/builtin-tool-gtd/src/client/components/SortableTodoList/store/store.test.ts +14 -8
  15. package/packages/builtin-tool-gtd/src/executor/index.test.ts +48 -227
  16. package/packages/builtin-tool-gtd/src/executor/index.ts +15 -158
  17. package/packages/builtin-tool-gtd/src/manifest.ts +12 -42
  18. package/packages/builtin-tool-gtd/src/systemRole.ts +14 -8
  19. package/packages/builtin-tool-gtd/src/types.ts +47 -41
  20. package/packages/builtin-tool-memory/package.json +8 -0
  21. package/packages/builtin-tool-memory/src/client/Inspector/AddContextMemory/index.tsx +60 -0
  22. package/packages/builtin-tool-memory/src/client/Inspector/AddExperienceMemory/index.tsx +60 -0
  23. package/packages/builtin-tool-memory/src/client/Inspector/AddIdentityMemory/index.tsx +60 -0
  24. package/packages/builtin-tool-memory/src/client/Inspector/AddPreferenceMemory/index.tsx +60 -0
  25. package/packages/builtin-tool-memory/src/client/Inspector/RemoveIdentityMemory/index.tsx +60 -0
  26. package/packages/builtin-tool-memory/src/client/Inspector/SearchUserMemory/index.tsx +67 -0
  27. package/packages/builtin-tool-memory/src/client/Inspector/UpdateIdentityMemory/index.tsx +60 -0
  28. package/packages/builtin-tool-memory/src/client/Inspector/index.ts +35 -0
  29. package/packages/builtin-tool-memory/src/client/Intervention/AddExperienceMemory/index.tsx +17 -0
  30. package/packages/builtin-tool-memory/src/client/Intervention/index.ts +13 -0
  31. package/packages/builtin-tool-memory/src/client/Render/AddExperienceMemory/index.tsx +17 -0
  32. package/packages/builtin-tool-memory/src/client/Render/SearchUserMemory/index.tsx +217 -0
  33. package/packages/builtin-tool-memory/src/client/Render/index.ts +15 -0
  34. package/packages/builtin-tool-memory/src/client/Streaming/AddExperienceMemory/index.tsx +17 -0
  35. package/packages/builtin-tool-memory/src/client/Streaming/index.ts +18 -0
  36. package/packages/builtin-tool-memory/src/client/components/ExperienceMemoryCard.tsx +231 -0
  37. package/packages/builtin-tool-memory/src/client/components/index.ts +1 -0
  38. package/packages/builtin-tool-memory/src/client/index.ts +27 -0
  39. package/packages/builtin-tool-memory/src/executor/index.ts +9 -1
  40. package/packages/builtin-tool-memory/src/types.ts +61 -0
  41. package/packages/context-engine/src/providers/GTDTodoInjector.ts +15 -7
  42. package/packages/conversation-flow/src/__tests__/fixtures/outputs/assistantGroup/tools-with-branches.json +4 -0
  43. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +1 -0
  44. package/packages/prompts/src/prompts/gtd/index.test.ts +32 -16
  45. package/packages/prompts/src/prompts/gtd/index.ts +9 -5
  46. package/packages/types/package.json +1 -1
  47. package/packages/types/src/discover/assistants.ts +4 -0
  48. package/packages/types/src/discover/groupAgents.ts +196 -0
  49. package/packages/types/src/discover/index.ts +5 -1
  50. package/packages/types/src/stepContext.ts +4 -1
  51. package/src/app/[variants]/(main)/community/(detail)/assistant/features/Details/Versions/index.tsx +2 -2
  52. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/DetailProvider.tsx +19 -0
  53. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Members/index.tsx +137 -0
  54. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Nav.tsx +88 -0
  55. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Overview/index.tsx +213 -0
  56. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Related/index.tsx +85 -0
  57. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/SystemRole/TagList.tsx +20 -0
  58. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/SystemRole/index.tsx +71 -0
  59. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Versions/index.tsx +119 -0
  60. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/index.tsx +51 -0
  61. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Header.tsx +253 -0
  62. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/AddGroupAgent.tsx +222 -0
  63. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/index.tsx +34 -0
  64. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/Summary/index.tsx +42 -0
  65. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/index.tsx +41 -0
  66. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/StatusPage/index.tsx +104 -0
  67. package/src/app/[variants]/(main)/community/(detail)/group_agent/index.tsx +103 -0
  68. package/src/app/[variants]/(main)/community/(detail)/group_agent/loading.tsx +1 -0
  69. package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +7 -1
  70. package/src/app/[variants]/(main)/community/(detail)/user/features/UserContent.tsx +2 -0
  71. package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupCard.tsx +186 -0
  72. package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupList.tsx +59 -0
  73. package/src/app/[variants]/(main)/community/(detail)/user/index.tsx +3 -1
  74. package/src/app/[variants]/(main)/community/(list)/assistant/features/List/Item.tsx +26 -8
  75. package/src/app/[variants]/(main)/community/(list)/assistant/index.tsx +1 -0
  76. package/src/app/[variants]/(main)/community/features/Search.tsx +1 -1
  77. package/src/app/[variants]/(main)/group/profile/features/GroupProfile/index.tsx +2 -0
  78. package/src/app/[variants]/(main)/group/profile/features/Header/AgentPublishButton/PublishResultModal.tsx +2 -1
  79. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/GroupForkConfirmModal.tsx +60 -0
  80. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/GroupPublishResultModal.tsx +62 -0
  81. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/PublishButton.tsx +122 -0
  82. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/index.tsx +46 -0
  83. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/types.ts +12 -0
  84. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/useMarketGroupPublish.ts +211 -0
  85. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/utils.ts +22 -0
  86. package/src/app/[variants]/(main)/resource/features/DndContextWrapper.tsx +4 -2
  87. package/src/app/[variants]/(main)/resource/library/_layout/Header/LibraryHead.tsx +30 -35
  88. package/src/app/[variants]/(main)/resource/library/_layout/Header/index.tsx +9 -11
  89. package/src/app/[variants]/router/desktopRouter.config.tsx +7 -0
  90. package/src/features/Conversation/Messages/AssistantGroup/Tool/Actions/index.tsx +11 -17
  91. package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/LoadingPlaceholder/index.tsx +13 -3
  92. package/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/CustomRender.tsx +43 -0
  93. package/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/FallbacktArgumentRender.tsx +59 -0
  94. package/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/index.tsx +46 -0
  95. package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/index.tsx +13 -19
  96. package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +17 -17
  97. package/src/features/Conversation/Messages/Tool/Tool/index.tsx +10 -9
  98. package/src/features/Conversation/TodoProgress/index.tsx +56 -23
  99. package/src/features/PluginsUI/Render/MCPType/index.tsx +1 -1
  100. package/src/features/ResourceManager/components/Explorer/Header/index.tsx +57 -4
  101. package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +6 -4
  102. package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +16 -5
  103. package/src/features/ResourceManager/components/LibraryHierarchy/styles.ts +5 -4
  104. package/src/hooks/useActiveTabKey.ts +1 -2
  105. package/src/locales/default/plugin.ts +1 -0
  106. package/src/locales/default/setting.ts +12 -0
  107. package/src/server/routers/lambda/market/agentGroup.ts +296 -0
  108. package/src/server/routers/lambda/market/index.ts +134 -4
  109. package/src/server/services/discover/index.ts +123 -7
  110. package/src/services/discover.ts +55 -0
  111. package/src/store/chat/slices/message/selectors/dbMessage.test.ts +11 -11
  112. package/src/store/discover/slices/groupAgent/action.ts +80 -0
  113. package/src/store/discover/store.ts +3 -0
  114. package/src/store/file/slices/resource/action.ts +4 -2
  115. package/src/tools/inspectors.ts +2 -0
  116. package/src/tools/interventions.ts +2 -0
  117. package/src/tools/renders.ts +3 -1
  118. package/src/tools/streamings.ts +2 -0
  119. package/packages/builtin-tool-gtd/src/client/Inspector/CompleteTodos/index.tsx +0 -52
  120. package/packages/builtin-tool-gtd/src/client/Inspector/RemoveTodos/index.tsx +0 -52
  121. package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/CustomRender.tsx +0 -113
  122. package/src/features/Conversation/Messages/Tool/Tool/Render.tsx +0 -47
  123. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/AbortResponse.tsx +0 -0
  124. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Arguments/index.tsx +0 -0
  125. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/ErrorResponse.tsx +0 -0
  126. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/ApprovalActions.tsx +0 -0
  127. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/Fallback.tsx +0 -0
  128. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/KeyValueEditor.tsx +0 -0
  129. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/ModeSelector.tsx +0 -0
  130. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/Intervention/index.tsx +0 -0
  131. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/PluginSettings.tsx +0 -0
  132. /package/src/features/Conversation/Messages/AssistantGroup/Tool/{Render → Detail}/RejectedResponse.tsx +0 -0
@@ -0,0 +1,119 @@
1
+ import { Block, Flexbox, Icon, Tag } from '@lobehub/ui';
2
+ import { cssVar } from 'antd-style';
3
+ import { CheckIcon, MinusIcon } from 'lucide-react';
4
+ import { memo, useMemo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import InlineTable from '@/components/InlineTable';
8
+ import PublishedTime from '@/components/PublishedTime';
9
+
10
+ import Title from '../../../../../features/Title';
11
+ import { useDetailContext } from '../../DetailProvider';
12
+
13
+ const Versions = memo(() => {
14
+ const { t } = useTranslation('discover');
15
+ const { versions = [], currentVersion } = useDetailContext();
16
+
17
+ const statusTagMap = useMemo(
18
+ () => ({
19
+ archived: {
20
+ color: 'default' as const,
21
+ label: t('groupAgents.details.version.status.archived', { defaultValue: 'Archived' }),
22
+ },
23
+ deprecated: {
24
+ color: 'warning' as const,
25
+ label: t('groupAgents.details.version.status.deprecated', { defaultValue: 'Deprecated' }),
26
+ },
27
+ published: {
28
+ color: 'success' as const,
29
+ label: t('groupAgents.details.version.status.published', { defaultValue: 'Published' }),
30
+ },
31
+ unpublished: {
32
+ color: 'default' as const,
33
+ label: t('groupAgents.details.version.status.unpublished', { defaultValue: 'Unpublished' }),
34
+ },
35
+ }),
36
+ [t],
37
+ );
38
+
39
+ if (!versions.length) {
40
+ return (
41
+ <Flexbox gap={16}>
42
+ <Title>
43
+ {t('groupAgents.details.version.title', { defaultValue: 'Version History' })}
44
+ </Title>
45
+ <Block padding={24} variant={'outlined'}>
46
+ {t('groupAgents.details.version.empty', { defaultValue: 'No version history available' })}
47
+ </Block>
48
+ </Flexbox>
49
+ );
50
+ }
51
+
52
+ return (
53
+ <Flexbox gap={16}>
54
+ <Title>
55
+ {t('groupAgents.details.version.title', { defaultValue: 'Version History' })}
56
+ </Title>
57
+ <Block variant={'outlined'}>
58
+ <InlineTable
59
+ columns={[
60
+ {
61
+ dataIndex: 'version',
62
+ render: (_: any, record: any) => {
63
+ const statusKey =
64
+ record.status &&
65
+ Object.prototype.hasOwnProperty.call(statusTagMap, record.status)
66
+ ? (record.status as keyof typeof statusTagMap)
67
+ : undefined;
68
+ const statusMeta = statusKey ? statusTagMap[statusKey] : undefined;
69
+
70
+ return (
71
+ <Flexbox align={'center'} gap={8} horizontal>
72
+ <code style={{ fontSize: 14 }}>{record.version}</code>
73
+ {(record.isLatest || record.version === currentVersion) && (
74
+ <Tag color={'info'}>
75
+ {t('groupAgents.details.version.table.isLatest', { defaultValue: 'Latest' })}
76
+ </Tag>
77
+ )}
78
+ {statusMeta && <Tag color={statusMeta.color}>{statusMeta.label}</Tag>}
79
+ </Flexbox>
80
+ );
81
+ },
82
+ title: t('groupAgents.details.version.table.version', { defaultValue: 'Version' }),
83
+ },
84
+ {
85
+ align: 'center',
86
+ dataIndex: 'isValidated',
87
+ render: (_: any, record: any) => (
88
+ <Icon
89
+ color={record.isValidated ? cssVar.colorSuccess : cssVar.colorTextDescription}
90
+ icon={record.isValidated ? CheckIcon : MinusIcon}
91
+ />
92
+ ),
93
+ title: t('groupAgents.details.version.table.isValidated', {
94
+ defaultValue: 'Validated',
95
+ }),
96
+ },
97
+ {
98
+ align: 'end',
99
+ dataIndex: 'createdAt',
100
+ render: (_: any, record: any) => (
101
+ <PublishedTime date={record.createdAt} showPrefix={false} />
102
+ ),
103
+ title: t('groupAgents.details.version.table.publishAt', {
104
+ defaultValue: 'Published At',
105
+ }),
106
+ },
107
+ ]}
108
+ dataSource={versions}
109
+ rowKey={'version'}
110
+ size={'middle'}
111
+ />
112
+ </Block>
113
+ </Flexbox>
114
+ );
115
+ });
116
+
117
+ Versions.displayName = 'GroupAgentVersions';
118
+
119
+ export default Versions;
@@ -0,0 +1,51 @@
1
+ import { Flexbox } from '@lobehub/ui';
2
+ import { useResponsive } from 'antd-style';
3
+ import { useQueryState } from 'nuqs';
4
+ import { memo } from 'react';
5
+
6
+ import Sidebar from '../Sidebar';
7
+ import Nav, { GroupAgentNavKey } from './Nav';
8
+ import Overview from './Overview';
9
+ import SystemRole from './SystemRole';
10
+ import Versions from './Versions';
11
+
12
+ const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
13
+ const { mobile = isMobile } = useResponsive();
14
+ const [activeTabParam, setActiveTab] = useQueryState('activeTab');
15
+ const activeTab = activeTabParam || GroupAgentNavKey.Overview;
16
+
17
+ return (
18
+ <Flexbox gap={24}>
19
+ {/* Navigation */}
20
+ <Nav
21
+ activeTab={activeTab as GroupAgentNavKey}
22
+ mobile={mobile}
23
+ setActiveTab={(tab) => setActiveTab(tab)}
24
+ />
25
+
26
+ <Flexbox
27
+ gap={48}
28
+ horizontal={!mobile}
29
+ style={mobile ? { flexDirection: 'column-reverse' } : undefined}
30
+ >
31
+ {/* Main Content */}
32
+ <Flexbox
33
+ style={{
34
+ overflow: 'hidden',
35
+ }}
36
+ width={'100%'}
37
+ >
38
+ {/* Tab Content */}
39
+ {activeTab === GroupAgentNavKey.Overview && <Overview />}
40
+ {activeTab === GroupAgentNavKey.SystemRole && <SystemRole />}
41
+ {activeTab === GroupAgentNavKey.Versions && <Versions />}
42
+ </Flexbox>
43
+
44
+ {/* Sidebar */}
45
+ <Sidebar mobile={mobile} />
46
+ </Flexbox>
47
+ </Flexbox>
48
+ );
49
+ });
50
+
51
+ export default Details;
@@ -0,0 +1,253 @@
1
+ 'use client';
2
+
3
+ import {
4
+ ActionIcon,
5
+ Avatar,
6
+ Button,
7
+ Flexbox,
8
+ Icon,
9
+ Text,
10
+ Tooltip,
11
+ TooltipGroup,
12
+ } from '@lobehub/ui';
13
+ import { App } from 'antd';
14
+ import { createStaticStyles, cssVar, useResponsive } from 'antd-style';
15
+ import { BookmarkCheckIcon, BookmarkIcon, DotIcon, HeartIcon, UsersIcon } from 'lucide-react';
16
+ import qs from 'query-string';
17
+ import { memo, useState } from 'react';
18
+ import { useTranslation } from 'react-i18next';
19
+ import { Link } from 'react-router-dom';
20
+ import useSWR from 'swr';
21
+ import urlJoin from 'url-join';
22
+
23
+ import PublishedTime from '@/components/PublishedTime';
24
+ import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
25
+ import { socialService } from '@/services/social';
26
+
27
+ import { useDetailContext } from './DetailProvider';
28
+
29
+ const styles = createStaticStyles(({ css, cssVar }) => ({
30
+ time: css`
31
+ font-size: 12px;
32
+ color: ${cssVar.colorTextDescription};
33
+ `,
34
+ }));
35
+
36
+ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
37
+ const { t } = useTranslation('discover');
38
+ const { message } = App.useApp();
39
+ const data = useDetailContext();
40
+ const { mobile = isMobile } = useResponsive();
41
+ const { isAuthenticated, signIn, session } = useMarketAuth();
42
+ const [favoriteLoading, setFavoriteLoading] = useState(false);
43
+ const [likeLoading, setLikeLoading] = useState(false);
44
+
45
+ const {
46
+ memberAgents = [],
47
+ author,
48
+ avatar,
49
+ title,
50
+ category,
51
+ identifier,
52
+ createdAt,
53
+ userName,
54
+ } = data;
55
+
56
+ const displayAvatar = avatar || title?.[0] || '👥';
57
+ const memberCount = memberAgents?.length || 0;
58
+
59
+ // Set access token for social service
60
+ if (session?.accessToken) {
61
+ socialService.setAccessToken(session.accessToken);
62
+ }
63
+
64
+ // TODO: Use 'group_agent' type when social service supports it
65
+ // Fetch favorite status
66
+ const { data: favoriteStatus, mutate: mutateFavorite } = useSWR(
67
+ identifier && isAuthenticated ? ['favorite-status', 'agent', identifier] : null,
68
+ () => socialService.checkFavoriteStatus('agent', identifier!),
69
+ { revalidateOnFocus: false },
70
+ );
71
+
72
+ const isFavorited = favoriteStatus?.isFavorited ?? false;
73
+
74
+ // Fetch like status
75
+ const { data: likeStatus, mutate: mutateLike } = useSWR(
76
+ identifier && isAuthenticated ? ['like-status', 'agent', identifier] : null,
77
+ () => socialService.checkLikeStatus('agent', identifier!),
78
+ { revalidateOnFocus: false },
79
+ );
80
+ const isLiked = likeStatus?.isLiked ?? false;
81
+
82
+ const handleFavoriteClick = async () => {
83
+ if (!isAuthenticated) {
84
+ await signIn();
85
+ return;
86
+ }
87
+
88
+ if (!identifier) return;
89
+
90
+ setFavoriteLoading(true);
91
+ try {
92
+ if (isFavorited) {
93
+ await socialService.removeFavorite('agent', identifier);
94
+ message.success(t('assistant.unfavoriteSuccess'));
95
+ } else {
96
+ await socialService.addFavorite('agent', identifier);
97
+ message.success(t('assistant.favoriteSuccess'));
98
+ }
99
+ await mutateFavorite();
100
+ } catch {
101
+ message.error(t('assistant.favoriteFailed'));
102
+ } finally {
103
+ setFavoriteLoading(false);
104
+ }
105
+ };
106
+
107
+ const handleLikeClick = async () => {
108
+ if (!isAuthenticated) {
109
+ await signIn();
110
+ return;
111
+ }
112
+
113
+ if (!identifier) return;
114
+
115
+ setLikeLoading(true);
116
+ try {
117
+ if (isLiked) {
118
+ await socialService.unlike('agent', identifier);
119
+ message.success(t('assistant.unlikeSuccess'));
120
+ } else {
121
+ await socialService.like('agent', identifier);
122
+ message.success(t('assistant.likeSuccess'));
123
+ }
124
+ await mutateLike();
125
+ } catch {
126
+ message.error(t('assistant.likeFailed'));
127
+ } finally {
128
+ setLikeLoading(false);
129
+ }
130
+ };
131
+
132
+ const cateButton = category ? (
133
+ <Link
134
+ to={qs.stringifyUrl({
135
+ query: { category },
136
+ url: '/community/group_agent',
137
+ })}
138
+ >
139
+ <Button size={'middle'} variant={'outlined'}>
140
+ {category}
141
+ </Button>
142
+ </Link>
143
+ ) : null;
144
+
145
+ return (
146
+ <Flexbox gap={12}>
147
+ <Flexbox align={'flex-start'} gap={16} horizontal width={'100%'}>
148
+ <Avatar avatar={displayAvatar} shape={'square'} size={mobile ? 48 : 64} />
149
+ <Flexbox
150
+ flex={1}
151
+ gap={4}
152
+ style={{
153
+ overflow: 'hidden',
154
+ }}
155
+ >
156
+ <Flexbox
157
+ align={'center'}
158
+ gap={8}
159
+ horizontal
160
+ justify={'space-between'}
161
+ style={{
162
+ overflow: 'hidden',
163
+ position: 'relative',
164
+ }}
165
+ >
166
+ <Flexbox
167
+ align={'center'}
168
+ flex={1}
169
+ gap={12}
170
+ horizontal
171
+ style={{
172
+ overflow: 'hidden',
173
+ position: 'relative',
174
+ }}
175
+ >
176
+ <Text
177
+ as={'h1'}
178
+ ellipsis
179
+ style={{ fontSize: mobile ? 18 : 24, margin: 0 }}
180
+ title={identifier}
181
+ >
182
+ {title}
183
+ </Text>
184
+ </Flexbox>
185
+ <Tooltip title={isLiked ? t('assistant.unlike') : t('assistant.like')}>
186
+ <ActionIcon
187
+ icon={HeartIcon}
188
+ loading={likeLoading}
189
+ onClick={handleLikeClick}
190
+ style={isLiked ? { color: '#ff4d4f' } : undefined}
191
+ />
192
+ </Tooltip>
193
+ <Tooltip title={isFavorited ? t('assistant.unfavorite') : t('assistant.favorite')}>
194
+ <ActionIcon
195
+ icon={isFavorited ? BookmarkCheckIcon : BookmarkIcon}
196
+ loading={favoriteLoading}
197
+ onClick={handleFavoriteClick}
198
+ variant={isFavorited ? 'outlined' : undefined}
199
+ />
200
+ </Tooltip>
201
+ </Flexbox>
202
+ <Flexbox align={'center'} gap={4} horizontal>
203
+ {(() => {
204
+ // API returns author as object {avatar, name, userName}, but type definition says string
205
+ const authorObj =
206
+ typeof author === 'object' && author !== null ? (author as any) : null;
207
+ const authorName = authorObj ? authorObj.name || authorObj.userName : author;
208
+
209
+ return authorName && userName ? (
210
+ <Link style={{ color: 'inherit' }} to={urlJoin('/community/user', userName)}>
211
+ {authorName}
212
+ </Link>
213
+ ) : (
214
+ authorName
215
+ );
216
+ })()}
217
+ <Icon icon={DotIcon} />
218
+ <PublishedTime
219
+ className={styles.time}
220
+ date={createdAt as string}
221
+ template={'MMM DD, YYYY'}
222
+ />
223
+ </Flexbox>
224
+ </Flexbox>
225
+ </Flexbox>
226
+ <TooltipGroup>
227
+ <Flexbox
228
+ align={'center'}
229
+ gap={mobile ? 12 : 24}
230
+ horizontal
231
+ style={{
232
+ color: cssVar.colorTextSecondary,
233
+ }}
234
+ >
235
+ {!mobile && cateButton}
236
+ {Boolean(memberCount) && (
237
+ <Tooltip
238
+ styles={{ root: { pointerEvents: 'none' } }}
239
+ title={t('groupAgents.memberCount', { defaultValue: 'Members' })}
240
+ >
241
+ <Flexbox align={'center'} gap={6} horizontal>
242
+ <Icon icon={UsersIcon} />
243
+ {memberCount}
244
+ </Flexbox>
245
+ </Tooltip>
246
+ )}
247
+ </Flexbox>
248
+ </TooltipGroup>
249
+ </Flexbox>
250
+ );
251
+ });
252
+
253
+ export default Header;
@@ -0,0 +1,222 @@
1
+ 'use client';
2
+
3
+ import { Button, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
4
+ import { App } from 'antd';
5
+ import { createStaticStyles } from 'antd-style';
6
+ import { ChevronDownIcon } from 'lucide-react';
7
+ import { memo, useState } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import { useNavigate } from 'react-router-dom';
10
+ import urlJoin from 'url-join';
11
+
12
+ import { chatGroupService } from '@/services/chatGroup';
13
+ import { discoverService } from '@/services/discover';
14
+ import { useAgentGroupStore } from '@/store/agentGroup';
15
+
16
+ import { useDetailContext } from '../../DetailProvider';
17
+
18
+ const styles = createStaticStyles(({ css }) => ({
19
+ buttonGroup: css`
20
+ width: 100%;
21
+ `,
22
+ menuButton: css`
23
+ padding-inline: 8px;
24
+ border-start-start-radius: 0 !important;
25
+ border-end-start-radius: 0 !important;
26
+ `,
27
+ primaryButton: css`
28
+ border-start-end-radius: 0 !important;
29
+ border-end-end-radius: 0 !important;
30
+ `,
31
+ }));
32
+
33
+ const AddGroupAgent = memo<{ mobile?: boolean }>(() => {
34
+ const {
35
+ avatar,
36
+ description,
37
+ tags,
38
+ title,
39
+ config,
40
+ backgroundColor,
41
+ identifier,
42
+ memberAgents = [],
43
+ } = useDetailContext();
44
+ const [isLoading, setIsLoading] = useState(false);
45
+ const { message, modal } = App.useApp();
46
+ const { t } = useTranslation('discover');
47
+ const navigate = useNavigate();
48
+ const loadGroups = useAgentGroupStore((s) => s.loadGroups);
49
+
50
+ const meta = {
51
+ avatar,
52
+ backgroundColor,
53
+ description,
54
+ tags,
55
+ title,
56
+ };
57
+
58
+ // Check if a group with the same title already exists
59
+ const checkDuplicateGroup = async () => {
60
+ if (!title) return false;
61
+ try {
62
+ const groups = await chatGroupService.getGroups();
63
+ return groups.some((g) => g.title === title);
64
+ } catch {
65
+ return false;
66
+ }
67
+ };
68
+
69
+ const showDuplicateConfirmation = (callback: () => void) => {
70
+ modal.confirm({
71
+ cancelText: t('cancel', { ns: 'common' }),
72
+ content: t('groupAgents.duplicateAdd.content', {
73
+ defaultValue: 'This group agent has already been added. Do you want to add it again?',
74
+ title,
75
+ }),
76
+ okText: t('groupAgents.duplicateAdd.ok', { defaultValue: 'Add Anyway' }),
77
+ onOk: callback,
78
+ title: t('groupAgents.duplicateAdd.title', { defaultValue: 'Group Already Added' }),
79
+ });
80
+ };
81
+
82
+ const createGroupFromMarket = async (shouldNavigate = true) => {
83
+ if (!config) {
84
+ message.error(
85
+ t('groupAgents.noConfig', { defaultValue: 'Group configuration not available' }),
86
+ );
87
+ return;
88
+ }
89
+
90
+ // Prepare group config
91
+ const groupConfig = {
92
+ config: {
93
+ allowDM: config.allowDM,
94
+ openingMessage: config.openingMessage,
95
+ openingQuestions: config.openingQuestions,
96
+ revealDM: config.revealDM,
97
+ },
98
+ content: config.systemRole,
99
+ ...meta,
100
+ };
101
+
102
+ // Prepare member agents from market data
103
+ const members = memberAgents.map((member: any) => {
104
+ const agent = member.agent || member;
105
+ const currentVersion = member.currentVersion || member;
106
+ return {
107
+ avatar: currentVersion.avatar,
108
+ backgroundColor: currentVersion.backgroundColor,
109
+ description: currentVersion.description,
110
+ model: currentVersion.model,
111
+ plugins: currentVersion.plugins,
112
+ provider: currentVersion.provider,
113
+ systemRole: currentVersion.systemRole || currentVersion.content,
114
+ tags: currentVersion.tags,
115
+ title: currentVersion.name || agent.name,
116
+ };
117
+ });
118
+
119
+ try {
120
+ // Create group with all members in one request
121
+ const result = await chatGroupService.createGroupWithMembers(groupConfig, members);
122
+
123
+ // Refresh group list
124
+ await loadGroups();
125
+
126
+ // Report installation to marketplace
127
+ if (identifier) {
128
+ discoverService.reportAgentInstall(identifier);
129
+ discoverService.reportAgentEvent({
130
+ event: 'add',
131
+ identifier,
132
+ source: location.pathname,
133
+ });
134
+ }
135
+
136
+ message.success(
137
+ t('groupAgents.addSuccess', { defaultValue: 'Group agent added successfully!' }),
138
+ );
139
+
140
+ if (shouldNavigate) {
141
+ navigate(urlJoin('/group', result.groupId));
142
+ }
143
+
144
+ return result;
145
+ } catch (error) {
146
+ console.error('Failed to create group from market:', error);
147
+ message.error(
148
+ t('groupAgents.addError', {
149
+ defaultValue: 'Failed to add group agent. Please try again.',
150
+ }),
151
+ );
152
+ throw error;
153
+ }
154
+ };
155
+
156
+ const handleAddAndConverse = async () => {
157
+ setIsLoading(true);
158
+ try {
159
+ const isDuplicate = await checkDuplicateGroup();
160
+ if (isDuplicate) {
161
+ showDuplicateConfirmation(() => createGroupFromMarket(true));
162
+ } else {
163
+ await createGroupFromMarket(true);
164
+ }
165
+ } finally {
166
+ setIsLoading(false);
167
+ }
168
+ };
169
+
170
+ const handleAdd = async () => {
171
+ setIsLoading(true);
172
+ try {
173
+ const isDuplicate = await checkDuplicateGroup();
174
+ if (isDuplicate) {
175
+ showDuplicateConfirmation(() => createGroupFromMarket(false));
176
+ } else {
177
+ await createGroupFromMarket(false);
178
+ }
179
+ } finally {
180
+ setIsLoading(false);
181
+ }
182
+ };
183
+
184
+ const menuItems = [
185
+ {
186
+ key: 'addGroup',
187
+ label: t('groupAgents.addGroup', { defaultValue: 'Add Group' }),
188
+ onClick: handleAdd,
189
+ },
190
+ ];
191
+
192
+ return (
193
+ <Flexbox className={styles.buttonGroup} gap={0} horizontal>
194
+ <Button
195
+ block
196
+ className={styles.primaryButton}
197
+ loading={isLoading}
198
+ onClick={handleAddAndConverse}
199
+ size={'large'}
200
+ style={{ flex: 1, width: 'unset' }}
201
+ type={'primary'}
202
+ >
203
+ {t('groupAgents.addAndConverse', { defaultValue: 'Add & Start Conversation' })}
204
+ </Button>
205
+ <DropdownMenu
206
+ items={menuItems}
207
+ popupProps={{ style: { minWidth: 267 } }}
208
+ triggerProps={{ disabled: isLoading }}
209
+ >
210
+ <Button
211
+ className={styles.menuButton}
212
+ disabled={isLoading}
213
+ icon={<Icon icon={ChevronDownIcon} />}
214
+ size={'large'}
215
+ type={'primary'}
216
+ />
217
+ </DropdownMenu>
218
+ </Flexbox>
219
+ );
220
+ });
221
+
222
+ export default AddGroupAgent;
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ import { Flexbox } from '@lobehub/ui';
4
+ import { memo } from 'react';
5
+ import urlJoin from 'url-join';
6
+
7
+ import { OFFICIAL_URL } from '@/const/url';
8
+
9
+ import ShareButton from '../../../../features/ShareButton';
10
+ import { useDetailContext } from '../../DetailProvider';
11
+ import AddGroupAgent from './AddGroupAgent';
12
+
13
+ const ActionButton = memo<{ mobile?: boolean }>(({ mobile }) => {
14
+ const { avatar, title, description, tags, identifier } = useDetailContext();
15
+
16
+ return (
17
+ <Flexbox align={'center'} gap={8} horizontal>
18
+ <AddGroupAgent mobile={mobile} />
19
+ {identifier && (
20
+ <ShareButton
21
+ meta={{
22
+ avatar: avatar,
23
+ desc: description,
24
+ hashtags: tags,
25
+ title: title,
26
+ url: urlJoin(OFFICIAL_URL, '/community/group_agent', identifier),
27
+ }}
28
+ />
29
+ )}
30
+ </Flexbox>
31
+ );
32
+ });
33
+
34
+ export default ActionButton;