@lobehub/lobehub 2.0.0-next.21 → 2.0.0-next.23

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 (27) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/apps/desktop/src/main/controllers/AuthCtr.ts +3 -3
  3. package/apps/desktop/src/main/controllers/MenuCtr.ts +5 -5
  4. package/apps/desktop/src/main/controllers/NotificationCtr.ts +29 -29
  5. package/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts +16 -16
  6. package/apps/desktop/src/main/controllers/ShortcutCtr.ts +2 -2
  7. package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +18 -18
  8. package/apps/desktop/src/main/controllers/UpdaterCtr.ts +4 -4
  9. package/apps/desktop/src/main/controllers/__tests__/TrayMenuCtr.test.ts +5 -5
  10. package/apps/desktop/src/main/controllers/index.ts +4 -4
  11. package/changelog/v1.json +14 -0
  12. package/docs/development/database-schema.dbml +2 -1
  13. package/package.json +2 -2
  14. package/packages/database/migrations/0042_improve_agent_index.sql +1 -0
  15. package/packages/database/migrations/meta/0042_snapshot.json +7800 -0
  16. package/packages/database/migrations/meta/_journal.json +7 -0
  17. package/packages/database/src/core/migrations.json +8 -0
  18. package/packages/database/src/models/agent.ts +16 -13
  19. package/packages/database/src/models/session.ts +20 -9
  20. package/packages/database/src/models/user.ts +2 -1
  21. package/packages/database/src/schemas/agent.ts +4 -1
  22. package/packages/memory-extract/package.json +1 -1
  23. package/packages/types/src/message/ui/params.ts +4 -4
  24. package/renovate.json +49 -13
  25. package/src/server/routers/lambda/message.ts +0 -2
  26. package/src/server/routers/lambda/user.ts +8 -6
  27. package/src/server/services/mcp/deps/MCPSystemDepsCheckService.test.ts +541 -0
@@ -294,6 +294,13 @@
294
294
  "when": 1761878697451,
295
295
  "tag": "0041_improve_index",
296
296
  "breakpoints": true
297
+ },
298
+ {
299
+ "idx": 42,
300
+ "version": "7",
301
+ "when": 1762232313711,
302
+ "tag": "0042_improve_agent_index",
303
+ "breakpoints": true
297
304
  }
298
305
  ],
299
306
  "version": "6"
@@ -746,5 +746,13 @@
746
746
  "bps": true,
747
747
  "folderMillis": 1761878697451,
748
748
  "hash": "2f740719356c4ea28a4f71b6089595871f6e185e056b43288a7d16f7d0aae196"
749
+ },
750
+ {
751
+ "sql": [
752
+ "CREATE INDEX \"agents_knowledge_bases_agent_id_idx\" ON \"agents_knowledge_bases\" USING btree (\"agent_id\");"
753
+ ],
754
+ "bps": true,
755
+ "folderMillis": 1762232313711,
756
+ "hash": "8988ec15592e000430d76566323f437b2ebde46122c5f7172067cd0a65f99614"
749
757
  }
750
758
  ]
@@ -28,19 +28,21 @@ export class AgentModel {
28
28
  };
29
29
 
30
30
  getAgentAssignedKnowledge = async (id: string) => {
31
- const knowledgeBaseResult = await this.db
32
- .select({ enabled: agentsKnowledgeBases.enabled, knowledgeBases })
33
- .from(agentsKnowledgeBases)
34
- .where(eq(agentsKnowledgeBases.agentId, id))
35
- .orderBy(desc(agentsKnowledgeBases.createdAt))
36
- .leftJoin(knowledgeBases, eq(knowledgeBases.id, agentsKnowledgeBases.knowledgeBaseId));
37
-
38
- const fileResult = await this.db
39
- .select({ enabled: agentsFiles.enabled, files })
40
- .from(agentsFiles)
41
- .where(eq(agentsFiles.agentId, id))
42
- .orderBy(desc(agentsFiles.createdAt))
43
- .leftJoin(files, eq(files.id, agentsFiles.fileId));
31
+ // Run both queries in parallel for better performance
32
+ const [knowledgeBaseResult, fileResult] = await Promise.all([
33
+ this.db
34
+ .select({ enabled: agentsKnowledgeBases.enabled, knowledgeBases })
35
+ .from(agentsKnowledgeBases)
36
+ .where(eq(agentsKnowledgeBases.agentId, id))
37
+ .orderBy(desc(agentsKnowledgeBases.createdAt))
38
+ .leftJoin(knowledgeBases, eq(knowledgeBases.id, agentsKnowledgeBases.knowledgeBaseId)),
39
+ this.db
40
+ .select({ enabled: agentsFiles.enabled, files })
41
+ .from(agentsFiles)
42
+ .where(eq(agentsFiles.agentId, id))
43
+ .orderBy(desc(agentsFiles.createdAt))
44
+ .leftJoin(files, eq(files.id, agentsFiles.fileId)),
45
+ ]);
44
46
 
45
47
  return {
46
48
  files: fileResult.map((item) => ({
@@ -61,6 +63,7 @@ export class AgentModel {
61
63
  const item = await this.db.query.agentsToSessions.findFirst({
62
64
  where: eq(agentsToSessions.sessionId, sessionId),
63
65
  });
66
+
64
67
  if (!item) return;
65
68
 
66
69
  const agentId = item.agentId;
@@ -123,17 +123,28 @@ export class SessionModel {
123
123
  findByIdOrSlug = async (
124
124
  idOrSlug: string,
125
125
  ): Promise<(SessionItem & { agent: AgentItem }) | undefined> => {
126
- const result = await this.db.query.sessions.findFirst({
127
- where: and(
128
- or(eq(sessions.id, idOrSlug), eq(sessions.slug, idOrSlug)),
129
- eq(sessions.userId, this.userId),
130
- ),
131
- with: { agentsToSessions: { columns: {}, with: { agent: true } }, group: true },
132
- });
126
+ // Use leftJoin instead of nested 'with' for better performance
127
+ const result = await this.db
128
+ .select({
129
+ agent: agents,
130
+ group: sessionGroups,
131
+ session: sessions,
132
+ })
133
+ .from(sessions)
134
+ .where(
135
+ and(
136
+ or(eq(sessions.id, idOrSlug), eq(sessions.slug, idOrSlug)),
137
+ eq(sessions.userId, this.userId),
138
+ ),
139
+ )
140
+ .leftJoin(agentsToSessions, eq(sessions.id, agentsToSessions.sessionId))
141
+ .leftJoin(agents, eq(agentsToSessions.agentId, agents.id))
142
+ .leftJoin(sessionGroups, eq(sessions.groupId, sessionGroups.id))
143
+ .limit(1);
133
144
 
134
- if (!result) return;
145
+ if (!result || !result[0]) return;
135
146
 
136
- return { ...result, agent: (result?.agentsToSessions?.[0] as any)?.agent } as any;
147
+ return { ...result[0].session, agent: result[0].agent, group: result[0].group } as any;
137
148
  };
138
149
 
139
150
  count = async (params?: {
@@ -82,7 +82,8 @@ export class UserModel {
82
82
  })
83
83
  .from(users)
84
84
  .where(eq(users.id, this.userId))
85
- .leftJoin(userSettings, eq(users.id, userSettings.id));
85
+ .leftJoin(userSettings, eq(users.id, userSettings.id))
86
+ .limit(1);
86
87
 
87
88
  if (!result || !result[0]) {
88
89
  throw new UserNotFoundError();
@@ -90,7 +90,10 @@ export const agentsKnowledgeBases = pgTable(
90
90
 
91
91
  ...timestamps,
92
92
  },
93
- (t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
93
+ (t) => [
94
+ primaryKey({ columns: [t.agentId, t.knowledgeBaseId] }),
95
+ index('agents_knowledge_bases_agent_id_idx').on(t.agentId),
96
+ ],
94
97
  );
95
98
 
96
99
  export const agentsFiles = pgTable(
@@ -10,7 +10,7 @@
10
10
  "@lobechat/model-runtime": "workspace:*",
11
11
  "@lobechat/prompts": "workspace:*",
12
12
  "dotenv": "^17.2.3",
13
- "ora": "^8.2.0"
13
+ "ora": "^9.0.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "tsx": "^4.19.2"
@@ -130,10 +130,10 @@ export const CreateMessageParamsSchema = z
130
130
  files: z.array(z.string()).optional(),
131
131
  fromModel: z.string().optional(),
132
132
  fromProvider: z.string().optional(),
133
- groupId: z.string().optional(),
133
+ groupId: z.string().nullable().optional(),
134
134
  targetId: z.string().nullable().optional(),
135
135
  threadId: z.string().nullable().optional(),
136
- topicId: z.string().optional(),
136
+ topicId: z.string().nullable().optional(),
137
137
  traceId: z.string().optional(),
138
138
  // Allow additional fields from UIChatMessage (many can be null)
139
139
  agentId: z.string().optional(),
@@ -180,9 +180,9 @@ export const CreateNewMessageParamsSchema = z
180
180
  plugin: ChatPluginPayloadSchema.optional(),
181
181
  // Grouping
182
182
  parentId: z.string().optional(),
183
- groupId: z.string().optional(),
183
+ groupId: z.string().nullable().optional(),
184
184
  // Context
185
- topicId: z.string().optional(),
185
+ topicId: z.string().nullable().optional(),
186
186
  threadId: z.string().nullable().optional(),
187
187
  targetId: z.string().nullable().optional(),
188
188
  // Model info
package/renovate.json CHANGED
@@ -15,28 +15,64 @@
15
15
  "workarounds:all"
16
16
  ],
17
17
  "ignoreDeps": [],
18
- "labels": [
19
- "dependencies"
20
- ],
18
+ "labels": ["dependencies"],
21
19
  "packageRules": [
20
+ // 1) Pinned deps: isolate (OK to use separate* here because there's no matchUpdateTypes)
21
+ {
22
+ "description": "Isolate PRs for pinned deps (exact x.y.z)",
23
+ "matchManagers": ["npm", "pnpm", "yarn", "bun"],
24
+ "matchDepTypes": [
25
+ "dependencies",
26
+ "devDependencies",
27
+ "optionalDependencies",
28
+ "peerDependencies"
29
+ ],
30
+ "matchCurrentValue": "^\\d+\\.\\d+\\.\\d+([+-][0-9A-Za-z.-]+)?$",
31
+ "groupName": null,
32
+ "separateMinorPatch": true,
33
+ "separateMajorMinor": true,
34
+ "separateMultipleMinor": true,
35
+ "separateMultipleMajor": true
36
+ },
37
+ // 2a) Non-pinned deps: override splitting so patch+minor can be combined
22
38
  {
23
- "groupName": "all non-minor dependencies",
24
- "groupSlug": "all-minor-patch",
25
- "matchPackageNames": [
26
- "*"
39
+ "description": "Non-pinned deps: allow patch+minor to group; keep majors separate",
40
+ "matchManagers": ["npm", "pnpm", "yarn", "bun"],
41
+ "matchDepTypes": [
42
+ "dependencies",
43
+ "devDependencies",
44
+ "optionalDependencies",
45
+ "peerDependencies"
27
46
  ],
28
- "matchUpdateTypes": [
29
- "patch"
30
- ]
47
+ "matchCurrentValue": "/(^[~^]|[<>=| -])/", // anything that looks like a range
48
+ "separateMinorPatch": false,
49
+ "separateMajorMinor": true
50
+ },
51
+ // 2b) Non-pinned deps: actually group patch+minor together
52
+ {
53
+ "description": "Non-pinned deps: group non-major updates",
54
+ "matchManagers": ["npm", "pnpm", "yarn", "bun"],
55
+ "matchDepTypes": [
56
+ "dependencies",
57
+ "devDependencies",
58
+ "optionalDependencies",
59
+ "peerDependencies"
60
+ ],
61
+ "matchCurrentValue": "/(^[~^]|[<>=| -])/",
62
+ "matchUpdateTypes": ["minor", "patch"], // only non-majors
63
+ "groupName": "deps (non-major)"
31
64
  }
32
65
  ],
33
- "postUpdateOptions": [
34
- "yarnDedupeHighest"
35
- ],
66
+ "postUpdateOptions": ["yarnDedupeHighest"],
36
67
  "prConcurrentLimit": 30,
37
68
  "prHourlyLimit": 0,
38
69
  "rangeStrategy": "bump",
39
70
  "rebaseWhen": "conflicted",
40
71
  "schedule": "on sunday before 6:00am",
72
+ "separateMajorMinor": true,
73
+ // Global defaults are fine; rule 2a overrides minor/patch splitting for ranged deps
74
+ "separateMinorPatch": true,
75
+ "separateMultipleMajor": true,
76
+ "separateMultipleMinor": true,
41
77
  "timezone": "UTC"
42
78
  }
@@ -283,5 +283,3 @@ export const messageRouter = router({
283
283
  return ctx.messageModel.updateTranslate(input.id, input.value);
284
284
  }),
285
285
  });
286
-
287
- export type MessageRouter = typeof messageRouter;
@@ -30,7 +30,9 @@ const userProcedure = authedProcedure.use(serverDatabase).use(async ({ ctx, next
30
30
  ctx: {
31
31
  clerkAuth: new ClerkAuth(),
32
32
  fileService: new FileService(ctx.serverDB, ctx.userId),
33
+ messageModel: new MessageModel(ctx.serverDB, ctx.userId),
33
34
  nextAuthUserService: new NextAuthUserService(ctx.serverDB),
35
+ sessionModel: new SessionModel(ctx.serverDB, ctx.userId),
34
36
  userModel: new UserModel(ctx.serverDB, ctx.userId),
35
37
  },
36
38
  });
@@ -97,12 +99,12 @@ export const userRouter = router({
97
99
  }
98
100
  }
99
101
 
100
- const messageModel = new MessageModel(ctx.serverDB, ctx.userId);
101
- const hasMoreThan4Messages = await messageModel.hasMoreThanN(4);
102
-
103
- const sessionModel = new SessionModel(ctx.serverDB, ctx.userId);
104
- const hasAnyMessages = await messageModel.hasMoreThanN(0);
105
- const hasExtraSession = await sessionModel.hasMoreThanN(1);
102
+ // Run all count queries in parallel
103
+ const [hasMoreThan4Messages, hasAnyMessages, hasExtraSession] = await Promise.all([
104
+ ctx.messageModel.hasMoreThanN(4),
105
+ ctx.messageModel.hasMoreThanN(0),
106
+ ctx.sessionModel.hasMoreThanN(1),
107
+ ]);
106
108
 
107
109
  return {
108
110
  avatar: state.avatar,