@lobehub/lobehub 2.0.0-next.304 → 2.0.0-next.306
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/.github/workflows/manual-build-desktop.yml +11 -1
- package/CHANGELOG.md +50 -0
- package/apps/desktop/.i18nrc.js +3 -3
- package/apps/desktop/electron.vite.config.ts +0 -2
- package/apps/desktop/resources/locales/ar/dialog.json +5 -1
- package/apps/desktop/resources/locales/ar/menu.json +16 -0
- package/apps/desktop/resources/locales/bg-BG/dialog.json +5 -1
- package/apps/desktop/resources/locales/bg-BG/menu.json +16 -0
- package/apps/desktop/resources/locales/de-DE/dialog.json +5 -1
- package/apps/desktop/resources/locales/de-DE/menu.json +16 -0
- package/apps/desktop/resources/locales/en/common.json +26 -0
- package/apps/desktop/resources/locales/en/dialog.json +27 -0
- package/apps/desktop/resources/locales/en/menu.json +73 -0
- package/apps/desktop/resources/locales/es-ES/dialog.json +5 -1
- package/apps/desktop/resources/locales/es-ES/menu.json +16 -0
- package/apps/desktop/resources/locales/fa-IR/dialog.json +5 -1
- package/apps/desktop/resources/locales/fa-IR/menu.json +16 -0
- package/apps/desktop/resources/locales/fr-FR/dialog.json +5 -1
- package/apps/desktop/resources/locales/fr-FR/menu.json +16 -0
- package/apps/desktop/resources/locales/it-IT/dialog.json +5 -1
- package/apps/desktop/resources/locales/it-IT/menu.json +16 -0
- package/apps/desktop/resources/locales/ja-JP/dialog.json +5 -1
- package/apps/desktop/resources/locales/ja-JP/menu.json +16 -0
- package/apps/desktop/resources/locales/ko-KR/dialog.json +5 -1
- package/apps/desktop/resources/locales/ko-KR/menu.json +16 -0
- package/apps/desktop/resources/locales/nl-NL/dialog.json +5 -1
- package/apps/desktop/resources/locales/nl-NL/menu.json +16 -0
- package/apps/desktop/resources/locales/pl-PL/dialog.json +5 -1
- package/apps/desktop/resources/locales/pl-PL/menu.json +16 -0
- package/apps/desktop/resources/locales/pt-BR/dialog.json +5 -1
- package/apps/desktop/resources/locales/pt-BR/menu.json +16 -0
- package/apps/desktop/resources/locales/ru-RU/dialog.json +5 -1
- package/apps/desktop/resources/locales/ru-RU/menu.json +16 -0
- package/apps/desktop/resources/locales/tr-TR/dialog.json +5 -1
- package/apps/desktop/resources/locales/tr-TR/menu.json +16 -0
- package/apps/desktop/resources/locales/vi-VN/dialog.json +5 -1
- package/apps/desktop/resources/locales/vi-VN/menu.json +16 -0
- package/apps/desktop/resources/locales/zh-TW/dialog.json +5 -1
- package/apps/desktop/resources/locales/zh-TW/menu.json +16 -0
- package/apps/desktop/scripts/update-test/README.md +15 -0
- package/apps/desktop/src/main/core/infrastructure/BackendProxyProtocolManager.ts +7 -6
- package/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts +38 -5
- package/apps/desktop/src/main/utils/logger.ts +2 -2
- package/changelog/v1.json +14 -0
- package/e2e/src/steps/community/detail-pages.steps.ts +3 -1
- package/e2e/src/steps/community/interactions.steps.ts +4 -4
- package/locales/en-US/auth.json +5 -0
- package/locales/zh-CN/auth.json +5 -0
- package/package.json +6 -5
- package/packages/builtin-tool-agent-builder/src/ExecutionRuntime/index.ts +362 -30
- package/packages/builtin-tool-agent-builder/src/client/Intervention/InstallPlugin.tsx +28 -4
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +9 -6
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +103 -0
- package/packages/context-engine/src/providers/GroupAgentBuilderContextInjector.ts +18 -31
- package/packages/context-engine/src/providers/__tests__/GroupAgentBuilderContextInjector.test.ts +307 -0
- package/packages/prompts/src/prompts/userMemory/__snapshots__/index.test.ts.snap +14 -38
- package/packages/prompts/src/prompts/userMemory/index.ts +5 -24
- package/scripts/electronWorkflow/buildDesktopChannel.ts +135 -0
- package/src/app/[variants]/(main)/_layout/index.tsx +2 -0
- package/src/app/[variants]/(main)/community/(detail)/assistant/index.tsx +1 -1
- package/src/app/[variants]/(main)/community/(detail)/mcp/index.tsx +1 -1
- package/src/app/[variants]/(main)/group/features/Conversation/MainChatInput/index.tsx +2 -2
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +2 -2
- package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -1
- package/src/features/Conversation/Messages/components/ContentLoading.tsx +8 -2
- package/src/features/DesktopNavigationBridge/index.tsx +0 -9
- package/src/features/Electron/AuthRequiredModal/index.tsx +151 -0
- package/src/locales/default/auth.ts +6 -0
- package/src/services/chat/mecha/agentConfigResolver.ts +65 -0
- package/src/services/chat/mecha/modelParamsResolver.test.ts +211 -0
- package/src/store/agentGroup/action.ts +30 -0
- package/src/store/agentGroup/slices/lifecycle.test.ts +77 -18
- package/src/store/agentGroup/slices/lifecycle.ts +7 -9
- package/src/store/chat/slices/operation/__tests__/selectors.test.ts +124 -0
- package/src/store/chat/slices/operation/selectors.ts +22 -0
- package/src/utils/errorResponse.ts +21 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/* eslint-disable unicorn/no-process-exit */
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
type ReleaseChannel = 'stable' | 'beta' | 'nightly';
|
|
7
|
+
|
|
8
|
+
const rootDir = path.resolve(__dirname, '../..');
|
|
9
|
+
const desktopDir = path.join(rootDir, 'apps/desktop');
|
|
10
|
+
const desktopPackageJsonPath = path.join(desktopDir, 'package.json');
|
|
11
|
+
const buildDir = path.join(desktopDir, 'build');
|
|
12
|
+
|
|
13
|
+
const iconTargets = ['icon.png', 'Icon.icns', 'icon.ico'];
|
|
14
|
+
|
|
15
|
+
const isFlag = (value: string) => value.startsWith('-');
|
|
16
|
+
|
|
17
|
+
const parseArgs = (args: string[]) => {
|
|
18
|
+
let channel = '';
|
|
19
|
+
let version = '';
|
|
20
|
+
let keepChanges = false;
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
23
|
+
const arg = args[i];
|
|
24
|
+
|
|
25
|
+
if (arg === '--channel' || arg === '-c') {
|
|
26
|
+
channel = args[i + 1] ?? '';
|
|
27
|
+
i += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (arg === '--version' || arg === '-v') {
|
|
32
|
+
version = args[i + 1] ?? '';
|
|
33
|
+
i += 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (arg === '--keep-changes') {
|
|
38
|
+
keepChanges = true;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!isFlag(arg)) {
|
|
43
|
+
if (!channel) {
|
|
44
|
+
channel = arg;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!version) {
|
|
49
|
+
version = arg;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { channel, keepChanges, version };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const resolveDefaultVersion = () => {
|
|
58
|
+
const rootPackageJsonPath = path.join(rootDir, 'package.json');
|
|
59
|
+
const rootPackageJson = fs.readJsonSync(rootPackageJsonPath);
|
|
60
|
+
return rootPackageJson.version as string | undefined;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const backupFile = async (filePath: string) => {
|
|
64
|
+
try {
|
|
65
|
+
return await fs.readFile(filePath);
|
|
66
|
+
} catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const restoreFile = async (filePath: string, content?: Buffer) => {
|
|
72
|
+
if (!content) return;
|
|
73
|
+
await fs.writeFile(filePath, content);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const validateChannel = (channel: string): channel is ReleaseChannel =>
|
|
77
|
+
channel === 'stable' || channel === 'beta' || channel === 'nightly';
|
|
78
|
+
|
|
79
|
+
const runCommand = (command: string, env?: Record<string, string | undefined>) => {
|
|
80
|
+
execSync(command, {
|
|
81
|
+
cwd: rootDir,
|
|
82
|
+
env: { ...process.env, ...env },
|
|
83
|
+
stdio: 'inherit',
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const main = async () => {
|
|
88
|
+
const { channel, version: rawVersion, keepChanges } = parseArgs(process.argv.slice(2));
|
|
89
|
+
|
|
90
|
+
if (!validateChannel(channel)) {
|
|
91
|
+
console.error(
|
|
92
|
+
'Missing or invalid channel. Usage: npm run desktop:build-channel -- <stable|beta|nightly> [version] [--keep-changes]',
|
|
93
|
+
);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const version = rawVersion || resolveDefaultVersion();
|
|
98
|
+
|
|
99
|
+
if (!version) {
|
|
100
|
+
console.error('Missing version. Provide it or ensure root package.json has a version.');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const packageJsonBackup = await backupFile(desktopPackageJsonPath);
|
|
105
|
+
const iconBackups = await Promise.all(
|
|
106
|
+
iconTargets.map(async (fileName) => ({
|
|
107
|
+
content: await backupFile(path.join(buildDir, fileName)),
|
|
108
|
+
fileName,
|
|
109
|
+
})),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
console.log(`🚦 CI-style build channel: ${channel}`);
|
|
113
|
+
console.log(`🏷️ Desktop version: ${version}`);
|
|
114
|
+
console.log(`🧩 Keep local changes: ${keepChanges ? 'yes' : 'no'}`);
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
runCommand(`npm run workflow:set-desktop-version ${version} ${channel}`);
|
|
118
|
+
runCommand('npm run desktop:build', { UPDATE_CHANNEL: channel });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('❌ Build failed:', error);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
} finally {
|
|
123
|
+
if (!keepChanges) {
|
|
124
|
+
await restoreFile(desktopPackageJsonPath, packageJsonBackup);
|
|
125
|
+
await Promise.all(
|
|
126
|
+
iconBackups.map(({ fileName, content }) =>
|
|
127
|
+
restoreFile(path.join(buildDir, fileName), content),
|
|
128
|
+
),
|
|
129
|
+
);
|
|
130
|
+
console.log('🧹 Restored local desktop package metadata and icons.');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
main();
|
|
@@ -12,6 +12,7 @@ import Loading from '@/components/Loading/BrandTextLoading';
|
|
|
12
12
|
import { isDesktop } from '@/const/version';
|
|
13
13
|
import { BANNER_HEIGHT } from '@/features/AlertBanner/CloudBanner';
|
|
14
14
|
import DesktopNavigationBridge from '@/features/DesktopNavigationBridge';
|
|
15
|
+
import AuthRequiredModal from '@/features/Electron/AuthRequiredModal';
|
|
15
16
|
import TitleBar from '@/features/Electron/titlebar/TitleBar';
|
|
16
17
|
import HotkeyHelperPanel from '@/features/HotkeyHelperPanel';
|
|
17
18
|
import NavPanel from '@/features/NavPanel';
|
|
@@ -45,6 +46,7 @@ const Layout: FC = () => {
|
|
|
45
46
|
{isDesktop && <TitleBar />}
|
|
46
47
|
{isDesktop && <DesktopAutoOidcOnFirstOpen />}
|
|
47
48
|
{isDesktop && <DesktopNavigationBridge />}
|
|
49
|
+
{isDesktop && <AuthRequiredModal />}
|
|
48
50
|
{showCloudPromotion && <CloudBanner />}
|
|
49
51
|
</Suspense>
|
|
50
52
|
<DndContextWrapper>
|
|
@@ -40,7 +40,7 @@ const AssistantDetailPage = memo<AssistantDetailPageProps>(({ mobile }) => {
|
|
|
40
40
|
return (
|
|
41
41
|
<TocProvider>
|
|
42
42
|
<DetailProvider config={data}>
|
|
43
|
-
<Flexbox gap={16}>
|
|
43
|
+
<Flexbox data-testid="assistant-detail-content" gap={16}>
|
|
44
44
|
<Header mobile={mobile} />
|
|
45
45
|
<Details mobile={mobile} />
|
|
46
46
|
</Flexbox>
|
|
@@ -35,7 +35,7 @@ const McpDetailPage = memo<McpDetailPageProps>(({ mobile }) => {
|
|
|
35
35
|
return (
|
|
36
36
|
<TocProvider>
|
|
37
37
|
<DetailProvider config={data}>
|
|
38
|
-
<Flexbox gap={16}>
|
|
38
|
+
<Flexbox data-testid="mcp-detail-content" gap={16}>
|
|
39
39
|
<Header mobile={mobile} />
|
|
40
40
|
<Details mobile={mobile} />
|
|
41
41
|
</Flexbox>
|
|
@@ -11,10 +11,10 @@ import { useSendMenuItems } from './useSendMenuItems';
|
|
|
11
11
|
const leftActions: ActionKeys[] = [
|
|
12
12
|
'model',
|
|
13
13
|
'search',
|
|
14
|
-
'typo',
|
|
15
14
|
'fileUpload',
|
|
15
|
+
'tools',
|
|
16
16
|
'---',
|
|
17
|
-
['
|
|
17
|
+
['typo', 'params', 'clear'],
|
|
18
18
|
'mainToken',
|
|
19
19
|
];
|
|
20
20
|
|
|
@@ -37,8 +37,8 @@ const AgentItem = memo<AgentItemProps>(({ item, style, className }) => {
|
|
|
37
37
|
s.agentUpdatingId === id,
|
|
38
38
|
]);
|
|
39
39
|
|
|
40
|
-
// Separate loading state from chat store - only
|
|
41
|
-
const isLoading = useChatStore(operationSelectors.
|
|
40
|
+
// Separate loading state from chat store - only show loading for this specific agent
|
|
41
|
+
const isLoading = useChatStore(operationSelectors.isAgentRunning(id));
|
|
42
42
|
|
|
43
43
|
// Get display title with fallback
|
|
44
44
|
const displayTitle = title || t('untitledAgent');
|
|
@@ -55,7 +55,8 @@ const GroupMessage = memo<GroupMessageProps>(({ id, index, disableEditing, isLat
|
|
|
55
55
|
|
|
56
56
|
// Get editing state from ConversationStore
|
|
57
57
|
const creating = useConversationStore(messageStateSelectors.isMessageCreating(id));
|
|
58
|
-
const
|
|
58
|
+
const generating = useConversationStore(messageStateSelectors.isMessageGenerating(id));
|
|
59
|
+
const newScreen = useNewScreen({ creating: creating || generating, isLatestItem });
|
|
59
60
|
|
|
60
61
|
const setMessageItemActionElementPortialContext = useSetMessageItemActionElementPortialContext();
|
|
61
62
|
const setMessageItemActionTypeContext = useSetMessageItemActionTypeContext();
|
|
@@ -18,10 +18,11 @@ interface ContentLoadingProps {
|
|
|
18
18
|
const ContentLoading = memo<ContentLoadingProps>(({ id }) => {
|
|
19
19
|
const { t } = useTranslation('chat');
|
|
20
20
|
const runningOp = useChatStore(operationSelectors.getDeepestRunningOperationByMessage(id));
|
|
21
|
+
console.log('runningOp', runningOp);
|
|
21
22
|
const [elapsedSeconds, setElapsedSeconds] = useState(0);
|
|
23
|
+
const [startTime, setStartTime] = useState(runningOp?.metadata?.startTime);
|
|
22
24
|
|
|
23
25
|
const operationType = runningOp?.type as OperationType | undefined;
|
|
24
|
-
const startTime = runningOp?.metadata?.startTime;
|
|
25
26
|
|
|
26
27
|
// Track elapsed time, reset when operation type changes
|
|
27
28
|
useEffect(() => {
|
|
@@ -39,7 +40,12 @@ const ContentLoading = memo<ContentLoadingProps>(({ id }) => {
|
|
|
39
40
|
const interval = setInterval(updateElapsed, 1000);
|
|
40
41
|
|
|
41
42
|
return () => clearInterval(interval);
|
|
42
|
-
}, [startTime
|
|
43
|
+
}, [startTime]);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
setElapsedSeconds(0);
|
|
47
|
+
setStartTime(Date.now());
|
|
48
|
+
}, [operationType, id]);
|
|
43
49
|
|
|
44
50
|
// Get localized label based on operation type
|
|
45
51
|
const operationLabel = operationType
|
|
@@ -4,8 +4,6 @@ import { useWatchBroadcast } from '@lobechat/electron-client-ipc';
|
|
|
4
4
|
import { memo, useCallback } from 'react';
|
|
5
5
|
import { useNavigate } from 'react-router-dom';
|
|
6
6
|
|
|
7
|
-
import { clearDesktopOnboardingCompleted } from '@/app/[variants]/(desktop)/desktop-onboarding/storage';
|
|
8
|
-
|
|
9
7
|
const DesktopNavigationBridge = memo(() => {
|
|
10
8
|
const navigate = useNavigate();
|
|
11
9
|
|
|
@@ -19,13 +17,6 @@ const DesktopNavigationBridge = memo(() => {
|
|
|
19
17
|
|
|
20
18
|
useWatchBroadcast('navigate', handleNavigate);
|
|
21
19
|
|
|
22
|
-
const handleAuthorizationRequired = useCallback(() => {
|
|
23
|
-
clearDesktopOnboardingCompleted();
|
|
24
|
-
navigate('/desktop-onboarding#5', { replace: true });
|
|
25
|
-
}, [navigate]);
|
|
26
|
-
|
|
27
|
-
useWatchBroadcast('authorizationRequired', handleAuthorizationRequired);
|
|
28
|
-
|
|
29
20
|
return null;
|
|
30
21
|
});
|
|
31
22
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useWatchBroadcast } from '@lobechat/electron-client-ipc';
|
|
4
|
+
import { Button, Flexbox, Icon, type ModalInstance, createModal } from '@lobehub/ui';
|
|
5
|
+
import { AlertCircle, LogIn } from 'lucide-react';
|
|
6
|
+
import { type ReactNode, memo, useCallback, useRef, useState } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
9
|
+
import { useElectronStore } from '@/store/electron';
|
|
10
|
+
|
|
11
|
+
interface ModalUpdateOptions {
|
|
12
|
+
closable?: boolean;
|
|
13
|
+
keyboard?: boolean;
|
|
14
|
+
maskClosable?: boolean;
|
|
15
|
+
title?: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface AuthRequiredModalContentProps {
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
setModalProps: (props: ModalUpdateOptions) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const AuthRequiredModalContent = memo<AuthRequiredModalContentProps>(
|
|
24
|
+
({ onClose, setModalProps }) => {
|
|
25
|
+
const { t } = useTranslation('auth');
|
|
26
|
+
const [isSigningIn, setIsSigningIn] = useState(false);
|
|
27
|
+
const isClosingRef = useRef(false);
|
|
28
|
+
|
|
29
|
+
const [dataSyncConfig, connectRemoteServer, refreshServerConfig, clearRemoteServerSyncError] =
|
|
30
|
+
useElectronStore((s) => [
|
|
31
|
+
s.dataSyncConfig,
|
|
32
|
+
s.connectRemoteServer,
|
|
33
|
+
s.refreshServerConfig,
|
|
34
|
+
s.clearRemoteServerSyncError,
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
// Update modal props based on signing in state
|
|
38
|
+
setModalProps({
|
|
39
|
+
closable: !isSigningIn,
|
|
40
|
+
keyboard: !isSigningIn,
|
|
41
|
+
maskClosable: !isSigningIn,
|
|
42
|
+
title: (
|
|
43
|
+
<Flexbox align="center" gap={8} horizontal>
|
|
44
|
+
<Icon icon={AlertCircle} />
|
|
45
|
+
{t('authModal.title')}
|
|
46
|
+
</Flexbox>
|
|
47
|
+
),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Listen for successful authorization to close the modal
|
|
51
|
+
useWatchBroadcast('authorizationSuccessful', async () => {
|
|
52
|
+
if (isClosingRef.current) return;
|
|
53
|
+
isClosingRef.current = true;
|
|
54
|
+
setIsSigningIn(false);
|
|
55
|
+
onClose();
|
|
56
|
+
await refreshServerConfig();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Listen for authorization failure
|
|
60
|
+
useWatchBroadcast('authorizationFailed', () => {
|
|
61
|
+
setIsSigningIn(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const handleSignIn = useCallback(async () => {
|
|
65
|
+
setIsSigningIn(true);
|
|
66
|
+
clearRemoteServerSyncError();
|
|
67
|
+
|
|
68
|
+
await connectRemoteServer({
|
|
69
|
+
remoteServerUrl: dataSyncConfig?.remoteServerUrl,
|
|
70
|
+
storageMode: dataSyncConfig?.storageMode || 'cloud',
|
|
71
|
+
});
|
|
72
|
+
}, [clearRemoteServerSyncError, connectRemoteServer, dataSyncConfig]);
|
|
73
|
+
|
|
74
|
+
const handleLater = useCallback(() => {
|
|
75
|
+
if (isClosingRef.current) return;
|
|
76
|
+
isClosingRef.current = true;
|
|
77
|
+
onClose();
|
|
78
|
+
}, [onClose]);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Flexbox gap={16} style={{ padding: 16 }}>
|
|
82
|
+
<p style={{ margin: 0 }}>{t('authModal.description')}</p>
|
|
83
|
+
<Flexbox gap={8} horizontal justify="flex-end">
|
|
84
|
+
<Button disabled={isSigningIn} onClick={handleLater}>
|
|
85
|
+
{t('authModal.later')}
|
|
86
|
+
</Button>
|
|
87
|
+
<Button
|
|
88
|
+
icon={<Icon icon={LogIn} />}
|
|
89
|
+
loading={isSigningIn}
|
|
90
|
+
onClick={handleSignIn}
|
|
91
|
+
type="primary"
|
|
92
|
+
>
|
|
93
|
+
{isSigningIn ? t('authModal.signingIn') : t('authModal.signIn')}
|
|
94
|
+
</Button>
|
|
95
|
+
</Flexbox>
|
|
96
|
+
</Flexbox>
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
AuthRequiredModalContent.displayName = 'AuthRequiredModalContent';
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Hook to create and manage the auth required modal
|
|
105
|
+
*/
|
|
106
|
+
export const useAuthRequiredModal = () => {
|
|
107
|
+
const instanceRef = useRef<ModalInstance | null>(null);
|
|
108
|
+
|
|
109
|
+
const open = useCallback(() => {
|
|
110
|
+
// Don't open multiple modals
|
|
111
|
+
if (instanceRef.current) return;
|
|
112
|
+
|
|
113
|
+
const setModalProps = (nextProps: ModalUpdateOptions) => {
|
|
114
|
+
instanceRef.current?.update?.(nextProps);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const handleClose = () => {
|
|
118
|
+
instanceRef.current?.close();
|
|
119
|
+
instanceRef.current = null;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
instanceRef.current = createModal({
|
|
123
|
+
children: <AuthRequiredModalContent onClose={handleClose} setModalProps={setModalProps} />,
|
|
124
|
+
closable: false,
|
|
125
|
+
footer: null,
|
|
126
|
+
keyboard: false,
|
|
127
|
+
maskClosable: false,
|
|
128
|
+
title: '',
|
|
129
|
+
});
|
|
130
|
+
}, []);
|
|
131
|
+
|
|
132
|
+
return { open };
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Component that listens for authorizationRequired IPC events and opens the modal
|
|
137
|
+
*/
|
|
138
|
+
const AuthRequiredModal = memo(() => {
|
|
139
|
+
const { open } = useAuthRequiredModal();
|
|
140
|
+
|
|
141
|
+
// Listen for IPC event to open the modal
|
|
142
|
+
useWatchBroadcast('authorizationRequired', () => {
|
|
143
|
+
open();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return null;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
AuthRequiredModal.displayName = 'AuthRequiredModal';
|
|
150
|
+
|
|
151
|
+
export default AuthRequiredModal;
|
|
@@ -28,6 +28,12 @@ export default {
|
|
|
28
28
|
'apikey.list.columns.status': 'Enabled Status',
|
|
29
29
|
'apikey.list.title': 'API Key List',
|
|
30
30
|
'apikey.validation.required': 'This field cannot be empty',
|
|
31
|
+
'authModal.description':
|
|
32
|
+
'Your login session has expired. Please sign in again to continue using cloud sync features.',
|
|
33
|
+
'authModal.later': 'Later',
|
|
34
|
+
'authModal.signIn': 'Sign In Again',
|
|
35
|
+
'authModal.signingIn': 'Signing in...',
|
|
36
|
+
'authModal.title': 'Session Expired',
|
|
31
37
|
'betterAuth.errors.confirmPasswordRequired': 'Please confirm your password',
|
|
32
38
|
'betterAuth.errors.emailExists': 'This email is already registered. Please sign in instead',
|
|
33
39
|
'betterAuth.errors.emailInvalid': 'Please enter a valid email address or username',
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type LobeAgentConfig,
|
|
6
6
|
type MessageMapScope,
|
|
7
7
|
} from '@lobechat/types';
|
|
8
|
+
import debug from 'debug';
|
|
8
9
|
import { produce } from 'immer';
|
|
9
10
|
|
|
10
11
|
import { getAgentStoreState } from '@/store/agent';
|
|
@@ -12,6 +13,8 @@ import { agentSelectors, chatConfigByIdSelectors } from '@/store/agent/selectors
|
|
|
12
13
|
import { getChatGroupStoreState } from '@/store/agentGroup';
|
|
13
14
|
import { agentGroupByIdSelectors, agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
14
15
|
|
|
16
|
+
const log = debug('mecha:agentConfigResolver');
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Applies params adjustments based on chatConfig settings.
|
|
17
20
|
*
|
|
@@ -99,6 +102,8 @@ export interface ResolvedAgentConfig {
|
|
|
99
102
|
export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAgentConfig => {
|
|
100
103
|
const { agentId, model, documentContent, plugins, targetAgentConfig } = ctx;
|
|
101
104
|
|
|
105
|
+
log('resolveAgentConfig called with agentId: %s, scope: %s', agentId, ctx.scope);
|
|
106
|
+
|
|
102
107
|
const agentStoreState = getAgentStoreState();
|
|
103
108
|
|
|
104
109
|
// Get base config from store
|
|
@@ -111,19 +116,46 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
111
116
|
// Check if this is a builtin agent
|
|
112
117
|
// First check agent store, then check if this is a supervisor agent in agentGroup store
|
|
113
118
|
let slug = agentSelectors.getAgentSlugById(agentId)(agentStoreState);
|
|
119
|
+
log('slug from agentStore: %s (agentId: %s)', slug, agentId);
|
|
114
120
|
|
|
115
121
|
// If not found in agent store, check if this is a supervisor agent in any group
|
|
116
122
|
// Supervisor agents have their slug stored in agentGroup store, not agent store
|
|
117
123
|
if (!slug) {
|
|
118
124
|
const groupStoreState = getChatGroupStoreState();
|
|
125
|
+
const groupMap = groupStoreState.groupMap;
|
|
126
|
+
const groupMapKeys = Object.keys(groupMap);
|
|
127
|
+
log(
|
|
128
|
+
'checking groupStore for supervisor - groupMap has %d groups: %o',
|
|
129
|
+
groupMapKeys.length,
|
|
130
|
+
groupMapKeys.map((key) => ({
|
|
131
|
+
groupId: key,
|
|
132
|
+
supervisorAgentId: groupMap[key]?.supervisorAgentId,
|
|
133
|
+
title: groupMap[key]?.title,
|
|
134
|
+
})),
|
|
135
|
+
);
|
|
136
|
+
|
|
119
137
|
const group = agentGroupByIdSelectors.groupBySupervisorAgentId(agentId)(groupStoreState);
|
|
138
|
+
log(
|
|
139
|
+
'groupBySupervisorAgentId result for agentId %s: %o',
|
|
140
|
+
agentId,
|
|
141
|
+
group
|
|
142
|
+
? {
|
|
143
|
+
groupId: group.id,
|
|
144
|
+
supervisorAgentId: group.supervisorAgentId,
|
|
145
|
+
title: group.title,
|
|
146
|
+
}
|
|
147
|
+
: null,
|
|
148
|
+
);
|
|
149
|
+
|
|
120
150
|
if (group) {
|
|
121
151
|
// This is a supervisor agent - use the builtin slug
|
|
122
152
|
slug = BUILTIN_AGENT_SLUGS.groupSupervisor;
|
|
153
|
+
log('agentId %s identified as group supervisor, assigned slug: %s', agentId, slug);
|
|
123
154
|
}
|
|
124
155
|
}
|
|
125
156
|
|
|
126
157
|
if (!slug) {
|
|
158
|
+
log('agentId %s is not a builtin agent (no slug found)', agentId);
|
|
127
159
|
// Regular agent - use provided plugins if available, fallback to agent's plugins
|
|
128
160
|
const finalPlugins = plugins && plugins.length > 0 ? plugins : basePlugins;
|
|
129
161
|
|
|
@@ -183,18 +215,45 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
183
215
|
// Build groupSupervisorContext if this is a group-supervisor agent
|
|
184
216
|
let groupSupervisorContext;
|
|
185
217
|
if (slug === BUILTIN_AGENT_SLUGS.groupSupervisor) {
|
|
218
|
+
log('building groupSupervisorContext for agentId: %s', agentId);
|
|
186
219
|
const groupStoreState = getChatGroupStoreState();
|
|
187
220
|
// Find the group by supervisor agent ID
|
|
188
221
|
const group = agentGroupSelectors.getGroupBySupervisorAgentId(agentId)(groupStoreState);
|
|
189
222
|
|
|
223
|
+
log(
|
|
224
|
+
'getGroupBySupervisorAgentId result: %o',
|
|
225
|
+
group
|
|
226
|
+
? {
|
|
227
|
+
agentsCount: group.agents?.length,
|
|
228
|
+
groupId: group.id,
|
|
229
|
+
supervisorAgentId: group.supervisorAgentId,
|
|
230
|
+
title: group.title,
|
|
231
|
+
}
|
|
232
|
+
: null,
|
|
233
|
+
);
|
|
234
|
+
|
|
190
235
|
if (group) {
|
|
191
236
|
const groupMembers = agentGroupSelectors.getGroupMembers(group.id)(groupStoreState);
|
|
237
|
+
log(
|
|
238
|
+
'groupMembers for groupId %s: %o',
|
|
239
|
+
group.id,
|
|
240
|
+
groupMembers.map((m) => ({ id: m.id, isSupervisor: m.isSupervisor, title: m.title })),
|
|
241
|
+
);
|
|
242
|
+
|
|
192
243
|
groupSupervisorContext = {
|
|
193
244
|
availableAgents: groupMembers.map((agent) => ({ id: agent.id, title: agent.title })),
|
|
194
245
|
groupId: group.id,
|
|
195
246
|
groupTitle: group.title || 'Group Chat',
|
|
196
247
|
systemPrompt: agentConfig.systemRole,
|
|
197
248
|
};
|
|
249
|
+
log('groupSupervisorContext built: %o', {
|
|
250
|
+
availableAgentsCount: groupSupervisorContext.availableAgents.length,
|
|
251
|
+
groupId: groupSupervisorContext.groupId,
|
|
252
|
+
groupTitle: groupSupervisorContext.groupTitle,
|
|
253
|
+
hasSystemPrompt: !!groupSupervisorContext.systemPrompt,
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
log('WARNING: group not found for supervisor agentId: %s', agentId);
|
|
198
257
|
}
|
|
199
258
|
}
|
|
200
259
|
|
|
@@ -258,6 +317,12 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
258
317
|
// Apply params adjustments based on chatConfig
|
|
259
318
|
const finalAgentConfig = applyParamsFromChatConfig(resolvedAgentConfig, resolvedChatConfig);
|
|
260
319
|
|
|
320
|
+
log('resolveAgentConfig completed for agentId: %s, result: %o', agentId, {
|
|
321
|
+
isBuiltinAgent: true,
|
|
322
|
+
pluginsCount: finalPlugins.length,
|
|
323
|
+
slug,
|
|
324
|
+
});
|
|
325
|
+
|
|
261
326
|
return {
|
|
262
327
|
agentConfig: finalAgentConfig,
|
|
263
328
|
chatConfig: resolvedChatConfig,
|