@lobehub/lobehub 2.0.0-next.334 → 2.0.0-next.336

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 (37) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/changelog/v1.json +17 -0
  3. package/docs/development/database-schema.dbml +34 -0
  4. package/package.json +1 -1
  5. package/packages/builtin-tool-group-management/src/manifest.ts +54 -53
  6. package/packages/builtin-tool-group-management/src/systemRole.ts +43 -111
  7. package/packages/context-engine/src/engine/tools/ToolArgumentsRepairer.ts +129 -0
  8. package/packages/context-engine/src/engine/tools/__tests__/ToolArgumentsRepairer.test.ts +186 -0
  9. package/packages/context-engine/src/engine/tools/index.ts +3 -0
  10. package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/index.ts +2 -0
  11. package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/with-assistant-group.json +156 -0
  12. package/packages/conversation-flow/src/__tests__/parse.test.ts +22 -0
  13. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +88 -11
  14. package/packages/database/migrations/0070_add_user_memory_activities.sql +35 -0
  15. package/packages/database/migrations/meta/0070_snapshot.json +10656 -0
  16. package/packages/database/migrations/meta/_journal.json +8 -1
  17. package/packages/database/src/schemas/userMemories/index.ts +71 -0
  18. package/packages/types/src/openai/chat.ts +0 -4
  19. package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +5 -1
  20. package/src/app/[variants]/(main)/community/(detail)/user/features/UserAgentCard.tsx +8 -8
  21. package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupCard.tsx +142 -15
  22. package/src/app/[variants]/(main)/community/(detail)/user/features/useUserDetail.ts +45 -20
  23. package/src/server/routers/lambda/market/agentGroup.ts +179 -1
  24. package/src/server/services/discover/index.ts +4 -0
  25. package/src/services/chat/chat.test.ts +109 -104
  26. package/src/services/chat/index.ts +13 -32
  27. package/src/services/chat/mecha/agentConfigResolver.test.ts +113 -0
  28. package/src/services/chat/mecha/agentConfigResolver.ts +15 -5
  29. package/src/services/marketApi.ts +14 -0
  30. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +13 -0
  31. package/src/store/chat/agents/createAgentExecutors.ts +13 -1
  32. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +5 -1
  33. package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +14 -0
  34. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +131 -7
  35. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +61 -62
  36. package/src/store/chat/slices/plugin/action.test.ts +71 -0
  37. package/src/store/chat/slices/plugin/actions/internals.ts +14 -5
@@ -490,7 +490,14 @@
490
490
  "when": 1768303764632,
491
491
  "tag": "0069_add_topic_shares_table",
492
492
  "breakpoints": true
493
+ },
494
+ {
495
+ "idx": 70,
496
+ "version": "7",
497
+ "when": 1768999498635,
498
+ "tag": "0070_add_user_memory_activities",
499
+ "breakpoints": true
493
500
  }
494
501
  ],
495
502
  "version": "6"
496
- }
503
+ }
@@ -124,6 +124,70 @@ export const userMemoriesPreferences = pgTable(
124
124
  ],
125
125
  );
126
126
 
127
+ export const userMemoriesActivities = pgTable(
128
+ 'user_memories_activities',
129
+ {
130
+ id: varchar255('id')
131
+ .$defaultFn(() => idGenerator('memory'))
132
+ .primaryKey(),
133
+
134
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
135
+ userMemoryId: varchar255('user_memory_id').references(() => userMemories.id, {
136
+ onDelete: 'cascade',
137
+ }),
138
+
139
+ metadata: jsonb('metadata').$type<Record<string, unknown>>(),
140
+ tags: text('tags').array(),
141
+
142
+ type: varchar255('type').notNull(),
143
+ status: varchar255('status').notNull().default('pending'),
144
+ timezone: varchar255('timezone'),
145
+ startsAt: timestamptz('starts_at'),
146
+ endsAt: timestamptz('ends_at'),
147
+
148
+ associatedObjects: jsonb('associated_objects').$type<{
149
+ extra?: Record<string, unknown>,
150
+ name?: string,
151
+ type?: string
152
+ }[]>(),
153
+ associatedSubjects: jsonb('associated_subjects').$type<{
154
+ extra?: Record<string, unknown>,
155
+ name?: string,
156
+ type?: string
157
+ }[]>(),
158
+ associatedLocations: jsonb('associated_locations').$type<{
159
+ address?: string;
160
+ name?: string;
161
+ tags?: string[];
162
+ type?: string;
163
+ }[]>(),
164
+
165
+ notes: text('notes'),
166
+ narrative: text('narrative'),
167
+ narrativeVector: vector('narrative_vector', { dimensions: 1024 }),
168
+ feedback: text('feedback'),
169
+ feedbackVector: vector('feedback_vector', { dimensions: 1024 }),
170
+
171
+ capturedAt: timestamptz('captured_at').notNull().defaultNow(),
172
+
173
+ ...timestamps,
174
+ },
175
+ (table) => [
176
+ index('user_memories_activities_narrative_vector_index').using(
177
+ 'hnsw',
178
+ table.narrativeVector.op('vector_cosine_ops'),
179
+ ),
180
+ index('user_memories_activities_feedback_vector_index').using(
181
+ 'hnsw',
182
+ table.feedbackVector.op('vector_cosine_ops'),
183
+ ),
184
+ index('user_memories_activities_type_index').on(table.type),
185
+ index('user_memories_activities_user_id_index').on(table.userId),
186
+ index('user_memories_activities_user_memory_id_index').on(table.userMemoryId),
187
+ index('user_memories_activities_status_index').on(table.status),
188
+ ],
189
+ );
190
+
127
191
  export const userMemoriesIdentities = pgTable(
128
192
  'user_memories_identities',
129
193
  {
@@ -242,3 +306,10 @@ export type UserMemoryExperiencesWithoutVectors = Omit<
242
306
  'situationVector' | 'actionVector' | 'keyLearningVector'
243
307
  >;
244
308
  export type NewUserMemoryExperience = typeof userMemoriesExperiences.$inferInsert;
309
+
310
+ export type UserMemoryActivity = typeof userMemoriesActivities.$inferSelect;
311
+ export type UserMemoryActivitiesWithoutVectors = Omit<
312
+ UserMemoryActivity,
313
+ 'narrativeVector' | 'feedbackVector'
314
+ >;
315
+ export type NewUserMemoryActivity = typeof userMemoriesActivities.$inferInsert;
@@ -86,10 +86,6 @@ export interface ChatStreamPayload {
86
86
  * @title Number of texts to return
87
87
  */
88
88
  n?: number;
89
- /**
90
- * List of enabled plugins
91
- */
92
- plugins?: string[];
93
89
  /**
94
90
  * @title Penalty coefficient in generated text to reduce topic changes
95
91
  * @default 0
@@ -19,7 +19,11 @@ export interface UserDetailContextConfig {
19
19
  isOwner: boolean;
20
20
  mobile?: boolean;
21
21
  onEditProfile?: (onSuccess?: (profile: MarketUserProfile) => void) => void;
22
- onStatusChange?: (identifier: string, action: 'publish' | 'unpublish' | 'deprecate') => void;
22
+ onStatusChange?: (
23
+ identifier: string,
24
+ action: 'publish' | 'unpublish' | 'deprecate',
25
+ type?: 'agent' | 'group',
26
+ ) => void;
23
27
  totalInstalls: number;
24
28
  user: DiscoverUserInfo;
25
29
  }
@@ -78,6 +78,7 @@ const styles = createStaticStyles(({ css, cssVar }) => {
78
78
  `,
79
79
  moreButton: css`
80
80
  position: absolute;
81
+ z-index: 10;
81
82
  inset-block-start: 12px;
82
83
  inset-inline-end: 12px;
83
84
 
@@ -259,14 +260,13 @@ const UserAgentCard = memo<UserAgentCardProps>(
259
260
  width={'100%'}
260
261
  >
261
262
  {isOwner && (
262
- <DropdownMenu items={menuItems}>
263
- <div
264
- className={cx('more-button', styles.moreButton)}
265
- onClick={(e) => e.stopPropagation()}
266
- >
267
- <Icon icon={MoreVerticalIcon} size={16} style={{ cursor: 'pointer' }} />
268
- </div>
269
- </DropdownMenu>
263
+ <div onClick={(e) => e.stopPropagation()}>
264
+ <DropdownMenu items={menuItems as any}>
265
+ <div className={cx('more-button', styles.moreButton)}>
266
+ <Icon icon={MoreVerticalIcon} size={16} style={{ cursor: 'pointer' }} />
267
+ </div>
268
+ </DropdownMenu>
269
+ </div>
270
270
  )}
271
271
  <Flexbox
272
272
  align={'flex-start'}
@@ -1,8 +1,28 @@
1
1
  'use client';
2
2
 
3
- import { Avatar, Block, Flexbox, Icon, Tag, Text, Tooltip, TooltipGroup } from '@lobehub/ui';
4
- import { createStaticStyles } from 'antd-style';
5
- import { ClockIcon, DownloadIcon, UsersIcon } from 'lucide-react';
3
+ import {
4
+ Tag as AntTag,
5
+ Avatar,
6
+ Block,
7
+ DropdownMenu,
8
+ Flexbox,
9
+ Icon,
10
+ Tag,
11
+ Text,
12
+ Tooltip,
13
+ TooltipGroup,
14
+ } from '@lobehub/ui';
15
+ import { createStaticStyles, cx } from 'antd-style';
16
+ import {
17
+ AlertTriangle,
18
+ ClockIcon,
19
+ DownloadIcon,
20
+ Eye,
21
+ EyeOff,
22
+ MoreVerticalIcon,
23
+ Pencil,
24
+ UsersIcon,
25
+ } from 'lucide-react';
6
26
  import qs from 'query-string';
7
27
  import { memo, useCallback } from 'react';
8
28
  import { useTranslation } from 'react-i18next';
@@ -10,9 +30,31 @@ import { Link, useNavigate } from 'react-router-dom';
10
30
  import urlJoin from 'url-join';
11
31
 
12
32
  import PublishedTime from '@/components/PublishedTime';
13
- import { type DiscoverGroupAgentItem } from '@/types/discover';
33
+ import { type DiscoverGroupAgentItem, type GroupAgentStatus } from '@/types/discover';
14
34
  import { formatIntergerNumber } from '@/utils/format';
15
35
 
36
+ import { useUserDetailContext } from './DetailProvider';
37
+
38
+ const getStatusTagColor = (status?: GroupAgentStatus) => {
39
+ switch (status) {
40
+ case 'published': {
41
+ return 'green';
42
+ }
43
+ case 'unpublished': {
44
+ return 'orange';
45
+ }
46
+ case 'deprecated': {
47
+ return 'red';
48
+ }
49
+ case 'archived': {
50
+ return 'default';
51
+ }
52
+ default: {
53
+ return 'default';
54
+ }
55
+ }
56
+ };
57
+
16
58
  const styles = createStaticStyles(({ css, cssVar }) => {
17
59
  return {
18
60
  desc: css`
@@ -25,6 +67,16 @@ const styles = createStaticStyles(({ css, cssVar }) => {
25
67
  border-block-start: 1px dashed ${cssVar.colorBorder};
26
68
  background: ${cssVar.colorBgContainer};
27
69
  `,
70
+ moreButton: css`
71
+ position: absolute;
72
+ z-index: 10;
73
+ inset-block-start: 12px;
74
+ inset-inline-end: 12px;
75
+
76
+ opacity: 0;
77
+
78
+ transition: opacity 0.2s;
79
+ `,
28
80
  secondaryDesc: css`
29
81
  font-size: 12px;
30
82
  color: ${cssVar.colorTextDescription};
@@ -47,15 +99,31 @@ const styles = createStaticStyles(({ css, cssVar }) => {
47
99
  color: ${cssVar.colorLink};
48
100
  }
49
101
  `,
102
+ wrapper: css`
103
+ &:hover .more-button {
104
+ opacity: 1;
105
+ }
106
+ `,
50
107
  };
51
108
  });
52
109
 
53
110
  type UserGroupCardProps = DiscoverGroupAgentItem;
54
111
 
55
112
  const UserGroupCard = memo<UserGroupCardProps>(
56
- ({ avatar, title, description, createdAt, category, installCount, identifier, memberCount }) => {
57
- const { t } = useTranslation(['discover']);
113
+ ({
114
+ avatar,
115
+ title,
116
+ description,
117
+ createdAt,
118
+ category,
119
+ installCount,
120
+ identifier,
121
+ memberCount,
122
+ status,
123
+ }) => {
124
+ const { t } = useTranslation(['discover', 'setting']);
58
125
  const navigate = useNavigate();
126
+ const { isOwner, onStatusChange } = useUserDetailContext();
59
127
 
60
128
  const link = qs.stringifyUrl(
61
129
  {
@@ -65,12 +133,55 @@ const UserGroupCard = memo<UserGroupCardProps>(
65
133
  { skipNull: true },
66
134
  );
67
135
 
136
+ const isPublished = status === 'published';
137
+
68
138
  const handleCardClick = useCallback(() => {
69
139
  navigate(link);
70
140
  }, [link, navigate]);
71
141
 
142
+ const handleEdit = useCallback(() => {
143
+ navigate(urlJoin('/group', identifier, 'profile'));
144
+ }, [identifier, navigate]);
145
+
146
+ const handleStatusAction = useCallback(
147
+ (action: 'publish' | 'unpublish' | 'deprecate') => {
148
+ onStatusChange?.(identifier, action, 'group');
149
+ },
150
+ [identifier, onStatusChange],
151
+ );
152
+
153
+ const menuItems = isOwner
154
+ ? [
155
+ {
156
+ icon: <Icon icon={Pencil} />,
157
+ key: 'edit',
158
+ label: t('setting:myAgents.actions.edit'),
159
+ onClick: handleEdit,
160
+ },
161
+ {
162
+ type: 'divider' as const,
163
+ },
164
+ {
165
+ icon: <Icon icon={isPublished ? EyeOff : Eye} />,
166
+ key: 'togglePublish',
167
+ label: isPublished
168
+ ? t('setting:myAgents.actions.unpublish')
169
+ : t('setting:myAgents.actions.publish'),
170
+ onClick: () => handleStatusAction(isPublished ? 'unpublish' : 'publish'),
171
+ },
172
+ {
173
+ danger: true,
174
+ icon: <Icon icon={AlertTriangle} />,
175
+ key: 'deprecate',
176
+ label: t('setting:myAgents.actions.deprecate'),
177
+ onClick: () => handleStatusAction('deprecate'),
178
+ },
179
+ ]
180
+ : [];
181
+
72
182
  return (
73
183
  <Block
184
+ className={styles.wrapper}
74
185
  clickable
75
186
  height={'100%'}
76
187
  onClick={handleCardClick}
@@ -82,6 +193,15 @@ const UserGroupCard = memo<UserGroupCardProps>(
82
193
  variant={'outlined'}
83
194
  width={'100%'}
84
195
  >
196
+ {isOwner && (
197
+ <div onClick={(e) => e.stopPropagation()}>
198
+ <DropdownMenu items={menuItems as any}>
199
+ <div className={cx('more-button', styles.moreButton)}>
200
+ <Icon icon={MoreVerticalIcon} size={16} style={{ cursor: 'pointer' }} />
201
+ </div>
202
+ </DropdownMenu>
203
+ </div>
204
+ )}
85
205
  <Flexbox
86
206
  align={'flex-start'}
87
207
  gap={16}
@@ -105,15 +225,22 @@ const UserGroupCard = memo<UserGroupCardProps>(
105
225
  overflow: 'hidden',
106
226
  }}
107
227
  >
108
- <Link
109
- onClick={(e) => e.stopPropagation()}
110
- style={{ color: 'inherit', flex: 1, overflow: 'hidden' }}
111
- to={link}
112
- >
113
- <Text as={'h3'} className={styles.title} ellipsis style={{ flex: 1 }}>
114
- {title}
115
- </Text>
116
- </Link>
228
+ <Flexbox align={'center'} gap={8} horizontal>
229
+ <Link
230
+ onClick={(e) => e.stopPropagation()}
231
+ style={{ color: 'inherit', flex: 1, overflow: 'hidden' }}
232
+ to={link}
233
+ >
234
+ <Text as={'h3'} className={styles.title} ellipsis style={{ flex: 1 }}>
235
+ {title}
236
+ </Text>
237
+ </Link>
238
+ {isOwner && status && (
239
+ <AntTag color={getStatusTagColor(status)} style={{ flexShrink: 0, margin: 0 }}>
240
+ {t(`setting:myAgents.status.${status}`)}
241
+ </AntTag>
242
+ )}
243
+ </Flexbox>
117
244
  </Flexbox>
118
245
  </Flexbox>
119
246
  </Flexbox>
@@ -6,8 +6,10 @@ import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
8
8
  import { marketApiService } from '@/services/marketApi';
9
+ import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
9
10
 
10
11
  export type AgentStatusAction = 'publish' | 'unpublish' | 'deprecate';
12
+ export type EntityType = 'agent' | 'group';
11
13
 
12
14
  interface UseUserDetailOptions {
13
15
  onMutate?: () => void;
@@ -17,43 +19,67 @@ export const useUserDetail = ({ onMutate }: UseUserDetailOptions = {}) => {
17
19
  const { t } = useTranslation('setting');
18
20
  const { message, modal } = App.useApp();
19
21
  const { session } = useMarketAuth();
22
+ const enableMarketTrustedClient = useServerConfigStore(
23
+ serverConfigSelectors.enableMarketTrustedClient,
24
+ );
20
25
 
21
26
  const handleStatusChange = useCallback(
22
- async (identifier: string, action: AgentStatusAction) => {
23
- if (!session?.accessToken) {
27
+ async (identifier: string, action: AgentStatusAction, type: EntityType = 'agent') => {
28
+ if (!enableMarketTrustedClient && !session?.accessToken) {
24
29
  message.error(t('myAgents.errors.notAuthenticated'));
25
30
  return;
26
31
  }
27
32
 
28
- const messageKey = `agent-status-${action}`;
33
+ const messageKey = `${type}-status-${action}`;
29
34
  const loadingText = t(`myAgents.actions.${action}Loading` as any);
30
35
  const successText = t(`myAgents.actions.${action}Success` as any);
31
36
  const errorText = t(`myAgents.actions.${action}Error` as any);
32
37
 
33
- async function executeStatusChange(identifier: string, action: AgentStatusAction) {
38
+ async function executeStatusChange(
39
+ identifier: string,
40
+ action: AgentStatusAction,
41
+ type: EntityType,
42
+ ) {
34
43
  try {
35
44
  message.loading({ content: loadingText, key: messageKey });
36
45
  marketApiService.setAccessToken(session!.accessToken);
37
46
 
38
- switch (action) {
39
- case 'publish': {
40
- await marketApiService.publishAgent(identifier);
41
- break;
42
- }
43
- case 'unpublish': {
44
- await marketApiService.unpublishAgent(identifier);
45
- break;
47
+ if (type === 'group') {
48
+ switch (action) {
49
+ case 'publish': {
50
+ await marketApiService.publishAgentGroup(identifier);
51
+ break;
52
+ }
53
+ case 'unpublish': {
54
+ await marketApiService.unpublishAgentGroup(identifier);
55
+ break;
56
+ }
57
+ case 'deprecate': {
58
+ await marketApiService.deprecateAgentGroup(identifier);
59
+ break;
60
+ }
46
61
  }
47
- case 'deprecate': {
48
- await marketApiService.deprecateAgent(identifier);
49
- break;
62
+ } else {
63
+ switch (action) {
64
+ case 'publish': {
65
+ await marketApiService.publishAgent(identifier);
66
+ break;
67
+ }
68
+ case 'unpublish': {
69
+ await marketApiService.unpublishAgent(identifier);
70
+ break;
71
+ }
72
+ case 'deprecate': {
73
+ await marketApiService.deprecateAgent(identifier);
74
+ break;
75
+ }
50
76
  }
51
77
  }
52
78
 
53
79
  message.success({ content: successText, key: messageKey });
54
80
  onMutate?.();
55
81
  } catch (error) {
56
- console.error(`[useUserDetail] ${action} agent error:`, error);
82
+ console.error(`[useUserDetail] ${action} ${type} error:`, error);
57
83
  message.error({
58
84
  content: `${errorText}: ${error instanceof Error ? error.message : 'Unknown error'}`,
59
85
  key: messageKey,
@@ -61,7 +87,6 @@ export const useUserDetail = ({ onMutate }: UseUserDetailOptions = {}) => {
61
87
  }
62
88
  }
63
89
 
64
- // For deprecate action, show confirmation dialog first
65
90
  if (action === 'deprecate') {
66
91
  modal.confirm({
67
92
  cancelText: t('myAgents.actions.cancel'),
@@ -69,16 +94,16 @@ export const useUserDetail = ({ onMutate }: UseUserDetailOptions = {}) => {
69
94
  okButtonProps: { danger: true },
70
95
  okText: t('myAgents.actions.confirmDeprecate'),
71
96
  onOk: async () => {
72
- await executeStatusChange(identifier, action);
97
+ await executeStatusChange(identifier, action, type);
73
98
  },
74
99
  title: t('myAgents.actions.deprecateConfirmTitle'),
75
100
  });
76
101
  return;
77
102
  }
78
103
 
79
- await executeStatusChange(identifier, action);
104
+ await executeStatusChange(identifier, action, type);
80
105
  },
81
- [session?.accessToken, message, modal, t, onMutate],
106
+ [enableMarketTrustedClient, session?.accessToken, message, modal, t, onMutate],
82
107
  );
83
108
 
84
109
  return {