@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
|
@@ -446,8 +446,8 @@ Then('I should be navigated to the assistant detail page', async function (this:
|
|
|
446
446
|
Then('I should see the assistant detail content', async function (this: CustomWorld) {
|
|
447
447
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
448
448
|
|
|
449
|
-
// Look for detail page
|
|
450
|
-
const detailContent = this.page.locator('[data-testid="detail-content"]
|
|
449
|
+
// Look for assistant detail page content
|
|
450
|
+
const detailContent = this.page.locator('[data-testid="assistant-detail-content"]');
|
|
451
451
|
await expect(detailContent).toBeVisible({ timeout: 30_000 });
|
|
452
452
|
});
|
|
453
453
|
|
|
@@ -561,8 +561,8 @@ Then('I should be navigated to the MCP detail page', async function (this: Custo
|
|
|
561
561
|
Then('I should see the MCP detail content', async function (this: CustomWorld) {
|
|
562
562
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
563
563
|
|
|
564
|
-
// Look for detail page
|
|
565
|
-
const detailContent = this.page.locator('[data-testid="detail-content"]
|
|
564
|
+
// Look for MCP detail page content
|
|
565
|
+
const detailContent = this.page.locator('[data-testid="mcp-detail-content"]');
|
|
566
566
|
await expect(detailContent).toBeVisible({ timeout: 30_000 });
|
|
567
567
|
});
|
|
568
568
|
|
package/locales/en-US/auth.json
CHANGED
|
@@ -28,6 +28,11 @@
|
|
|
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": "Your login session has expired. Please sign in again to continue using cloud sync features.",
|
|
32
|
+
"authModal.later": "Later",
|
|
33
|
+
"authModal.signIn": "Sign In Again",
|
|
34
|
+
"authModal.signingIn": "Signing in...",
|
|
35
|
+
"authModal.title": "Session Expired",
|
|
31
36
|
"betterAuth.errors.confirmPasswordRequired": "Please confirm your password",
|
|
32
37
|
"betterAuth.errors.emailExists": "This email is already registered. Please sign in instead",
|
|
33
38
|
"betterAuth.errors.emailInvalid": "Please enter a valid email address or username",
|
package/locales/zh-CN/auth.json
CHANGED
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
"apikey.list.columns.status": "启用状态",
|
|
29
29
|
"apikey.list.title": "API Key 列表",
|
|
30
30
|
"apikey.validation.required": "内容不得为空",
|
|
31
|
+
"authModal.description": "您的登录会话已过期,请重新登录以继续使用云同步功能。",
|
|
32
|
+
"authModal.later": "稍后",
|
|
33
|
+
"authModal.signIn": "重新登录",
|
|
34
|
+
"authModal.signingIn": "登录中...",
|
|
35
|
+
"authModal.title": "登录已过期",
|
|
31
36
|
"betterAuth.errors.confirmPasswordRequired": "请确认密码",
|
|
32
37
|
"betterAuth.errors.emailExists": "该邮箱已注册,请直接登录",
|
|
33
38
|
"betterAuth.errors.emailInvalid": "请输入有效的邮箱地址或用户名",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.306",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -35,18 +35,19 @@
|
|
|
35
35
|
"prebuild": "tsx scripts/prebuild.mts && npm run lint",
|
|
36
36
|
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 next build --webpack",
|
|
37
37
|
"postbuild": "npm run build-sitemap && npm run build-migrate-db",
|
|
38
|
-
"build-migrate-db": "bun run db:migrate",
|
|
39
|
-
"build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts",
|
|
40
38
|
"build:analyze": "NODE_OPTIONS=--max-old-space-size=81920 ANALYZE=true next build --webpack",
|
|
41
39
|
"build:docker": "npm run prebuild && NODE_OPTIONS=--max-old-space-size=8192 DOCKER=true next build --webpack && npm run build-sitemap",
|
|
42
40
|
"build:electron": "cross-env NODE_OPTIONS=--max-old-space-size=8192 NEXT_PUBLIC_IS_DESKTOP_APP=1 tsx scripts/electronWorkflow/buildNextApp.mts",
|
|
43
41
|
"build:vercel": "npm run prebuild && cross-env NODE_OPTIONS=--max-old-space-size=6144 next build --webpack && npm run postbuild",
|
|
42
|
+
"build-migrate-db": "bun run db:migrate",
|
|
43
|
+
"build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts",
|
|
44
44
|
"clean:node_modules": "bash -lc 'set -e; echo \"Removing all node_modules...\"; rm -rf node_modules; pnpm -r exec rm -rf node_modules; rm -rf apps/desktop/node_modules; echo \"All node_modules removed.\"'",
|
|
45
45
|
"db:generate": "drizzle-kit generate && npm run workflow:dbml",
|
|
46
46
|
"db:migrate": "MIGRATION_DB=1 tsx ./scripts/migrateServerDB/index.ts",
|
|
47
47
|
"db:studio": "drizzle-kit studio",
|
|
48
48
|
"db:visualize": "dbdocs build docs/development/database-schema.dbml --project lobe-chat",
|
|
49
49
|
"desktop:build": "npm run desktop:build-next && npm run desktop:prepare-dist && npm run desktop:build-electron",
|
|
50
|
+
"desktop:build-channel": "tsx scripts/electronWorkflow/buildDesktopChannel.ts",
|
|
50
51
|
"desktop:build-electron": "tsx scripts/electronWorkflow/buildElectron.ts",
|
|
51
52
|
"desktop:build-local": "npm run desktop:build-next && npm run desktop:prepare-dist && npm run build-local --prefix=./apps/desktop",
|
|
52
53
|
"desktop:build-next": "npm run build:electron",
|
|
@@ -86,11 +87,11 @@
|
|
|
86
87
|
"start": "next start -p 3210",
|
|
87
88
|
"stylelint": "stylelint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
|
|
88
89
|
"test": "npm run test-app && npm run test-server",
|
|
89
|
-
"test-app": "vitest run",
|
|
90
|
-
"test-app:coverage": "vitest --coverage --silent='passed-only'",
|
|
91
90
|
"test:e2e": "pnpm --filter @lobechat/e2e-tests test",
|
|
92
91
|
"test:e2e:smoke": "pnpm --filter @lobechat/e2e-tests test:smoke",
|
|
93
92
|
"test:update": "vitest -u",
|
|
93
|
+
"test-app": "vitest run",
|
|
94
|
+
"test-app:coverage": "vitest --coverage --silent='passed-only'",
|
|
94
95
|
"tunnel:cloudflare": "cloudflared tunnel --url http://localhost:3010",
|
|
95
96
|
"tunnel:ngrok": "ngrok http http://localhost:3011",
|
|
96
97
|
"type-check": "tsgo --noEmit",
|
|
@@ -344,7 +344,139 @@ export class AgentBuilderExecutionRuntime {
|
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
/**
|
|
347
|
-
* Open OAuth window and wait for authentication completion
|
|
347
|
+
* Open OAuth window and wait for LobehubSkill authentication completion
|
|
348
|
+
* Returns a Promise that resolves when OAuth completes or fails
|
|
349
|
+
*/
|
|
350
|
+
private openLobehubSkillOAuthWindowAndWait(
|
|
351
|
+
oauthUrl: string,
|
|
352
|
+
provider: string,
|
|
353
|
+
): Promise<{ cancelled: boolean; success: boolean }> {
|
|
354
|
+
return new Promise((resolve) => {
|
|
355
|
+
// Configuration
|
|
356
|
+
const WINDOW_CHECK_INTERVAL_MS = 500;
|
|
357
|
+
const POLL_INTERVAL_MS = 1000;
|
|
358
|
+
const POLL_TIMEOUT_MS = 300_000; // 5 minutes timeout
|
|
359
|
+
|
|
360
|
+
let pollInterval: ReturnType<typeof setInterval> | null = null;
|
|
361
|
+
let pollTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
362
|
+
let windowCheckInterval: ReturnType<typeof setInterval> | null = null;
|
|
363
|
+
let messageHandler: ((event: MessageEvent) => void) | null = null;
|
|
364
|
+
let resolved = false;
|
|
365
|
+
|
|
366
|
+
const cleanup = () => {
|
|
367
|
+
if (windowCheckInterval) {
|
|
368
|
+
clearInterval(windowCheckInterval);
|
|
369
|
+
windowCheckInterval = null;
|
|
370
|
+
}
|
|
371
|
+
if (pollInterval) {
|
|
372
|
+
clearInterval(pollInterval);
|
|
373
|
+
pollInterval = null;
|
|
374
|
+
}
|
|
375
|
+
if (pollTimeout) {
|
|
376
|
+
clearTimeout(pollTimeout);
|
|
377
|
+
pollTimeout = null;
|
|
378
|
+
}
|
|
379
|
+
if (messageHandler) {
|
|
380
|
+
window.removeEventListener('message', messageHandler);
|
|
381
|
+
messageHandler = null;
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
const resolveOnce = (result: { cancelled: boolean; success: boolean }) => {
|
|
386
|
+
if (resolved) return;
|
|
387
|
+
resolved = true;
|
|
388
|
+
cleanup();
|
|
389
|
+
resolve(result);
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// Listen for OAuth success message from popup window
|
|
393
|
+
messageHandler = async (event: MessageEvent) => {
|
|
394
|
+
// Verify origin for security
|
|
395
|
+
if (event.origin !== window.location.origin) return;
|
|
396
|
+
|
|
397
|
+
if (
|
|
398
|
+
event.data?.type === 'LOBEHUB_SKILL_AUTH_SUCCESS' &&
|
|
399
|
+
event.data?.provider === provider
|
|
400
|
+
) {
|
|
401
|
+
console.log('[LobehubSkill] OAuth success message received for provider:', provider);
|
|
402
|
+
|
|
403
|
+
// Refresh status to get the connected state
|
|
404
|
+
const server = await getToolStoreState().checkLobehubSkillStatus(provider);
|
|
405
|
+
const isConnected = server?.status === LobehubSkillStatus.CONNECTED;
|
|
406
|
+
|
|
407
|
+
resolveOnce({ cancelled: false, success: isConnected });
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
window.addEventListener('message', messageHandler);
|
|
412
|
+
|
|
413
|
+
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
414
|
+
const checkAuthStatus = async (): Promise<boolean> => {
|
|
415
|
+
try {
|
|
416
|
+
// Check LobehubSkill status
|
|
417
|
+
const server = await getToolStoreState().checkLobehubSkillStatus(provider);
|
|
418
|
+
return server?.status === LobehubSkillStatus.CONNECTED;
|
|
419
|
+
} catch (error) {
|
|
420
|
+
console.error('[LobehubSkill] Failed to check auth status:', error);
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const startFallbackPolling = () => {
|
|
426
|
+
if (pollInterval) return;
|
|
427
|
+
|
|
428
|
+
pollInterval = setInterval(async () => {
|
|
429
|
+
const isConnected = await checkAuthStatus();
|
|
430
|
+
if (isConnected) {
|
|
431
|
+
resolveOnce({ cancelled: false, success: true });
|
|
432
|
+
}
|
|
433
|
+
}, POLL_INTERVAL_MS);
|
|
434
|
+
|
|
435
|
+
// Timeout after 5 minutes
|
|
436
|
+
pollTimeout = setTimeout(() => {
|
|
437
|
+
resolveOnce({ cancelled: true, success: false });
|
|
438
|
+
}, POLL_TIMEOUT_MS);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// Open OAuth window
|
|
442
|
+
const oauthWindow = window.open(oauthUrl, '_blank', 'width=600,height=700');
|
|
443
|
+
|
|
444
|
+
if (oauthWindow) {
|
|
445
|
+
// Monitor window close
|
|
446
|
+
windowCheckInterval = setInterval(async () => {
|
|
447
|
+
try {
|
|
448
|
+
if (oauthWindow.closed) {
|
|
449
|
+
if (windowCheckInterval) {
|
|
450
|
+
clearInterval(windowCheckInterval);
|
|
451
|
+
windowCheckInterval = null;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Window closed, check auth status
|
|
455
|
+
const isConnected = await checkAuthStatus();
|
|
456
|
+
resolveOnce({ cancelled: !isConnected, success: isConnected });
|
|
457
|
+
}
|
|
458
|
+
} catch {
|
|
459
|
+
// COOP blocked window.closed access, fall back to polling
|
|
460
|
+
console.log(
|
|
461
|
+
'[LobehubSkill] COOP blocked window.closed access, falling back to polling',
|
|
462
|
+
);
|
|
463
|
+
if (windowCheckInterval) {
|
|
464
|
+
clearInterval(windowCheckInterval);
|
|
465
|
+
windowCheckInterval = null;
|
|
466
|
+
}
|
|
467
|
+
startFallbackPolling();
|
|
468
|
+
}
|
|
469
|
+
}, WINDOW_CHECK_INTERVAL_MS);
|
|
470
|
+
} else {
|
|
471
|
+
// Window was blocked, use polling
|
|
472
|
+
console.log('[LobehubSkill] OAuth window was blocked, falling back to polling');
|
|
473
|
+
startFallbackPolling();
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Open OAuth window and wait for Klavis authentication completion
|
|
348
480
|
* Returns a Promise that resolves when OAuth completes or fails
|
|
349
481
|
*/
|
|
350
482
|
private openOAuthWindowAndWait(
|
|
@@ -777,34 +909,187 @@ export class AgentBuilderExecutionRuntime {
|
|
|
777
909
|
success: true,
|
|
778
910
|
};
|
|
779
911
|
} else {
|
|
780
|
-
// Server exists but not connected - need to reconnect
|
|
912
|
+
// Server exists but not connected - need to reconnect via OAuth
|
|
913
|
+
try {
|
|
914
|
+
// Get OAuth authorization URL with correct redirectUri
|
|
915
|
+
const redirectUri =
|
|
916
|
+
typeof window !== 'undefined'
|
|
917
|
+
? `${window.location.origin}/oauth/callback/success?provider=${encodeURIComponent(identifier)}`
|
|
918
|
+
: undefined;
|
|
919
|
+
const authInfo = await getToolStoreState().getLobehubSkillAuthorizeUrl(
|
|
920
|
+
identifier,
|
|
921
|
+
{
|
|
922
|
+
redirectUri,
|
|
923
|
+
},
|
|
924
|
+
);
|
|
925
|
+
|
|
926
|
+
if (!authInfo.authorizeUrl) {
|
|
927
|
+
return {
|
|
928
|
+
content: `LobehubSkill provider "${lobehubSkillProviderInfo.label}" requires OAuth authorization but no authorization URL is available.`,
|
|
929
|
+
state: {
|
|
930
|
+
installed: false,
|
|
931
|
+
isLobehubSkill: true,
|
|
932
|
+
pluginId: identifier,
|
|
933
|
+
pluginName: lobehubSkillProviderInfo.label,
|
|
934
|
+
serverStatus: lobehubSkillServer.status,
|
|
935
|
+
success: false,
|
|
936
|
+
} as InstallPluginState,
|
|
937
|
+
success: false,
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Open OAuth window and wait for authorization
|
|
942
|
+
const authResult = await this.openLobehubSkillOAuthWindowAndWait(
|
|
943
|
+
authInfo.authorizeUrl,
|
|
944
|
+
identifier,
|
|
945
|
+
);
|
|
946
|
+
|
|
947
|
+
if (authResult.success) {
|
|
948
|
+
// OAuth successful, enable the plugin
|
|
949
|
+
const agentState = getAgentStoreState();
|
|
950
|
+
const currentPlugins =
|
|
951
|
+
agentSelectors.getAgentConfigById(agentId)(agentState).plugins || [];
|
|
952
|
+
|
|
953
|
+
if (!currentPlugins.includes(identifier)) {
|
|
954
|
+
await getAgentStoreState().optimisticUpdateAgentConfig(agentId, {
|
|
955
|
+
plugins: [...currentPlugins, identifier],
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
return {
|
|
960
|
+
content: `Successfully reconnected and enabled LobehubSkill provider: ${lobehubSkillProviderInfo.label}`,
|
|
961
|
+
state: {
|
|
962
|
+
installed: true,
|
|
963
|
+
isLobehubSkill: true,
|
|
964
|
+
pluginId: identifier,
|
|
965
|
+
pluginName: lobehubSkillProviderInfo.label,
|
|
966
|
+
serverStatus: 'connected',
|
|
967
|
+
success: true,
|
|
968
|
+
} as InstallPluginState,
|
|
969
|
+
success: true,
|
|
970
|
+
};
|
|
971
|
+
} else {
|
|
972
|
+
// OAuth cancelled or failed
|
|
973
|
+
return {
|
|
974
|
+
content: `OAuth authorization was cancelled or failed for LobehubSkill provider: ${lobehubSkillProviderInfo.label}. Please try again.`,
|
|
975
|
+
state: {
|
|
976
|
+
installed: false,
|
|
977
|
+
isLobehubSkill: true,
|
|
978
|
+
pluginId: identifier,
|
|
979
|
+
pluginName: lobehubSkillProviderInfo.label,
|
|
980
|
+
serverStatus: lobehubSkillServer.status,
|
|
981
|
+
success: false,
|
|
982
|
+
} as InstallPluginState,
|
|
983
|
+
success: false,
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
} catch (authError) {
|
|
987
|
+
const err = authError as Error;
|
|
988
|
+
return {
|
|
989
|
+
content: `Failed to reconnect LobehubSkill provider "${lobehubSkillProviderInfo.label}": ${err.message}`,
|
|
990
|
+
error: authError,
|
|
991
|
+
state: {
|
|
992
|
+
error: err.message,
|
|
993
|
+
installed: false,
|
|
994
|
+
isLobehubSkill: true,
|
|
995
|
+
pluginId: identifier,
|
|
996
|
+
pluginName: lobehubSkillProviderInfo.label,
|
|
997
|
+
serverStatus: lobehubSkillServer.status,
|
|
998
|
+
success: false,
|
|
999
|
+
} as InstallPluginState,
|
|
1000
|
+
success: false,
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
} else {
|
|
1005
|
+
// Server doesn't exist - need to initiate OAuth authorization
|
|
1006
|
+
try {
|
|
1007
|
+
// Get OAuth authorization URL with correct redirectUri
|
|
1008
|
+
const redirectUri =
|
|
1009
|
+
typeof window !== 'undefined'
|
|
1010
|
+
? `${window.location.origin}/oauth/callback/success?provider=${encodeURIComponent(identifier)}`
|
|
1011
|
+
: undefined;
|
|
1012
|
+
const authInfo = await getToolStoreState().getLobehubSkillAuthorizeUrl(identifier, {
|
|
1013
|
+
redirectUri,
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
if (!authInfo.authorizeUrl) {
|
|
1017
|
+
return {
|
|
1018
|
+
content: `LobehubSkill provider "${lobehubSkillProviderInfo.label}" requires OAuth authorization but no authorization URL is available.`,
|
|
1019
|
+
state: {
|
|
1020
|
+
installed: false,
|
|
1021
|
+
isLobehubSkill: true,
|
|
1022
|
+
pluginId: identifier,
|
|
1023
|
+
pluginName: lobehubSkillProviderInfo.label,
|
|
1024
|
+
serverStatus: 'not_connected',
|
|
1025
|
+
success: false,
|
|
1026
|
+
} as InstallPluginState,
|
|
1027
|
+
success: false,
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Open OAuth window and wait for authorization
|
|
1032
|
+
const authResult = await this.openLobehubSkillOAuthWindowAndWait(
|
|
1033
|
+
authInfo.authorizeUrl,
|
|
1034
|
+
identifier,
|
|
1035
|
+
);
|
|
1036
|
+
|
|
1037
|
+
if (authResult.success) {
|
|
1038
|
+
// OAuth successful, enable the plugin
|
|
1039
|
+
const agentState = getAgentStoreState();
|
|
1040
|
+
const currentPlugins =
|
|
1041
|
+
agentSelectors.getAgentConfigById(agentId)(agentState).plugins || [];
|
|
1042
|
+
|
|
1043
|
+
if (!currentPlugins.includes(identifier)) {
|
|
1044
|
+
await getAgentStoreState().optimisticUpdateAgentConfig(agentId, {
|
|
1045
|
+
plugins: [...currentPlugins, identifier],
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
return {
|
|
1050
|
+
content: `Successfully connected and enabled LobehubSkill provider: ${lobehubSkillProviderInfo.label}`,
|
|
1051
|
+
state: {
|
|
1052
|
+
installed: true,
|
|
1053
|
+
isLobehubSkill: true,
|
|
1054
|
+
pluginId: identifier,
|
|
1055
|
+
pluginName: lobehubSkillProviderInfo.label,
|
|
1056
|
+
serverStatus: 'connected',
|
|
1057
|
+
success: true,
|
|
1058
|
+
} as InstallPluginState,
|
|
1059
|
+
success: true,
|
|
1060
|
+
};
|
|
1061
|
+
} else {
|
|
1062
|
+
// OAuth cancelled or failed
|
|
1063
|
+
return {
|
|
1064
|
+
content: `OAuth authorization was cancelled or failed for LobehubSkill provider: ${lobehubSkillProviderInfo.label}. Please try again.`,
|
|
1065
|
+
state: {
|
|
1066
|
+
installed: false,
|
|
1067
|
+
isLobehubSkill: true,
|
|
1068
|
+
pluginId: identifier,
|
|
1069
|
+
pluginName: lobehubSkillProviderInfo.label,
|
|
1070
|
+
serverStatus: 'not_connected',
|
|
1071
|
+
success: false,
|
|
1072
|
+
} as InstallPluginState,
|
|
1073
|
+
success: false,
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
} catch (authError) {
|
|
1077
|
+
const err = authError as Error;
|
|
781
1078
|
return {
|
|
782
|
-
content: `LobehubSkill provider "${lobehubSkillProviderInfo.label}"
|
|
1079
|
+
content: `Failed to authorize LobehubSkill provider "${lobehubSkillProviderInfo.label}": ${err.message}`,
|
|
1080
|
+
error: authError,
|
|
783
1081
|
state: {
|
|
1082
|
+
error: err.message,
|
|
784
1083
|
installed: false,
|
|
785
1084
|
isLobehubSkill: true,
|
|
786
1085
|
pluginId: identifier,
|
|
787
1086
|
pluginName: lobehubSkillProviderInfo.label,
|
|
788
|
-
serverStatus:
|
|
1087
|
+
serverStatus: 'not_connected',
|
|
789
1088
|
success: false,
|
|
790
1089
|
} as InstallPluginState,
|
|
791
1090
|
success: false,
|
|
792
1091
|
};
|
|
793
1092
|
}
|
|
794
|
-
} else {
|
|
795
|
-
// Server doesn't exist - need to connect first
|
|
796
|
-
return {
|
|
797
|
-
content: `LobehubSkill provider "${lobehubSkillProviderInfo.label}" is not connected. Please connect it from the tools settings first.`,
|
|
798
|
-
state: {
|
|
799
|
-
installed: false,
|
|
800
|
-
isLobehubSkill: true,
|
|
801
|
-
pluginId: identifier,
|
|
802
|
-
pluginName: lobehubSkillProviderInfo.label,
|
|
803
|
-
serverStatus: 'not_connected',
|
|
804
|
-
success: false,
|
|
805
|
-
} as InstallPluginState,
|
|
806
|
-
success: false,
|
|
807
|
-
};
|
|
808
1093
|
}
|
|
809
1094
|
}
|
|
810
1095
|
}
|
|
@@ -878,18 +1163,65 @@ export class AgentBuilderExecutionRuntime {
|
|
|
878
1163
|
};
|
|
879
1164
|
}
|
|
880
1165
|
|
|
881
|
-
// Plugin needs to be installed -
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1166
|
+
// Plugin needs to be installed - trigger actual installation from market
|
|
1167
|
+
try {
|
|
1168
|
+
// Call the store's installMCPPlugin to trigger the real installation flow
|
|
1169
|
+
const installSuccess = await getToolStoreState().installMCPPlugin(identifier);
|
|
1170
|
+
|
|
1171
|
+
if (installSuccess) {
|
|
1172
|
+
// Installation successful, enable it for the agent
|
|
1173
|
+
const agentState = getAgentStoreState();
|
|
1174
|
+
const currentPlugins =
|
|
1175
|
+
agentSelectors.getAgentConfigById(agentId)(agentState).plugins || [];
|
|
1176
|
+
|
|
1177
|
+
if (!currentPlugins.includes(identifier)) {
|
|
1178
|
+
await getAgentStoreState().optimisticUpdateAgentConfig(agentId, {
|
|
1179
|
+
plugins: [...currentPlugins, identifier],
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
// Refresh tool state to get the installed plugin info
|
|
1184
|
+
await getToolStoreState().refreshPlugins();
|
|
1185
|
+
const freshToolState = getToolStoreState();
|
|
1186
|
+
const installedPlugin =
|
|
1187
|
+
pluginSelectors.getInstalledPluginById(identifier)(freshToolState);
|
|
1188
|
+
|
|
1189
|
+
return {
|
|
1190
|
+
content: `Successfully installed and enabled MCP plugin "${installedPlugin?.manifest?.meta?.title || identifier}".`,
|
|
1191
|
+
state: {
|
|
1192
|
+
installed: true,
|
|
1193
|
+
pluginId: identifier,
|
|
1194
|
+
pluginName: installedPlugin?.manifest?.meta?.title || identifier,
|
|
1195
|
+
success: true,
|
|
1196
|
+
} as InstallPluginState,
|
|
1197
|
+
success: true,
|
|
1198
|
+
};
|
|
1199
|
+
} else {
|
|
1200
|
+
// Installation failed or was cancelled
|
|
1201
|
+
return {
|
|
1202
|
+
content: `Failed to install MCP plugin "${identifier}". Installation was cancelled or configuration is needed.`,
|
|
1203
|
+
state: {
|
|
1204
|
+
installed: false,
|
|
1205
|
+
pluginId: identifier,
|
|
1206
|
+
success: false,
|
|
1207
|
+
} as InstallPluginState,
|
|
1208
|
+
success: false,
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
} catch (installError) {
|
|
1212
|
+
const err = installError as Error;
|
|
1213
|
+
return {
|
|
1214
|
+
content: `Failed to install MCP plugin "${identifier}": ${err.message}`,
|
|
1215
|
+
error: installError,
|
|
1216
|
+
state: {
|
|
1217
|
+
error: err.message,
|
|
1218
|
+
installed: false,
|
|
1219
|
+
pluginId: identifier,
|
|
1220
|
+
success: false,
|
|
1221
|
+
} as InstallPluginState,
|
|
1222
|
+
success: false,
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
893
1225
|
} catch (error) {
|
|
894
1226
|
const err = error as Error;
|
|
895
1227
|
return {
|
|
@@ -12,6 +12,7 @@ import { useToolStore } from '@/store/tool';
|
|
|
12
12
|
import {
|
|
13
13
|
klavisStoreSelectors,
|
|
14
14
|
lobehubSkillStoreSelectors,
|
|
15
|
+
mcpStoreSelectors,
|
|
15
16
|
pluginSelectors,
|
|
16
17
|
} from '@/store/tool/selectors';
|
|
17
18
|
import { KlavisServerStatus } from '@/store/tool/slices/klavisStore/types';
|
|
@@ -44,6 +45,14 @@ const InstallPluginIntervention = memo<BuiltinInterventionProps<InstallPluginPar
|
|
|
44
45
|
lobehubSkillStoreSelectors.getServers(s).find((srv) => srv.identifier === identifier),
|
|
45
46
|
);
|
|
46
47
|
|
|
48
|
+
// Get Market MCP plugin info
|
|
49
|
+
const marketPlugin = useToolStore((s) => mcpStoreSelectors.getPluginById(identifier)(s));
|
|
50
|
+
|
|
51
|
+
// Get Builtin tool info
|
|
52
|
+
const builtinTool = useToolStore((s) =>
|
|
53
|
+
s.builtinTools.find((tool) => tool.identifier === identifier),
|
|
54
|
+
);
|
|
55
|
+
|
|
47
56
|
// Check if it's a Klavis tool
|
|
48
57
|
const klavisTypeInfo = KLAVIS_SERVER_TYPES.find((t) => t.identifier === identifier);
|
|
49
58
|
const isKlavis = source === 'official' && !!klavisTypeInfo;
|
|
@@ -166,19 +175,34 @@ const InstallPluginIntervention = memo<BuiltinInterventionProps<InstallPluginPar
|
|
|
166
175
|
);
|
|
167
176
|
}
|
|
168
177
|
|
|
169
|
-
// Render MCP marketplace plugin
|
|
178
|
+
// Render MCP marketplace plugin or Builtin tool
|
|
170
179
|
// Note: The actual installation happens in ExecutionRuntime after user approves
|
|
180
|
+
const pluginName = marketPlugin?.name || builtinTool?.manifest?.meta?.title || identifier;
|
|
181
|
+
const pluginIcon = marketPlugin?.icon || builtinTool?.manifest?.meta?.avatar;
|
|
182
|
+
const pluginType = source === 'market' ? 'MCP Plugin' : 'Builtin Tool';
|
|
183
|
+
|
|
171
184
|
return (
|
|
172
185
|
<Flexbox
|
|
173
186
|
gap={12}
|
|
174
187
|
style={{ background: 'var(--lobe-fill-tertiary)', borderRadius: 8, padding: 16 }}
|
|
175
188
|
>
|
|
176
189
|
<Flexbox align="center" gap={12} horizontal>
|
|
177
|
-
|
|
190
|
+
{pluginIcon && typeof pluginIcon === 'string' && pluginIcon.startsWith('http') ? (
|
|
191
|
+
<Image
|
|
192
|
+
alt={pluginName}
|
|
193
|
+
height={40}
|
|
194
|
+
src={pluginIcon}
|
|
195
|
+
style={{ borderRadius: 8 }}
|
|
196
|
+
unoptimized
|
|
197
|
+
width={40}
|
|
198
|
+
/>
|
|
199
|
+
) : (
|
|
200
|
+
<Avatar avatar={pluginIcon || '🔧'} size={40} style={{ borderRadius: 8 }} />
|
|
201
|
+
)}
|
|
178
202
|
<Flexbox flex={1} gap={4}>
|
|
179
203
|
<Flexbox align="center" gap={8} horizontal>
|
|
180
|
-
<span style={{ fontWeight: 600 }}>{
|
|
181
|
-
<span style={{ color: 'var(--lobe-text-tertiary)', fontSize: 12 }}>
|
|
204
|
+
<span style={{ fontWeight: 600 }}>{pluginName}</span>
|
|
205
|
+
<span style={{ color: 'var(--lobe-text-tertiary)', fontSize: 12 }}>{pluginType}</span>
|
|
182
206
|
</Flexbox>
|
|
183
207
|
<span style={{ color: 'var(--lobe-text-secondary)', fontSize: 12 }}>
|
|
184
208
|
{t('agentBuilder.installPlugin.clickApproveToInstall')}
|
|
@@ -7,9 +7,9 @@ const log = debug('context-engine:processor:GroupMessageFlattenProcessor');
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Group Message Flatten Processor
|
|
10
|
-
* Responsible for flattening role=assistantGroup messages into standard assistant + tool message sequences
|
|
10
|
+
* Responsible for flattening role=assistantGroup and role=supervisor messages into standard assistant + tool message sequences
|
|
11
11
|
*
|
|
12
|
-
* AssistantGroup messages are created when assistant messages with tools are merged with their tool results.
|
|
12
|
+
* AssistantGroup/Supervisor messages are created when assistant messages with tools are merged with their tool results.
|
|
13
13
|
* This processor converts them back to a flat structure that AI models can understand.
|
|
14
14
|
*/
|
|
15
15
|
export class GroupMessageFlattenProcessor extends BaseProcessor {
|
|
@@ -31,8 +31,11 @@ export class GroupMessageFlattenProcessor extends BaseProcessor {
|
|
|
31
31
|
|
|
32
32
|
// Process each message
|
|
33
33
|
for (const message of clonedContext.messages) {
|
|
34
|
-
// Check if this is an assistantGroup message with children field
|
|
35
|
-
if (
|
|
34
|
+
// Check if this is an assistantGroup or supervisor message with children field
|
|
35
|
+
if (
|
|
36
|
+
(message.role === 'assistantGroup' || message.role === 'supervisor') &&
|
|
37
|
+
message.children
|
|
38
|
+
) {
|
|
36
39
|
// If children array is empty, skip this message entirely (no content to flatten)
|
|
37
40
|
if (message.children.length === 0) {
|
|
38
41
|
continue;
|
|
@@ -42,7 +45,7 @@ export class GroupMessageFlattenProcessor extends BaseProcessor {
|
|
|
42
45
|
groupMessagesFlattened++;
|
|
43
46
|
|
|
44
47
|
log(
|
|
45
|
-
`Flattening
|
|
48
|
+
`Flattening ${message.role} message ${message.id} with ${message.children.length} children`,
|
|
46
49
|
);
|
|
47
50
|
|
|
48
51
|
// Flatten each child
|
|
@@ -148,7 +151,7 @@ export class GroupMessageFlattenProcessor extends BaseProcessor {
|
|
|
148
151
|
clonedContext.metadata.toolMessagesCreated = toolMessagesCreated;
|
|
149
152
|
|
|
150
153
|
log(
|
|
151
|
-
`AssistantGroup message flatten processing completed: ${groupMessagesFlattened} groups flattened, ${assistantMessagesCreated} assistant messages created, ${toolMessagesCreated} tool messages created`,
|
|
154
|
+
`AssistantGroup/Supervisor message flatten processing completed: ${groupMessagesFlattened} groups flattened, ${assistantMessagesCreated} assistant messages created, ${toolMessagesCreated} tool messages created`,
|
|
152
155
|
);
|
|
153
156
|
|
|
154
157
|
return this.markAsExecuted(clonedContext);
|