@lobehub/lobehub 2.0.0-next.286 → 2.0.0-next.288

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 (72) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/apps/desktop/src/main/const/theme.ts +0 -3
  3. package/apps/desktop/src/main/core/browser/Browser.ts +1 -1
  4. package/apps/desktop/src/main/core/browser/WindowThemeManager.ts +3 -2
  5. package/apps/desktop/src/main/core/browser/__tests__/Browser.test.ts +0 -1
  6. package/apps/desktop/src/main/core/browser/__tests__/WindowThemeManager.test.ts +8 -5
  7. package/changelog/v1.json +14 -0
  8. package/locales/en-US/plugin.json +3 -5
  9. package/locales/zh-CN/plugin.json +3 -5
  10. package/locales/zh-CN/tool.json +2 -0
  11. package/package.json +1 -1
  12. package/packages/builtin-agents/src/agents/group-supervisor/index.ts +12 -1
  13. package/packages/builtin-agents/src/agents/group-supervisor/systemRole.ts +0 -7
  14. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/EditLocalFile/index.tsx +93 -0
  15. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/GlobLocalFiles/index.tsx +73 -0
  16. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/GrepContent/index.tsx +69 -0
  17. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ListLocalFiles/index.tsx +68 -0
  18. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ReadLocalFile/index.tsx +74 -0
  19. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/SearchLocalFiles/index.tsx +70 -0
  20. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/WriteLocalFile/index.tsx +57 -0
  21. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/index.ts +14 -0
  22. package/packages/builtin-tool-cloud-sandbox/src/client/Render/WriteFile/index.tsx +54 -35
  23. package/packages/builtin-tool-cloud-sandbox/src/client/components/FilePathDisplay.tsx +52 -0
  24. package/packages/builtin-tool-group-management/src/client/Inspector/ExecuteTasks/index.tsx +90 -0
  25. package/packages/builtin-tool-group-management/src/client/Inspector/index.ts +2 -0
  26. package/packages/builtin-tool-group-management/src/client/Intervention/ExecuteTasks.tsx +237 -0
  27. package/packages/builtin-tool-group-management/src/client/Intervention/index.ts +4 -1
  28. package/packages/builtin-tool-group-management/src/client/Render/index.ts +1 -1
  29. package/packages/builtin-tool-group-management/src/client/Streaming/ExecuteTask/index.tsx +69 -0
  30. package/packages/builtin-tool-group-management/src/client/Streaming/ExecuteTasks/index.tsx +87 -0
  31. package/packages/builtin-tool-group-management/src/client/Streaming/index.ts +4 -0
  32. package/packages/builtin-tool-group-management/src/executor.test.ts +8 -311
  33. package/packages/builtin-tool-group-management/src/executor.ts +5 -160
  34. package/packages/builtin-tool-group-management/src/manifest.ts +50 -94
  35. package/packages/builtin-tool-group-management/src/systemRole.ts +251 -172
  36. package/packages/builtin-tool-group-management/src/types.ts +29 -40
  37. package/packages/context-engine/src/engine/messages/MessagesEngine.ts +6 -4
  38. package/packages/context-engine/src/engine/messages/types.ts +4 -4
  39. package/packages/context-engine/src/processors/GroupRoleTransform.ts +261 -0
  40. package/packages/context-engine/src/processors/__tests__/GroupRoleTransform.test.ts +553 -0
  41. package/packages/context-engine/src/processors/index.ts +2 -2
  42. package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +4 -16
  43. package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +23 -28
  44. package/packages/desktop-bridge/src/index.ts +3 -0
  45. package/packages/prompts/src/prompts/agentGroup/__snapshots__/index.test.ts.snap +0 -7
  46. package/packages/prompts/src/prompts/agentGroup/groupContext.ts +0 -7
  47. package/src/app/[variants]/(main)/group/features/Conversation/AgentWelcome/OpeningQuestions.tsx +4 -8
  48. package/src/app/[variants]/(main)/group/features/Conversation/MainChatInput/GroupChat.tsx +0 -3
  49. package/src/app/[variants]/(main)/group/features/Conversation/useGroupContext.ts +3 -0
  50. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +15 -2
  51. package/src/features/ChatInput/Desktop/index.tsx +1 -3
  52. package/src/features/Conversation/store/slices/message/action/crud.ts +2 -2
  53. package/src/features/ElectronTitlebar/Connection/ConnectionMode.tsx +2 -2
  54. package/src/features/ElectronTitlebar/SimpleTitleBar.tsx +1 -2
  55. package/src/features/ElectronTitlebar/index.tsx +2 -2
  56. package/src/hooks/useUserAvatar.test.ts +23 -4
  57. package/src/locales/default/plugin.ts +3 -5
  58. package/src/locales/default/tool.ts +3 -0
  59. package/src/services/chat/mecha/agentConfigResolver.test.ts +160 -0
  60. package/src/services/chat/mecha/agentConfigResolver.ts +15 -3
  61. package/src/services/chat/mecha/contextEngineering.ts +2 -1
  62. package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +4 -2
  63. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +2 -0
  64. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +1 -18
  65. package/src/store/chat/slices/message/selectors/displayMessage.test.ts +24 -0
  66. package/src/store/chat/slices/message/selectors/displayMessage.ts +6 -1
  67. package/src/store/chat/slices/topic/action.test.ts +10 -4
  68. package/src/store/chat/slices/topic/action.ts +3 -2
  69. package/src/store/electron/selectors/sync.ts +17 -1
  70. package/packages/context-engine/src/processors/GroupMessageSender.ts +0 -138
  71. package/packages/context-engine/src/processors/__tests__/GroupMessageSender.test.ts +0 -274
  72. package/src/features/ElectronTitlebar/const.ts +0 -1
@@ -7,13 +7,6 @@ exports[`GroupContextInjector > Edge Cases > should handle empty members array 1
7
7
  You are "", acting as a in the multi-agent group "Empty Group".
8
8
  Your internal agent ID is (for system use only, never expose to users).
9
9
 
10
- <critical_output_rules>
11
- IMPORTANT: Your responses must contain ONLY your actual reply content.
12
- - Messages in conversation history start with '<speaker name="..." />' - this identifies who sent each message
13
- - NEVER start your response with '<speaker' tag - the system adds this automatically
14
- - Just output your actual response content directly
15
- </critical_output_rules>
16
-
17
10
  <group_description>The following describes the purpose and goals of this agent group:
18
11
 
19
12
  Empty group description
@@ -22,6 +15,29 @@ Empty group description
22
15
  <group_participants>The following agents are available in this group:
23
16
 
24
17
 
18
+ </group_participants>
19
+
20
+ <identity_rules>
21
+ - NEVER expose or display agent IDs to users - always refer to agents by their names
22
+ </identity_rules>
23
+ </group_context>"
24
+ `;
25
+
26
+ exports[`GroupContextInjector > Identity Rules Section > should always include identity rules 1`] = `
27
+ "Base prompt.
28
+
29
+ <group_context>
30
+ You are "", acting as a in the multi-agent group "".
31
+ Your internal agent ID is (for system use only, never expose to users).
32
+
33
+ <group_description>The following describes the purpose and goals of this agent group:
34
+
35
+
36
+ </group_description>
37
+
38
+ <group_participants>The following agents are available in this group:
39
+
40
+
25
41
  </group_participants>
26
42
 
27
43
  <identity_rules>
@@ -37,13 +53,6 @@ exports[`GroupContextInjector > Variable Replacement > should handle config with
37
53
  You are "", acting as a in the multi-agent group "Test Group".
38
54
  Your internal agent ID is (for system use only, never expose to users).
39
55
 
40
- <critical_output_rules>
41
- IMPORTANT: Your responses must contain ONLY your actual reply content.
42
- - Messages in conversation history start with '<speaker name="..." />' - this identifies who sent each message
43
- - NEVER start your response with '<speaker' tag - the system adds this automatically
44
- - Just output your actual response content directly
45
- </critical_output_rules>
46
-
47
56
  <group_description>The following describes the purpose and goals of this agent group:
48
57
 
49
58
  Test group description
@@ -67,13 +76,6 @@ exports[`GroupContextInjector > Variable Replacement > should handle config with
67
76
  You are "Editor", acting as a participant in the multi-agent group "".
68
77
  Your internal agent ID is agt_editor (for system use only, never expose to users).
69
78
 
70
- <critical_output_rules>
71
- IMPORTANT: Your responses must contain ONLY your actual reply content.
72
- - Messages in conversation history start with '<speaker name="..." />' - this identifies who sent each message
73
- - NEVER start your response with '<speaker' tag - the system adds this automatically
74
- - Just output your actual response content directly
75
- </critical_output_rules>
76
-
77
79
  <group_description>The following describes the purpose and goals of this agent group:
78
80
 
79
81
 
@@ -97,13 +99,6 @@ exports[`GroupContextInjector > Variable Replacement > should handle empty confi
97
99
  You are "", acting as a in the multi-agent group "".
98
100
  Your internal agent ID is (for system use only, never expose to users).
99
101
 
100
- <critical_output_rules>
101
- IMPORTANT: Your responses must contain ONLY your actual reply content.
102
- - Messages in conversation history start with '<speaker name="..." />' - this identifies who sent each message
103
- - NEVER start your response with '<speaker' tag - the system adds this automatically
104
- - Just output your actual response content directly
105
- </critical_output_rules>
106
-
107
102
  <group_description>The following describes the purpose and goals of this agent group:
108
103
 
109
104
 
@@ -7,3 +7,6 @@ export {
7
7
  locales,
8
8
  RouteVariants,
9
9
  } from './routeVariants';
10
+
11
+ // Desktop window constants
12
+ export const TITLE_BAR_HEIGHT = 38;
@@ -65,13 +65,6 @@ exports[`groupContextTemplate > should match snapshot 1`] = `
65
65
  "You are "{{AGENT_NAME}}", acting as a {{AGENT_ROLE}} in the multi-agent group "{{GROUP_TITLE}}".
66
66
  Your internal agent ID is {{AGENT_ID}} (for system use only, never expose to users).
67
67
 
68
- <critical_output_rules>
69
- IMPORTANT: Your responses must contain ONLY your actual reply content.
70
- - Messages in conversation history start with '<speaker name="..." />' - this identifies who sent each message
71
- - NEVER start your response with '<speaker' tag - the system adds this automatically
72
- - Just output your actual response content directly
73
- </critical_output_rules>
74
-
75
68
  <group_description>The following describes the purpose and goals of this agent group:
76
69
 
77
70
  {{SYSTEM_PROMPT}}
@@ -19,13 +19,6 @@ export interface GroupContextMemberInfo {
19
19
  export const groupContextTemplate = `You are "{{AGENT_NAME}}", acting as a {{AGENT_ROLE}} in the multi-agent group "{{GROUP_TITLE}}".
20
20
  Your internal agent ID is {{AGENT_ID}} (for system use only, never expose to users).
21
21
 
22
- <critical_output_rules>
23
- IMPORTANT: Your responses must contain ONLY your actual reply content.
24
- - Messages in conversation history start with '<speaker name="..." />' - this identifies who sent each message
25
- - NEVER start your response with '<speaker' tag - the system adds this automatically
26
- - Just output your actual response content directly
27
- </critical_output_rules>
28
-
29
22
  <group_description>The following describes the purpose and goals of this agent group:
30
23
 
31
24
  {{SYSTEM_PROMPT}}
@@ -1,13 +1,11 @@
1
1
  'use client';
2
2
 
3
3
  import { Block, Flexbox } from '@lobehub/ui';
4
- import { createStaticStyles , responsive } from 'antd-style';
4
+ import { createStaticStyles, responsive } from 'antd-style';
5
5
  import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
- import { useChatStore } from '@/store/chat';
9
-
10
- // import { useSend } from '../../features/ChatInput/useSend';
8
+ import { useConversationStore } from '@/features/Conversation';
11
9
 
12
10
  const styles = createStaticStyles(({ css, cssVar }) => ({
13
11
  card: css`
@@ -38,8 +36,7 @@ interface OpeningQuestionsProps {
38
36
 
39
37
  const OpeningQuestions = memo<OpeningQuestionsProps>(({ mobile, questions }) => {
40
38
  const { t } = useTranslation('welcome');
41
- const [updateMessageInput] = useChatStore((s) => [s.updateMessageInput]);
42
- // const { send: sendMessage } = useSend();
39
+ const [sendMessage] = useConversationStore((s) => [s.sendMessage]);
43
40
 
44
41
  return (
45
42
  <div className={styles.container}>
@@ -52,8 +49,7 @@ const OpeningQuestions = memo<OpeningQuestionsProps>(({ mobile, questions }) =>
52
49
  clickable
53
50
  key={question}
54
51
  onClick={() => {
55
- updateMessageInput(question);
56
- // sendMessage({ isWelcomeQuestion: true });
52
+ sendMessage({ message: question });
57
53
  }}
58
54
  paddingBlock={8}
59
55
  paddingInline={12}
@@ -92,9 +92,6 @@ const Desktop = memo((props: { targetMemberId?: string }) => {
92
92
  onMarkdownContentChange={(content) => {
93
93
  useChatStore.setState({ inputMessage: content });
94
94
  }}
95
- onSend={() => {
96
- // send({ targetMemberId: props.targetMemberId });
97
- }}
98
95
  rightActions={isDMPortal ? [] : rightActions}
99
96
  // sendButtonProps={{ disabled, generating, onStop: stop }}
100
97
  sendMenu={{
@@ -23,9 +23,12 @@ export function useGroupContext(): ConversationContext {
23
23
  const supervisorAgentId = currentGroup?.supervisorAgentId;
24
24
 
25
25
  // Group context uses supervisorAgentId as agentId for message storage
26
+ // When in group mode (not group_agent thread mode), the supervisor is responding
27
+ // so we mark isSupervisor: true for proper UI rendering
26
28
  return {
27
29
  agentId: supervisorAgentId || '',
28
30
  groupId: groupId ?? undefined,
31
+ isSupervisor: !threadId, // Supervisor responds in main group chat, not in agent threads
29
32
  scope: threadId ? 'group_agent' : 'group',
30
33
  threadId,
31
34
  topicId,
@@ -26,6 +26,8 @@ import {
26
26
  import { useMemo } from 'react';
27
27
  import { useTranslation } from 'react-i18next';
28
28
 
29
+ import { useElectronStore } from '@/store/electron';
30
+ import { electronSyncSelectors } from '@/store/electron/selectors';
29
31
  import { SettingsTabs } from '@/store/global/initialState';
30
32
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
31
33
  import { useUserStore } from '@/store/user';
@@ -63,13 +65,24 @@ export const useCategory = () => {
63
65
  userProfileSelectors.userAvatar(s),
64
66
  userProfileSelectors.nickName(s),
65
67
  ]);
68
+ const remoteServerUrl = useElectronStore(electronSyncSelectors.remoteServerUrl);
69
+
70
+ // Process avatar URL for desktop environment
71
+ const avatarUrl = useMemo(() => {
72
+ if (!avatar) return undefined;
73
+ if (isDesktop && avatar.startsWith('/') && remoteServerUrl) {
74
+ return remoteServerUrl + avatar;
75
+ }
76
+ return avatar;
77
+ }, [avatar, remoteServerUrl]);
78
+
66
79
  const categoryGroups: CategoryGroup[] = useMemo(() => {
67
80
  const groups: CategoryGroup[] = [];
68
81
 
69
82
  // 个人资料组 - Profile 相关设置
70
83
  const profileItems: CategoryItem[] = [
71
84
  {
72
- icon: avatar ? <Avatar avatar={avatar} shape={'square'} size={26} /> : UserCircle,
85
+ icon: avatarUrl ? <Avatar avatar={avatarUrl} shape={'square'} size={26} /> : UserCircle,
73
86
  key: SettingsTabs.Profile,
74
87
  label: username ? username : tAuth('tab.profile'),
75
88
  },
@@ -227,7 +240,7 @@ export const useCategory = () => {
227
240
  showAiImage,
228
241
  showApiKeyManage,
229
242
  isLoginWithClerk,
230
- avatar,
243
+ avatarUrl,
231
244
  username,
232
245
  ]);
233
246
 
@@ -95,9 +95,7 @@ const DesktopChatInput = memo<DesktopChatInputProps>(
95
95
  <ChatInputActionBar
96
96
  left={<ActionBar dropdownPlacement={dropdownPlacement} />}
97
97
  right={<SendArea />}
98
- style={{
99
- paddingRight: 8,
100
- }}
98
+ style={{ paddingRight: 8 }}
101
99
  />
102
100
  }
103
101
  fullscreen={expand}
@@ -298,8 +298,8 @@ export const messageCRUDSlice: StateCreator<
298
298
 
299
299
  let ids = [message.id];
300
300
 
301
- // Handle assistantGroup messages: delete all child blocks and tool results
302
- if (message.role === 'assistantGroup' && message.children) {
301
+ // Handle assistantGroup and supervisor messages: delete all child blocks and tool results
302
+ if ((message.role === 'assistantGroup' || message.role === 'supervisor') && message.children) {
303
303
  const childIds = message.children.map((child: AssistantContentBlock) => child.id);
304
304
  ids = ids.concat(childIds);
305
305
 
@@ -82,12 +82,12 @@ const ConnectionMode = memo<ConnectionModeProps>(({ setWaiting }) => {
82
82
 
83
83
  const connect = useElectronStore((s) => s.connectRemoteServer);
84
84
  const storageMode = useElectronStore(electronSyncSelectors.storageMode);
85
- const remoteServerUrl = useElectronStore(electronSyncSelectors.remoteServerUrl);
85
+ const rawRemoteServerUrl = useElectronStore(electronSyncSelectors.rawRemoteServerUrl);
86
86
 
87
87
  const [selectedOption, setSelectedOption] = useState<RemoteStorageMode>(
88
88
  storageMode === StorageModeEnum.SelfHost ? StorageModeEnum.SelfHost : StorageModeEnum.Cloud,
89
89
  );
90
- const [selfHostedUrl, setSelfHostedUrl] = useState(remoteServerUrl);
90
+ const [selfHostedUrl, setSelfHostedUrl] = useState(rawRemoteServerUrl);
91
91
 
92
92
  const validateUrl = useCallback((url: string) => {
93
93
  if (!url) {
@@ -1,13 +1,12 @@
1
1
  'use client';
2
2
 
3
+ import { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
3
4
  import { Flexbox } from '@lobehub/ui';
4
5
  import { type FC } from 'react';
5
6
 
6
7
  import { ProductLogo } from '@/components/Branding/ProductLogo';
7
8
  import { electronStylish } from '@/styles/electron';
8
9
 
9
- import { TITLE_BAR_HEIGHT } from './const';
10
-
11
10
  /**
12
11
  * A simple, minimal TitleBar for Electron windows.
13
12
  * Provides draggable area without business logic (navigation, updates, etc.)
@@ -1,3 +1,4 @@
1
+ import { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
1
2
  import { Flexbox } from '@lobehub/ui';
2
3
  import { Divider } from 'antd';
3
4
  import { memo, useMemo } from 'react';
@@ -11,7 +12,6 @@ import NavigationBar from './NavigationBar';
11
12
  import { UpdateModal } from './UpdateModal';
12
13
  import { UpdateNotification } from './UpdateNotification';
13
14
  import WinControl from './WinControl';
14
- import { TITLE_BAR_HEIGHT } from './const';
15
15
  import { useWatchThemeUpdate } from './hooks/useWatchThemeUpdate';
16
16
 
17
17
  const isMac = isMacOS();
@@ -66,5 +66,5 @@ const TitleBar = memo(() => {
66
66
 
67
67
  export default TitleBar;
68
68
 
69
- export { TITLE_BAR_HEIGHT } from './const';
70
69
  export { default as SimpleTitleBar } from './SimpleTitleBar';
70
+ export { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
@@ -19,6 +19,7 @@ vi.mock('@lobechat/const', async (importOriginal) => {
19
19
  return mockIsDesktop;
20
20
  },
21
21
  DEFAULT_USER_AVATAR: 'default-avatar.png',
22
+ OFFICIAL_URL: 'https://app.lobehub.com',
22
23
  };
23
24
  });
24
25
 
@@ -77,7 +78,7 @@ describe('useUserAvatar', () => {
77
78
  expect(result.current).toBe(mockAvatar);
78
79
  });
79
80
 
80
- it('should prepend remote server URL when avatar starts with / in desktop environment', () => {
81
+ it('should prepend remote server URL when avatar starts with / in desktop environment (selfHost mode)', () => {
81
82
  mockIsDesktop = true;
82
83
  const mockAvatar = '/api/avatar.png';
83
84
  const mockServerUrl = 'https://server.com';
@@ -85,7 +86,7 @@ describe('useUserAvatar', () => {
85
86
  act(() => {
86
87
  useUserStore.setState({ user: { avatar: mockAvatar } as any });
87
88
  useElectronStore.setState({
88
- dataSyncConfig: { remoteServerUrl: mockServerUrl, storageMode: 'cloud' },
89
+ dataSyncConfig: { remoteServerUrl: mockServerUrl, storageMode: 'selfHost' },
89
90
  });
90
91
  });
91
92
 
@@ -102,7 +103,7 @@ describe('useUserAvatar', () => {
102
103
  act(() => {
103
104
  useUserStore.setState({ user: { avatar: mockAvatar } as any });
104
105
  useElectronStore.setState({
105
- dataSyncConfig: { remoteServerUrl: mockServerUrl, storageMode: 'cloud' },
106
+ dataSyncConfig: { remoteServerUrl: mockServerUrl, storageMode: 'selfHost' },
106
107
  });
107
108
  });
108
109
 
@@ -111,7 +112,7 @@ describe('useUserAvatar', () => {
111
112
  expect(result.current).toBe(mockAvatar);
112
113
  });
113
114
 
114
- it('should handle empty remote server URL in desktop environment', () => {
115
+ it('should use OFFICIAL_URL when storageMode is cloud in desktop environment', () => {
115
116
  mockIsDesktop = true;
116
117
  const mockAvatar = '/api/avatar.png';
117
118
 
@@ -124,6 +125,24 @@ describe('useUserAvatar', () => {
124
125
 
125
126
  const { result } = renderHook(() => useUserAvatar());
126
127
 
128
+ // In cloud mode, selector returns OFFICIAL_URL regardless of remoteServerUrl config
129
+ expect(result.current).toBe('https://app.lobehub.com/api/avatar.png');
130
+ });
131
+
132
+ it('should return original avatar when storageMode is selfHost but no URL configured', () => {
133
+ mockIsDesktop = true;
134
+ const mockAvatar = '/api/avatar.png';
135
+
136
+ act(() => {
137
+ useUserStore.setState({ user: { avatar: mockAvatar } as any });
138
+ useElectronStore.setState({
139
+ dataSyncConfig: { remoteServerUrl: '', storageMode: 'selfHost' },
140
+ });
141
+ });
142
+
143
+ const { result } = renderHook(() => useUserAvatar());
144
+
145
+ // In selfHost mode with empty URL, avatar is not prepended
127
146
  expect(result.current).toBe(mockAvatar);
128
147
  });
129
148
  });
@@ -61,18 +61,16 @@ export default {
61
61
  'builtins.lobe-group-agent-builder.inspector.title': 'Title',
62
62
  'builtins.lobe-group-agent-builder.title': 'Group Builder Expert',
63
63
  'builtins.lobe-group-management.apiName.broadcast': 'All speak',
64
- 'builtins.lobe-group-management.apiName.createAgent': 'Add group member',
65
64
  'builtins.lobe-group-management.apiName.createWorkflow': 'Plan workflow',
66
- 'builtins.lobe-group-management.apiName.executeTask': 'Execute task',
65
+ 'builtins.lobe-group-management.apiName.executeAgentTask': 'Execute agent task',
66
+ 'builtins.lobe-group-management.apiName.executeAgentTasks': 'Execute parallel agent tasks',
67
67
  'builtins.lobe-group-management.apiName.getAgentInfo': 'Get member info',
68
68
  'builtins.lobe-group-management.apiName.interrupt': 'Interrupt task',
69
- 'builtins.lobe-group-management.apiName.inviteAgent': 'Invite member',
70
- 'builtins.lobe-group-management.apiName.removeAgent': 'Remove member',
71
- 'builtins.lobe-group-management.apiName.searchAgent': 'Find relevant experts',
72
69
  'builtins.lobe-group-management.apiName.speak': 'Designated member speaks',
73
70
  'builtins.lobe-group-management.apiName.summarize': 'Summarize conversation',
74
71
  'builtins.lobe-group-management.apiName.vote': 'Start vote',
75
72
  'builtins.lobe-group-management.inspector.broadcast.title': 'Following Agents speak:',
73
+ 'builtins.lobe-group-management.inspector.executeAgentTasks.title': 'Assigning tasks to:',
76
74
  'builtins.lobe-group-management.inspector.speak.title': 'Designated Agent speaks:',
77
75
  'builtins.lobe-group-management.title': 'Group Coordinator',
78
76
  'builtins.lobe-gtd.apiName.clearTodos': 'Clear todos',
@@ -16,6 +16,9 @@ export default {
16
16
  'agentGroupManagement.executeTask.thread': 'Thread ID',
17
17
  'agentGroupManagement.executeTask.timeout': 'Execution Timed Out',
18
18
  'agentGroupManagement.executeTask.tokens': 'Token Usage',
19
+ 'agentGroupManagement.executeTasks.intervention.instructionPlaceholder':
20
+ 'Detailed instruction for the agent to perform this task...',
21
+ 'agentGroupManagement.executeTasks.intervention.titlePlaceholder': 'Task title...',
19
22
  'codeInterpreter-legacy.error': 'Execution Error',
20
23
  'codeInterpreter-legacy.executing': 'Executing...',
21
24
  'codeInterpreter-legacy.files': 'Files:',
@@ -1,4 +1,5 @@
1
1
  import * as builtinAgents from '@lobechat/builtin-agents';
2
+ import { GroupManagementIdentifier } from '@lobechat/builtin-tool-group-management';
2
3
  import { GTDIdentifier } from '@lobechat/builtin-tool-gtd';
3
4
  import { NotebookIdentifier } from '@lobechat/builtin-tool-notebook';
4
5
  import { PageAgentIdentifier } from '@lobechat/builtin-tool-page-agent';
@@ -6,6 +7,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
6
7
 
7
8
  import * as agentStore from '@/store/agent';
8
9
  import * as agentSelectors from '@/store/agent/selectors';
10
+ import * as agentGroupStore from '@/store/agentGroup';
11
+ import * as agentGroupSelectors from '@/store/agentGroup/selectors';
9
12
 
10
13
  import { resolveAgentConfig } from './agentConfigResolver';
11
14
 
@@ -623,4 +626,161 @@ describe('resolveAgentConfig', () => {
623
626
  expect(result.chatConfig.enableHistoryCount).toBe(false);
624
627
  });
625
628
  });
629
+
630
+ describe('supervisor agent (slug from agentGroup store)', () => {
631
+ const mockGroupStoreState = { groupMap: {} };
632
+ const mockGroupWithSupervisor = {
633
+ agents: [
634
+ { id: 'supervisor-agent-id', isSupervisor: true, title: 'Supervisor' },
635
+ { id: 'member-agent-1', isSupervisor: false, title: 'Agent 1' },
636
+ { id: 'member-agent-2', isSupervisor: false, title: 'Agent 2' },
637
+ ],
638
+ config: { systemPrompt: 'Custom group system prompt' },
639
+ id: 'group-123',
640
+ supervisorAgentId: 'supervisor-agent-id',
641
+ title: 'Test Group',
642
+ };
643
+
644
+ beforeEach(() => {
645
+ // No slug in agent store - simulates supervisor agent not being in agentMap with slug
646
+ vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(() => undefined);
647
+
648
+ // Mock agentGroup store
649
+ vi.spyOn(agentGroupStore, 'getChatGroupStoreState').mockReturnValue(
650
+ mockGroupStoreState as any,
651
+ );
652
+ });
653
+
654
+ it('should detect supervisor agent from agentGroup store when not found in agent store', () => {
655
+ // Mock: supervisor agent is found in agentGroup store
656
+ vi.spyOn(
657
+ agentGroupSelectors.agentGroupByIdSelectors,
658
+ 'groupBySupervisorAgentId',
659
+ ).mockReturnValue(() => mockGroupWithSupervisor as any);
660
+
661
+ // Mock: getGroupBySupervisorAgentId for building groupSupervisorContext
662
+ vi.spyOn(
663
+ agentGroupSelectors.agentGroupSelectors,
664
+ 'getGroupBySupervisorAgentId',
665
+ ).mockReturnValue(() => mockGroupWithSupervisor as any);
666
+
667
+ // Mock: getGroupMembers returns non-supervisor agents
668
+ vi.spyOn(agentGroupSelectors.agentGroupSelectors, 'getGroupMembers').mockReturnValue(
669
+ () =>
670
+ [
671
+ { id: 'member-agent-1', title: 'Agent 1' },
672
+ { id: 'member-agent-2', title: 'Agent 2' },
673
+ ] as any,
674
+ );
675
+
676
+ // Mock: getAgentRuntimeConfig for supervisor agent
677
+ vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
678
+ chatConfig: { enableHistoryCount: false },
679
+ plugins: [GroupManagementIdentifier, GTDIdentifier],
680
+ systemRole: 'You are a group supervisor...',
681
+ });
682
+
683
+ const result = resolveAgentConfig({ agentId: 'supervisor-agent-id' });
684
+
685
+ expect(result.isBuiltinAgent).toBe(true);
686
+ expect(result.slug).toBe('group-supervisor');
687
+ expect(result.plugins).toContain(GroupManagementIdentifier);
688
+ expect(result.plugins).toContain(GTDIdentifier);
689
+ });
690
+
691
+ it('should pass groupSupervisorContext to getAgentRuntimeConfig', () => {
692
+ // Mock: supervisor agent is found in agentGroup store
693
+ vi.spyOn(
694
+ agentGroupSelectors.agentGroupByIdSelectors,
695
+ 'groupBySupervisorAgentId',
696
+ ).mockReturnValue(() => mockGroupWithSupervisor as any);
697
+
698
+ // Mock: getGroupBySupervisorAgentId for building groupSupervisorContext
699
+ vi.spyOn(
700
+ agentGroupSelectors.agentGroupSelectors,
701
+ 'getGroupBySupervisorAgentId',
702
+ ).mockReturnValue(() => mockGroupWithSupervisor as any);
703
+
704
+ // Mock: getGroupMembers returns non-supervisor agents
705
+ vi.spyOn(agentGroupSelectors.agentGroupSelectors, 'getGroupMembers').mockReturnValue(
706
+ () =>
707
+ [
708
+ { id: 'member-agent-1', title: 'Agent 1' },
709
+ { id: 'member-agent-2', title: 'Agent 2' },
710
+ ] as any,
711
+ );
712
+
713
+ const getAgentRuntimeConfigSpy = vi
714
+ .spyOn(builtinAgents, 'getAgentRuntimeConfig')
715
+ .mockReturnValue({
716
+ chatConfig: { enableHistoryCount: false },
717
+ plugins: [GroupManagementIdentifier],
718
+ systemRole: 'You are a group supervisor...',
719
+ });
720
+
721
+ resolveAgentConfig({ agentId: 'supervisor-agent-id' });
722
+
723
+ expect(getAgentRuntimeConfigSpy).toHaveBeenCalledWith(
724
+ 'group-supervisor',
725
+ expect.objectContaining({
726
+ groupSupervisorContext: {
727
+ availableAgents: [
728
+ { id: 'member-agent-1', title: 'Agent 1' },
729
+ { id: 'member-agent-2', title: 'Agent 2' },
730
+ ],
731
+ groupId: 'group-123',
732
+ groupTitle: 'Test Group',
733
+ systemPrompt: 'Custom group system prompt',
734
+ },
735
+ }),
736
+ );
737
+ });
738
+
739
+ it('should treat as regular agent when supervisor is not found in agentGroup store', () => {
740
+ // Mock: supervisor agent is NOT found in agentGroup store
741
+ vi.spyOn(
742
+ agentGroupSelectors.agentGroupByIdSelectors,
743
+ 'groupBySupervisorAgentId',
744
+ ).mockReturnValue(() => undefined);
745
+
746
+ const result = resolveAgentConfig({ agentId: 'some-other-agent' });
747
+
748
+ expect(result.isBuiltinAgent).toBe(false);
749
+ expect(result.slug).toBeUndefined();
750
+ expect(result.plugins).toEqual(['plugin-a', 'plugin-b']); // Falls back to agent config plugins
751
+ });
752
+
753
+ it('should work correctly when regenerating supervisor message', () => {
754
+ // This simulates the regenerate flow where agentId is the supervisor agent ID
755
+ vi.spyOn(
756
+ agentGroupSelectors.agentGroupByIdSelectors,
757
+ 'groupBySupervisorAgentId',
758
+ ).mockReturnValue(() => mockGroupWithSupervisor as any);
759
+
760
+ vi.spyOn(
761
+ agentGroupSelectors.agentGroupSelectors,
762
+ 'getGroupBySupervisorAgentId',
763
+ ).mockReturnValue(() => mockGroupWithSupervisor as any);
764
+
765
+ vi.spyOn(agentGroupSelectors.agentGroupSelectors, 'getGroupMembers').mockReturnValue(
766
+ () => [{ id: 'member-agent-1', title: 'Agent 1' }] as any,
767
+ );
768
+
769
+ vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
770
+ chatConfig: { enableHistoryCount: false },
771
+ plugins: [GroupManagementIdentifier, GTDIdentifier],
772
+ systemRole: 'Supervisor system role',
773
+ });
774
+
775
+ const result = resolveAgentConfig({ agentId: 'supervisor-agent-id' });
776
+
777
+ // Should correctly identify as builtin supervisor agent
778
+ expect(result.isBuiltinAgent).toBe(true);
779
+ expect(result.slug).toBe('group-supervisor');
780
+ // Should have group management tool injected
781
+ expect(result.plugins).toContain(GroupManagementIdentifier);
782
+ // Should have proper system role
783
+ expect(result.agentConfig.systemRole).toBe('Supervisor system role');
784
+ });
785
+ });
626
786
  });
@@ -10,7 +10,7 @@ import { produce } from 'immer';
10
10
  import { getAgentStoreState } from '@/store/agent';
11
11
  import { agentSelectors, chatConfigByIdSelectors } from '@/store/agent/selectors';
12
12
  import { getChatGroupStoreState } from '@/store/agentGroup';
13
- import { agentGroupSelectors } from '@/store/agentGroup/selectors';
13
+ import { agentGroupByIdSelectors, agentGroupSelectors } from '@/store/agentGroup/selectors';
14
14
 
15
15
  /**
16
16
  * Applies params adjustments based on chatConfig settings.
@@ -49,7 +49,7 @@ export interface AgentConfigResolverContext {
49
49
  agentId: string;
50
50
 
51
51
  // Builtin agent specific context
52
- /** Document content for page-agent */
52
+ /** Document content for page-agent */
53
53
  documentContent?: string;
54
54
 
55
55
  /** Current model being used (for template variables) */
@@ -109,7 +109,19 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
109
109
  const basePlugins = agentConfig.plugins ?? [];
110
110
 
111
111
  // Check if this is a builtin agent
112
- const slug = agentSelectors.getAgentSlugById(agentId)(agentStoreState);
112
+ // First check agent store, then check if this is a supervisor agent in agentGroup store
113
+ let slug = agentSelectors.getAgentSlugById(agentId)(agentStoreState);
114
+
115
+ // If not found in agent store, check if this is a supervisor agent in any group
116
+ // Supervisor agents have their slug stored in agentGroup store, not agent store
117
+ if (!slug) {
118
+ const groupStoreState = getChatGroupStoreState();
119
+ const group = agentGroupByIdSelectors.groupBySupervisorAgentId(agentId)(groupStoreState);
120
+ if (group) {
121
+ // This is a supervisor agent - use the builtin slug
122
+ slug = BUILTIN_AGENT_SLUGS.groupSupervisor;
123
+ }
124
+ }
113
125
 
114
126
  if (!slug) {
115
127
  // Regular agent - use provided plugins if available, fallback to agent's plugins
@@ -143,7 +143,8 @@ export const contextEngineering = async ({
143
143
  currentAgentRole,
144
144
  groupTitle: groupDetail.title || undefined,
145
145
  members,
146
- systemPrompt: groupDetail.config?.systemPrompt || undefined,
146
+ // Use group.content as the group description (shared prompt/content)
147
+ systemPrompt: groupDetail.content || undefined,
147
148
  };
148
149
  log('agentGroup built: %o', agentGroup);
149
150
  }