@lobehub/lobehub 2.0.0-next.353 → 2.0.0-next.355

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 (144) hide show
  1. package/.agents/skills/add-provider-doc/SKILL.md +90 -0
  2. package/.agents/skills/add-setting-env/SKILL.md +106 -0
  3. package/.agents/skills/debug/SKILL.md +66 -0
  4. package/.agents/skills/desktop/SKILL.md +78 -0
  5. package/.agents/skills/desktop/references/feature-implementation.md +99 -0
  6. package/.agents/skills/desktop/references/local-tools.md +133 -0
  7. package/.agents/skills/desktop/references/menu-config.md +103 -0
  8. package/.agents/skills/desktop/references/window-management.md +143 -0
  9. package/.agents/skills/drizzle/SKILL.md +129 -0
  10. package/.agents/skills/drizzle/references/db-migrations.md +50 -0
  11. package/.agents/skills/hotkey/SKILL.md +90 -0
  12. package/{.cursor/rules/i18n.mdc → .agents/skills/i18n/SKILL.md} +14 -23
  13. package/.agents/skills/linear/SKILL.md +51 -0
  14. package/.agents/skills/microcopy/SKILL.md +83 -0
  15. package/.agents/skills/modal/SKILL.md +102 -0
  16. package/{.cursor/rules/project-structure.mdc → .agents/skills/project-overview/SKILL.md} +65 -37
  17. package/.agents/skills/react/SKILL.md +73 -0
  18. package/.agents/skills/react/references/layout-kit.md +100 -0
  19. package/.agents/skills/recent-data/SKILL.md +108 -0
  20. package/.agents/skills/testing/SKILL.md +89 -0
  21. package/.agents/skills/testing/references/agent-runtime-e2e.md +126 -0
  22. package/.agents/skills/testing/references/db-model-test.md +124 -0
  23. package/.agents/skills/testing/references/desktop-controller-test.md +124 -0
  24. package/.agents/skills/testing/references/electron-ipc-test.md +63 -0
  25. package/.agents/skills/testing/references/zustand-store-action-test.md +150 -0
  26. package/.agents/skills/typescript/SKILL.md +52 -0
  27. package/.agents/skills/zustand/SKILL.md +78 -0
  28. package/.agents/skills/zustand/references/action-patterns.md +125 -0
  29. package/.agents/skills/zustand/references/slice-organization.md +125 -0
  30. package/AGENTS.md +42 -55
  31. package/CHANGELOG.md +60 -0
  32. package/CLAUDE.md +57 -46
  33. package/GEMINI.md +47 -39
  34. package/changelog/v1.json +18 -0
  35. package/locales/en-US/plugin.json +3 -0
  36. package/locales/zh-CN/plugin.json +3 -0
  37. package/package.json +1 -1
  38. package/packages/builtin-tool-memory/src/client/Render/SearchUserMemory/index.tsx +3 -11
  39. package/packages/context-engine/src/engine/messages/MessagesEngine.ts +0 -13
  40. package/packages/context-engine/src/engine/messages/__tests__/MessagesEngine.test.ts +0 -25
  41. package/packages/database/src/models/__tests__/topics/topic.create.test.ts +3 -3
  42. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
  43. package/src/app/[variants]/(main)/agent/features/Conversation/ConversationArea.tsx +4 -0
  44. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
  45. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +1 -1
  46. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/InboxItem.tsx +19 -29
  47. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/List.tsx +1 -1
  48. package/src/app/[variants]/(main)/home/_layout/Body/Agent/ModalProvider.tsx +1 -1
  49. package/src/features/FileViewer/Renderer/PDF/index.tsx +2 -3
  50. package/src/features/ShareModal/SharePdf/PdfPreview.tsx +1 -2
  51. package/src/libs/pdfjs/index.tsx +25 -0
  52. package/src/locales/default/plugin.ts +3 -0
  53. package/src/server/modules/Mecha/ContextEngineering/__tests__/serverMessagesEngine.test.ts +0 -25
  54. package/src/services/chat/chat.test.ts +19 -19
  55. package/src/services/chat/index.ts +8 -3
  56. package/src/services/chat/mecha/agentConfigResolver.test.ts +72 -55
  57. package/src/services/chat/mecha/agentConfigResolver.ts +28 -4
  58. package/src/services/chat/mecha/contextEngineering.test.ts +21 -14
  59. package/src/services/chat/mecha/contextEngineering.ts +12 -0
  60. package/src/services/chat/types.ts +7 -1
  61. package/src/store/chat/agents/createAgentExecutors.ts +15 -4
  62. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +1 -0
  63. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +6 -2
  64. package/src/store/test-coverage.md +5 -5
  65. package/.cursor/rules/add-provider-doc.mdc +0 -183
  66. package/.cursor/rules/add-setting-env.mdc +0 -175
  67. package/.cursor/rules/cursor-rules.mdc +0 -28
  68. package/.cursor/rules/db-migrations.mdc +0 -46
  69. package/.cursor/rules/debug-usage.mdc +0 -86
  70. package/.cursor/rules/desktop-controller-tests.mdc +0 -189
  71. package/.cursor/rules/desktop-feature-implementation.mdc +0 -155
  72. package/.cursor/rules/desktop-local-tools-implement.mdc +0 -81
  73. package/.cursor/rules/desktop-menu-configuration.mdc +0 -209
  74. package/.cursor/rules/desktop-window-management.mdc +0 -301
  75. package/.cursor/rules/drizzle-schema-style-guide.mdc +0 -218
  76. package/.cursor/rules/hotkey.mdc +0 -162
  77. package/.cursor/rules/linear.mdc +0 -53
  78. package/.cursor/rules/microcopy-cn.mdc +0 -158
  79. package/.cursor/rules/microcopy-en.mdc +0 -148
  80. package/.cursor/rules/modal-imperative.mdc +0 -162
  81. package/.cursor/rules/packages/react-layout-kit.mdc +0 -122
  82. package/.cursor/rules/project-introduce.mdc +0 -36
  83. package/.cursor/rules/react.mdc +0 -169
  84. package/.cursor/rules/recent-data-usage.mdc +0 -139
  85. package/.cursor/rules/rules-index.mdc +0 -44
  86. package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +0 -285
  87. package/.cursor/rules/testing-guide/db-model-test.mdc +0 -455
  88. package/.cursor/rules/testing-guide/electron-ipc-test.mdc +0 -80
  89. package/.cursor/rules/testing-guide/testing-guide.mdc +0 -534
  90. package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +0 -574
  91. package/.cursor/rules/typescript.mdc +0 -55
  92. package/.cursor/rules/zustand-action-patterns.mdc +0 -328
  93. package/.cursor/rules/zustand-slice-organization.mdc +0 -308
  94. package/src/libs/pdfjs/pdf.worker.ts +0 -1
  95. package/src/libs/pdfjs/worker.ts +0 -12
  96. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/AGENTS.md +0 -0
  97. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/SKILL.md +0 -0
  98. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-event-handler-refs.md +0 -0
  99. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-use-latest.md +0 -0
  100. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-api-routes.md +0 -0
  101. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-defer-await.md +0 -0
  102. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-dependencies.md +0 -0
  103. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-parallel.md +0 -0
  104. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-suspense-boundaries.md +0 -0
  105. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-barrel-imports.md +0 -0
  106. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-conditional.md +0 -0
  107. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-defer-third-party.md +0 -0
  108. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-dynamic-imports.md +0 -0
  109. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-preload.md +0 -0
  110. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-event-listeners.md +0 -0
  111. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-localstorage-schema.md +0 -0
  112. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-passive-event-listeners.md +0 -0
  113. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-swr-dedup.md +0 -0
  114. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-batch-dom-css.md +0 -0
  115. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-function-results.md +0 -0
  116. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-property-access.md +0 -0
  117. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-storage.md +0 -0
  118. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-combine-iterations.md +0 -0
  119. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-early-exit.md +0 -0
  120. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-hoist-regexp.md +0 -0
  121. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-index-maps.md +0 -0
  122. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-length-check-first.md +0 -0
  123. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-min-max-loop.md +0 -0
  124. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-set-map-lookups.md +0 -0
  125. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-tosorted-immutable.md +0 -0
  126. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-activity.md +0 -0
  127. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-animate-svg-wrapper.md +0 -0
  128. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-conditional-render.md +0 -0
  129. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-content-visibility.md +0 -0
  130. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hoist-jsx.md +0 -0
  131. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hydration-no-flicker.md +0 -0
  132. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-svg-precision.md +0 -0
  133. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-defer-reads.md +0 -0
  134. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-dependencies.md +0 -0
  135. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-derived-state.md +0 -0
  136. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-functional-setstate.md +0 -0
  137. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-lazy-state-init.md +0 -0
  138. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-memo.md +0 -0
  139. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-transitions.md +0 -0
  140. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-after-nonblocking.md +0 -0
  141. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-lru.md +0 -0
  142. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-react.md +0 -0
  143. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-parallel-fetching.md +0 -0
  144. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-serialization.md +0 -0
@@ -155,7 +155,7 @@ describe('ChatService', () => {
155
155
  ]),
156
156
  messages: expect.anything(),
157
157
  }),
158
- undefined,
158
+ expect.anything(),
159
159
  );
160
160
  });
161
161
 
@@ -185,7 +185,7 @@ describe('ChatService', () => {
185
185
  type: 'enabled',
186
186
  },
187
187
  }),
188
- undefined,
188
+ expect.anything(),
189
189
  );
190
190
  });
191
191
 
@@ -214,7 +214,7 @@ describe('ChatService', () => {
214
214
  type: 'disabled',
215
215
  },
216
216
  }),
217
- undefined,
217
+ expect.anything(),
218
218
  );
219
219
  });
220
220
 
@@ -244,7 +244,7 @@ describe('ChatService', () => {
244
244
  type: 'enabled',
245
245
  },
246
246
  }),
247
- undefined,
247
+ expect.anything(),
248
248
  );
249
249
  });
250
250
 
@@ -270,7 +270,7 @@ describe('ChatService', () => {
270
270
  expect.objectContaining({
271
271
  reasoning_effort: 'high',
272
272
  }),
273
- undefined,
273
+ expect.anything(),
274
274
  );
275
275
  });
276
276
 
@@ -296,7 +296,7 @@ describe('ChatService', () => {
296
296
  expect.objectContaining({
297
297
  thinkingBudget: 5000,
298
298
  }),
299
- undefined,
299
+ expect.anything(),
300
300
  );
301
301
  });
302
302
  });
@@ -370,7 +370,7 @@ describe('ChatService', () => {
370
370
  enabledSearch: undefined,
371
371
  tools: undefined,
372
372
  },
373
- undefined,
373
+ expect.anything(),
374
374
  );
375
375
  });
376
376
 
@@ -396,7 +396,7 @@ describe('ChatService', () => {
396
396
  stream: true,
397
397
  tools: undefined,
398
398
  },
399
- undefined,
399
+ expect.anything(),
400
400
  );
401
401
  });
402
402
  });
@@ -494,7 +494,7 @@ describe('ChatService', () => {
494
494
  enabledSearch: undefined,
495
495
  tools: undefined,
496
496
  },
497
- undefined,
497
+ expect.anything(),
498
498
  );
499
499
  });
500
500
 
@@ -583,7 +583,7 @@ describe('ChatService', () => {
583
583
  enabledSearch: undefined,
584
584
  tools: undefined,
585
585
  },
586
- undefined,
586
+ expect.anything(),
587
587
  );
588
588
  });
589
589
 
@@ -781,7 +781,7 @@ describe('ChatService', () => {
781
781
  { content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
782
782
  ],
783
783
  },
784
- undefined,
784
+ expect.anything(),
785
785
  );
786
786
  });
787
787
 
@@ -888,7 +888,7 @@ describe('ChatService', () => {
888
888
  { content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
889
889
  ],
890
890
  },
891
- undefined,
891
+ expect.anything(),
892
892
  );
893
893
  });
894
894
 
@@ -926,7 +926,7 @@ describe('ChatService', () => {
926
926
  { content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
927
927
  ],
928
928
  },
929
- undefined,
929
+ expect.anything(),
930
930
  );
931
931
  });
932
932
  });
@@ -985,7 +985,7 @@ describe('ChatService', () => {
985
985
  }),
986
986
  ]),
987
987
  }),
988
- undefined,
988
+ expect.anything(),
989
989
  );
990
990
  });
991
991
 
@@ -1036,7 +1036,7 @@ describe('ChatService', () => {
1036
1036
  expect.objectContaining({
1037
1037
  enabledSearch: true,
1038
1038
  }),
1039
- undefined,
1039
+ expect.anything(),
1040
1040
  );
1041
1041
  });
1042
1042
 
@@ -1087,7 +1087,7 @@ describe('ChatService', () => {
1087
1087
  expect.objectContaining({
1088
1088
  enabledSearch: undefined,
1089
1089
  }),
1090
- undefined,
1090
+ expect.anything(),
1091
1091
  );
1092
1092
  });
1093
1093
  });
@@ -1385,7 +1385,7 @@ describe('ChatService private methods', () => {
1385
1385
  expect.objectContaining({
1386
1386
  enabledContextCaching: false,
1387
1387
  }),
1388
- undefined,
1388
+ expect.anything(),
1389
1389
  );
1390
1390
  });
1391
1391
 
@@ -1438,7 +1438,7 @@ describe('ChatService private methods', () => {
1438
1438
  expect.objectContaining({
1439
1439
  reasoning_effort: 'high',
1440
1440
  }),
1441
- undefined,
1441
+ expect.anything(),
1442
1442
  );
1443
1443
  });
1444
1444
 
@@ -1464,7 +1464,7 @@ describe('ChatService private methods', () => {
1464
1464
  expect.objectContaining({
1465
1465
  thinkingBudget: 5000,
1466
1466
  }),
1467
- undefined,
1467
+ expect.anything(),
1468
1468
  );
1469
1469
  });
1470
1470
  });
@@ -284,7 +284,7 @@ class ChatService {
284
284
  stream: chatConfig.enableStreaming !== false,
285
285
  tools,
286
286
  },
287
- options,
287
+ { ...options, agentId: targetAgentId, topicId },
288
288
  );
289
289
  };
290
290
 
@@ -314,7 +314,7 @@ class ChatService {
314
314
  };
315
315
 
316
316
  getChatCompletion = async (params: Partial<ChatStreamPayload>, options?: FetchOptions) => {
317
- const { signal, responseAnimation } = options ?? {};
317
+ const { agentId, signal, responseAnimation, topicId } = options ?? {};
318
318
 
319
319
  const { provider = ModelProvider.OpenAI, ...res } = params;
320
320
 
@@ -401,7 +401,12 @@ class ChatService {
401
401
  const traceHeader = createTraceHeader({ ...options?.trace });
402
402
 
403
403
  const headers = await createHeaderWithAuth({
404
- headers: { 'Content-Type': 'application/json', ...traceHeader },
404
+ headers: {
405
+ 'Content-Type': 'application/json',
406
+ ...traceHeader,
407
+ ...(agentId && { 'x-agent-id': agentId }),
408
+ ...(topicId && { 'x-topic-id': topicId }),
409
+ },
405
410
  provider,
406
411
  });
407
412
 
@@ -48,6 +48,31 @@ describe('resolveAgentConfig', () => {
48
48
  expect(result.isBuiltinAgent).toBe(false);
49
49
  });
50
50
 
51
+ it('should treat agent with non-builtin slug as regular agent', () => {
52
+ // Agent has a random slug that is NOT a valid builtin agent slug
53
+ vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
54
+ () => 'sister-religious-mostly-effort',
55
+ );
56
+
57
+ const result = resolveAgentConfig({ agentId: 'test-agent' });
58
+
59
+ expect(result.isBuiltinAgent).toBe(false);
60
+ expect(result.slug).toBeUndefined();
61
+ expect(result.plugins).toEqual(['plugin-a', 'plugin-b']);
62
+ });
63
+
64
+ it('should treat agent with random slug as regular agent', () => {
65
+ // Another example of random/custom slug
66
+ vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
67
+ () => 'my-custom-agent-slug',
68
+ );
69
+
70
+ const result = resolveAgentConfig({ agentId: 'test-agent' });
71
+
72
+ expect(result.isBuiltinAgent).toBe(false);
73
+ expect(result.slug).toBeUndefined();
74
+ });
75
+
51
76
  it('should return empty array when agent config has no plugins', () => {
52
77
  vi.spyOn(agentSelectors.agentSelectors, 'getAgentConfigById').mockReturnValue(
53
78
  () =>
@@ -668,6 +693,29 @@ describe('resolveAgentConfig', () => {
668
693
  expect(result.agentConfig.systemRole.trim()).toBe('You are a helpful assistant');
669
694
  expect(result.chatConfig.enableHistoryCount).toBe(false);
670
695
  });
696
+
697
+ it('should not duplicate injection when page-agent itself is used in page scope', () => {
698
+ // page-agent is a builtin agent with slug 'page-agent'
699
+ vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
700
+ () => 'page-agent',
701
+ );
702
+
703
+ vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
704
+ plugins: [PageAgentIdentifier],
705
+ systemRole: 'Page agent system prompt',
706
+ });
707
+
708
+ const result = resolveAgentConfig({
709
+ agentId: 'page-agent-id',
710
+ scope: 'page',
711
+ });
712
+
713
+ // page-agent should NOT have its tools/systemRole injected again
714
+ expect(result.plugins.filter((p) => p === PageAgentIdentifier)).toHaveLength(1);
715
+ expect(result.agentConfig.systemRole).toBe('Page agent system prompt');
716
+ expect(result.isBuiltinAgent).toBe(true);
717
+ expect(result.slug).toBe('page-agent');
718
+ });
671
719
  });
672
720
 
673
721
  describe('supervisor agent (detected via groupId)', () => {
@@ -695,13 +743,11 @@ describe('resolveAgentConfig', () => {
695
743
  });
696
744
 
697
745
  describe('supervisor with own slug (priority check)', () => {
698
- // This tests the fix for LOBE-4127: When supervisor agent has its own slug,
699
- // it should still use 'group-supervisor' slug when in group scope
700
-
746
+ // When supervisor agent has its own slug, it should still use 'group-supervisor' slug when in group scope
701
747
  it('should use group-supervisor slug even when agent has its own slug in group scope', () => {
702
748
  // Supervisor agent has its own slug (e.g., from being a builtin agent)
703
749
  vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
704
- () => 'some-agent-slug',
750
+ () => 'agent-builder',
705
751
  );
706
752
 
707
753
  // Mock: groupById returns the group
@@ -734,54 +780,6 @@ describe('resolveAgentConfig', () => {
734
780
  expect(result.slug).toBe('group-supervisor');
735
781
  expect(result.plugins).toContain(GroupManagementIdentifier);
736
782
  });
737
-
738
- it('should use agent own slug when scope is not group', () => {
739
- // Supervisor agent has its own slug
740
- vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
741
- () => 'some-agent-slug',
742
- );
743
-
744
- // Mock: groupById returns the group
745
- vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
746
- () => mockGroupWithSupervisor as any,
747
- );
748
-
749
- vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
750
- plugins: ['agent-specific-plugin'],
751
- systemRole: 'Agent specific system role',
752
- });
753
-
754
- const result = resolveAgentConfig({
755
- agentId: 'supervisor-agent-id',
756
- groupId: 'group-123',
757
- scope: 'main', // Not 'group' scope
758
- });
759
-
760
- // Should use agent's own slug since scope is not 'group'
761
- expect(result.isBuiltinAgent).toBe(true);
762
- expect(result.slug).toBe('some-agent-slug');
763
- });
764
-
765
- it('should use agent own slug when groupId is not provided', () => {
766
- // Supervisor agent has its own slug
767
- vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
768
- () => 'some-agent-slug',
769
- );
770
-
771
- vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
772
- plugins: ['agent-specific-plugin'],
773
- systemRole: 'Agent specific system role',
774
- });
775
-
776
- const result = resolveAgentConfig({
777
- agentId: 'supervisor-agent-id',
778
- scope: 'group', // Even with group scope, no groupId
779
- });
780
-
781
- // Should use agent's own slug since no groupId
782
- expect(result.isBuiltinAgent).toBe(true);
783
- expect(result.slug).toBe('some-agent-slug');
784
- });
785
783
  });
786
784
 
787
785
  it('should detect supervisor agent using groupId for direct lookup', () => {
@@ -872,6 +870,25 @@ describe('resolveAgentConfig', () => {
872
870
  expect(result.plugins).toEqual(['plugin-a', 'plugin-b']); // Falls back to agent config plugins
873
871
  });
874
872
 
873
+ it('should treat as regular agent when scope is not group even with groupId', () => {
874
+ // Mock: groupById returns the group (supervisor agent exists)
875
+ vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
876
+ () => mockGroupWithSupervisor as any,
877
+ );
878
+
879
+ // groupId is provided but scope is 'main', not 'group'
880
+ const result = resolveAgentConfig({
881
+ agentId: 'supervisor-agent-id',
882
+ groupId: 'group-123',
883
+ scope: 'main', // Not 'group' scope
884
+ });
885
+
886
+ // Should NOT be identified as group-supervisor because scope !== 'group'
887
+ expect(result.isBuiltinAgent).toBe(false);
888
+ expect(result.slug).toBeUndefined();
889
+ expect(result.plugins).toEqual(['plugin-a', 'plugin-b']);
890
+ });
891
+
875
892
  it('should treat as regular agent when agentId does not match group supervisorAgentId', () => {
876
893
  // Mock: groupById returns the group
877
894
  vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
@@ -1016,7 +1033,7 @@ describe('resolveAgentConfig', () => {
1016
1033
 
1017
1034
  it('should filter lobe-gtd for builtin agent when isSubTask is true', () => {
1018
1035
  vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
1019
- () => 'some-builtin-slug',
1036
+ () => 'agent-builder',
1020
1037
  );
1021
1038
  vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
1022
1039
  plugins: ['lobe-gtd', 'runtime-plugin'],
@@ -1034,7 +1051,7 @@ describe('resolveAgentConfig', () => {
1034
1051
 
1035
1052
  it('should keep lobe-gtd for builtin agent when isSubTask is false', () => {
1036
1053
  vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
1037
- () => 'some-builtin-slug',
1054
+ () => 'agent-builder',
1038
1055
  );
1039
1056
  vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
1040
1057
  plugins: ['lobe-gtd', 'runtime-plugin'],
@@ -1089,7 +1106,7 @@ describe('resolveAgentConfig', () => {
1089
1106
 
1090
1107
  it('should return empty plugins for builtin agent when disableTools is true', () => {
1091
1108
  vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
1092
- () => 'some-builtin-slug',
1109
+ () => 'agent-builder',
1093
1110
  );
1094
1111
  vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
1095
1112
  plugins: ['runtime-plugin-1', 'runtime-plugin-2'],
@@ -1,4 +1,8 @@
1
- import { BUILTIN_AGENT_SLUGS, getAgentRuntimeConfig } from '@lobechat/builtin-agents';
1
+ import {
2
+ BUILTIN_AGENT_SLUGS,
3
+ type BuiltinAgentSlug,
4
+ getAgentRuntimeConfig,
5
+ } from '@lobechat/builtin-agents';
2
6
  import { PageAgentIdentifier } from '@lobechat/builtin-tool-page-agent';
3
7
  import {
4
8
  type LobeAgentChatConfig,
@@ -15,6 +19,18 @@ import { agentGroupByIdSelectors, agentGroupSelectors } from '@/store/agentGroup
15
19
 
16
20
  const log = debug('mecha:agentConfigResolver');
17
21
 
22
+ /**
23
+ * Set of valid builtin agent slugs for O(1) lookup
24
+ */
25
+ const VALID_BUILTIN_SLUGS = new Set<string>(Object.values(BUILTIN_AGENT_SLUGS));
26
+
27
+ /**
28
+ * Check if a slug is a valid builtin agent slug
29
+ */
30
+ const isBuiltinAgentSlug = (slug: string): slug is BuiltinAgentSlug => {
31
+ return VALID_BUILTIN_SLUGS.has(slug);
32
+ };
33
+
18
34
  /**
19
35
  * Applies params adjustments based on chatConfig settings.
20
36
  *
@@ -187,12 +203,20 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
187
203
 
188
204
  // If not identified as supervisor, check agent store for slug
189
205
  if (!slug) {
190
- slug = agentSelectors.getAgentSlugById(agentId)(agentStoreState) ?? undefined;
191
- log('slug from agentStore: %s (agentId: %s)', slug, agentId);
206
+ const storeSlug = agentSelectors.getAgentSlugById(agentId)(agentStoreState) ?? undefined;
207
+ log('slug from agentStore: %s (agentId: %s)', storeSlug, agentId);
208
+
209
+ // Only use the slug if it's a valid builtin agent slug
210
+ // Regular agents may have random slugs that should be ignored
211
+ if (storeSlug && isBuiltinAgentSlug(storeSlug)) {
212
+ slug = storeSlug;
213
+ } else if (storeSlug) {
214
+ log('slug %s is not a valid builtin agent slug, treating as regular agent', storeSlug);
215
+ }
192
216
  }
193
217
 
194
218
  if (!slug) {
195
- log('agentId %s is not a builtin agent (no slug found)', agentId);
219
+ log('agentId %s is not a builtin agent (no valid builtin slug found)', agentId);
196
220
  // Regular agent - use provided plugins if available, fallback to agent's plugins
197
221
  const finalPlugins = plugins && plugins.length > 0 ? plugins : basePlugins;
198
222
 
@@ -583,7 +583,7 @@ describe('contextEngineering', () => {
583
583
  });
584
584
 
585
585
  describe('Message preprocessing processors', () => {
586
- it('should truncate message history when enabled', async () => {
586
+ it('should keep all messages (no history truncation)', async () => {
587
587
  const messages: UIChatMessage[] = [
588
588
  {
589
589
  role: 'user',
@@ -626,13 +626,12 @@ describe('contextEngineering', () => {
626
626
  messages,
627
627
  model: 'gpt-4',
628
628
  provider: 'openai',
629
- enableHistoryCount: true,
630
- historyCount: 4, // Should keep last 2 messages
631
629
  });
632
630
 
633
- // Should only keep the last 2 messages
634
- expect(result).toHaveLength(4);
631
+ // Should keep all messages
632
+ expect(result).toHaveLength(5);
635
633
  expect(result).toEqual([
634
+ { content: 'Message 1', role: 'user' },
636
635
  { content: 'Response 1', role: 'assistant' },
637
636
  { content: 'Message 2', role: 'user' },
638
637
  { content: 'Response 2', role: 'assistant' },
@@ -704,7 +703,7 @@ describe('contextEngineering', () => {
704
703
  ]);
705
704
  });
706
705
 
707
- it('should combine all preprocessing steps correctly', async () => {
706
+ it('should combine system role and input template correctly', async () => {
708
707
  const messages: UIChatMessage[] = [
709
708
  {
710
709
  role: 'user',
@@ -735,16 +734,18 @@ describe('contextEngineering', () => {
735
734
  provider: 'openai',
736
735
  systemRole: 'System instructions.',
737
736
  inputTemplate: 'Processed: {{text}}',
738
- enableHistoryCount: true,
739
- historyCount: 2, // Should keep last 1 message
740
737
  });
741
738
 
742
- // System role should be first
739
+ // System role should be first, followed by all messages with input template applied to user messages
743
740
  expect(result).toEqual([
744
741
  {
745
742
  content: 'System instructions.',
746
743
  role: 'system',
747
744
  },
745
+ {
746
+ content: 'Processed: Old message 1',
747
+ role: 'user',
748
+ },
748
749
  {
749
750
  role: 'assistant',
750
751
  content: 'Old response',
@@ -782,7 +783,7 @@ describe('contextEngineering', () => {
782
783
  ]);
783
784
  });
784
785
 
785
- it('should handle history truncation with system role injection correctly', async () => {
786
+ it('should handle system role injection with all messages (no history truncation)', async () => {
786
787
  const messages: UIChatMessage[] = [
787
788
  {
788
789
  role: 'user',
@@ -812,18 +813,24 @@ describe('contextEngineering', () => {
812
813
  model: 'gpt-4',
813
814
  provider: 'openai',
814
815
  systemRole: 'System role here.',
815
- enableHistoryCount: true,
816
- historyCount: 1, // Should keep only 1 message
817
816
  });
818
817
 
819
- // Should have system role + 1 truncated message
818
+ // Should have system role + all messages
820
819
  expect(result).toEqual([
821
820
  {
822
821
  content: 'System role here.',
823
822
  role: 'system',
824
823
  },
825
824
  {
826
- content: 'Message 3', // Only the last message should remain
825
+ content: 'Message 1',
826
+ role: 'user',
827
+ },
828
+ {
829
+ content: 'Message 2',
830
+ role: 'user',
831
+ },
832
+ {
833
+ content: 'Message 3',
827
834
  role: 'user',
828
835
  },
829
836
  ]);
@@ -394,6 +394,18 @@ export const contextEngineering = async ({
394
394
  ...(gtdConfig && { gtd: gtdConfig }),
395
395
  });
396
396
 
397
+ log('Input messages count: %d', messages.length);
398
+
397
399
  const result = await engine.process();
400
+
401
+ log('Output messages count: %d', result.messages.length);
402
+
403
+ if (messages.length > 0 && result.messages.length === 0) {
404
+ log(
405
+ 'WARNING: Messages were reduced to 0! Input messages: %o',
406
+ messages.map((m) => ({ id: m.id, role: m.role })),
407
+ );
408
+ }
409
+
398
410
  return result.messages;
399
411
  };
@@ -1,12 +1,18 @@
1
1
  import { type FetchSSEOptions } from '@lobechat/fetch-sse';
2
- import { type RuntimeInitialContext, type RuntimeStepContext, type TracePayload } from '@lobechat/types';
2
+ import {
3
+ type RuntimeInitialContext,
4
+ type RuntimeStepContext,
5
+ type TracePayload,
6
+ } from '@lobechat/types';
3
7
 
4
8
  export interface FetchOptions extends FetchSSEOptions {
9
+ agentId?: string;
5
10
  historySummary?: string;
6
11
  /** Initial context for page editor (captured at operation start) */
7
12
  initialContext?: RuntimeInitialContext;
8
13
  signal?: AbortSignal | undefined;
9
14
  /** Step context for page editor (updated each step) */
10
15
  stepContext?: RuntimeStepContext;
16
+ topicId?: string;
11
17
  trace?: TracePayload;
12
18
  }
@@ -22,10 +22,9 @@ import type { ChatToolPayload, ConversationContext, CreateMessageParams } from '
22
22
  import debug from 'debug';
23
23
  import pMap from 'p-map';
24
24
 
25
- import type { ResolvedAgentConfig } from '@/services/chat/mecha';
26
-
27
25
  import { LOADING_FLAT } from '@/const/message';
28
26
  import { aiAgentService } from '@/services/aiAgent';
27
+ import type { ResolvedAgentConfig } from '@/services/chat/mecha';
29
28
  import { agentByIdSelectors } from '@/store/agent/selectors';
30
29
  import { getAgentStoreState } from '@/store/agent/store';
31
30
  import type { ChatStore } from '@/store/chat/store';
@@ -96,7 +95,12 @@ export const createAgentExecutors = (context: {
96
95
  const llmPayload = (instruction as AgentInstructionCallLlm)
97
96
  .payload as GeneralAgentCallLLMInstructionPayload;
98
97
 
99
- log(`${stagePrefix} Starting session`);
98
+ log(
99
+ `${stagePrefix} Starting session. Input: state.messages=%d, llmPayload.messages=%d, messageKey=%s`,
100
+ state.messages.length,
101
+ llmPayload.messages.length,
102
+ context.messageKey,
103
+ );
100
104
 
101
105
  let assistantMessageId: string;
102
106
 
@@ -184,6 +188,12 @@ export const createAgentExecutors = (context: {
184
188
  // Get latest messages from store (already updated by internal_fetchAIChatMessage)
185
189
  const latestMessages = context.get().dbMessagesMap[context.messageKey] || [];
186
190
 
191
+ log(
192
+ `${stagePrefix} After fetch: dbMessagesMap[${context.messageKey}]=%d messages, available keys=%o`,
193
+ latestMessages.length,
194
+ Object.keys(context.get().dbMessagesMap),
195
+ );
196
+
187
197
  // Get updated assistant message to extract usage/cost information
188
198
  const assistantMessage = latestMessages.find((m) => m.id === assistantMessageId);
189
199
 
@@ -206,10 +216,11 @@ export const createAgentExecutors = (context: {
206
216
  }
207
217
 
208
218
  log(
209
- '[%s:%d] call_llm completed, finishType: %s',
219
+ '[%s:%d] call_llm completed, finishType: %s, outputMessages: %d',
210
220
  state.operationId,
211
221
  state.stepCount,
212
222
  finishType,
223
+ latestMessages.length,
213
224
  );
214
225
 
215
226
  // Accumulate usage and cost to state
@@ -259,6 +259,7 @@ export const conversationLifecycle: StateCreator<
259
259
  if (data?.topics) {
260
260
  const pageSize = systemStatusSelectors.topicPageSize(useGlobalStore.getState());
261
261
  get().internal_updateTopics(operationContext.agentId, {
262
+ groupId: operationContext.groupId,
262
263
  items: data.topics.items,
263
264
  pageSize,
264
265
  total: data.topics.total,
@@ -757,20 +757,24 @@ export const streamingExecutor: StateCreator<
757
757
  nextContext = { ...nextContext, stepContext };
758
758
 
759
759
  log(
760
- '[internal_execAgentRuntime][step-%d]: phase=%s, status=%s, stepContext=%O',
760
+ '[internal_execAgentRuntime][step-%d]: phase=%s, status=%s, state.messages=%d, dbMessagesMap[%s]=%d, stepContext=%O',
761
761
  stepCount,
762
762
  nextContext.phase,
763
763
  state.status,
764
+ state.messages.length,
765
+ messageKey,
766
+ currentDBMessages.length,
764
767
  stepContext,
765
768
  );
766
769
 
767
770
  const result = await runtime.step(state, nextContext);
768
771
 
769
772
  log(
770
- '[internal_execAgentRuntime] Step %d completed, events: %d, newStatus=%s',
773
+ '[internal_execAgentRuntime] Step %d completed, events: %d, newStatus=%s, newState.messages=%d',
771
774
  stepCount,
772
775
  result.events.length,
773
776
  result.newState.status,
777
+ result.newState.messages.length,
774
778
  );
775
779
 
776
780
  // After parallel tool batch completes, refresh messages to ensure all tool results are synced