@lobehub/lobehub 2.0.0-next.307 → 2.0.0-next.309
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/changelog/v1.json +21 -0
- package/locales/ar/agentGroup.json +5 -0
- package/locales/ar/chat.json +26 -0
- package/locales/ar/models.json +43 -5
- package/locales/ar/plugin.json +4 -5
- package/locales/ar/setting.json +11 -0
- package/locales/ar/subscription.json +2 -0
- package/locales/ar/tool.json +2 -0
- package/locales/bg-BG/agentGroup.json +5 -0
- package/locales/bg-BG/chat.json +26 -0
- package/locales/bg-BG/models.json +49 -3
- package/locales/bg-BG/plugin.json +4 -5
- package/locales/bg-BG/setting.json +11 -0
- package/locales/bg-BG/subscription.json +2 -0
- package/locales/bg-BG/tool.json +2 -0
- package/locales/de-DE/agentGroup.json +5 -0
- package/locales/de-DE/chat.json +26 -0
- package/locales/de-DE/models.json +48 -5
- package/locales/de-DE/plugin.json +4 -5
- package/locales/de-DE/setting.json +11 -0
- package/locales/de-DE/subscription.json +2 -0
- package/locales/de-DE/tool.json +2 -0
- package/locales/en-US/models.json +8 -6
- package/locales/en-US/plugin.json +3 -4
- package/locales/en-US/setting.json +10 -11
- package/locales/en-US/tool.json +2 -0
- package/locales/es-ES/agentGroup.json +5 -0
- package/locales/es-ES/chat.json +26 -0
- package/locales/es-ES/models.json +43 -5
- package/locales/es-ES/plugin.json +4 -5
- package/locales/es-ES/setting.json +11 -0
- package/locales/es-ES/subscription.json +2 -0
- package/locales/es-ES/tool.json +2 -0
- package/locales/fa-IR/agentGroup.json +5 -0
- package/locales/fa-IR/chat.json +26 -0
- package/locales/fa-IR/models.json +42 -5
- package/locales/fa-IR/plugin.json +4 -5
- package/locales/fa-IR/setting.json +11 -0
- package/locales/fa-IR/subscription.json +2 -0
- package/locales/fa-IR/tool.json +2 -0
- package/locales/fr-FR/agentGroup.json +5 -0
- package/locales/fr-FR/chat.json +26 -0
- package/locales/fr-FR/models.json +5 -5
- package/locales/fr-FR/plugin.json +4 -5
- package/locales/fr-FR/setting.json +11 -0
- package/locales/fr-FR/subscription.json +2 -0
- package/locales/fr-FR/tool.json +2 -0
- package/locales/it-IT/agentGroup.json +5 -0
- package/locales/it-IT/chat.json +26 -0
- package/locales/it-IT/models.json +1 -3
- package/locales/it-IT/plugin.json +4 -5
- package/locales/it-IT/setting.json +11 -0
- package/locales/it-IT/subscription.json +2 -0
- package/locales/it-IT/tool.json +2 -0
- package/locales/ja-JP/agentGroup.json +5 -0
- package/locales/ja-JP/chat.json +26 -0
- package/locales/ja-JP/models.json +1 -5
- package/locales/ja-JP/plugin.json +4 -5
- package/locales/ja-JP/setting.json +11 -0
- package/locales/ja-JP/subscription.json +2 -0
- package/locales/ja-JP/tool.json +2 -0
- package/locales/ko-KR/agentGroup.json +5 -0
- package/locales/ko-KR/chat.json +26 -0
- package/locales/ko-KR/models.json +1 -3
- package/locales/ko-KR/plugin.json +4 -5
- package/locales/ko-KR/setting.json +11 -0
- package/locales/ko-KR/subscription.json +2 -0
- package/locales/ko-KR/tool.json +2 -0
- package/locales/nl-NL/agentGroup.json +5 -0
- package/locales/nl-NL/chat.json +26 -0
- package/locales/nl-NL/models.json +35 -3
- package/locales/nl-NL/plugin.json +4 -5
- package/locales/nl-NL/setting.json +11 -0
- package/locales/nl-NL/subscription.json +2 -0
- package/locales/nl-NL/tool.json +2 -0
- package/locales/pl-PL/agentGroup.json +5 -0
- package/locales/pl-PL/chat.json +26 -0
- package/locales/pl-PL/models.json +1 -3
- package/locales/pl-PL/plugin.json +4 -5
- package/locales/pl-PL/setting.json +11 -0
- package/locales/pl-PL/subscription.json +2 -0
- package/locales/pl-PL/tool.json +2 -0
- package/locales/pt-BR/agentGroup.json +5 -0
- package/locales/pt-BR/chat.json +26 -0
- package/locales/pt-BR/models.json +50 -5
- package/locales/pt-BR/plugin.json +4 -5
- package/locales/pt-BR/setting.json +11 -0
- package/locales/pt-BR/subscription.json +2 -0
- package/locales/pt-BR/tool.json +2 -0
- package/locales/ru-RU/agentGroup.json +5 -0
- package/locales/ru-RU/chat.json +26 -0
- package/locales/ru-RU/models.json +22 -3
- package/locales/ru-RU/plugin.json +4 -5
- package/locales/ru-RU/setting.json +11 -0
- package/locales/ru-RU/subscription.json +2 -0
- package/locales/ru-RU/tool.json +2 -0
- package/locales/tr-TR/agentGroup.json +5 -0
- package/locales/tr-TR/chat.json +26 -0
- package/locales/tr-TR/models.json +36 -3
- package/locales/tr-TR/plugin.json +4 -5
- package/locales/tr-TR/setting.json +11 -0
- package/locales/tr-TR/subscription.json +2 -0
- package/locales/tr-TR/tool.json +2 -0
- package/locales/vi-VN/agentGroup.json +5 -0
- package/locales/vi-VN/chat.json +26 -0
- package/locales/vi-VN/models.json +1 -1
- package/locales/vi-VN/plugin.json +4 -5
- package/locales/vi-VN/setting.json +11 -0
- package/locales/vi-VN/subscription.json +2 -0
- package/locales/vi-VN/tool.json +2 -0
- package/locales/zh-CN/models.json +52 -5
- package/locales/zh-CN/plugin.json +6 -7
- package/locales/zh-CN/setting.json +10 -11
- package/locales/zh-CN/tool.json +2 -2
- package/locales/zh-TW/agentGroup.json +5 -0
- package/locales/zh-TW/chat.json +26 -0
- package/locales/zh-TW/models.json +54 -5
- package/locales/zh-TW/plugin.json +4 -5
- package/locales/zh-TW/setting.json +11 -0
- package/locales/zh-TW/subscription.json +2 -0
- package/locales/zh-TW/tool.json +2 -0
- package/package.json +1 -1
- package/packages/agent-runtime/src/types/state.ts +2 -0
- package/packages/builtin-tool-group-agent-builder/src/client/Inspector/GetAgentInfo/index.tsx +68 -0
- package/packages/builtin-tool-group-agent-builder/src/client/Inspector/index.ts +3 -0
- package/packages/builtin-tool-group-management/src/client/Inspector/ExecuteAgentTask/index.tsx +52 -8
- package/packages/builtin-tool-group-management/src/client/Render/ExecuteTask/index.tsx +2 -21
- package/packages/builtin-tool-group-management/src/executor.test.ts +6 -4
- package/packages/builtin-tool-group-management/src/manifest.ts +5 -1
- package/packages/builtin-tool-group-management/src/types.ts +2 -0
- package/packages/builtin-tool-local-system/src/client/Render/WriteFile/index.tsx +48 -5
- package/packages/builtin-tool-local-system/src/client/Streaming/WriteFile/index.tsx +39 -0
- package/packages/builtin-tool-local-system/src/client/Streaming/index.ts +2 -0
- package/packages/builtin-tool-memory/src/manifest.ts +581 -19
- package/packages/model-bank/src/aiModels/qiniu.ts +24 -0
- package/packages/types/src/topic/thread.ts +2 -2
- package/packages/types/src/userMemory/layers.ts +19 -8
- package/packages/types/src/userMemory/shared.ts +7 -1
- package/src/app/[variants]/(auth)/_layout/index.tsx +0 -2
- package/src/app/[variants]/(auth)/layout.tsx +0 -2
- package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -3
- package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -3
- package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/agent/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/agent/features/Portal/index.tsx +0 -2
- package/src/app/[variants]/(main)/community/(list)/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/community/(list)/assistant/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/community/(list)/mcp/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/community/(list)/model/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/community/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/group/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/group/features/Conversation/Header/index.tsx +4 -2
- package/src/app/[variants]/(main)/group/features/Portal/index.tsx +0 -2
- package/src/app/[variants]/(main)/home/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/home/index.tsx +0 -2
- package/src/app/[variants]/(main)/image/_layout/Topics/TopicUrlSync.tsx +0 -2
- package/src/app/[variants]/(main)/image/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/memory/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/page/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/resource/(home)/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/resource/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/resource/library/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/resource/library/features/Container.tsx +0 -2
- package/src/app/[variants]/(main)/settings/_layout/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/about/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/agent/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/apikey/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/chat-appearance/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/common/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/hotkey/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/image/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/memory/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/proxy/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/security/index.tsx +1 -3
- package/src/app/[variants]/(main)/settings/storage/index.tsx +0 -2
- package/src/app/[variants]/(main)/settings/tts/index.tsx +0 -2
- package/src/app/[variants]/(mobile)/(home)/_layout/index.tsx +0 -2
- package/src/app/[variants]/(mobile)/_layout/index.tsx +1 -3
- package/src/app/[variants]/(mobile)/chat/_layout/index.tsx +0 -2
- package/src/app/[variants]/(mobile)/chat/settings/_layout/index.tsx +0 -2
- package/src/app/[variants]/(mobile)/community/(detail)/_layout/index.tsx +0 -2
- package/src/app/[variants]/(mobile)/community/(list)/_layout/index.tsx +0 -2
- package/src/app/[variants]/(mobile)/community/_layout/index.tsx +0 -2
- package/src/app/[variants]/(mobile)/router/MobileClientRouter.tsx +0 -2
- package/src/app/[variants]/(mobile)/settings/index.tsx +0 -2
- package/src/app/[variants]/onboarding/_layout/index.tsx +0 -2
- package/src/app/[variants]/router/DesktopClientRouter.tsx +0 -2
- package/src/components/ModelSelect/index.tsx +6 -56
- package/src/components/server/MobileNavLayout.tsx +0 -2
- package/src/components/server/ServerLayout.tsx +0 -2
- package/src/features/ModelSwitchPanel/components/Footer.tsx +0 -2
- package/src/features/ModelSwitchPanel/components/List/MultipleProvidersModelItem.tsx +0 -1
- package/src/features/ModelSwitchPanel/components/List/SingleProviderModelItem.tsx +0 -1
- package/src/features/ModelSwitchPanel/components/List/VirtualItemRenderer.tsx +0 -1
- package/src/features/ModelSwitchPanel/components/List/index.tsx +15 -13
- package/src/features/ModelSwitchPanel/components/PanelContent.tsx +0 -2
- package/src/features/ModelSwitchPanel/index.tsx +21 -23
- package/src/features/ResourceManager/components/Explorer/MasonryView/index.tsx +0 -2
- package/src/features/User/UserAvatar.tsx +0 -2
- package/src/locales/default/plugin.ts +3 -1
- package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +11 -3
- package/src/server/modules/Mecha/AgentToolsEngine/index.ts +14 -6
- package/src/server/modules/Mecha/AgentToolsEngine/types.ts +4 -3
- package/src/server/routers/lambda/aiAgent.ts +10 -0
- package/src/server/services/agent/index.test.ts +155 -0
- package/src/server/services/agent/index.ts +25 -0
- package/src/server/services/agentRuntime/AgentRuntimeService.ts +2 -0
- package/src/server/services/agentRuntime/types.ts +1 -0
- package/src/server/services/aiAgent/__tests__/execAgent.threadId.test.ts +29 -9
- package/src/server/services/aiAgent/index.ts +175 -6
- package/src/server/services/lobehubSkill/index.ts +109 -0
- package/src/server/services/toolExecution/builtin.ts +28 -2
- package/src/server/services/toolExecution/types.ts +3 -0
- package/src/store/chat/agents/GroupOrchestration/__tests__/call-supervisor.test.ts +305 -0
- package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +2 -1
- package/src/store/chat/agents/createAgentExecutors.ts +4 -2
- package/src/store/chat/slices/plugin/actions/exector.ts +92 -0
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +82 -177
|
@@ -4,6 +4,7 @@ import { type ChatToolPayload } from '@lobechat/types';
|
|
|
4
4
|
import { safeParseJSON } from '@lobechat/utils';
|
|
5
5
|
import debug from 'debug';
|
|
6
6
|
|
|
7
|
+
import { LobehubSkillService } from '@/server/services/lobehubSkill';
|
|
7
8
|
import { SearchService } from '@/server/services/search';
|
|
8
9
|
|
|
9
10
|
import { type IToolExecutor, type ToolExecutionContext, type ToolExecutionResult } from './types';
|
|
@@ -19,11 +20,36 @@ export class BuiltinToolsExecutor implements IToolExecutor {
|
|
|
19
20
|
payload: ChatToolPayload,
|
|
20
21
|
context: ToolExecutionContext,
|
|
21
22
|
): Promise<ToolExecutionResult> {
|
|
22
|
-
const { identifier, apiName, arguments: argsStr } = payload;
|
|
23
|
+
const { identifier, apiName, arguments: argsStr, source } = payload;
|
|
23
24
|
const args = safeParseJSON(argsStr) || {};
|
|
24
25
|
|
|
25
|
-
log(
|
|
26
|
+
log(
|
|
27
|
+
'Executing builtin tool: %s:%s (source: %s) with args: %O',
|
|
28
|
+
identifier,
|
|
29
|
+
apiName,
|
|
30
|
+
source,
|
|
31
|
+
args,
|
|
32
|
+
);
|
|
26
33
|
|
|
34
|
+
// Route LobeHub Skills to dedicated service
|
|
35
|
+
if (source === 'lobehubSkill') {
|
|
36
|
+
if (!context.serverDB || !context.userId) {
|
|
37
|
+
return {
|
|
38
|
+
content: 'Server context not available for LobeHub Skills execution.',
|
|
39
|
+
error: { code: 'CONTEXT_NOT_AVAILABLE' },
|
|
40
|
+
success: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const skillService = new LobehubSkillService(context.serverDB, context.userId);
|
|
45
|
+
return skillService.execute({
|
|
46
|
+
args,
|
|
47
|
+
provider: identifier,
|
|
48
|
+
toolName: apiName,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Default: original builtin runtime logic
|
|
27
53
|
const ServerRuntime = BuiltinToolServerRuntimes[identifier];
|
|
28
54
|
|
|
29
55
|
if (!ServerRuntime) {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { type LobeToolManifest } from '@lobechat/context-engine';
|
|
2
|
+
import { type LobeChatDatabase } from '@lobechat/database';
|
|
2
3
|
import { type ChatToolPayload } from '@lobechat/types';
|
|
3
4
|
|
|
4
5
|
export interface ToolExecutionContext {
|
|
6
|
+
/** Server database for LobeHub Skills execution */
|
|
7
|
+
serverDB?: LobeChatDatabase;
|
|
5
8
|
toolManifestMap: Record<string, LobeToolManifest>;
|
|
6
9
|
userId?: string;
|
|
7
10
|
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import type { AgentState } from '@lobechat/agent-runtime';
|
|
2
|
+
import type { UIChatMessage } from '@lobechat/types';
|
|
3
|
+
import { nanoid } from '@lobechat/utils';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import type { ChatStore } from '@/store/chat/store';
|
|
7
|
+
|
|
8
|
+
import { createGroupOrchestrationExecutors } from '../createGroupOrchestrationExecutors';
|
|
9
|
+
|
|
10
|
+
const TEST_IDS = {
|
|
11
|
+
GROUP_ID: 'test-group-id',
|
|
12
|
+
OPERATION_ID: 'test-operation-id',
|
|
13
|
+
ORCHESTRATION_OPERATION_ID: 'test-orchestration-operation-id',
|
|
14
|
+
SUPERVISOR_AGENT_ID: 'test-supervisor-agent-id',
|
|
15
|
+
TOPIC_ID: 'test-topic-id',
|
|
16
|
+
USER_MESSAGE_ID: 'test-user-message-id',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create a minimal mock store for group orchestration executor tests
|
|
21
|
+
*/
|
|
22
|
+
const createMockStore = (overrides: Partial<ChatStore> = {}): ChatStore => {
|
|
23
|
+
const operations: Record<string, any> = {};
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
dbMessagesMap: {},
|
|
27
|
+
internal_execAgentRuntime: vi.fn().mockResolvedValue(undefined),
|
|
28
|
+
messagesMap: {},
|
|
29
|
+
operations,
|
|
30
|
+
startOperation: vi.fn().mockImplementation((config) => {
|
|
31
|
+
const operationId = `op_${nanoid()}`;
|
|
32
|
+
const abortController = new AbortController();
|
|
33
|
+
operations[operationId] = {
|
|
34
|
+
abortController,
|
|
35
|
+
context: config.context || {},
|
|
36
|
+
id: operationId,
|
|
37
|
+
status: 'running',
|
|
38
|
+
type: config.type,
|
|
39
|
+
};
|
|
40
|
+
return { abortController, operationId };
|
|
41
|
+
}),
|
|
42
|
+
...overrides,
|
|
43
|
+
} as unknown as ChatStore;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create initial agent state for testing
|
|
48
|
+
*/
|
|
49
|
+
const createInitialState = (overrides: Partial<AgentState> = {}): AgentState => {
|
|
50
|
+
return {
|
|
51
|
+
cost: {
|
|
52
|
+
calculatedAt: new Date().toISOString(),
|
|
53
|
+
currency: 'USD',
|
|
54
|
+
llm: { byModel: [], currency: 'USD', total: 0 },
|
|
55
|
+
tools: { byTool: [], currency: 'USD', total: 0 },
|
|
56
|
+
total: 0,
|
|
57
|
+
},
|
|
58
|
+
createdAt: new Date().toISOString(),
|
|
59
|
+
lastModified: new Date().toISOString(),
|
|
60
|
+
maxSteps: 10,
|
|
61
|
+
messages: [],
|
|
62
|
+
operationId: TEST_IDS.OPERATION_ID,
|
|
63
|
+
status: 'running',
|
|
64
|
+
stepCount: 0,
|
|
65
|
+
toolManifestMap: {},
|
|
66
|
+
usage: {
|
|
67
|
+
humanInteraction: {
|
|
68
|
+
approvalRequests: 0,
|
|
69
|
+
promptRequests: 0,
|
|
70
|
+
selectRequests: 0,
|
|
71
|
+
totalWaitingTimeMs: 0,
|
|
72
|
+
},
|
|
73
|
+
llm: { apiCalls: 0, processingTimeMs: 0, tokens: { input: 0, output: 0, total: 0 } },
|
|
74
|
+
tools: { byTool: [], totalCalls: 0, totalTimeMs: 0 },
|
|
75
|
+
},
|
|
76
|
+
userInterventionConfig: { allowList: [], approvalMode: 'auto' },
|
|
77
|
+
...overrides,
|
|
78
|
+
} as AgentState;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
describe('createGroupOrchestrationExecutors', () => {
|
|
82
|
+
describe('call_supervisor executor', () => {
|
|
83
|
+
it('should NOT pass operationId to internal_execAgentRuntime (creates new child operation)', async () => {
|
|
84
|
+
const mockStore = createMockStore({
|
|
85
|
+
dbMessagesMap: {
|
|
86
|
+
[`group_${TEST_IDS.GROUP_ID}_${TEST_IDS.TOPIC_ID}`]: [
|
|
87
|
+
{
|
|
88
|
+
content: 'Hello',
|
|
89
|
+
createdAt: Date.now(),
|
|
90
|
+
id: TEST_IDS.USER_MESSAGE_ID,
|
|
91
|
+
role: 'user',
|
|
92
|
+
updatedAt: Date.now(),
|
|
93
|
+
} as UIChatMessage,
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const executors = createGroupOrchestrationExecutors({
|
|
99
|
+
get: () => mockStore,
|
|
100
|
+
messageContext: {
|
|
101
|
+
agentId: TEST_IDS.GROUP_ID,
|
|
102
|
+
scope: 'group',
|
|
103
|
+
topicId: TEST_IDS.TOPIC_ID,
|
|
104
|
+
},
|
|
105
|
+
orchestrationOperationId: TEST_IDS.ORCHESTRATION_OPERATION_ID,
|
|
106
|
+
supervisorAgentId: TEST_IDS.SUPERVISOR_AGENT_ID,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const callSupervisorExecutor = executors.call_supervisor!;
|
|
110
|
+
|
|
111
|
+
await callSupervisorExecutor(
|
|
112
|
+
{
|
|
113
|
+
payload: { round: 1, supervisorAgentId: TEST_IDS.SUPERVISOR_AGENT_ID },
|
|
114
|
+
type: 'call_supervisor',
|
|
115
|
+
},
|
|
116
|
+
createInitialState(),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Verify internal_execAgentRuntime was called
|
|
120
|
+
expect(mockStore.internal_execAgentRuntime).toHaveBeenCalledTimes(1);
|
|
121
|
+
|
|
122
|
+
// Verify operationId is NOT passed (should be undefined)
|
|
123
|
+
// This ensures a new child operation is created
|
|
124
|
+
const callArgs = (mockStore.internal_execAgentRuntime as any).mock.calls[0][0];
|
|
125
|
+
expect(callArgs.operationId).toBeUndefined();
|
|
126
|
+
|
|
127
|
+
// Verify parentOperationId is passed correctly
|
|
128
|
+
expect(callArgs.parentOperationId).toBe(TEST_IDS.ORCHESTRATION_OPERATION_ID);
|
|
129
|
+
|
|
130
|
+
// Verify isSupervisor is passed in context
|
|
131
|
+
expect(callArgs.context.isSupervisor).toBe(true);
|
|
132
|
+
|
|
133
|
+
// Verify agentId is the supervisor agent id
|
|
134
|
+
expect(callArgs.context.agentId).toBe(TEST_IDS.SUPERVISOR_AGENT_ID);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should pass isSupervisor: true in context for supervisor messages metadata', async () => {
|
|
138
|
+
const mockStore = createMockStore({
|
|
139
|
+
dbMessagesMap: {
|
|
140
|
+
[`group_${TEST_IDS.GROUP_ID}_${TEST_IDS.TOPIC_ID}`]: [
|
|
141
|
+
{
|
|
142
|
+
content: 'Hello',
|
|
143
|
+
createdAt: Date.now(),
|
|
144
|
+
id: TEST_IDS.USER_MESSAGE_ID,
|
|
145
|
+
role: 'user',
|
|
146
|
+
updatedAt: Date.now(),
|
|
147
|
+
} as UIChatMessage,
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const executors = createGroupOrchestrationExecutors({
|
|
153
|
+
get: () => mockStore,
|
|
154
|
+
messageContext: {
|
|
155
|
+
agentId: TEST_IDS.GROUP_ID,
|
|
156
|
+
scope: 'group',
|
|
157
|
+
topicId: TEST_IDS.TOPIC_ID,
|
|
158
|
+
},
|
|
159
|
+
orchestrationOperationId: TEST_IDS.ORCHESTRATION_OPERATION_ID,
|
|
160
|
+
supervisorAgentId: TEST_IDS.SUPERVISOR_AGENT_ID,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const callSupervisorExecutor = executors.call_supervisor!;
|
|
164
|
+
|
|
165
|
+
await callSupervisorExecutor(
|
|
166
|
+
{
|
|
167
|
+
payload: { round: 1, supervisorAgentId: TEST_IDS.SUPERVISOR_AGENT_ID },
|
|
168
|
+
type: 'call_supervisor',
|
|
169
|
+
},
|
|
170
|
+
createInitialState(),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const callArgs = (mockStore.internal_execAgentRuntime as any).mock.calls[0][0];
|
|
174
|
+
|
|
175
|
+
// The key assertion: isSupervisor must be true
|
|
176
|
+
// This is used by createAgentExecutors to set metadata.isSupervisor on assistant messages
|
|
177
|
+
expect(callArgs.context).toMatchObject({
|
|
178
|
+
agentId: TEST_IDS.SUPERVISOR_AGENT_ID,
|
|
179
|
+
isSupervisor: true,
|
|
180
|
+
scope: 'group',
|
|
181
|
+
topicId: TEST_IDS.TOPIC_ID,
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('call_agent executor', () => {
|
|
187
|
+
it('should NOT pass operationId to internal_execAgentRuntime (creates new child operation)', async () => {
|
|
188
|
+
const mockStore = createMockStore({
|
|
189
|
+
dbMessagesMap: {
|
|
190
|
+
[`group_${TEST_IDS.GROUP_ID}_${TEST_IDS.TOPIC_ID}`]: [
|
|
191
|
+
{
|
|
192
|
+
content: 'Hello',
|
|
193
|
+
createdAt: Date.now(),
|
|
194
|
+
id: TEST_IDS.USER_MESSAGE_ID,
|
|
195
|
+
role: 'user',
|
|
196
|
+
updatedAt: Date.now(),
|
|
197
|
+
} as UIChatMessage,
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const executors = createGroupOrchestrationExecutors({
|
|
203
|
+
get: () => mockStore,
|
|
204
|
+
messageContext: {
|
|
205
|
+
agentId: TEST_IDS.GROUP_ID,
|
|
206
|
+
scope: 'group',
|
|
207
|
+
topicId: TEST_IDS.TOPIC_ID,
|
|
208
|
+
},
|
|
209
|
+
orchestrationOperationId: TEST_IDS.ORCHESTRATION_OPERATION_ID,
|
|
210
|
+
supervisorAgentId: TEST_IDS.SUPERVISOR_AGENT_ID,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const callAgentExecutor = executors.call_agent!;
|
|
214
|
+
const targetAgentId = 'target-agent-id';
|
|
215
|
+
|
|
216
|
+
await callAgentExecutor(
|
|
217
|
+
{
|
|
218
|
+
payload: { agentId: targetAgentId, instruction: 'Please respond' },
|
|
219
|
+
type: 'call_agent',
|
|
220
|
+
},
|
|
221
|
+
createInitialState(),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Verify internal_execAgentRuntime was called
|
|
225
|
+
expect(mockStore.internal_execAgentRuntime).toHaveBeenCalledTimes(1);
|
|
226
|
+
|
|
227
|
+
// Verify operationId is NOT passed (should be undefined)
|
|
228
|
+
const callArgs = (mockStore.internal_execAgentRuntime as any).mock.calls[0][0];
|
|
229
|
+
expect(callArgs.operationId).toBeUndefined();
|
|
230
|
+
|
|
231
|
+
// Verify parentOperationId is passed correctly
|
|
232
|
+
expect(callArgs.parentOperationId).toBe(TEST_IDS.ORCHESTRATION_OPERATION_ID);
|
|
233
|
+
|
|
234
|
+
// Verify subAgentId is passed (NOT isSupervisor)
|
|
235
|
+
expect(callArgs.context.subAgentId).toBe(targetAgentId);
|
|
236
|
+
expect(callArgs.context.isSupervisor).toBeUndefined();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('operation structure comparison', () => {
|
|
241
|
+
it('call_supervisor and call_agent should both create independent child operations', async () => {
|
|
242
|
+
const mockStore = createMockStore({
|
|
243
|
+
dbMessagesMap: {
|
|
244
|
+
[`group_${TEST_IDS.GROUP_ID}_${TEST_IDS.TOPIC_ID}`]: [
|
|
245
|
+
{
|
|
246
|
+
content: 'Hello',
|
|
247
|
+
createdAt: Date.now(),
|
|
248
|
+
id: TEST_IDS.USER_MESSAGE_ID,
|
|
249
|
+
role: 'user',
|
|
250
|
+
updatedAt: Date.now(),
|
|
251
|
+
} as UIChatMessage,
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const executors = createGroupOrchestrationExecutors({
|
|
257
|
+
get: () => mockStore,
|
|
258
|
+
messageContext: {
|
|
259
|
+
agentId: TEST_IDS.GROUP_ID,
|
|
260
|
+
scope: 'group',
|
|
261
|
+
topicId: TEST_IDS.TOPIC_ID,
|
|
262
|
+
},
|
|
263
|
+
orchestrationOperationId: TEST_IDS.ORCHESTRATION_OPERATION_ID,
|
|
264
|
+
supervisorAgentId: TEST_IDS.SUPERVISOR_AGENT_ID,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Execute call_supervisor
|
|
268
|
+
await executors.call_supervisor!(
|
|
269
|
+
{
|
|
270
|
+
payload: { round: 1, supervisorAgentId: TEST_IDS.SUPERVISOR_AGENT_ID },
|
|
271
|
+
type: 'call_supervisor',
|
|
272
|
+
},
|
|
273
|
+
createInitialState(),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// Execute call_agent
|
|
277
|
+
await executors.call_agent!(
|
|
278
|
+
{
|
|
279
|
+
payload: { agentId: 'agent-1', instruction: 'test' },
|
|
280
|
+
type: 'call_agent',
|
|
281
|
+
},
|
|
282
|
+
createInitialState(),
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
// Verify both were called
|
|
286
|
+
expect(mockStore.internal_execAgentRuntime).toHaveBeenCalledTimes(2);
|
|
287
|
+
|
|
288
|
+
// Get both call arguments
|
|
289
|
+
const supervisorCallArgs = (mockStore.internal_execAgentRuntime as any).mock.calls[0][0];
|
|
290
|
+
const agentCallArgs = (mockStore.internal_execAgentRuntime as any).mock.calls[1][0];
|
|
291
|
+
|
|
292
|
+
// Both should NOT have operationId (create new child operations)
|
|
293
|
+
expect(supervisorCallArgs.operationId).toBeUndefined();
|
|
294
|
+
expect(agentCallArgs.operationId).toBeUndefined();
|
|
295
|
+
|
|
296
|
+
// Both should have same parentOperationId (orchestration operation)
|
|
297
|
+
expect(supervisorCallArgs.parentOperationId).toBe(TEST_IDS.ORCHESTRATION_OPERATION_ID);
|
|
298
|
+
expect(agentCallArgs.parentOperationId).toBe(TEST_IDS.ORCHESTRATION_OPERATION_ID);
|
|
299
|
+
|
|
300
|
+
// Supervisor should have isSupervisor: true
|
|
301
|
+
expect(supervisorCallArgs.context.isSupervisor).toBe(true);
|
|
302
|
+
expect(agentCallArgs.context.isSupervisor).toBeUndefined();
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
@@ -126,10 +126,11 @@ export const createGroupOrchestrationExecutors = (
|
|
|
126
126
|
|
|
127
127
|
// Execute Supervisor agent with the supervisor's agentId in context
|
|
128
128
|
// Mark isSupervisor=true so assistant messages get metadata.isSupervisor for UI rendering
|
|
129
|
+
// Note: Don't pass operationId - let it create a new child operation (same as call_agent)
|
|
130
|
+
// This ensures each call has its own immutable context with isSupervisor properly set
|
|
129
131
|
await get().internal_execAgentRuntime({
|
|
130
132
|
context: { ...messageContext, agentId: supervisorAgentId, isSupervisor: true },
|
|
131
133
|
messages,
|
|
132
|
-
operationId: state.operationId,
|
|
133
134
|
parentMessageId: lastMessage.id,
|
|
134
135
|
parentMessageType: lastMessage.role as 'user' | 'assistant' | 'tool',
|
|
135
136
|
parentOperationId: orchestrationOperationId,
|
|
@@ -1087,12 +1087,14 @@ export const createAgentExecutors = (context: {
|
|
|
1087
1087
|
}
|
|
1088
1088
|
|
|
1089
1089
|
if (status.status === 'failed') {
|
|
1090
|
-
|
|
1090
|
+
// Extract error message (error is always a string in TaskStatusResult)
|
|
1091
|
+
const errorMessage = status.error || 'Unknown error';
|
|
1092
|
+
log('[%s] Task failed: %s', taskLogId, errorMessage);
|
|
1091
1093
|
await context
|
|
1092
1094
|
.get()
|
|
1093
1095
|
.optimisticUpdateMessageContent(
|
|
1094
1096
|
taskMessageId,
|
|
1095
|
-
`Task failed: ${
|
|
1097
|
+
`Task failed: ${errorMessage}`,
|
|
1096
1098
|
undefined,
|
|
1097
1099
|
{ operationId: state.operationId },
|
|
1098
1100
|
);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type MCPToolCallResult } from '@/libs/mcp';
|
|
2
|
+
import { useToolStore } from '@/store/tool';
|
|
3
|
+
import { type ChatToolPayload } from '@/types/message';
|
|
4
|
+
import { safeParseJSON } from '@/utils/safeParseJSON';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Executor function type for remote tool invocation
|
|
8
|
+
* @param payload - Tool call payload
|
|
9
|
+
* @returns Promise with MCPToolCallResult data
|
|
10
|
+
*/
|
|
11
|
+
export type RemoteToolExecutor = (payload: ChatToolPayload) => Promise<MCPToolCallResult>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create a failed MCPToolCallResult
|
|
15
|
+
*/
|
|
16
|
+
const createFailedResult = (
|
|
17
|
+
errorMessage: string,
|
|
18
|
+
): { content: string; error: any; state: any; success: false } => ({
|
|
19
|
+
content: errorMessage,
|
|
20
|
+
error: { message: errorMessage },
|
|
21
|
+
state: {},
|
|
22
|
+
success: false,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const klavisExecutor: RemoteToolExecutor = async (p) => {
|
|
26
|
+
// payload.identifier 现在是存储用的 identifier(如 'google-calendar')
|
|
27
|
+
const identifier = p.identifier;
|
|
28
|
+
const klavisServers = useToolStore.getState().servers || [];
|
|
29
|
+
const server = klavisServers.find((s) => s.identifier === identifier);
|
|
30
|
+
|
|
31
|
+
if (!server) {
|
|
32
|
+
return createFailedResult(`Klavis server not found: ${identifier}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Parse arguments
|
|
36
|
+
const args = safeParseJSON(p.arguments) || {};
|
|
37
|
+
|
|
38
|
+
// Call Klavis tool via store action
|
|
39
|
+
const result = await useToolStore.getState().callKlavisTool({
|
|
40
|
+
serverUrl: server.serverUrl,
|
|
41
|
+
toolArgs: args,
|
|
42
|
+
toolName: p.apiName,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (!result.success) {
|
|
46
|
+
return createFailedResult(result.error || 'Klavis tool execution failed');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// result.data is MCPToolCallProcessedResult from server
|
|
50
|
+
// Convert to MCPToolCallResult format
|
|
51
|
+
const toolResult = result.data;
|
|
52
|
+
if (toolResult) {
|
|
53
|
+
return {
|
|
54
|
+
content: toolResult.content,
|
|
55
|
+
error: toolResult.state?.isError ? toolResult.state : undefined,
|
|
56
|
+
state: toolResult.state,
|
|
57
|
+
success: toolResult.success,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return createFailedResult('Klavis tool returned empty result');
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const lobehubSkillExecutor: RemoteToolExecutor = async (p) => {
|
|
65
|
+
// payload.identifier is the provider id (e.g., 'linear', 'microsoft')
|
|
66
|
+
const provider = p.identifier;
|
|
67
|
+
|
|
68
|
+
// Parse arguments
|
|
69
|
+
const args = safeParseJSON(p.arguments) || {};
|
|
70
|
+
|
|
71
|
+
// Call LobeHub Skill tool via store action
|
|
72
|
+
const result = await useToolStore.getState().callLobehubSkillTool({
|
|
73
|
+
args,
|
|
74
|
+
provider,
|
|
75
|
+
toolName: p.apiName,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!result.success) {
|
|
79
|
+
return createFailedResult(
|
|
80
|
+
result.error || `LobeHub Skill tool ${provider} ${p.apiName} execution failed`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Convert to MCPToolCallResult format
|
|
85
|
+
const content = typeof result.data === 'string' ? result.data : JSON.stringify(result.data);
|
|
86
|
+
return {
|
|
87
|
+
content,
|
|
88
|
+
error: undefined,
|
|
89
|
+
state: { content: [{ text: content, type: 'text' }] },
|
|
90
|
+
success: true,
|
|
91
|
+
};
|
|
92
|
+
};
|