@lobehub/lobehub 2.0.0-next.342 → 2.0.0-next.343

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 (79) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/changelog/v1.json +12 -0
  3. package/package.json +1 -1
  4. package/packages/database/src/models/__tests__/userMemories.test.ts +62 -5
  5. package/packages/database/src/models/agentCronJob.ts +9 -9
  6. package/packages/database/src/models/userMemory/__tests__/identity.test.ts +5 -5
  7. package/packages/database/src/models/userMemory/experience.ts +91 -1
  8. package/packages/database/src/models/userMemory/identity.ts +93 -2
  9. package/packages/database/src/models/userMemory/model.ts +27 -8
  10. package/packages/types/src/userMemory/experience.ts +25 -0
  11. package/packages/types/src/userMemory/identity.ts +27 -0
  12. package/packages/types/src/userMemory/index.ts +1 -0
  13. package/packages/types/src/userMemory/shared.ts +30 -0
  14. package/src/app/[variants]/(main)/group/profile/features/Header/index.tsx +3 -4
  15. package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +7 -8
  16. package/src/app/[variants]/(main)/memory/(home)/features/Persona/PersonaDetail.tsx +58 -0
  17. package/src/app/[variants]/(main)/memory/(home)/features/Persona/PersonaHeader.tsx +22 -0
  18. package/src/app/[variants]/(main)/memory/(home)/features/Persona/PersonaSummary.tsx +43 -0
  19. package/src/app/[variants]/(main)/memory/(home)/features/Persona/index.tsx +53 -0
  20. package/src/app/[variants]/(main)/memory/(home)/features/RoleTagCloud/index.tsx +2 -2
  21. package/src/app/[variants]/(main)/memory/(home)/index.tsx +15 -3
  22. package/src/app/[variants]/(main)/memory/experiences/features/List/GridView/ExperienceCard.tsx +3 -3
  23. package/src/app/[variants]/(main)/memory/experiences/features/List/GridView/index.tsx +3 -3
  24. package/src/app/[variants]/(main)/memory/experiences/features/List/TimelineView/ExperienceCard.tsx +3 -3
  25. package/src/app/[variants]/(main)/memory/experiences/features/List/TimelineView/index.tsx +3 -3
  26. package/src/app/[variants]/(main)/memory/features/SourceLink.tsx +2 -11
  27. package/src/app/[variants]/(main)/memory/features/TimeLineView/TimeLineCard.tsx +2 -9
  28. package/src/app/[variants]/(main)/memory/identities/features/IdentityRightPanel.tsx +1 -1
  29. package/src/app/[variants]/(main)/memory/identities/features/List/GridView/IdentityCard.tsx +5 -4
  30. package/src/app/[variants]/(main)/memory/identities/features/List/GridView/index.tsx +3 -3
  31. package/src/app/[variants]/(main)/memory/identities/features/List/TimelineView/IdentityCard.tsx +6 -6
  32. package/src/app/[variants]/(main)/memory/identities/features/List/TimelineView/index.tsx +6 -4
  33. package/src/app/[variants]/(main)/settings/profile/index.tsx +8 -8
  34. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +0 -1
  35. package/src/app/[variants]/(main)/settings/skill/features/Actions.tsx +0 -1
  36. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +9 -10
  37. package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +9 -10
  38. package/src/app/[variants]/(main)/settings/skill/features/McpSkillItem.tsx +4 -5
  39. package/src/app/[variants]/(main)/settings/skill/features/SkillList.tsx +4 -5
  40. package/src/app/[variants]/share/t/[id]/SharedMessageList.tsx +1 -4
  41. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +47 -121
  42. package/src/app/[variants]/share/t/[id]/_layout/style.ts +59 -0
  43. package/src/app/[variants]/share/t/[id]/features/Portal/index.tsx +4 -5
  44. package/src/app/[variants]/share/t/[id]/index.tsx +30 -37
  45. package/src/components/404/index.tsx +15 -9
  46. package/src/components/DragUpload/index.tsx +15 -16
  47. package/src/features/EditorCanvas/DocumentIdMode.tsx +1 -2
  48. package/src/features/IntegrationDetailModal/index.tsx +11 -12
  49. package/src/features/ResourceManager/index.tsx +13 -6
  50. package/src/features/ShareModal/ShareImage/Preview.tsx +19 -28
  51. package/src/features/ShareModal/ShareImage/style.ts +4 -2
  52. package/src/features/ShareModal/index.tsx +5 -1
  53. package/src/features/ShareModal/style.ts +1 -0
  54. package/src/features/ShareModal/useContainerStyles.ts +1 -1
  55. package/src/features/SharePopover/index.tsx +16 -9
  56. package/src/features/SharePopover/style.ts +2 -2
  57. package/src/features/SkillStore/CommunityList/Item.tsx +2 -2
  58. package/src/features/SkillStore/LobeHubList/Item.tsx +2 -2
  59. package/src/features/SkillStore/LobeHubList/index.tsx +2 -3
  60. package/src/features/SkillStore/style.ts +4 -4
  61. package/src/layout/GlobalProvider/ServerVersionOutdatedAlert.tsx +28 -20
  62. package/src/server/routers/lambda/userMemories.ts +61 -5
  63. package/src/server/routers/lambda/userMemory.ts +5 -1
  64. package/src/services/chat/index.ts +2 -2
  65. package/src/services/userMemory/index.ts +25 -1
  66. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +0 -1
  67. package/src/store/userMemory/initialState.ts +22 -52
  68. package/src/store/userMemory/slices/context/action.ts +1 -1
  69. package/src/store/userMemory/slices/context/index.ts +1 -0
  70. package/src/store/userMemory/slices/context/initialState.ts +22 -0
  71. package/src/store/userMemory/slices/experience/action.ts +10 -22
  72. package/src/store/userMemory/slices/experience/index.ts +1 -0
  73. package/src/store/userMemory/slices/experience/initialState.ts +22 -0
  74. package/src/store/userMemory/slices/home/action.ts +17 -0
  75. package/src/store/userMemory/slices/identity/action.ts +36 -24
  76. package/src/store/userMemory/slices/identity/initialState.ts +7 -4
  77. package/src/store/userMemory/slices/preference/action.ts +1 -1
  78. package/src/store/userMemory/slices/preference/index.ts +1 -0
  79. package/src/store/userMemory/slices/preference/initialState.ts +22 -0
@@ -8,7 +8,7 @@ import { Loader2, MoreVerticalIcon, Plus, Unplug } from 'lucide-react';
8
8
  import React, { memo } from 'react';
9
9
  import { useTranslation } from 'react-i18next';
10
10
 
11
- import { useItemStyles } from '../style';
11
+ import { itemStyles } from '../style';
12
12
  import { useSkillConnect } from './useSkillConnect';
13
13
 
14
14
  interface ItemProps {
@@ -25,7 +25,7 @@ interface ItemProps {
25
25
  const Item = memo<ItemProps>(
26
26
  ({ description, icon, identifier, label, onOpenDetail, serverName, type }) => {
27
27
  const { t } = useTranslation('setting');
28
- const { styles } = useItemStyles();
28
+ const styles = itemStyles;
29
29
  const { modal } = App.useApp();
30
30
 
31
31
  const { handleConnect, handleDisconnect, isConnected, isConnecting } = useSkillConnect({
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { KLAVIS_SERVER_TYPES, LOBEHUB_SKILL_PROVIDERS } from '@lobechat/const';
4
- import { createStyles } from 'antd-style';
4
+ import { createStaticStyles } from 'antd-style';
5
5
  import isEqual from 'fast-deep-equal';
6
6
  import type { Klavis } from 'klavis';
7
7
  import { memo, useMemo, useState } from 'react';
@@ -17,7 +17,7 @@ import Empty from '../Empty';
17
17
  import Item from './Item';
18
18
  import { useSkillConnect } from './useSkillConnect';
19
19
 
20
- const useStyles = createStyles(({ css }) => ({
20
+ const styles = createStaticStyles(({ css }) => ({
21
21
  grid: css`
22
22
  display: grid;
23
23
  grid-template-columns: repeat(2, 1fr);
@@ -69,7 +69,6 @@ const DetailModalWithConnect = memo<DetailModalWithConnectProps>(({ detailState,
69
69
  DetailModalWithConnect.displayName = 'DetailModalWithConnect';
70
70
 
71
71
  export const LobeHubList = memo<LobeHubListProps>(({ keywords }) => {
72
- const { styles } = useStyles();
73
72
  const [detailState, setDetailState] = useState<DetailState | null>(null);
74
73
 
75
74
  const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
@@ -1,6 +1,6 @@
1
- import { createStyles } from 'antd-style';
1
+ import { createStaticStyles } from 'antd-style';
2
2
 
3
- export const useItemStyles = createStyles(({ css, token }) => ({
3
+ export const itemStyles = createStaticStyles(({ css, cssVar }) => ({
4
4
  container: css`
5
5
  position: relative;
6
6
  overflow: hidden;
@@ -11,7 +11,7 @@ export const useItemStyles = createStyles(({ css, token }) => ({
11
11
  overflow: hidden;
12
12
 
13
13
  font-size: 12px;
14
- color: ${token.colorTextSecondary};
14
+ color: ${cssVar.colorTextSecondary};
15
15
  text-overflow: ellipsis;
16
16
  white-space: nowrap;
17
17
  `,
@@ -20,7 +20,7 @@ export const useItemStyles = createStyles(({ css, token }) => ({
20
20
 
21
21
  font-size: 14px;
22
22
  font-weight: 500;
23
- color: ${token.colorText};
23
+ color: ${cssVar.colorText};
24
24
  text-overflow: ellipsis;
25
25
  white-space: nowrap;
26
26
  `,
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import { Button, Flexbox, Icon } from '@lobehub/ui';
4
- import { createStyles } from 'antd-style';
4
+ import { createStaticStyles, useTheme } from 'antd-style';
5
5
  import { TriangleAlert, X } from 'lucide-react';
6
- import { useState } from 'react';
6
+ import { useMemo, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
 
9
9
  import { MANUAL_UPGRADE_URL } from '@/const/url';
@@ -12,7 +12,7 @@ import { useElectronStore } from '@/store/electron';
12
12
  import { electronSyncSelectors } from '@/store/electron/selectors';
13
13
  import { useGlobalStore } from '@/store/global';
14
14
 
15
- const useStyles = createStyles(({ css, token }) => ({
15
+ const styles = createStaticStyles(({ css, cssVar }) => ({
16
16
  closeButton: css`
17
17
  cursor: pointer;
18
18
 
@@ -26,15 +26,15 @@ const useStyles = createStyles(({ css, token }) => ({
26
26
 
27
27
  width: 28px;
28
28
  height: 28px;
29
- border-radius: ${token.borderRadius}px;
29
+ border-radius: ${cssVar.borderRadius};
30
30
 
31
- color: ${token.colorTextSecondary};
31
+ color: ${cssVar.colorTextSecondary};
32
32
 
33
33
  transition: all 0.2s;
34
34
 
35
35
  &:hover {
36
- color: ${token.colorText};
37
- background: ${token.colorFillSecondary};
36
+ color: ${cssVar.colorText};
37
+ background: ${cssVar.colorFillSecondary};
38
38
  }
39
39
  `,
40
40
  container: css`
@@ -46,7 +46,7 @@ const useStyles = createStyles(({ css, token }) => ({
46
46
  align-items: center;
47
47
  justify-content: center;
48
48
 
49
- background: ${token.colorBgMask};
49
+ background: ${cssVar.colorBgMask};
50
50
  `,
51
51
  content: css`
52
52
  position: relative;
@@ -55,47 +55,55 @@ const useStyles = createStyles(({ css, token }) => ({
55
55
 
56
56
  max-width: 480px;
57
57
  padding: 24px;
58
- border: 1px solid ${token.yellowBorder};
59
- border-radius: ${token.borderRadiusLG}px;
58
+ border: 1px solid var(--content-yellow-border, ${cssVar.colorWarningBorder});
59
+ border-radius: ${cssVar.borderRadiusLG};
60
60
 
61
- background: ${token.colorBgContainer};
62
- box-shadow: ${token.boxShadowSecondary};
61
+ background: ${cssVar.colorBgContainer};
62
+ box-shadow: ${cssVar.boxShadowSecondary};
63
63
  `,
64
64
  desc: css`
65
65
  line-height: 1.6;
66
- color: ${token.colorTextSecondary};
66
+ color: ${cssVar.colorTextSecondary};
67
67
  `,
68
68
  title: css`
69
69
  font-size: 16px;
70
70
  font-weight: bold;
71
- color: ${token.colorWarningText};
71
+ color: ${cssVar.colorWarningText};
72
72
  `,
73
73
  titleIcon: css`
74
74
  flex-shrink: 0;
75
- color: ${token.colorWarning};
75
+ color: ${cssVar.colorWarning};
76
76
  `,
77
77
  warning: css`
78
78
  padding: 12px;
79
- border-radius: ${token.borderRadius}px;
80
- color: ${token.colorWarningText};
81
- background: ${token.yellowBg};
79
+ border-radius: ${cssVar.borderRadius};
80
+ color: ${cssVar.colorWarningText};
81
+ background: var(--warning-yellow-bg, ${cssVar.colorWarningBg});
82
82
  `,
83
83
  }));
84
84
 
85
85
  const ServerVersionOutdatedAlert = () => {
86
- const { styles } = useStyles();
86
+ const theme = useTheme();
87
87
  const { t } = useTranslation('common');
88
88
  const [dismissed, setDismissed] = useState(false);
89
89
  const isServerVersionOutdated = useGlobalStore((s) => s.isServerVersionOutdated);
90
90
  const storageMode = useElectronStore(electronSyncSelectors.storageMode);
91
91
 
92
+ const cssVariables = useMemo<Record<string, string>>(
93
+ () => ({
94
+ '--content-yellow-border': theme.yellowBorder,
95
+ '--warning-yellow-bg': theme.yellowBg,
96
+ }),
97
+ [theme.yellowBorder, theme.yellowBg],
98
+ );
99
+
92
100
  // Only show alert when using self-hosted server, not cloud
93
101
  if (storageMode !== 'selfHost') return null;
94
102
  if (!isServerVersionOutdated || dismissed) return null;
95
103
 
96
104
  return (
97
105
  <div className={styles.container}>
98
- <div className={styles.content}>
106
+ <div className={styles.content} style={cssVariables}>
99
107
  <div className={styles.closeButton} onClick={() => setDismissed(true)}>
100
108
  <Icon icon={X} />
101
109
  </div>
@@ -21,6 +21,7 @@ import { z } from 'zod';
21
21
  import {
22
22
  type IdentityEntryBasePayload,
23
23
  type IdentityEntryPayload,
24
+ UserMemoryExperienceModel,
24
25
  UserMemoryIdentityModel,
25
26
  UserMemoryModel,
26
27
  } from '@/database/models/userMemory';
@@ -203,6 +204,8 @@ const memoryProcedure = authedProcedure.use(serverDatabase).use(async (opts) =>
203
204
  const { ctx } = opts;
204
205
  return opts.next({
205
206
  ctx: {
207
+ experienceModel: new UserMemoryExperienceModel(ctx.serverDB, ctx.userId),
208
+ identityModel: new UserMemoryIdentityModel(ctx.serverDB, ctx.userId),
206
209
  memoryModel: new UserMemoryModel(ctx.serverDB, ctx.userId),
207
210
  },
208
211
  });
@@ -220,13 +223,66 @@ export const userMemoriesRouter = router({
220
223
  }
221
224
  }),
222
225
 
223
- queryIdentitiesForInjection: authedProcedure
224
- .use(serverDatabase)
226
+ queryExperiences: memoryProcedure
227
+ .input(
228
+ z
229
+ .object({
230
+ order: z.enum(['asc', 'desc']).optional(),
231
+ page: z.coerce.number().int().min(1).optional(),
232
+ pageSize: z.coerce.number().int().min(1).max(100).optional(),
233
+ q: z.string().optional(),
234
+ sort: z.enum(['capturedAt', 'scoreConfidence']).optional(),
235
+ tags: z.array(z.string()).optional(),
236
+ types: z.array(z.string()).optional(),
237
+ })
238
+ .optional(),
239
+ )
240
+ .query(async ({ ctx, input }) => {
241
+ const params = input ?? {};
242
+ const fallbackPage = params.page ?? 1;
243
+ const fallbackPageSize = params.pageSize ?? 20;
244
+
245
+ try {
246
+ return await ctx.experienceModel.queryList(params);
247
+ } catch (error) {
248
+ console.error('Failed to query experiences:', error);
249
+ return { items: [], page: fallbackPage, pageSize: fallbackPageSize, total: 0 };
250
+ }
251
+ }),
252
+
253
+ queryIdentities: memoryProcedure
254
+ .input(
255
+ z
256
+ .object({
257
+ order: z.enum(['asc', 'desc']).optional(),
258
+ page: z.coerce.number().int().min(1).optional(),
259
+ pageSize: z.coerce.number().int().min(1).max(100).optional(),
260
+ q: z.string().optional(),
261
+ relationships: z.array(z.string()).optional(),
262
+ sort: z.enum(['capturedAt', 'type']).optional(),
263
+ tags: z.array(z.string()).optional(),
264
+ types: z.array(z.string()).optional(),
265
+ })
266
+ .optional(),
267
+ )
268
+ .query(async ({ ctx, input }) => {
269
+ const params = input ?? {};
270
+ const fallbackPage = params.page ?? 1;
271
+ const fallbackPageSize = params.pageSize ?? 20;
272
+
273
+ try {
274
+ return await ctx.identityModel.queryList(params);
275
+ } catch (error) {
276
+ console.error('Failed to query identities:', error);
277
+ return { items: [], page: fallbackPage, pageSize: fallbackPageSize, total: 0 };
278
+ }
279
+ }),
280
+
281
+ queryIdentitiesForInjection: memoryProcedure
225
282
  .input(z.object({ limit: z.coerce.number().int().min(1).max(100).optional() }).optional())
226
283
  .query(async ({ ctx, input }) => {
227
284
  try {
228
- const identityModel = new UserMemoryIdentityModel(ctx.serverDB, ctx.userId);
229
- return await identityModel.queryForInjection(input?.limit ?? 50);
285
+ return await ctx.identityModel.queryForInjection(input?.limit ?? 50);
230
286
  } catch (error) {
231
287
  console.error('Failed to query identities for injection:', error);
232
288
  return [];
@@ -262,7 +318,7 @@ export const userMemoriesRouter = router({
262
318
  pageSize: z.coerce.number().int().min(1).max(100).optional(),
263
319
  q: z.string().optional(),
264
320
  sort: z
265
- .enum(['scoreConfidence', 'scoreImpact', 'scorePriority', 'scoreUrgency'])
321
+ .enum(['capturedAt', 'scoreConfidence', 'scoreImpact', 'scorePriority', 'scoreUrgency'])
266
322
  .optional(),
267
323
  tags: z.array(z.string()).optional(),
268
324
  types: z.array(z.string()).optional(),
@@ -27,7 +27,6 @@ const userMemoryProcedure = authedProcedure.use(serverDatabase).use(async (opts)
27
27
 
28
28
  export const userMemoryRouter = router({
29
29
  // ============ Identity CRUD ============
30
-
31
30
  createIdentity: userMemoryProcedure
32
31
  .input(CreateUserMemoryIdentitySchema)
33
32
  .mutation(async ({ ctx, input }) => {
@@ -83,6 +82,11 @@ export const userMemoryRouter = router({
83
82
  return ctx.userMemoryModel.getAllIdentities();
84
83
  }),
85
84
 
85
+ // ============ Persona ============
86
+ getPersona: userMemoryProcedure.query(async () => {
87
+ return { content: '', summary: '' };
88
+ }),
89
+
86
90
  getPreferences: userMemoryProcedure.query(async ({ ctx }) => {
87
91
  return ctx.userMemoryModel.searchPreferences({});
88
92
  }),
@@ -10,7 +10,6 @@ import {
10
10
  import { AgentRuntimeError, type ChatCompletionErrorPayload } from '@lobechat/model-runtime';
11
11
  import {
12
12
  ChatErrorType,
13
- type MessageMapScope,
14
13
  type RuntimeInitialContext,
15
14
  type RuntimeStepContext,
16
15
  type TracePayload,
@@ -75,7 +74,6 @@ interface GetChatCompletionPayload extends Partial<Omit<ChatStreamPayload, 'mess
75
74
  * Required to ensure config consistency and proper isSubTask filtering.
76
75
  */
77
76
  resolvedAgentConfig: ResolvedAgentConfig;
78
- scope?: MessageMapScope;
79
77
  topicId?: string;
80
78
  }
81
79
 
@@ -349,6 +347,8 @@ class ChatService {
349
347
  // Get the chat config to check streaming preference
350
348
  const chatConfig = agentChatConfigSelectors.currentChatConfig(getAgentStoreState());
351
349
 
350
+ delete (res as any).scope;
351
+
352
352
  const payload = merge(
353
353
  {
354
354
  model: DEFAULT_AGENT_CONFIG.model,
@@ -11,6 +11,10 @@ import {
11
11
  type AddExperienceMemoryResult,
12
12
  type AddIdentityMemoryResult,
13
13
  type AddPreferenceMemoryResult,
14
+ type ExperienceListParams,
15
+ type ExperienceListResult,
16
+ type IdentityListParams,
17
+ type IdentityListResult,
14
18
  type LayersEnum,
15
19
  type RemoveIdentityMemoryResult,
16
20
  type SearchMemoryParams,
@@ -57,6 +61,26 @@ class UserMemoryService {
57
61
  return lambdaClient.userMemories.getMemoryDetail.query(params);
58
62
  };
59
63
 
64
+ getPersona = async () => {
65
+ return lambdaClient.userMemory.getPersona.query();
66
+ };
67
+
68
+ /**
69
+ * Query experiences with pagination, search, and sorting
70
+ * Returns flat structure optimized for frontend display
71
+ */
72
+ queryExperiences = async (params?: ExperienceListParams): Promise<ExperienceListResult> => {
73
+ return lambdaClient.userMemories.queryExperiences.query(params);
74
+ };
75
+
76
+ /**
77
+ * Query identities with pagination, search, and sorting
78
+ * Returns flat structure optimized for frontend display
79
+ */
80
+ queryIdentities = async (params?: IdentityListParams): Promise<IdentityListResult> => {
81
+ return lambdaClient.userMemories.queryIdentities.query(params);
82
+ };
83
+
60
84
  retrieveMemory = async (params: SearchMemoryParams): Promise<SearchMemoryResult> => {
61
85
  return lambdaClient.userMemories.toolSearchMemory.query(params);
62
86
  };
@@ -96,7 +120,7 @@ class UserMemoryService {
96
120
  page?: number;
97
121
  pageSize?: number;
98
122
  q?: string;
99
- sort?: 'scoreConfidence' | 'scoreImpact' | 'scorePriority' | 'scoreUrgency';
123
+ sort?: 'capturedAt' | 'scoreConfidence' | 'scoreImpact' | 'scorePriority' | 'scoreUrgency';
100
124
  tags?: string[];
101
125
  types?: TypesEnum[];
102
126
  }) => {
@@ -481,7 +481,6 @@ export const streamingExecutor: StateCreator<
481
481
  // Pass pre-resolved config to avoid duplicate resolveAgentConfig calls
482
482
  // This ensures isSubTask filtering and other runtime modifications are preserved
483
483
  resolvedAgentConfig: agentConfig,
484
- scope, // Pass scope to chat service for page-agent injection
485
484
  topicId: topicId ?? undefined, // Pass topicId for GTD context injection
486
485
  ...agentConfigData.params,
487
486
  },
@@ -1,46 +1,32 @@
1
1
  import type { RetrieveMemoryParams, RetrieveMemoryResult } from '@lobechat/types';
2
2
 
3
- import {
4
- type DisplayContextMemory,
5
- type DisplayExperienceMemory,
6
- type DisplayPreferenceMemory,
7
- } from '@/database/repositories/userMemory';
8
-
9
3
  import { type AgentMemorySliceState, agentMemoryInitialState } from './slices/agent';
4
+ import { type ContextSliceState, contextInitialState } from './slices/context';
5
+ import { type ExperienceSliceState, experienceInitialState } from './slices/experience';
10
6
  import { type IdentitySliceState, identityInitialState } from './slices/identity';
7
+ import { type PreferenceSliceState, preferenceInitialState } from './slices/preference';
8
+
9
+ export interface PersonaData {
10
+ content: string;
11
+ summary: string;
12
+ }
11
13
 
12
- export interface UserMemoryStoreState extends AgentMemorySliceState, IdentitySliceState {
14
+ export interface UserMemoryStoreState
15
+ extends
16
+ AgentMemorySliceState,
17
+ ContextSliceState,
18
+ ExperienceSliceState,
19
+ IdentitySliceState,
20
+ PreferenceSliceState {
13
21
  activeParams?: RetrieveMemoryParams;
14
22
  activeParamsKey?: string;
15
- contexts: DisplayContextMemory[];
16
- contextsHasMore: boolean;
17
- contextsInit: boolean;
18
- contextsPage: number;
19
- contextsQuery?: string;
20
- contextsSearchLoading?: boolean;
21
- contextsSort?: 'scoreImpact' | 'scoreUrgency';
22
- contextsTotal: number;
23
23
  editingMemoryContent?: string;
24
24
  editingMemoryId?: string;
25
25
  editingMemoryLayer?: 'context' | 'experience' | 'identity' | 'preference';
26
- experiences: DisplayExperienceMemory[];
27
- experiencesHasMore: boolean;
28
- experiencesInit: boolean;
29
- experiencesPage: number;
30
- experiencesQuery?: string;
31
- experiencesSearchLoading?: boolean;
32
- experiencesSort?: 'scoreConfidence';
33
- experiencesTotal: number;
34
26
  memoryFetchedAtMap: Record<string, number>;
35
27
  memoryMap: Record<string, RetrieveMemoryResult>;
36
- preferences: DisplayPreferenceMemory[];
37
- preferencesHasMore: boolean;
38
- preferencesInit: boolean;
39
- preferencesPage: number;
40
- preferencesQuery?: string;
41
- preferencesSearchLoading?: boolean;
42
- preferencesSort?: 'scorePriority';
43
- preferencesTotal: number;
28
+ persona?: PersonaData;
29
+ personaInit: boolean;
44
30
  roles: { count: number; tag: string }[];
45
31
  tags: { count: number; tag: string }[];
46
32
  tagsInit: boolean;
@@ -48,35 +34,19 @@ export interface UserMemoryStoreState extends AgentMemorySliceState, IdentitySli
48
34
 
49
35
  export const initialState: UserMemoryStoreState = {
50
36
  ...agentMemoryInitialState,
37
+ ...contextInitialState,
38
+ ...experienceInitialState,
51
39
  ...identityInitialState,
40
+ ...preferenceInitialState,
52
41
  activeParams: undefined,
53
42
  activeParamsKey: undefined,
54
- contexts: [],
55
- contextsHasMore: true,
56
- contextsInit: false,
57
- contextsPage: 1,
58
- contextsQuery: undefined,
59
- contextsSort: undefined,
60
- contextsTotal: 0,
61
43
  editingMemoryContent: undefined,
62
44
  editingMemoryId: undefined,
63
45
  editingMemoryLayer: undefined,
64
- experiences: [],
65
- experiencesHasMore: true,
66
- experiencesInit: false,
67
- experiencesPage: 1,
68
- experiencesQuery: undefined,
69
- experiencesSort: undefined,
70
- experiencesTotal: 0,
71
46
  memoryFetchedAtMap: {},
72
47
  memoryMap: {},
73
- preferences: [],
74
- preferencesHasMore: true,
75
- preferencesInit: false,
76
- preferencesPage: 1,
77
- preferencesQuery: undefined,
78
- preferencesSort: undefined,
79
- preferencesTotal: 0,
48
+ persona: undefined,
49
+ personaInit: false,
80
50
  roles: [],
81
51
  tags: [],
82
52
  tagsInit: false,
@@ -16,7 +16,7 @@ export interface ContextQueryParams {
16
16
  page?: number;
17
17
  pageSize?: number;
18
18
  q?: string;
19
- sort?: 'scoreImpact' | 'scoreUrgency';
19
+ sort?: 'capturedAt' | 'scoreImpact' | 'scoreUrgency';
20
20
  }
21
21
 
22
22
  export interface ContextAction {
@@ -1 +1,2 @@
1
1
  export { type ContextAction,createContextSlice } from './action';
2
+ export { contextInitialState, type ContextSliceState } from './initialState';
@@ -0,0 +1,22 @@
1
+ import { type DisplayContextMemory } from '@/database/repositories/userMemory';
2
+
3
+ export interface ContextSliceState {
4
+ contexts: DisplayContextMemory[];
5
+ contextsHasMore: boolean;
6
+ contextsInit: boolean;
7
+ contextsPage: number;
8
+ contextsQuery?: string;
9
+ contextsSearchLoading?: boolean;
10
+ contextsSort?: 'capturedAt' | 'scoreImpact' | 'scoreUrgency';
11
+ contextsTotal: number;
12
+ }
13
+
14
+ export const contextInitialState: ContextSliceState = {
15
+ contexts: [],
16
+ contextsHasMore: true,
17
+ contextsInit: false,
18
+ contextsPage: 1,
19
+ contextsQuery: undefined,
20
+ contextsSort: undefined,
21
+ contextsTotal: 0,
22
+ };
@@ -1,3 +1,4 @@
1
+ import type { ExperienceListResult } from '@lobechat/types';
1
2
  import { uniqBy } from 'es-toolkit/compat';
2
3
  import { produce } from 'immer';
3
4
  import useSWR, { type SWRResponse } from 'swr';
@@ -5,7 +6,6 @@ import { type StateCreator } from 'zustand/vanilla';
5
6
 
6
7
  import { userMemoryService } from '@/services/userMemory';
7
8
  import { memoryCRUDService } from '@/services/userMemory/index';
8
- import { LayersEnum } from '@/types/userMemory';
9
9
  import { setNamespace } from '@/utils/storeDebug';
10
10
 
11
11
  import { type UserMemoryStore } from '../../store';
@@ -16,14 +16,14 @@ export interface ExperienceQueryParams {
16
16
  page?: number;
17
17
  pageSize?: number;
18
18
  q?: string;
19
- sort?: 'scoreConfidence';
19
+ sort?: 'capturedAt' | 'scoreConfidence';
20
20
  }
21
21
 
22
22
  export interface ExperienceAction {
23
23
  deleteExperience: (id: string) => Promise<void>;
24
24
  loadMoreExperiences: () => void;
25
25
  resetExperiencesList: (params?: Omit<ExperienceQueryParams, 'page' | 'pageSize'>) => void;
26
- useFetchExperiences: (params: ExperienceQueryParams) => SWRResponse<any>;
26
+ useFetchExperiences: (params: ExperienceQueryParams) => SWRResponse<ExperienceListResult>;
27
27
  }
28
28
 
29
29
  export const createExperienceSlice: StateCreator<
@@ -81,44 +81,32 @@ export const createExperienceSlice: StateCreator<
81
81
  return useSWR(
82
82
  swrKey,
83
83
  async () => {
84
- const result = await userMemoryService.queryMemories({
85
- layer: LayersEnum.Experience,
84
+ // Use the new dedicated queryExperiences API
85
+ return userMemoryService.queryExperiences({
86
86
  page: params.page,
87
87
  pageSize: params.pageSize,
88
88
  q: params.q,
89
89
  sort: params.sort,
90
90
  });
91
-
92
- return result;
93
91
  },
94
92
  {
95
- onSuccess(data: any) {
93
+ onSuccess(data: ExperienceListResult) {
96
94
  set(
97
95
  produce((draft) => {
98
96
  draft.experiencesSearchLoading = false;
97
+ draft.experiencesTotal = data.total;
99
98
 
100
- // 设置基础信息
101
99
  if (!draft.experiencesInit) {
102
100
  draft.experiencesInit = true;
103
- draft.experiencesTotal = data.total;
104
101
  }
105
102
 
106
- // 转换数据结构
107
- const transformedItems = data.items.map((item: any) => ({
108
- ...item.memory,
109
- ...item.experience,
110
- }));
111
-
112
- // 累积数据逻辑
103
+ // Backend now returns flat structure directly, no transformation needed
113
104
  if (page === 1) {
114
- // 第一页,直接设置
115
- draft.experiences = uniqBy(transformedItems, 'id');
105
+ draft.experiences = uniqBy(data.items, 'id');
116
106
  } else {
117
- // 后续页面,累积数据
118
- draft.experiences = uniqBy([...draft.experiences, ...transformedItems], 'id');
107
+ draft.experiences = uniqBy([...draft.experiences, ...data.items], 'id');
119
108
  }
120
109
 
121
- // 更新 hasMore
122
110
  draft.experiencesHasMore = data.items.length >= (params.pageSize || 20);
123
111
  }),
124
112
  false,
@@ -1 +1,2 @@
1
1
  export { createExperienceSlice, type ExperienceAction } from './action';
2
+ export { experienceInitialState, type ExperienceSliceState } from './initialState';
@@ -0,0 +1,22 @@
1
+ import type { ExperienceListItem } from '@lobechat/types';
2
+
3
+ export interface ExperienceSliceState {
4
+ experiences: ExperienceListItem[];
5
+ experiencesHasMore: boolean;
6
+ experiencesInit: boolean;
7
+ experiencesPage: number;
8
+ experiencesQuery?: string;
9
+ experiencesSearchLoading?: boolean;
10
+ experiencesSort?: 'capturedAt' | 'scoreConfidence';
11
+ experiencesTotal: number;
12
+ }
13
+
14
+ export const experienceInitialState: ExperienceSliceState = {
15
+ experiences: [],
16
+ experiencesHasMore: true,
17
+ experiencesInit: false,
18
+ experiencesPage: 1,
19
+ experiencesQuery: undefined,
20
+ experiencesSort: undefined,
21
+ experiencesTotal: 0,
22
+ };
@@ -5,12 +5,15 @@ import { type QueryIdentityRolesResult } from '@/database/models/userMemory';
5
5
  import { useClientDataSWR } from '@/libs/swr';
6
6
  import { userMemoryService } from '@/services/userMemory';
7
7
 
8
+ import { type PersonaData } from '../../initialState';
8
9
  import type { UserMemoryStore } from '../../store';
9
10
 
10
11
  const FETCH_TAGS_KEY = 'useFetchTags';
12
+ const FETCH_PERSONA_KEY = 'useFetchPersona';
11
13
  const n = (namespace: string) => namespace;
12
14
 
13
15
  export interface HomeAction {
16
+ useFetchPersona: () => SWRResponse<PersonaData>;
14
17
  useFetchTags: () => SWRResponse<QueryIdentityRolesResult>;
15
18
  }
16
19
 
@@ -20,6 +23,20 @@ export const createHomeSlice: StateCreator<
20
23
  [],
21
24
  HomeAction
22
25
  > = (set) => ({
26
+ useFetchPersona: () =>
27
+ useClientDataSWR(FETCH_PERSONA_KEY, () => userMemoryService.getPersona(), {
28
+ onSuccess: (data: PersonaData | undefined) => {
29
+ set(
30
+ {
31
+ persona: data,
32
+ personaInit: true,
33
+ },
34
+ false,
35
+ n('useFetchPersona/onSuccess'),
36
+ );
37
+ },
38
+ }),
39
+
23
40
  useFetchTags: () =>
24
41
  useClientDataSWR(
25
42
  FETCH_TAGS_KEY,