@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.
- package/CHANGELOG.md +58 -0
- package/apps/desktop/src/main/const/theme.ts +0 -3
- package/apps/desktop/src/main/core/browser/Browser.ts +1 -1
- package/apps/desktop/src/main/core/browser/WindowThemeManager.ts +3 -2
- package/apps/desktop/src/main/core/browser/__tests__/Browser.test.ts +0 -1
- package/apps/desktop/src/main/core/browser/__tests__/WindowThemeManager.test.ts +8 -5
- package/changelog/v1.json +14 -0
- package/locales/en-US/plugin.json +3 -5
- package/locales/zh-CN/plugin.json +3 -5
- package/locales/zh-CN/tool.json +2 -0
- package/package.json +1 -1
- package/packages/builtin-agents/src/agents/group-supervisor/index.ts +12 -1
- package/packages/builtin-agents/src/agents/group-supervisor/systemRole.ts +0 -7
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/EditLocalFile/index.tsx +93 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/GlobLocalFiles/index.tsx +73 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/GrepContent/index.tsx +69 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ListLocalFiles/index.tsx +68 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ReadLocalFile/index.tsx +74 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/SearchLocalFiles/index.tsx +70 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/WriteLocalFile/index.tsx +57 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/index.ts +14 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Render/WriteFile/index.tsx +54 -35
- package/packages/builtin-tool-cloud-sandbox/src/client/components/FilePathDisplay.tsx +52 -0
- package/packages/builtin-tool-group-management/src/client/Inspector/ExecuteTasks/index.tsx +90 -0
- package/packages/builtin-tool-group-management/src/client/Inspector/index.ts +2 -0
- package/packages/builtin-tool-group-management/src/client/Intervention/ExecuteTasks.tsx +237 -0
- package/packages/builtin-tool-group-management/src/client/Intervention/index.ts +4 -1
- package/packages/builtin-tool-group-management/src/client/Render/index.ts +1 -1
- package/packages/builtin-tool-group-management/src/client/Streaming/ExecuteTask/index.tsx +69 -0
- package/packages/builtin-tool-group-management/src/client/Streaming/ExecuteTasks/index.tsx +87 -0
- package/packages/builtin-tool-group-management/src/client/Streaming/index.ts +4 -0
- package/packages/builtin-tool-group-management/src/executor.test.ts +8 -311
- package/packages/builtin-tool-group-management/src/executor.ts +5 -160
- package/packages/builtin-tool-group-management/src/manifest.ts +50 -94
- package/packages/builtin-tool-group-management/src/systemRole.ts +251 -172
- package/packages/builtin-tool-group-management/src/types.ts +29 -40
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +6 -4
- package/packages/context-engine/src/engine/messages/types.ts +4 -4
- package/packages/context-engine/src/processors/GroupRoleTransform.ts +261 -0
- package/packages/context-engine/src/processors/__tests__/GroupRoleTransform.test.ts +553 -0
- package/packages/context-engine/src/processors/index.ts +2 -2
- package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +4 -16
- package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +23 -28
- package/packages/desktop-bridge/src/index.ts +3 -0
- package/packages/prompts/src/prompts/agentGroup/__snapshots__/index.test.ts.snap +0 -7
- package/packages/prompts/src/prompts/agentGroup/groupContext.ts +0 -7
- package/src/app/[variants]/(main)/group/features/Conversation/AgentWelcome/OpeningQuestions.tsx +4 -8
- package/src/app/[variants]/(main)/group/features/Conversation/MainChatInput/GroupChat.tsx +0 -3
- package/src/app/[variants]/(main)/group/features/Conversation/useGroupContext.ts +3 -0
- package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +15 -2
- package/src/features/ChatInput/Desktop/index.tsx +1 -3
- package/src/features/Conversation/store/slices/message/action/crud.ts +2 -2
- package/src/features/ElectronTitlebar/Connection/ConnectionMode.tsx +2 -2
- package/src/features/ElectronTitlebar/SimpleTitleBar.tsx +1 -2
- package/src/features/ElectronTitlebar/index.tsx +2 -2
- package/src/hooks/useUserAvatar.test.ts +23 -4
- package/src/locales/default/plugin.ts +3 -5
- package/src/locales/default/tool.ts +3 -0
- package/src/services/chat/mecha/agentConfigResolver.test.ts +160 -0
- package/src/services/chat/mecha/agentConfigResolver.ts +15 -3
- package/src/services/chat/mecha/contextEngineering.ts +2 -1
- package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +4 -2
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +2 -0
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +1 -18
- package/src/store/chat/slices/message/selectors/displayMessage.test.ts +24 -0
- package/src/store/chat/slices/message/selectors/displayMessage.ts +6 -1
- package/src/store/chat/slices/topic/action.test.ts +10 -4
- package/src/store/chat/slices/topic/action.ts +3 -2
- package/src/store/electron/selectors/sync.ts +17 -1
- package/packages/context-engine/src/processors/GroupMessageSender.ts +0 -138
- package/packages/context-engine/src/processors/__tests__/GroupMessageSender.test.ts +0 -274
- 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
|
|
|
@@ -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}}
|
package/src/app/[variants]/(main)/group/features/Conversation/AgentWelcome/OpeningQuestions.tsx
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Block, Flexbox } from '@lobehub/ui';
|
|
4
|
-
import { createStaticStyles
|
|
4
|
+
import { createStaticStyles, responsive } from 'antd-style';
|
|
5
5
|
import { memo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
|
-
import {
|
|
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 [
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
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(
|
|
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: '
|
|
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: '
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|