@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
@@ -14,6 +14,7 @@ import { agentGroupSelectors } from '@/store/agentGroup/selectors';
14
14
  import { useGroupProfileStore } from '@/store/groupProfile';
15
15
 
16
16
  import AutoSaveHint from '../Header/AutoSaveHint';
17
+ import GroupPublishButton from '../Header/GroupPublishButton';
17
18
  import GroupHeader from './GroupHeader';
18
19
 
19
20
  const GroupProfile = memo(() => {
@@ -75,6 +76,7 @@ const GroupProfile = memo(() => {
75
76
  >
76
77
  {t('startConversation')}
77
78
  </Button>
79
+ <GroupPublishButton />
78
80
  </Flexbox>
79
81
  </Flexbox>
80
82
  <Divider />
@@ -19,7 +19,8 @@ const PublishResultModal = memo<PublishResultModalProps>(({ identifier, onCancel
19
19
 
20
20
  const handleGoToMarket = () => {
21
21
  if (identifier) {
22
- navigate(`/community/assistant/${identifier}`);
22
+ console.log('identifier', identifier);
23
+ navigate(`/community/group_agent/${identifier}`);
23
24
  }
24
25
  onCancel();
25
26
  };
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ import { Avatar, Flexbox, Modal } from '@lobehub/ui';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import type { OriginalGroupInfo } from './types';
8
+
9
+ interface GroupForkConfirmModalProps {
10
+ loading?: boolean;
11
+ onCancel: () => void;
12
+ onConfirm: () => void;
13
+ open: boolean;
14
+ originalGroup: OriginalGroupInfo | null;
15
+ }
16
+
17
+ const GroupForkConfirmModal = memo<GroupForkConfirmModalProps>(
18
+ ({ open, onCancel, onConfirm, originalGroup, loading }) => {
19
+ const { t } = useTranslation('setting');
20
+
21
+ if (!originalGroup) return null;
22
+
23
+ const authorName = originalGroup.author?.name || originalGroup.author?.userName || 'Unknown';
24
+
25
+ return (
26
+ <Modal
27
+ cancelText={t('cancel', { ns: 'common' })}
28
+ centered
29
+ closable
30
+ confirmLoading={loading}
31
+ okText={t('marketPublish.forkConfirm.confirmGroup')}
32
+ onCancel={onCancel}
33
+ onOk={onConfirm}
34
+ open={open}
35
+ title={t('marketPublish.forkConfirm.titleGroup')}
36
+ width={480}
37
+ >
38
+ <Flexbox gap={16} style={{ marginTop: 16 }}>
39
+ <Flexbox align="center" gap={12} horizontal>
40
+ <Avatar avatar={originalGroup.avatar} size={48} style={{ flex: 'none' }} />
41
+ <Flexbox gap={4}>
42
+ <div style={{ fontWeight: 500 }}>{originalGroup.name}</div>
43
+ <div style={{ fontSize: 12, opacity: 0.6 }}>
44
+ {t('marketPublish.forkConfirm.by', { author: authorName })}
45
+ </div>
46
+ </Flexbox>
47
+ </Flexbox>
48
+
49
+ <p style={{ lineHeight: 1.6, margin: 0 }}>
50
+ {t('marketPublish.forkConfirm.descriptionGroup')}
51
+ </p>
52
+ </Flexbox>
53
+ </Modal>
54
+ );
55
+ },
56
+ );
57
+
58
+ GroupForkConfirmModal.displayName = 'GroupForkConfirmModal';
59
+
60
+ export default GroupForkConfirmModal;
@@ -0,0 +1,62 @@
1
+ 'use client';
2
+
3
+ import { FluentEmoji, Modal, Text } from '@lobehub/ui';
4
+ import { Result } from 'antd';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { useNavigate } from 'react-router-dom';
8
+
9
+ interface GroupPublishResultModalProps {
10
+ identifier?: string;
11
+ onCancel: () => void;
12
+ open: boolean;
13
+ }
14
+
15
+ const GroupPublishResultModal = memo<GroupPublishResultModalProps>(
16
+ ({ identifier, onCancel, open }) => {
17
+ const navigate = useNavigate();
18
+ const { t } = useTranslation('setting');
19
+ const { t: tCommon } = useTranslation('common');
20
+
21
+ const handleGoToMarket = () => {
22
+ if (identifier) {
23
+ navigate(`/community/group_agent/${identifier}`);
24
+ }
25
+ onCancel();
26
+ };
27
+
28
+ return (
29
+ <Modal
30
+ cancelText={tCommon('cancel')}
31
+ centered
32
+ okText={t('marketPublish.resultModal.view')}
33
+ onCancel={onCancel}
34
+ onOk={handleGoToMarket}
35
+ open={open}
36
+ title={null}
37
+ width={440}
38
+ >
39
+ <Result
40
+ icon={<FluentEmoji emoji={'🎉'} size={96} type={'anim'} />}
41
+ style={{
42
+ paddingBottom: 32,
43
+ paddingTop: 48,
44
+ width: '100%',
45
+ }}
46
+ subTitle={
47
+ <Text fontSize={14} type={'secondary'}>
48
+ {t('marketPublish.resultModal.messageGroup')}
49
+ </Text>
50
+ }
51
+ title={
52
+ <Text fontSize={28} weight={'bold'}>
53
+ {t('marketPublish.resultModal.title')}
54
+ </Text>
55
+ }
56
+ />
57
+ </Modal>
58
+ );
59
+ },
60
+ );
61
+
62
+ export default GroupPublishResultModal;
@@ -0,0 +1,122 @@
1
+ import { Button } from '@lobehub/ui';
2
+ import { ShapesUploadIcon } from '@lobehub/ui/icons';
3
+ import { memo, useCallback, useMemo, useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { message } from '@/components/AntdStaticMethods';
7
+ import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
8
+ import { resolveMarketAuthError } from '@/layout/AuthProvider/MarketAuth/errors';
9
+
10
+ import GroupForkConfirmModal from './GroupForkConfirmModal';
11
+ import type { MarketPublishAction, OriginalGroupInfo } from './types';
12
+ import { useMarketGroupPublish } from './useMarketGroupPublish';
13
+
14
+ interface GroupPublishButtonProps {
15
+ action: MarketPublishAction;
16
+ onPublishSuccess?: (identifier: string) => void;
17
+ }
18
+
19
+ const PublishButton = memo<GroupPublishButtonProps>(({ action, onPublishSuccess }) => {
20
+ const { t } = useTranslation(['setting', 'marketAuth']);
21
+
22
+ const { isAuthenticated, isLoading, signIn } = useMarketAuth();
23
+ const { checkOwnership, isCheckingOwnership, isPublishing, publish } = useMarketGroupPublish({
24
+ action,
25
+ onSuccess: onPublishSuccess,
26
+ });
27
+
28
+ // Fork confirmation modal state
29
+ const [showForkModal, setShowForkModal] = useState(false);
30
+ const [originalGroupInfo, setOriginalGroupInfo] = useState<OriginalGroupInfo | null>(null);
31
+
32
+ const buttonConfig = useMemo(() => {
33
+ if (action === 'upload') {
34
+ return {
35
+ authenticated: t('marketPublish.uploadGroup.tooltip'),
36
+ unauthenticated: t('marketPublish.uploadGroup.tooltip'),
37
+ } as const;
38
+ }
39
+
40
+ const submitText = t('submitGroupModal.tooltips');
41
+
42
+ return {
43
+ authenticated: submitText,
44
+ unauthenticated: t('marketPublish.submitGroup.tooltip'),
45
+ } as const;
46
+ }, [action, t]);
47
+
48
+ const doPublish = useCallback(async () => {
49
+ // Check ownership before publishing
50
+ const { needsForkConfirm, originalGroup } = await checkOwnership();
51
+
52
+ if (needsForkConfirm && originalGroup) {
53
+ // Show fork confirmation modal
54
+ setOriginalGroupInfo(originalGroup);
55
+ setShowForkModal(true);
56
+ return;
57
+ }
58
+
59
+ // No confirmation needed, proceed with publish
60
+ await publish();
61
+ }, [checkOwnership, publish]);
62
+
63
+ const handleButtonClick = useCallback(async () => {
64
+ if (!isAuthenticated) {
65
+ try {
66
+ await signIn();
67
+ // After authentication, proceed with ownership check and publish
68
+ await doPublish();
69
+ } catch (error) {
70
+ console.error(`[GroupPublishButton][${action}] Authorization failed:`, error);
71
+ const normalizedError = resolveMarketAuthError(error);
72
+ message.error({
73
+ content: t(`errors.${normalizedError.code}`, { ns: 'marketAuth' }),
74
+ key: 'market-auth',
75
+ });
76
+ }
77
+ return;
78
+ }
79
+
80
+ // User is authenticated, check ownership and publish
81
+ await doPublish();
82
+ }, [action, doPublish, isAuthenticated, signIn, t]);
83
+
84
+ const handleForkConfirm = useCallback(async () => {
85
+ setShowForkModal(false);
86
+ setOriginalGroupInfo(null);
87
+ // User confirmed, proceed with publish
88
+ await publish();
89
+ }, [publish]);
90
+
91
+ const handleForkCancel = useCallback(() => {
92
+ setShowForkModal(false);
93
+ setOriginalGroupInfo(null);
94
+ }, []);
95
+
96
+ const buttonTitle = isAuthenticated ? buttonConfig.authenticated : buttonConfig.unauthenticated;
97
+ const loading = isLoading || isCheckingOwnership || isPublishing;
98
+
99
+ return (
100
+ <>
101
+ <Button
102
+ icon={ShapesUploadIcon}
103
+ loading={loading}
104
+ onClick={handleButtonClick}
105
+ title={buttonTitle}
106
+ >
107
+ {t('publishToCommunity')}
108
+ </Button>
109
+ <GroupForkConfirmModal
110
+ loading={isPublishing}
111
+ onCancel={handleForkCancel}
112
+ onConfirm={handleForkConfirm}
113
+ open={showForkModal}
114
+ originalGroup={originalGroupInfo}
115
+ />
116
+ </>
117
+ );
118
+ });
119
+
120
+ PublishButton.displayName = 'GroupPublishButton';
121
+
122
+ export default PublishButton;
@@ -0,0 +1,46 @@
1
+ import isEqual from 'fast-deep-equal';
2
+ import { memo, useCallback, useState } from 'react';
3
+
4
+ import { useAgentGroupStore } from '@/store/agentGroup';
5
+ import { agentGroupSelectors } from '@/store/agentGroup/selectors';
6
+
7
+ import GroupPublishResultModal from './GroupPublishResultModal';
8
+ import PublishButton from './PublishButton';
9
+
10
+ /**
11
+ * Group Publish Button Component
12
+ *
13
+ * Simplified version - backend handles ownership check automatically.
14
+ * The action type (submit vs upload) is determined by backend based on:
15
+ * 1. Whether the identifier exists
16
+ * 2. Whether the current user is the owner
17
+ */
18
+ const GroupPublishButton = memo(() => {
19
+ const currentGroup = useAgentGroupStore(agentGroupSelectors.currentGroup, isEqual);
20
+
21
+ const [showResultModal, setShowResultModal] = useState(false);
22
+ const [publishedIdentifier, setPublishedIdentifier] = useState<string>();
23
+
24
+ const handlePublishSuccess = useCallback((identifier: string) => {
25
+ setPublishedIdentifier(identifier);
26
+ setShowResultModal(true);
27
+ }, []);
28
+
29
+ // Determine action based on whether we have an existing marketIdentifier
30
+ // Backend will verify ownership and decide to create new or update
31
+ // marketIdentifier is stored in editorData
32
+ const action = currentGroup?.editorData?.marketIdentifier ? 'upload' : 'submit';
33
+
34
+ return (
35
+ <>
36
+ <PublishButton action={action} onPublishSuccess={handlePublishSuccess} />
37
+ <GroupPublishResultModal
38
+ identifier={publishedIdentifier}
39
+ onCancel={() => setShowResultModal(false)}
40
+ open={showResultModal}
41
+ />
42
+ </>
43
+ );
44
+ });
45
+
46
+ export default GroupPublishButton;
@@ -0,0 +1,12 @@
1
+ export type MarketPublishAction = 'submit' | 'upload';
2
+
3
+ export interface OriginalGroupInfo {
4
+ author?: {
5
+ avatar?: string;
6
+ name?: string;
7
+ userName?: string;
8
+ };
9
+ avatar?: string;
10
+ identifier: string;
11
+ name: string;
12
+ }
@@ -0,0 +1,211 @@
1
+ import isEqual from 'fast-deep-equal';
2
+ import { useCallback, useRef, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+
5
+ import { message } from '@/components/AntdStaticMethods';
6
+ import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
7
+ import { lambdaClient } from '@/libs/trpc/client';
8
+ import { useAgentGroupStore } from '@/store/agentGroup';
9
+ import { agentGroupSelectors } from '@/store/agentGroup/selectors';
10
+ import { useGlobalStore } from '@/store/global';
11
+ import { globalGeneralSelectors } from '@/store/global/selectors';
12
+
13
+ import type { MarketPublishAction, OriginalGroupInfo } from './types';
14
+ import { generateDefaultChangelog } from './utils';
15
+
16
+ interface UseMarketGroupPublishOptions {
17
+ action: MarketPublishAction;
18
+ onSuccess?: (identifier: string) => void;
19
+ }
20
+
21
+ export interface CheckOwnershipResult {
22
+ needsForkConfirm: boolean;
23
+ originalGroup: OriginalGroupInfo | null;
24
+ }
25
+
26
+ export const useMarketGroupPublish = ({ action, onSuccess }: UseMarketGroupPublishOptions) => {
27
+ const { t } = useTranslation('setting');
28
+ const [isPublishing, setIsPublishing] = useState(false);
29
+ const [isCheckingOwnership, setIsCheckingOwnership] = useState(false);
30
+ const isPublishingRef = useRef(false);
31
+ const { isAuthenticated } = useMarketAuth();
32
+
33
+ // Group data from store
34
+ const currentGroup = useAgentGroupStore(agentGroupSelectors.currentGroup);
35
+ const currentGroupMeta = useAgentGroupStore(agentGroupSelectors.currentGroupMeta, isEqual);
36
+ const currentGroupConfig = useAgentGroupStore(agentGroupSelectors.currentGroupConfig, isEqual);
37
+ const currentGroupAgents = useAgentGroupStore(agentGroupSelectors.currentGroupAgents);
38
+ const updateGroupMeta = useAgentGroupStore((s) => s.updateGroupMeta);
39
+ const language = useGlobalStore(globalGeneralSelectors.currentLanguage);
40
+
41
+ const isSubmit = action === 'submit';
42
+
43
+ /**
44
+ * Check ownership before publishing
45
+ * Returns whether fork confirmation is needed and original group info
46
+ */
47
+ const checkOwnership = useCallback(async (): Promise<CheckOwnershipResult> => {
48
+ // marketIdentifier is stored in editorData
49
+ const identifier = currentGroup?.editorData?.marketIdentifier as string | undefined;
50
+
51
+ // No identifier means new group, no need to check
52
+ if (!identifier) {
53
+ return { needsForkConfirm: false, originalGroup: null };
54
+ }
55
+
56
+ try {
57
+ setIsCheckingOwnership(true);
58
+ const result = await lambdaClient.market.agentGroup.checkOwnership.query({ identifier });
59
+
60
+ // If group doesn't exist or user is owner, no confirmation needed
61
+ if (!result.exists || result.isOwner) {
62
+ return { needsForkConfirm: false, originalGroup: null };
63
+ }
64
+
65
+ // User is not owner, need fork confirmation
66
+ return {
67
+ needsForkConfirm: true,
68
+ originalGroup: result.originalGroup as OriginalGroupInfo,
69
+ };
70
+ } catch (error) {
71
+ console.error('[useMarketGroupPublish] Failed to check ownership:', error);
72
+ // On error, proceed without confirmation
73
+ return { needsForkConfirm: false, originalGroup: null };
74
+ } finally {
75
+ setIsCheckingOwnership(false);
76
+ }
77
+ }, [currentGroup]);
78
+
79
+ const publish = useCallback(async () => {
80
+ // Prevent duplicate publishing
81
+ if (isPublishingRef.current) {
82
+ return { success: false };
83
+ }
84
+
85
+ // Check authentication
86
+ if (!isAuthenticated) {
87
+ return { success: false };
88
+ }
89
+
90
+ if (!currentGroup) {
91
+ message.error({ content: t('marketPublish.modal.messages.noGroup') });
92
+ return { success: false };
93
+ }
94
+
95
+ const messageKey = isSubmit ? 'submit-group' : 'upload-group-version';
96
+ const loadingMessage = isSubmit
97
+ ? t('marketPublish.modal.loading.submitGroup')
98
+ : t('marketPublish.modal.loading.uploadGroup');
99
+
100
+ const changelog = generateDefaultChangelog();
101
+
102
+ try {
103
+ isPublishingRef.current = true;
104
+ setIsPublishing(true);
105
+ message.loading({ content: loadingMessage, key: messageKey });
106
+
107
+ // Prepare member agents data
108
+ const memberAgents = currentGroupAgents.map((agent, index) => ({
109
+ // Only include avatar if it's not null/undefined
110
+ ...(agent.avatar ? { avatar: agent.avatar } : {}),
111
+ config: {
112
+ // Include agent configuration
113
+ model: agent.model,
114
+ params: agent.params,
115
+ systemRole: agent.systemRole,
116
+ },
117
+ // Market requires at least 1 character for description
118
+ description: agent.description || 'No description provided',
119
+ displayOrder: index,
120
+ identifier: agent.id, // Use local agent ID as identifier
121
+ name: agent.title || 'Untitled Agent',
122
+ role: agent.isSupervisor ? ('supervisor' as const) : ('participant' as const),
123
+ // TODO: Construct proper A2A URL for the agent
124
+ url: `https://api.lobehub.com/a2a/agents/${agent.id}`,
125
+ }));
126
+
127
+ // Use tRPC publishOrCreate
128
+ const result = await lambdaClient.market.agentGroup.publishOrCreate.mutate({
129
+ // Only include avatar if it's not null/undefined
130
+ ...(currentGroupMeta.avatar ? { avatar: currentGroupMeta.avatar } : {}),
131
+ // Only include backgroundColor if it's not null/undefined
132
+ ...(currentGroup.backgroundColor ? { backgroundColor: currentGroup.backgroundColor } : {}),
133
+ category: 'productivity', // TODO: Allow user to select category
134
+ changelog,
135
+ // Include group-level config (systemPrompt from content, openingMessage, etc.)
136
+ config: {
137
+ // Group systemPrompt is stored in currentGroup.content
138
+ ...(currentGroup.content !== undefined &&
139
+ currentGroup.content !== null && {
140
+ systemPrompt: currentGroup.content,
141
+ }),
142
+ ...(currentGroupConfig.openingMessage !== undefined && {
143
+ openingMessage: currentGroupConfig.openingMessage,
144
+ }),
145
+ ...(currentGroupConfig.openingQuestions !== undefined &&
146
+ currentGroupConfig.openingQuestions.length > 0 && {
147
+ openingQuestions: currentGroupConfig.openingQuestions,
148
+ }),
149
+ ...(currentGroupConfig.allowDM !== undefined && { allowDM: currentGroupConfig.allowDM }),
150
+ ...(currentGroupConfig.revealDM !== undefined && { revealDM: currentGroupConfig.revealDM }),
151
+ },
152
+ // Market requires at least 1 character for description
153
+ description: currentGroupMeta.description || 'No description provided',
154
+ // marketIdentifier is stored in editorData
155
+ identifier: currentGroup.editorData?.marketIdentifier as string | undefined,
156
+ memberAgents,
157
+ name: currentGroupMeta.title || 'Untitled Group',
158
+ visibility: 'public', // TODO: Allow user to select visibility
159
+ });
160
+
161
+ // Save marketIdentifier to editorData if new group
162
+ if (result.isNewGroup) {
163
+ await updateGroupMeta({
164
+ editorData: {
165
+ ...currentGroup.editorData,
166
+ marketIdentifier: result.identifier,
167
+ },
168
+ });
169
+ }
170
+
171
+ message.success({
172
+ content: t('submitAgentModal.success'),
173
+ key: messageKey,
174
+ });
175
+
176
+ onSuccess?.(result.identifier);
177
+ return { identifier: result.identifier, success: true };
178
+ } catch (error) {
179
+ const errorMessage =
180
+ error instanceof Error ? error.message : t('unknownError', { ns: 'common' });
181
+ message.error({
182
+ content: t('marketPublish.modal.messages.publishFailed', {
183
+ message: errorMessage,
184
+ }),
185
+ key: messageKey,
186
+ });
187
+ return { success: false };
188
+ } finally {
189
+ isPublishingRef.current = false;
190
+ setIsPublishing(false);
191
+ }
192
+ }, [
193
+ currentGroup,
194
+ currentGroupAgents,
195
+ currentGroupConfig,
196
+ currentGroupMeta,
197
+ isAuthenticated,
198
+ isSubmit,
199
+ language,
200
+ onSuccess,
201
+ t,
202
+ updateGroupMeta,
203
+ ]);
204
+
205
+ return {
206
+ checkOwnership,
207
+ isCheckingOwnership,
208
+ isPublishing,
209
+ publish,
210
+ };
211
+ };
@@ -0,0 +1,22 @@
1
+ import { customAlphabet } from 'nanoid/non-secure';
2
+
3
+ /**
4
+ * Generate a market identifier (8-character lowercase alphanumeric string)
5
+ * Format: [a-z0-9]{8}
6
+ * @returns A unique 8-character market identifier
7
+ */
8
+ export const generateMarketIdentifier = () => {
9
+ const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
10
+ const generate = customAlphabet(alphabet, 8);
11
+ return generate();
12
+ };
13
+
14
+ /**
15
+ * Generate a default changelog based on current timestamp
16
+ * @returns A timestamp-based changelog string
17
+ */
18
+ export const generateDefaultChangelog = () => {
19
+ const now = new Date();
20
+ const formattedDate = now.toISOString().split('T')[0];
21
+ return `Release ${formattedDate}`;
22
+ };
@@ -193,14 +193,16 @@ export const DndContextWrapper = memo<PropsWithChildren>(({ children }) => {
193
193
 
194
194
  document.addEventListener('dragstart', handleDragStart);
195
195
  document.addEventListener('drag', handleDrag);
196
- document.addEventListener('drop', handleDrop);
196
+ // Use capture phase so drops still work even if some UI stops propagation
197
+ // (e.g., header dropdowns / menus).
198
+ document.addEventListener('drop', handleDrop, true);
197
199
  document.addEventListener('dragover', handleDragOver);
198
200
  document.addEventListener('dragend', handleDragEnd);
199
201
 
200
202
  return () => {
201
203
  document.removeEventListener('dragstart', handleDragStart);
202
204
  document.removeEventListener('drag', handleDrag);
203
- document.removeEventListener('drop', handleDrop);
205
+ document.removeEventListener('drop', handleDrop, true);
204
206
  document.removeEventListener('dragover', handleDragOver);
205
207
  document.removeEventListener('dragend', handleDragEnd);
206
208
  };