@lobehub/lobehub 2.0.0-next.312 → 2.0.0-next.313
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 +26 -0
- package/apps/desktop/src/main/controllers/AuthCtr.ts +75 -7
- package/changelog/v1.json +9 -0
- package/docs/usage/providers/internlm.mdx +2 -2
- package/docs/usage/providers/internlm.zh-CN.mdx +3 -3
- package/locales/en-US/error.json +10 -1
- package/locales/en-US/subscription.json +1 -1
- package/locales/zh-CN/desktop-onboarding.json +5 -0
- package/locales/zh-CN/error.json +10 -1
- package/locales/zh-CN/subscription.json +1 -1
- package/package.json +1 -1
- package/packages/agent-runtime/src/agents/GeneralChatAgent.ts +14 -2
- package/packages/agent-runtime/src/agents/__tests__/GeneralChatAgent.test.ts +275 -1
- package/packages/builtin-tool-cloud-sandbox/package.json +1 -0
- package/packages/builtin-tool-cloud-sandbox/src/ExecutionRuntime/index.ts +105 -134
- package/packages/builtin-tool-cloud-sandbox/src/executor/index.ts +254 -0
- package/packages/builtin-tool-cloud-sandbox/src/index.ts +1 -0
- package/packages/builtin-tool-cloud-sandbox/src/types/api.ts +22 -0
- package/packages/builtin-tool-cloud-sandbox/src/types/index.ts +4 -0
- package/packages/builtin-tool-cloud-sandbox/src/types/params.ts +85 -0
- package/packages/builtin-tool-cloud-sandbox/src/types/service.ts +48 -0
- package/packages/builtin-tool-cloud-sandbox/src/{types.ts → types/state.ts} +0 -23
- package/packages/builtin-tool-memory/src/manifest.ts +5 -5
- package/packages/editor-runtime/src/__tests__/EditorRuntime.real.test.ts +1 -1
- package/packages/editor-runtime/src/__tests__/EditorRuntime.test.ts +1 -1
- package/packages/electron-client-ipc/src/events/index.ts +5 -1
- package/packages/electron-client-ipc/src/events/remoteServer.ts +23 -0
- package/packages/memory-user-memory/src/schemas/index.ts +0 -1
- package/packages/model-bank/src/modelProviders/internlm.ts +1 -1
- package/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts +5 -15
- package/packages/model-runtime/src/providers/internlm/index.test.ts +15 -15
- package/packages/model-runtime/src/providers/internlm/index.ts +1 -1
- package/packages/types/src/tool/intervention.ts +4 -2
- package/packages/types/src/user/preference.ts +1 -0
- package/src/app/[variants]/(desktop)/desktop-onboarding/features/LoginStep.tsx +84 -26
- package/src/app/[variants]/(main)/_layout/DesktopAutoOidcOnFirstOpen.tsx +4 -0
- package/src/business/server/user.ts +4 -0
- package/src/features/Conversation/Messages/Task/Actions/index.tsx +0 -2
- package/src/features/Conversation/Messages/Task/index.tsx +1 -1
- package/src/features/Conversation/Messages/Tasks/shared/ProcessingState.tsx +0 -2
- package/src/features/NavPanel/components/NavPanelDraggable.tsx +0 -14
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +4 -3
- package/src/features/SharePopover/index.tsx +5 -3
- package/src/hooks/useAppOrigin.ts +16 -0
- package/src/layout/GlobalProvider/useUserStateRedirect.ts +37 -24
- package/src/libs/trusted-client/index.ts +2 -5
- package/src/locales/default/desktop-onboarding.ts +5 -0
- package/src/locales/default/error.ts +11 -0
- package/src/locales/default/subscription.ts +1 -1
- package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +2 -0
- package/src/server/routers/lambda/user.ts +24 -10
- package/src/server/services/agentRuntime/AgentRuntimeService.test.ts +3 -0
- package/src/server/services/agentRuntime/AgentRuntimeService.ts +8 -5
- package/src/server/services/agentRuntime/types.ts +7 -0
- package/src/server/services/aiAgent/__tests__/execGroupSubAgentTask.test.ts +3 -0
- package/src/server/services/aiAgent/index.ts +10 -4
- package/src/server/services/market/index.ts +7 -0
- package/src/server/services/sandbox/index.ts +120 -0
- package/src/server/services/toolExecution/builtin.ts +12 -18
- package/src/server/services/toolExecution/index.ts +1 -1
- package/src/server/services/toolExecution/serverRuntimes/cloudSandbox.ts +31 -0
- package/src/server/services/toolExecution/serverRuntimes/index.ts +55 -0
- package/src/server/services/toolExecution/serverRuntimes/types.ts +14 -0
- package/src/server/services/toolExecution/serverRuntimes/webBrowsing.ts +20 -0
- package/src/server/services/toolExecution/types.ts +2 -0
- package/src/services/{codeInterpreter.ts → cloudSandbox.ts} +3 -3
- package/src/services/electron/remoteServer.ts +8 -0
- package/src/store/chat/agents/GroupOrchestration/__tests__/batch-exec-async-tasks.test.ts +626 -0
- package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +294 -0
- package/src/store/chat/slices/plugin/action.test.ts +0 -48
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +0 -131
- package/src/store/tool/slices/builtin/executors/index.ts +2 -0
- package/src/store/user/slices/settings/selectors/toolIntervention.test.ts +143 -0
- package/src/store/user/slices/settings/selectors/toolIntervention.ts +11 -2
- package/packages/memory-user-memory/src/schemas/jsonSchemas.ts +0 -37
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.313](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.312...v2.0.0-next.313)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-19**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Fix server agent task run with headless, internlm provider base url and homepage.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Fix server agent task run with headless, closes [#11600](https://github.com/lobehub/lobe-chat/issues/11600) ([435eede](https://github.com/lobehub/lobe-chat/commit/435eede))
|
|
21
|
+
- **misc**: Internlm provider base url and homepage, closes [#11612](https://github.com/lobehub/lobe-chat/issues/11612) ([38725da](https://github.com/lobehub/lobe-chat/commit/38725da))
|
|
22
|
+
|
|
23
|
+
</details>
|
|
24
|
+
|
|
25
|
+
<div align="right">
|
|
26
|
+
|
|
27
|
+
[](#readme-top)
|
|
28
|
+
|
|
29
|
+
</div>
|
|
30
|
+
|
|
5
31
|
## [Version 2.0.0-next.312](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.311...v2.0.0-next.312)
|
|
6
32
|
|
|
7
33
|
<sup>Released on **2026-01-19**</sup>
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AuthorizationPhase,
|
|
3
|
+
AuthorizationProgress,
|
|
4
|
+
DataSyncConfig,
|
|
5
|
+
MarketAuthorizationParams,
|
|
6
|
+
} from '@lobechat/electron-client-ipc';
|
|
2
7
|
import { BrowserWindow, shell } from 'electron';
|
|
3
8
|
import crypto from 'node:crypto';
|
|
4
9
|
import querystring from 'node:querystring';
|
|
@@ -9,9 +14,11 @@ import { createLogger } from '@/utils/logger';
|
|
|
9
14
|
import RemoteServerConfigCtr from './RemoteServerConfigCtr';
|
|
10
15
|
import { ControllerModule, IpcMethod } from './index';
|
|
11
16
|
|
|
12
|
-
// Create logger
|
|
13
17
|
const logger = createLogger('controllers:AuthCtr');
|
|
14
18
|
|
|
19
|
+
const MAX_POLL_TIME = 2 * 60 * 1000; // 2 minutes (reduced from 5 minutes for better UX)
|
|
20
|
+
const POLL_INTERVAL = 3000; // 3 seconds
|
|
21
|
+
|
|
15
22
|
/**
|
|
16
23
|
* Authentication Controller
|
|
17
24
|
* Implements OAuth authorization flow using intermediate page + polling mechanism
|
|
@@ -107,6 +114,12 @@ export default class AuthCtr extends ControllerModule {
|
|
|
107
114
|
await shell.openExternal(authUrl.toString());
|
|
108
115
|
logger.debug('Opening authorization URL in default browser');
|
|
109
116
|
|
|
117
|
+
this.broadcastAuthorizationProgress({
|
|
118
|
+
elapsed: 0,
|
|
119
|
+
maxPollTime: MAX_POLL_TIME,
|
|
120
|
+
phase: 'browser_opened',
|
|
121
|
+
});
|
|
122
|
+
|
|
110
123
|
// Start polling for credentials
|
|
111
124
|
this.startPolling();
|
|
112
125
|
|
|
@@ -117,6 +130,24 @@ export default class AuthCtr extends ControllerModule {
|
|
|
117
130
|
}
|
|
118
131
|
}
|
|
119
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Cancel current authorization process
|
|
135
|
+
*/
|
|
136
|
+
@IpcMethod()
|
|
137
|
+
async cancelAuthorization() {
|
|
138
|
+
if (this.authRequestState) {
|
|
139
|
+
logger.info('User cancelled authorization');
|
|
140
|
+
this.clearAuthorizationState();
|
|
141
|
+
this.broadcastAuthorizationProgress({
|
|
142
|
+
elapsed: 0,
|
|
143
|
+
maxPollTime: MAX_POLL_TIME,
|
|
144
|
+
phase: 'cancelled',
|
|
145
|
+
});
|
|
146
|
+
return { success: true };
|
|
147
|
+
}
|
|
148
|
+
return { error: 'No active authorization', success: false };
|
|
149
|
+
}
|
|
150
|
+
|
|
120
151
|
/**
|
|
121
152
|
* Request Market OAuth authorization (desktop)
|
|
122
153
|
*/
|
|
@@ -152,14 +183,29 @@ export default class AuthCtr extends ControllerModule {
|
|
|
152
183
|
}
|
|
153
184
|
|
|
154
185
|
logger.info('Starting credential polling');
|
|
155
|
-
|
|
156
|
-
const maxPollTime = 5 * 60 * 1000; // 5 minutes
|
|
186
|
+
|
|
157
187
|
const startTime = Date.now();
|
|
158
188
|
|
|
189
|
+
// Broadcast initial state
|
|
190
|
+
this.broadcastAuthorizationProgress({
|
|
191
|
+
elapsed: 0,
|
|
192
|
+
maxPollTime: MAX_POLL_TIME,
|
|
193
|
+
phase: 'waiting_for_auth',
|
|
194
|
+
});
|
|
195
|
+
|
|
159
196
|
this.pollingInterval = setInterval(async () => {
|
|
197
|
+
const elapsed = Date.now() - startTime;
|
|
198
|
+
|
|
199
|
+
// Broadcast progress on every tick
|
|
200
|
+
this.broadcastAuthorizationProgress({
|
|
201
|
+
elapsed,
|
|
202
|
+
maxPollTime: MAX_POLL_TIME,
|
|
203
|
+
phase: 'waiting_for_auth',
|
|
204
|
+
});
|
|
205
|
+
|
|
160
206
|
try {
|
|
161
207
|
// Check if polling has timed out
|
|
162
|
-
if (
|
|
208
|
+
if (elapsed > MAX_POLL_TIME) {
|
|
163
209
|
logger.warn('Credential polling timed out');
|
|
164
210
|
this.clearAuthorizationState();
|
|
165
211
|
this.broadcastAuthorizationFailed('Authorization timed out');
|
|
@@ -173,6 +219,13 @@ export default class AuthCtr extends ControllerModule {
|
|
|
173
219
|
logger.info('Successfully received credentials from polling');
|
|
174
220
|
this.stopPolling();
|
|
175
221
|
|
|
222
|
+
// Broadcast verifying state
|
|
223
|
+
this.broadcastAuthorizationProgress({
|
|
224
|
+
elapsed,
|
|
225
|
+
maxPollTime: MAX_POLL_TIME,
|
|
226
|
+
phase: 'verifying',
|
|
227
|
+
});
|
|
228
|
+
|
|
176
229
|
// Validate state parameter
|
|
177
230
|
if (result.state !== this.authRequestState) {
|
|
178
231
|
logger.error(
|
|
@@ -198,7 +251,7 @@ export default class AuthCtr extends ControllerModule {
|
|
|
198
251
|
this.clearAuthorizationState();
|
|
199
252
|
this.broadcastAuthorizationFailed('Polling error: ' + error.message);
|
|
200
253
|
}
|
|
201
|
-
},
|
|
254
|
+
}, POLL_INTERVAL);
|
|
202
255
|
}
|
|
203
256
|
|
|
204
257
|
/**
|
|
@@ -511,6 +564,21 @@ export default class AuthCtr extends ControllerModule {
|
|
|
511
564
|
}
|
|
512
565
|
}
|
|
513
566
|
|
|
567
|
+
/**
|
|
568
|
+
* Broadcast authorization progress event
|
|
569
|
+
*/
|
|
570
|
+
private broadcastAuthorizationProgress(progress: AuthorizationProgress) {
|
|
571
|
+
// Avoid logging too frequently
|
|
572
|
+
// logger.debug('Broadcasting authorizationProgress event');
|
|
573
|
+
const allWindows = BrowserWindow.getAllWindows();
|
|
574
|
+
|
|
575
|
+
for (const win of allWindows) {
|
|
576
|
+
if (!win.isDestroyed()) {
|
|
577
|
+
win.webContents.send('authorizationProgress', progress);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
514
582
|
/**
|
|
515
583
|
* Broadcast authorization failed event
|
|
516
584
|
*/
|
|
@@ -563,7 +631,7 @@ export default class AuthCtr extends ControllerModule {
|
|
|
563
631
|
// Hash codeVerifier using SHA-256
|
|
564
632
|
const encoder = new TextEncoder();
|
|
565
633
|
const data = encoder.encode(codeVerifier);
|
|
566
|
-
const digest = await crypto.subtle.digest('SHA-256', data
|
|
634
|
+
const digest = await crypto.subtle.digest('SHA-256', data.buffer);
|
|
567
635
|
|
|
568
636
|
// Convert hash result to base64url encoding
|
|
569
637
|
const challenge = Buffer.from(digest)
|
package/changelog/v1.json
CHANGED
|
@@ -14,7 +14,7 @@ tags:
|
|
|
14
14
|
|
|
15
15
|
<Image cover src={'https://github.com/user-attachments/assets/be7dcd49-0165-4f7b-bf90-0739cc9dd212'} />
|
|
16
16
|
|
|
17
|
-
[InternLM](https://
|
|
17
|
+
[InternLM](https://internlm.intern-ai.org.cn/) is a large pre-trained language model jointly launched by the Shanghai Artificial Intelligence Laboratory and Shusheng Group. This model focuses on natural language processing, aimed at understanding and generating human language, boasting powerful semantic comprehension and text generation capabilities.
|
|
18
18
|
|
|
19
19
|
This article will guide you on how to use InternLM in LobeChat.
|
|
20
20
|
|
|
@@ -39,7 +39,7 @@ This article will guide you on how to use InternLM in LobeChat.
|
|
|
39
39
|
|
|
40
40
|
<Image alt={'Enter API Key'} inStep src={'https://github.com/user-attachments/assets/8ec7656e-1e3d-41e0-95a0-f6883135c2fc'} />
|
|
41
41
|
|
|
42
|
-
- Enter the obtained
|
|
42
|
+
- Enter the obtained API Key
|
|
43
43
|
- Choose a InternLM model for your AI assistant to start a conversation
|
|
44
44
|
|
|
45
45
|
<Image alt={'Select InternLM Model and Start Conversation'} inStep src={'https://github.com/user-attachments/assets/76ad163e-ee19-4f95-a712-85bea764d3ec'} />
|
|
@@ -12,7 +12,7 @@ tags:
|
|
|
12
12
|
|
|
13
13
|
<Image cover src={'https://github.com/user-attachments/assets/be7dcd49-0165-4f7b-bf90-0739cc9dd212'} />
|
|
14
14
|
|
|
15
|
-
[书生浦语(InternLM)](https://
|
|
15
|
+
[书生浦语(InternLM)](https://internlm.intern-ai.org.cn/) 是由上海人工智能实验室与书生集团联合推出的一款大型预训练语言模型。该模型专注于自然语言处理,旨在理解和生成自然语言,具备强大的语义理解和文本生成能力。
|
|
16
16
|
|
|
17
17
|
本文将指导你如何在 LobeChat 中使用书生浦语。
|
|
18
18
|
|
|
@@ -32,11 +32,11 @@ tags:
|
|
|
32
32
|
### 步骤二:在 LobeChat 中配置书生浦语
|
|
33
33
|
|
|
34
34
|
- 访问 LobeChat 的`设置`界面
|
|
35
|
-
- 在`AI 服务商`下找到
|
|
35
|
+
- 在`AI 服务商`下找到 `书生` 的设置项
|
|
36
36
|
|
|
37
37
|
<Image alt={'填入 API 密钥'} inStep src={'https://github.com/user-attachments/assets/8ec7656e-1e3d-41e0-95a0-f6883135c2fc'} />
|
|
38
38
|
|
|
39
|
-
- 填入获得的
|
|
39
|
+
- 填入获得的 API Key
|
|
40
40
|
- 为你的 AI 助手选择一个书生浦语的模型即可开始对话
|
|
41
41
|
|
|
42
42
|
<Image alt={'选择书生浦语模型并开始对话'} inStep src={'https://github.com/user-attachments/assets/76ad163e-ee19-4f95-a712-85bea764d3ec'} />
|
package/locales/en-US/error.json
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
"import.importConfigFile.title": "Import Failed",
|
|
13
13
|
"import.incompatible.description": "This file was exported from a higher version. Please try upgrading to the latest version and then re-importing.",
|
|
14
14
|
"import.incompatible.title": "Current application does not support importing this file",
|
|
15
|
+
"inviteCode.currentEmail": "Current account: {{email}}",
|
|
16
|
+
"inviteCode.desc": "An invite code is required to access LobeHub. Please enter a valid invite code to continue.",
|
|
17
|
+
"inviteCode.friends": "Friends",
|
|
18
|
+
"inviteCode.getCodeHint": "Get an invite code from:",
|
|
19
|
+
"inviteCode.title": "Invite Code Required",
|
|
15
20
|
"loginRequired.desc": "You will be redirected to the login page shortly",
|
|
16
21
|
"loginRequired.title": "Please log in to use this feature",
|
|
17
22
|
"notFound.backHome": "Back to Home",
|
|
@@ -144,5 +149,9 @@
|
|
|
144
149
|
"upload.networkError": "Please check your network connection and ensure that the file storage service's cross-origin configuration is correct.",
|
|
145
150
|
"upload.title": "File upload failed. Please check your network connection or try again later",
|
|
146
151
|
"upload.unknownError": "Error reason: {{reason}}",
|
|
147
|
-
"upload.uploadFailed": "File upload failed."
|
|
152
|
+
"upload.uploadFailed": "File upload failed.",
|
|
153
|
+
"waitlist.currentEmail": "Current account: {{email}}",
|
|
154
|
+
"waitlist.desc": "Your account is not on the whitelist. Please contact the administrator to request access.",
|
|
155
|
+
"waitlist.switchAccount": "Switch Account",
|
|
156
|
+
"waitlist.title": "Access Restricted"
|
|
148
157
|
}
|
|
@@ -288,7 +288,7 @@
|
|
|
288
288
|
"referral.rules.backfill.description": "Forgot to enter invite code? You can backfill within 3 days of registration",
|
|
289
289
|
"referral.rules.backfill.expiredTip": "Backfill period has expired. Cannot backfill after 3 days of registration",
|
|
290
290
|
"referral.rules.backfill.link": "Backfill Invite Code",
|
|
291
|
-
"referral.rules.backfill.placeholder": "Enter invite code",
|
|
291
|
+
"referral.rules.backfill.placeholder": "Enter invite code or link",
|
|
292
292
|
"referral.rules.backfill.submit": "Confirm Binding",
|
|
293
293
|
"referral.rules.backfill.success": "Invite code bound successfully",
|
|
294
294
|
"referral.rules.backfill.title": "Backfill Invite Code",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"screen4.title": "您希望如何共享数据?",
|
|
59
59
|
"screen4.title2": "您的选择将帮助我们改进",
|
|
60
60
|
"screen4.title3": "您可以随时在设置中更改",
|
|
61
|
+
"screen5.actions.cancel": "取消",
|
|
61
62
|
"screen5.actions.connectToServer": "连接服务器",
|
|
62
63
|
"screen5.actions.connecting": "正在连接…",
|
|
63
64
|
"screen5.actions.signInCloud": "登录 LobeHub Cloud",
|
|
@@ -65,6 +66,10 @@
|
|
|
65
66
|
"screen5.actions.signingIn": "正在登录…",
|
|
66
67
|
"screen5.actions.signingOut": "正在退出…",
|
|
67
68
|
"screen5.actions.tryAgain": "重试",
|
|
69
|
+
"screen5.auth.phase.browserOpened": "浏览器已打开,请前往登录…",
|
|
70
|
+
"screen5.auth.phase.verifying": "正在验证凭证…",
|
|
71
|
+
"screen5.auth.phase.waitingForAuth": "等待授权完成…",
|
|
72
|
+
"screen5.auth.remaining": "剩余时间:{{time}}秒",
|
|
68
73
|
"screen5.badge": "登录",
|
|
69
74
|
"screen5.description": "登录以在所有设备间同步代理、群组、设置和上下文。",
|
|
70
75
|
"screen5.errors.desktopOnlyOidc": "OIDC 授权仅在桌面端运行时可用。",
|
package/locales/zh-CN/error.json
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
"import.importConfigFile.title": "导入遇到了问题",
|
|
13
13
|
"import.incompatible.description": "该文件由更高版本导出。请升级到最新版后再导入",
|
|
14
14
|
"import.incompatible.title": "版本不兼容",
|
|
15
|
+
"inviteCode.currentEmail": "当前账号:{{email}}",
|
|
16
|
+
"inviteCode.desc": "需要邀请码才能访问 LobeHub。请输入有效的邀请码以继续。",
|
|
17
|
+
"inviteCode.friends": "好友",
|
|
18
|
+
"inviteCode.getCodeHint": "获取邀请码:",
|
|
19
|
+
"inviteCode.title": "需要邀请码",
|
|
15
20
|
"loginRequired.desc": "将为你跳转到登录页。登录后即可继续",
|
|
16
21
|
"loginRequired.title": "需要登录后继续",
|
|
17
22
|
"notFound.backHome": "返回首页",
|
|
@@ -144,5 +149,9 @@
|
|
|
144
149
|
"upload.networkError": "网络异常或跨域配置不正确。请检查文件存储服务的 CORS 设置后重试",
|
|
145
150
|
"upload.title": "上传未能完成",
|
|
146
151
|
"upload.unknownError": "原因:{{reason}}",
|
|
147
|
-
"upload.uploadFailed": "上传未能完成"
|
|
152
|
+
"upload.uploadFailed": "上传未能完成",
|
|
153
|
+
"waitlist.currentEmail": "当前账号:{{email}}",
|
|
154
|
+
"waitlist.desc": "你的账号不在白名单中。请联系管理员申请访问权限。",
|
|
155
|
+
"waitlist.switchAccount": "切换账号",
|
|
156
|
+
"waitlist.title": "访问受限"
|
|
148
157
|
}
|
|
@@ -288,7 +288,7 @@
|
|
|
288
288
|
"referral.rules.backfill.description": "忘记填写邀请码?注册三天内可以补填",
|
|
289
289
|
"referral.rules.backfill.expiredTip": "补填期限已过,注册超过三天后无法补填",
|
|
290
290
|
"referral.rules.backfill.link": "补填邀请码",
|
|
291
|
-
"referral.rules.backfill.placeholder": "
|
|
291
|
+
"referral.rules.backfill.placeholder": "请输入邀请码或链接",
|
|
292
292
|
"referral.rules.backfill.submit": "确认绑定",
|
|
293
293
|
"referral.rules.backfill.success": "邀请码绑定成功",
|
|
294
294
|
"referral.rules.backfill.title": "补填邀请码",
|
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.313",
|
|
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",
|
|
@@ -88,10 +88,22 @@ export class GeneralChatAgent implements Agent {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// Priority 0: CRITICAL - Check security blacklist FIRST
|
|
91
|
-
// This overrides ALL other settings, including auto-run mode
|
|
92
91
|
const securityCheck = InterventionChecker.checkSecurityBlacklist(securityBlacklist, toolArgs);
|
|
92
|
+
|
|
93
|
+
// Priority 0.5: Headless mode - fully automated for async tasks
|
|
94
|
+
// In headless mode: blacklisted tools are skipped, all other tools execute directly
|
|
95
|
+
if (approvalMode === 'headless') {
|
|
96
|
+
if (securityCheck.blocked) {
|
|
97
|
+
// Skip blacklisted tools entirely (don't execute, don't wait for approval)
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
// All other tools execute directly
|
|
101
|
+
toolsToExecute.push(toolCalling);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// For non-headless modes: security blacklist requires intervention
|
|
93
106
|
if (securityCheck.blocked) {
|
|
94
|
-
// Security blacklist always requires intervention
|
|
95
107
|
toolsNeedingIntervention.push(toolCalling);
|
|
96
108
|
continue;
|
|
97
109
|
}
|
|
@@ -911,7 +911,12 @@ describe('GeneralChatAgent', () => {
|
|
|
911
911
|
|
|
912
912
|
const context = createMockContext('task_result', {
|
|
913
913
|
parentMessageId: 'task-parent-msg',
|
|
914
|
-
result: {
|
|
914
|
+
result: {
|
|
915
|
+
success: true,
|
|
916
|
+
taskMessageId: 'task-1',
|
|
917
|
+
threadId: 'thread-1',
|
|
918
|
+
result: 'Task result',
|
|
919
|
+
},
|
|
915
920
|
});
|
|
916
921
|
|
|
917
922
|
const result = await agent.runner(context, state);
|
|
@@ -1519,4 +1524,273 @@ describe('GeneralChatAgent', () => {
|
|
|
1519
1524
|
]);
|
|
1520
1525
|
});
|
|
1521
1526
|
});
|
|
1527
|
+
|
|
1528
|
+
describe('headless mode (for async tasks)', () => {
|
|
1529
|
+
it('should execute all tools directly in headless mode including those requiring approval', async () => {
|
|
1530
|
+
const agent = new GeneralChatAgent({
|
|
1531
|
+
agentConfig: { maxSteps: 100 },
|
|
1532
|
+
operationId: 'test-session',
|
|
1533
|
+
modelRuntimeConfig: mockModelRuntimeConfig,
|
|
1534
|
+
});
|
|
1535
|
+
|
|
1536
|
+
const toolCall: ChatToolPayload = {
|
|
1537
|
+
id: 'call-1',
|
|
1538
|
+
identifier: 'dangerous-tool',
|
|
1539
|
+
apiName: 'delete',
|
|
1540
|
+
arguments: '{}',
|
|
1541
|
+
type: 'default',
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1544
|
+
const state = createMockState({
|
|
1545
|
+
toolManifestMap: {
|
|
1546
|
+
'dangerous-tool': {
|
|
1547
|
+
identifier: 'dangerous-tool',
|
|
1548
|
+
humanIntervention: 'required', // Tool requires approval
|
|
1549
|
+
},
|
|
1550
|
+
},
|
|
1551
|
+
userInterventionConfig: {
|
|
1552
|
+
approvalMode: 'headless', // Headless mode for async tasks
|
|
1553
|
+
},
|
|
1554
|
+
});
|
|
1555
|
+
|
|
1556
|
+
const context = createMockContext('llm_result', {
|
|
1557
|
+
hasToolsCalling: true,
|
|
1558
|
+
toolsCalling: [toolCall],
|
|
1559
|
+
parentMessageId: 'msg-1',
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
const result = await agent.runner(context, state);
|
|
1563
|
+
|
|
1564
|
+
// Should execute directly in headless mode
|
|
1565
|
+
expect(result).toEqual([
|
|
1566
|
+
{
|
|
1567
|
+
type: 'call_tool',
|
|
1568
|
+
payload: {
|
|
1569
|
+
parentMessageId: 'msg-1',
|
|
1570
|
+
toolCalling: toolCall,
|
|
1571
|
+
},
|
|
1572
|
+
},
|
|
1573
|
+
]);
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
it('should execute tools with "always" policy in headless mode', async () => {
|
|
1577
|
+
const agent = new GeneralChatAgent({
|
|
1578
|
+
agentConfig: { maxSteps: 100 },
|
|
1579
|
+
operationId: 'test-session',
|
|
1580
|
+
modelRuntimeConfig: mockModelRuntimeConfig,
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
const alwaysTool: ChatToolPayload = {
|
|
1584
|
+
id: 'call-1',
|
|
1585
|
+
identifier: 'agent-builder',
|
|
1586
|
+
apiName: 'installPlugin',
|
|
1587
|
+
arguments: '{"identifier":"some-plugin","source":"market"}',
|
|
1588
|
+
type: 'builtin',
|
|
1589
|
+
};
|
|
1590
|
+
|
|
1591
|
+
const state = createMockState({
|
|
1592
|
+
toolManifestMap: {
|
|
1593
|
+
'agent-builder': {
|
|
1594
|
+
identifier: 'agent-builder',
|
|
1595
|
+
api: [
|
|
1596
|
+
{
|
|
1597
|
+
name: 'installPlugin',
|
|
1598
|
+
humanIntervention: 'always', // Always requires intervention normally
|
|
1599
|
+
},
|
|
1600
|
+
],
|
|
1601
|
+
},
|
|
1602
|
+
},
|
|
1603
|
+
userInterventionConfig: {
|
|
1604
|
+
approvalMode: 'headless', // Headless mode bypasses even 'always'
|
|
1605
|
+
},
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
const context = createMockContext('llm_result', {
|
|
1609
|
+
hasToolsCalling: true,
|
|
1610
|
+
toolsCalling: [alwaysTool],
|
|
1611
|
+
parentMessageId: 'msg-1',
|
|
1612
|
+
});
|
|
1613
|
+
|
|
1614
|
+
const result = await agent.runner(context, state);
|
|
1615
|
+
|
|
1616
|
+
// Should execute directly in headless mode, even for 'always' policy
|
|
1617
|
+
expect(result).toEqual([
|
|
1618
|
+
{
|
|
1619
|
+
type: 'call_tool',
|
|
1620
|
+
payload: {
|
|
1621
|
+
parentMessageId: 'msg-1',
|
|
1622
|
+
toolCalling: alwaysTool,
|
|
1623
|
+
},
|
|
1624
|
+
},
|
|
1625
|
+
]);
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
it('should skip security blacklisted tools in headless mode', async () => {
|
|
1629
|
+
const agent = new GeneralChatAgent({
|
|
1630
|
+
agentConfig: { maxSteps: 100 },
|
|
1631
|
+
operationId: 'test-session',
|
|
1632
|
+
modelRuntimeConfig: mockModelRuntimeConfig,
|
|
1633
|
+
});
|
|
1634
|
+
|
|
1635
|
+
const blacklistedTool: ChatToolPayload = {
|
|
1636
|
+
id: 'call-1',
|
|
1637
|
+
identifier: 'bash',
|
|
1638
|
+
apiName: 'bash',
|
|
1639
|
+
arguments: '{"command":"rm -rf /"}', // Matches security blacklist
|
|
1640
|
+
type: 'builtin',
|
|
1641
|
+
};
|
|
1642
|
+
|
|
1643
|
+
const state = createMockState({
|
|
1644
|
+
toolManifestMap: {
|
|
1645
|
+
bash: {
|
|
1646
|
+
identifier: 'bash',
|
|
1647
|
+
humanIntervention: 'never',
|
|
1648
|
+
},
|
|
1649
|
+
},
|
|
1650
|
+
userInterventionConfig: {
|
|
1651
|
+
approvalMode: 'headless',
|
|
1652
|
+
},
|
|
1653
|
+
// Using default security blacklist which blocks "rm -rf /"
|
|
1654
|
+
});
|
|
1655
|
+
|
|
1656
|
+
const context = createMockContext('llm_result', {
|
|
1657
|
+
hasToolsCalling: true,
|
|
1658
|
+
toolsCalling: [blacklistedTool],
|
|
1659
|
+
parentMessageId: 'msg-1',
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
const result = await agent.runner(context, state);
|
|
1663
|
+
|
|
1664
|
+
// Should return empty array (tool is skipped, not executed or pending)
|
|
1665
|
+
expect(result).toEqual([]);
|
|
1666
|
+
});
|
|
1667
|
+
|
|
1668
|
+
it('should handle mixed tools in headless mode - execute safe ones, skip blacklisted', async () => {
|
|
1669
|
+
const agent = new GeneralChatAgent({
|
|
1670
|
+
agentConfig: { maxSteps: 100 },
|
|
1671
|
+
operationId: 'test-session',
|
|
1672
|
+
modelRuntimeConfig: mockModelRuntimeConfig,
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
const safeTool: ChatToolPayload = {
|
|
1676
|
+
id: 'call-1',
|
|
1677
|
+
identifier: 'web-search',
|
|
1678
|
+
apiName: 'search',
|
|
1679
|
+
arguments: '{"query":"hello"}',
|
|
1680
|
+
type: 'default',
|
|
1681
|
+
};
|
|
1682
|
+
|
|
1683
|
+
const blacklistedTool: ChatToolPayload = {
|
|
1684
|
+
id: 'call-2',
|
|
1685
|
+
identifier: 'bash',
|
|
1686
|
+
apiName: 'bash',
|
|
1687
|
+
arguments: '{"command":"rm -rf /"}', // Matches security blacklist
|
|
1688
|
+
type: 'builtin',
|
|
1689
|
+
};
|
|
1690
|
+
|
|
1691
|
+
const alwaysTool: ChatToolPayload = {
|
|
1692
|
+
id: 'call-3',
|
|
1693
|
+
identifier: 'agent-builder',
|
|
1694
|
+
apiName: 'installPlugin',
|
|
1695
|
+
arguments: '{}',
|
|
1696
|
+
type: 'builtin',
|
|
1697
|
+
};
|
|
1698
|
+
|
|
1699
|
+
const state = createMockState({
|
|
1700
|
+
toolManifestMap: {
|
|
1701
|
+
'web-search': {
|
|
1702
|
+
identifier: 'web-search',
|
|
1703
|
+
humanIntervention: 'required',
|
|
1704
|
+
},
|
|
1705
|
+
'bash': {
|
|
1706
|
+
identifier: 'bash',
|
|
1707
|
+
},
|
|
1708
|
+
'agent-builder': {
|
|
1709
|
+
identifier: 'agent-builder',
|
|
1710
|
+
api: [
|
|
1711
|
+
{
|
|
1712
|
+
name: 'installPlugin',
|
|
1713
|
+
humanIntervention: 'always',
|
|
1714
|
+
},
|
|
1715
|
+
],
|
|
1716
|
+
},
|
|
1717
|
+
},
|
|
1718
|
+
userInterventionConfig: {
|
|
1719
|
+
approvalMode: 'headless',
|
|
1720
|
+
},
|
|
1721
|
+
});
|
|
1722
|
+
|
|
1723
|
+
const context = createMockContext('llm_result', {
|
|
1724
|
+
hasToolsCalling: true,
|
|
1725
|
+
toolsCalling: [safeTool, blacklistedTool, alwaysTool],
|
|
1726
|
+
parentMessageId: 'msg-1',
|
|
1727
|
+
});
|
|
1728
|
+
|
|
1729
|
+
const result = await agent.runner(context, state);
|
|
1730
|
+
|
|
1731
|
+
// Should execute safeTool and alwaysTool, skip blacklistedTool
|
|
1732
|
+
expect(result).toEqual([
|
|
1733
|
+
{
|
|
1734
|
+
type: 'call_tools_batch',
|
|
1735
|
+
payload: {
|
|
1736
|
+
parentMessageId: 'msg-1',
|
|
1737
|
+
toolsCalling: [safeTool, alwaysTool],
|
|
1738
|
+
},
|
|
1739
|
+
},
|
|
1740
|
+
]);
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1743
|
+
it('should execute multiple tools as batch in headless mode', async () => {
|
|
1744
|
+
const agent = new GeneralChatAgent({
|
|
1745
|
+
agentConfig: { maxSteps: 100 },
|
|
1746
|
+
operationId: 'test-session',
|
|
1747
|
+
modelRuntimeConfig: mockModelRuntimeConfig,
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
const tool1: ChatToolPayload = {
|
|
1751
|
+
id: 'call-1',
|
|
1752
|
+
identifier: 'search',
|
|
1753
|
+
apiName: 'search',
|
|
1754
|
+
arguments: '{}',
|
|
1755
|
+
type: 'default',
|
|
1756
|
+
};
|
|
1757
|
+
|
|
1758
|
+
const tool2: ChatToolPayload = {
|
|
1759
|
+
id: 'call-2',
|
|
1760
|
+
identifier: 'crawl',
|
|
1761
|
+
apiName: 'crawl',
|
|
1762
|
+
arguments: '{}',
|
|
1763
|
+
type: 'default',
|
|
1764
|
+
};
|
|
1765
|
+
|
|
1766
|
+
const state = createMockState({
|
|
1767
|
+
toolManifestMap: {
|
|
1768
|
+
search: { identifier: 'search', humanIntervention: 'required' },
|
|
1769
|
+
crawl: { identifier: 'crawl', humanIntervention: 'always' },
|
|
1770
|
+
},
|
|
1771
|
+
userInterventionConfig: {
|
|
1772
|
+
approvalMode: 'headless',
|
|
1773
|
+
},
|
|
1774
|
+
});
|
|
1775
|
+
|
|
1776
|
+
const context = createMockContext('llm_result', {
|
|
1777
|
+
hasToolsCalling: true,
|
|
1778
|
+
toolsCalling: [tool1, tool2],
|
|
1779
|
+
parentMessageId: 'msg-1',
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
const result = await agent.runner(context, state);
|
|
1783
|
+
|
|
1784
|
+
// Should execute both tools as batch in headless mode
|
|
1785
|
+
expect(result).toEqual([
|
|
1786
|
+
{
|
|
1787
|
+
type: 'call_tools_batch',
|
|
1788
|
+
payload: {
|
|
1789
|
+
parentMessageId: 'msg-1',
|
|
1790
|
+
toolsCalling: [tool1, tool2],
|
|
1791
|
+
},
|
|
1792
|
+
},
|
|
1793
|
+
]);
|
|
1794
|
+
});
|
|
1795
|
+
});
|
|
1522
1796
|
});
|