@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
@@ -202,6 +202,66 @@ export const agentGroupRouter = router({
202
202
 
203
203
 
204
204
  /**
205
+ * Deprecate agent group
206
+ * POST /market/agent-group/:identifier/deprecate
207
+ */
208
+ deprecateAgentGroup: agentGroupProcedure
209
+ .input(z.object({ identifier: z.string() }))
210
+ .mutation(async ({ input, ctx }) => {
211
+ log('deprecateAgentGroup input: %O', input);
212
+
213
+ try {
214
+ const deprecateUrl = `${MARKET_BASE_URL}/api/v1/agent-groups/${input.identifier}/deprecate`;
215
+
216
+ const headers: Record<string, string> = {
217
+ 'Content-Type': 'application/json',
218
+ };
219
+
220
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
221
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
222
+
223
+ if (userInfo) {
224
+ const trustedClientToken = generateTrustedClientToken(userInfo);
225
+ if (trustedClientToken) {
226
+ headers['x-lobe-trust-token'] = trustedClientToken;
227
+ }
228
+ }
229
+
230
+ if (!headers['x-lobe-trust-token'] && accessToken) {
231
+ headers['Authorization'] = `Bearer ${accessToken}`;
232
+ }
233
+
234
+ const response = await fetch(deprecateUrl, {
235
+ headers,
236
+ method: 'POST',
237
+ });
238
+
239
+ if (!response.ok) {
240
+ const errorText = await response.text();
241
+ log(
242
+ 'Deprecate agent group failed: %s %s - %s',
243
+ response.status,
244
+ response.statusText,
245
+ errorText,
246
+ );
247
+ throw new Error(`Failed to deprecate agent group: ${response.statusText}`);
248
+ }
249
+
250
+ log('Deprecate agent group success');
251
+ return { success: true };
252
+ } catch (error) {
253
+ log('Error deprecating agent group: %O', error);
254
+ throw new TRPCError({
255
+ cause: error,
256
+ code: 'INTERNAL_SERVER_ERROR',
257
+ message: error instanceof Error ? error.message : 'Failed to deprecate agent group',
258
+ });
259
+ }
260
+ }),
261
+
262
+
263
+
264
+ /**
205
265
  * Fork an agent group
206
266
  * POST /market/agent-group/:identifier/fork
207
267
  */
@@ -340,7 +400,6 @@ getAgentGroupForkSource: agentGroupProcedure
340
400
 
341
401
 
342
402
 
343
-
344
403
  /**
345
404
  * Get all forks of an agent group
346
405
  * GET /market/agent-group/:identifier/forks
@@ -401,6 +460,66 @@ getAgentGroupForks: agentGroupProcedure
401
460
 
402
461
 
403
462
 
463
+ /**
464
+ * Publish agent group
465
+ * POST /market/agent-group/:identifier/publish
466
+ */
467
+ publishAgentGroup: agentGroupProcedure
468
+ .input(z.object({ identifier: z.string() }))
469
+ .mutation(async ({ input, ctx }) => {
470
+ log('publishAgentGroup input: %O', input);
471
+
472
+ try {
473
+ const publishUrl = `${MARKET_BASE_URL}/api/v1/agent-groups/${input.identifier}/publish`;
474
+
475
+ const headers: Record<string, string> = {
476
+ 'Content-Type': 'application/json',
477
+ };
478
+
479
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
480
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
481
+
482
+ if (userInfo) {
483
+ const trustedClientToken = generateTrustedClientToken(userInfo);
484
+ if (trustedClientToken) {
485
+ headers['x-lobe-trust-token'] = trustedClientToken;
486
+ }
487
+ }
488
+
489
+ if (!headers['x-lobe-trust-token'] && accessToken) {
490
+ headers['Authorization'] = `Bearer ${accessToken}`;
491
+ }
492
+
493
+ const response = await fetch(publishUrl, {
494
+ headers,
495
+ method: 'POST',
496
+ });
497
+
498
+ if (!response.ok) {
499
+ const errorText = await response.text();
500
+ log(
501
+ 'Publish agent group failed: %s %s - %s',
502
+ response.status,
503
+ response.statusText,
504
+ errorText,
505
+ );
506
+ throw new Error(`Failed to publish agent group: ${response.statusText}`);
507
+ }
508
+
509
+ log('Publish agent group success');
510
+ return { success: true };
511
+ } catch (error) {
512
+ log('Error publishing agent group: %O', error);
513
+ throw new TRPCError({
514
+ cause: error,
515
+ code: 'INTERNAL_SERVER_ERROR',
516
+ message: error instanceof Error ? error.message : 'Failed to publish agent group',
517
+ });
518
+ }
519
+ }),
520
+
521
+
522
+
404
523
  /**
405
524
  * Unified publish or create agent group flow
406
525
  * 1. Check if identifier exists and if current user is owner
@@ -494,6 +613,65 @@ publishOrCreate: agentGroupProcedure
494
613
  });
495
614
  }
496
615
  }),
616
+
617
+
618
+ /**
619
+ * Unpublish agent group
620
+ * POST /market/agent-group/:identifier/unpublish
621
+ */
622
+ unpublishAgentGroup: agentGroupProcedure
623
+ .input(z.object({ identifier: z.string() }))
624
+ .mutation(async ({ input, ctx }) => {
625
+ log('unpublishAgentGroup input: %O', input);
626
+
627
+ try {
628
+ const unpublishUrl = `${MARKET_BASE_URL}/api/v1/agent-groups/${input.identifier}/unpublish`;
629
+
630
+ const headers: Record<string, string> = {
631
+ 'Content-Type': 'application/json',
632
+ };
633
+
634
+ const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
635
+ const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
636
+
637
+ if (userInfo) {
638
+ const trustedClientToken = generateTrustedClientToken(userInfo);
639
+ if (trustedClientToken) {
640
+ headers['x-lobe-trust-token'] = trustedClientToken;
641
+ }
642
+ }
643
+
644
+ if (!headers['x-lobe-trust-token'] && accessToken) {
645
+ headers['Authorization'] = `Bearer ${accessToken}`;
646
+ }
647
+
648
+ const response = await fetch(unpublishUrl, {
649
+ headers,
650
+ method: 'POST',
651
+ });
652
+
653
+ if (!response.ok) {
654
+ const errorText = await response.text();
655
+ log(
656
+ 'Unpublish agent group failed: %s %s - %s',
657
+ response.status,
658
+ response.statusText,
659
+ errorText,
660
+ );
661
+ throw new Error(`Failed to unpublish agent group: ${response.statusText}`);
662
+ }
663
+
664
+ log('Unpublish agent group success');
665
+ return { success: true };
666
+ } catch (error) {
667
+ log('Error unpublishing agent group: %O', error);
668
+ throw new TRPCError({
669
+ cause: error,
670
+ code: 'INTERNAL_SERVER_ERROR',
671
+ message: error instanceof Error ? error.message : 'Failed to unpublish agent group',
672
+ });
673
+ }
674
+ }),
497
675
  });
498
676
 
499
677
  export type AgentGroupRouter = typeof agentGroupRouter;
@@ -1761,6 +1761,7 @@ export class DiscoverService {
1761
1761
  knowledgeCount: agent.knowledgeCount || 0,
1762
1762
  pluginCount: agent.pluginCount || 0,
1763
1763
  schemaVersion: 1,
1764
+ status: agent.status,
1764
1765
  tags: agent.tags || [],
1765
1766
  title: agent.name || agent.identifier,
1766
1767
  tokenUsage: agent.tokenUsage || 0,
@@ -1780,6 +1781,7 @@ export class DiscoverService {
1780
1781
  isOfficial: group.isOfficial || false,
1781
1782
  memberCount: 0, // Will be populated from memberAgents in detail view
1782
1783
  schemaVersion: 1,
1784
+ status: group.status,
1783
1785
  tags: group.tags || [],
1784
1786
  title: group.name || group.identifier,
1785
1787
  updatedAt: group.updatedAt,
@@ -1802,6 +1804,7 @@ export class DiscoverService {
1802
1804
  knowledgeCount: agent.knowledgeCount || 0,
1803
1805
  pluginCount: agent.pluginCount || 0,
1804
1806
  schemaVersion: 1,
1807
+ status: agent.status,
1805
1808
  tags: agent.tags || [],
1806
1809
  title: agent.name || agent.identifier,
1807
1810
  tokenUsage: agent.tokenUsage || 0,
@@ -1824,6 +1827,7 @@ export class DiscoverService {
1824
1827
  isOfficial: group.isOfficial || false,
1825
1828
  memberCount: 0, // Will be populated from memberAgents in detail view
1826
1829
  schemaVersion: 1,
1830
+ status: group.status,
1827
1831
  tags: group.tags || [],
1828
1832
  title: group.name || group.identifier,
1829
1833
  updatedAt: group.updatedAt,
@@ -15,6 +15,35 @@ import { aiModelSelectors } from '@/store/aiInfra';
15
15
  import { useToolStore } from '@/store/tool';
16
16
 
17
17
  import { chatService } from './index';
18
+ import type { ResolvedAgentConfig } from './mecha';
19
+
20
+ /**
21
+ * Default mock resolvedAgentConfig for tests
22
+ */
23
+ const createMockResolvedConfig = (overrides?: {
24
+ agentConfig?: Partial<ResolvedAgentConfig['agentConfig']>;
25
+ chatConfig?: Partial<ResolvedAgentConfig['chatConfig']>;
26
+ plugins?: string[];
27
+ isBuiltinAgent?: boolean;
28
+ }): ResolvedAgentConfig =>
29
+ ({
30
+ agentConfig: {
31
+ model: DEFAULT_AGENT_CONFIG.model,
32
+ provider: 'openai',
33
+ systemRole: '',
34
+ chatConfig: {},
35
+ params: {},
36
+ tts: {},
37
+ ...overrides?.agentConfig,
38
+ },
39
+ chatConfig: {
40
+ searchMode: 'off',
41
+ autoCreateTopicThreshold: 2,
42
+ ...overrides?.chatConfig,
43
+ },
44
+ isBuiltinAgent: overrides?.isBuiltinAgent ?? false,
45
+ plugins: overrides?.plugins ?? [],
46
+ }) as ResolvedAgentConfig;
18
47
 
19
48
  // Mocking external dependencies
20
49
  vi.mock('i18next', () => ({
@@ -109,7 +138,10 @@ describe('ChatService', () => {
109
138
  ],
110
139
  });
111
140
  });
112
- await chatService.createAssistantMessage({ messages, plugins: enabledPlugins });
141
+ await chatService.createAssistantMessage({
142
+ messages,
143
+ resolvedAgentConfig: createMockResolvedConfig({ plugins: enabledPlugins }),
144
+ });
113
145
 
114
146
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
115
147
  expect.objectContaining({
@@ -136,21 +168,14 @@ describe('ChatService', () => {
136
168
  vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
137
169
  vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['enableReasoning']);
138
170
 
139
- // Mock agent chat config with reasoning enabled
140
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
141
- () =>
142
- ({
143
- enableReasoning: true,
144
- reasoningBudgetToken: 2048,
145
- searchMode: 'off',
146
- }) as any,
147
- );
148
-
149
171
  await chatService.createAssistantMessage({
150
172
  messages,
151
173
  model: 'deepseek-reasoner',
152
174
  provider: 'deepseek',
153
- plugins: [],
175
+ resolvedAgentConfig: createMockResolvedConfig({
176
+ agentConfig: { model: 'deepseek-reasoner', provider: 'deepseek' },
177
+ chatConfig: { enableReasoning: true, reasoningBudgetToken: 2048 },
178
+ }),
154
179
  });
155
180
 
156
181
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -172,20 +197,14 @@ describe('ChatService', () => {
172
197
  vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
173
198
  vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['enableReasoning']);
174
199
 
175
- // Mock agent chat config with reasoning disabled
176
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
177
- () =>
178
- ({
179
- enableReasoning: false,
180
- searchMode: 'off',
181
- }) as any,
182
- );
183
-
184
200
  await chatService.createAssistantMessage({
185
201
  messages,
186
202
  model: 'deepseek-reasoner',
187
203
  provider: 'deepseek',
188
- plugins: [],
204
+ resolvedAgentConfig: createMockResolvedConfig({
205
+ agentConfig: { model: 'deepseek-reasoner', provider: 'deepseek' },
206
+ chatConfig: { enableReasoning: false },
207
+ }),
189
208
  });
190
209
 
191
210
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -207,21 +226,15 @@ describe('ChatService', () => {
207
226
  vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
208
227
  vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['enableReasoning']);
209
228
 
210
- // Mock agent chat config with reasoning enabled but no custom budget
211
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
212
- () =>
213
- ({
214
- enableReasoning: true,
215
- // reasoningBudgetToken is undefined
216
- searchMode: 'off',
217
- }) as any,
218
- );
219
-
220
229
  await chatService.createAssistantMessage({
221
230
  messages,
222
231
  model: 'deepseek-reasoner',
223
232
  provider: 'deepseek',
224
- plugins: [],
233
+ resolvedAgentConfig: createMockResolvedConfig({
234
+ agentConfig: { model: 'deepseek-reasoner', provider: 'deepseek' },
235
+ // enableReasoning is true, but reasoningBudgetToken is undefined
236
+ chatConfig: { enableReasoning: true },
237
+ }),
225
238
  });
226
239
 
227
240
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -243,20 +256,14 @@ describe('ChatService', () => {
243
256
  vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
244
257
  vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['reasoningEffort']);
245
258
 
246
- // Mock agent chat config with reasoning effort set
247
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
248
- () =>
249
- ({
250
- reasoningEffort: 'high',
251
- searchMode: 'off',
252
- }) as any,
253
- );
254
-
255
259
  await chatService.createAssistantMessage({
256
260
  messages,
257
261
  model: 'test-model',
258
262
  provider: 'test-provider',
259
- plugins: [],
263
+ resolvedAgentConfig: createMockResolvedConfig({
264
+ agentConfig: { model: 'test-model', provider: 'test-provider' },
265
+ chatConfig: { reasoningEffort: 'high' },
266
+ }),
260
267
  });
261
268
 
262
269
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -275,20 +282,14 @@ describe('ChatService', () => {
275
282
  vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
276
283
  vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['thinkingBudget']);
277
284
 
278
- // Mock agent chat config with thinking budget set
279
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
280
- () =>
281
- ({
282
- thinkingBudget: 5000,
283
- searchMode: 'off',
284
- }) as any,
285
- );
286
-
287
285
  await chatService.createAssistantMessage({
288
286
  messages,
289
287
  model: 'test-model',
290
288
  provider: 'test-provider',
291
- plugins: [],
289
+ resolvedAgentConfig: createMockResolvedConfig({
290
+ agentConfig: { model: 'test-model', provider: 'test-provider' },
291
+ chatConfig: { thinkingBudget: 5000 },
292
+ }),
292
293
  });
293
294
 
294
295
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -329,9 +330,11 @@ describe('ChatService', () => {
329
330
  const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
330
331
  await chatService.createAssistantMessage({
331
332
  messages,
332
- plugins: [],
333
333
  model: 'gpt-4-vision-preview',
334
334
  provider: 'openai',
335
+ resolvedAgentConfig: createMockResolvedConfig({
336
+ agentConfig: { model: 'gpt-4-vision-preview', provider: 'openai' },
337
+ }),
335
338
  });
336
339
 
337
340
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -378,7 +381,10 @@ describe('ChatService', () => {
378
381
  ] as UIChatMessage[];
379
382
 
380
383
  const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
381
- await chatService.createAssistantMessage({ messages, plugins: [] });
384
+ await chatService.createAssistantMessage({
385
+ messages,
386
+ resolvedAgentConfig: createMockResolvedConfig(),
387
+ });
382
388
 
383
389
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
384
390
  {
@@ -435,8 +441,10 @@ describe('ChatService', () => {
435
441
 
436
442
  await chatService.createAssistantMessage({
437
443
  messages,
438
- plugins: [],
439
444
  model: 'gpt-4-vision-preview',
445
+ resolvedAgentConfig: createMockResolvedConfig({
446
+ agentConfig: { model: 'gpt-4-vision-preview' },
447
+ }),
440
448
  });
441
449
 
442
450
  // Verify the utility functions were called
@@ -525,8 +533,10 @@ describe('ChatService', () => {
525
533
 
526
534
  await chatService.createAssistantMessage({
527
535
  messages,
528
- plugins: [],
529
536
  model: 'gpt-4-vision-preview',
537
+ resolvedAgentConfig: createMockResolvedConfig({
538
+ agentConfig: { model: 'gpt-4-vision-preview' },
539
+ }),
530
540
  });
531
541
 
532
542
  // Verify the utility functions were called
@@ -630,8 +640,10 @@ describe('ChatService', () => {
630
640
 
631
641
  await chatService.createAssistantMessage({
632
642
  messages,
633
- plugins: [],
634
643
  model: 'gpt-4-vision-preview',
644
+ resolvedAgentConfig: createMockResolvedConfig({
645
+ agentConfig: { model: 'gpt-4-vision-preview' },
646
+ }),
635
647
  });
636
648
 
637
649
  // Verify isDesktopLocalStaticServerUrl was called for each image
@@ -730,7 +742,10 @@ describe('ChatService', () => {
730
742
  messages,
731
743
  model: 'gpt-3.5-turbo-1106',
732
744
  top_p: 1,
733
- plugins: ['seo'],
745
+ resolvedAgentConfig: createMockResolvedConfig({
746
+ agentConfig: { model: 'gpt-3.5-turbo-1106' },
747
+ plugins: ['seo'],
748
+ }),
734
749
  });
735
750
 
736
751
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -832,7 +847,10 @@ describe('ChatService', () => {
832
847
  messages,
833
848
  model: 'gpt-3.5-turbo-1106',
834
849
  top_p: 1,
835
- plugins: ['seo'],
850
+ resolvedAgentConfig: createMockResolvedConfig({
851
+ agentConfig: { model: 'gpt-3.5-turbo-1106' },
852
+ plugins: ['seo'],
853
+ }),
836
854
  });
837
855
 
838
856
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -888,7 +906,9 @@ describe('ChatService', () => {
888
906
  messages,
889
907
  model: 'gpt-3.5-turbo-1106',
890
908
  top_p: 1,
891
- plugins: ['ttt'],
909
+ resolvedAgentConfig: createMockResolvedConfig({
910
+ agentConfig: { model: 'gpt-3.5-turbo-1106' },
911
+ }),
892
912
  });
893
913
 
894
914
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -949,7 +969,10 @@ describe('ChatService', () => {
949
969
  mockToolsEngine as any,
950
970
  );
951
971
 
952
- await chatService.createAssistantMessage({ messages, plugins: [] });
972
+ await chatService.createAssistantMessage({
973
+ messages,
974
+ resolvedAgentConfig: createMockResolvedConfig(),
975
+ });
953
976
 
954
977
  // Verify tools were passed to getChatCompletion
955
978
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -1003,7 +1026,10 @@ describe('ChatService', () => {
1003
1026
  mockToolsEngine as any,
1004
1027
  );
1005
1028
 
1006
- await chatService.createAssistantMessage({ messages, plugins: [] });
1029
+ await chatService.createAssistantMessage({
1030
+ messages,
1031
+ resolvedAgentConfig: createMockResolvedConfig(),
1032
+ });
1007
1033
 
1008
1034
  // Verify enabledSearch was set to true
1009
1035
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -1051,7 +1077,10 @@ describe('ChatService', () => {
1051
1077
  mockToolsEngine as any,
1052
1078
  );
1053
1079
 
1054
- await chatService.createAssistantMessage({ messages, plugins: [] });
1080
+ await chatService.createAssistantMessage({
1081
+ messages,
1082
+ resolvedAgentConfig: createMockResolvedConfig(),
1083
+ });
1055
1084
 
1056
1085
  // Verify enabledSearch was not set
1057
1086
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -1342,20 +1371,14 @@ describe('ChatService private methods', () => {
1342
1371
  'disableContextCaching',
1343
1372
  ]);
1344
1373
 
1345
- // Mock agent chat config with context caching disabled
1346
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
1347
- () =>
1348
- ({
1349
- disableContextCaching: true,
1350
- searchMode: 'off',
1351
- }) as any,
1352
- );
1353
-
1354
1374
  await chatService.createAssistantMessage({
1355
1375
  messages,
1356
1376
  model: 'test-model',
1357
1377
  provider: 'test-provider',
1358
- plugins: [],
1378
+ resolvedAgentConfig: createMockResolvedConfig({
1379
+ agentConfig: { model: 'test-model', provider: 'test-provider' },
1380
+ chatConfig: { disableContextCaching: true },
1381
+ }),
1359
1382
  });
1360
1383
 
1361
1384
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -1378,20 +1401,14 @@ describe('ChatService private methods', () => {
1378
1401
  'disableContextCaching',
1379
1402
  ]);
1380
1403
 
1381
- // Mock agent chat config with context caching enabled (default)
1382
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
1383
- () =>
1384
- ({
1385
- disableContextCaching: false,
1386
- searchMode: 'off',
1387
- }) as any,
1388
- );
1389
-
1390
1404
  await chatService.createAssistantMessage({
1391
1405
  messages,
1392
1406
  model: 'test-model',
1393
1407
  provider: 'test-provider',
1394
- plugins: [],
1408
+ resolvedAgentConfig: createMockResolvedConfig({
1409
+ agentConfig: { model: 'test-model', provider: 'test-provider' },
1410
+ chatConfig: { disableContextCaching: false },
1411
+ }),
1395
1412
  });
1396
1413
 
1397
1414
  // enabledContextCaching should not be present in the call
@@ -1407,20 +1424,14 @@ describe('ChatService private methods', () => {
1407
1424
  vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
1408
1425
  vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['reasoningEffort']);
1409
1426
 
1410
- // Mock agent chat config with reasoning effort set
1411
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
1412
- () =>
1413
- ({
1414
- reasoningEffort: 'high',
1415
- searchMode: 'off',
1416
- }) as any,
1417
- );
1418
-
1419
1427
  await chatService.createAssistantMessage({
1420
1428
  messages,
1421
1429
  model: 'test-model',
1422
1430
  provider: 'test-provider',
1423
- plugins: [],
1431
+ resolvedAgentConfig: createMockResolvedConfig({
1432
+ agentConfig: { model: 'test-model', provider: 'test-provider' },
1433
+ chatConfig: { reasoningEffort: 'high' },
1434
+ }),
1424
1435
  });
1425
1436
 
1426
1437
  expect(getChatCompletionSpy).toHaveBeenCalledWith(
@@ -1439,20 +1450,14 @@ describe('ChatService private methods', () => {
1439
1450
  vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
1440
1451
  vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['thinkingBudget']);
1441
1452
 
1442
- // Mock agent chat config with thinking budget set
1443
- vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
1444
- () =>
1445
- ({
1446
- thinkingBudget: 5000,
1447
- searchMode: 'off',
1448
- }) as any,
1449
- );
1450
-
1451
1453
  await chatService.createAssistantMessage({
1452
1454
  messages,
1453
1455
  model: 'test-model',
1454
1456
  provider: 'test-provider',
1455
- plugins: [],
1457
+ resolvedAgentConfig: createMockResolvedConfig({
1458
+ agentConfig: { model: 'test-model', provider: 'test-provider' },
1459
+ chatConfig: { thinkingBudget: 5000 },
1460
+ }),
1456
1461
  });
1457
1462
 
1458
1463
  expect(getChatCompletionSpy).toHaveBeenCalledWith(