@lobehub/lobehub 2.0.0-next.306 → 2.0.0-next.308

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.
Files changed (254) hide show
  1. package/.vscode/settings.json +18 -3
  2. package/CHANGELOG.md +61 -0
  3. package/changelog/v1.json +21 -0
  4. package/locales/ar/agentGroup.json +5 -0
  5. package/locales/ar/chat.json +26 -0
  6. package/locales/ar/models.json +43 -5
  7. package/locales/ar/plugin.json +4 -5
  8. package/locales/ar/setting.json +11 -0
  9. package/locales/ar/subscription.json +2 -0
  10. package/locales/ar/tool.json +2 -0
  11. package/locales/bg-BG/agentGroup.json +5 -0
  12. package/locales/bg-BG/chat.json +26 -0
  13. package/locales/bg-BG/models.json +49 -3
  14. package/locales/bg-BG/plugin.json +4 -5
  15. package/locales/bg-BG/setting.json +11 -0
  16. package/locales/bg-BG/subscription.json +2 -0
  17. package/locales/bg-BG/tool.json +2 -0
  18. package/locales/de-DE/agentGroup.json +5 -0
  19. package/locales/de-DE/chat.json +26 -0
  20. package/locales/de-DE/models.json +48 -5
  21. package/locales/de-DE/plugin.json +4 -5
  22. package/locales/de-DE/setting.json +11 -0
  23. package/locales/de-DE/subscription.json +2 -0
  24. package/locales/de-DE/tool.json +2 -0
  25. package/locales/en-US/models.json +8 -6
  26. package/locales/en-US/plugin.json +2 -4
  27. package/locales/en-US/setting.json +10 -11
  28. package/locales/en-US/tool.json +2 -0
  29. package/locales/es-ES/agentGroup.json +5 -0
  30. package/locales/es-ES/chat.json +26 -0
  31. package/locales/es-ES/models.json +43 -5
  32. package/locales/es-ES/plugin.json +4 -5
  33. package/locales/es-ES/setting.json +11 -0
  34. package/locales/es-ES/subscription.json +2 -0
  35. package/locales/es-ES/tool.json +2 -0
  36. package/locales/fa-IR/agentGroup.json +5 -0
  37. package/locales/fa-IR/chat.json +26 -0
  38. package/locales/fa-IR/models.json +42 -5
  39. package/locales/fa-IR/plugin.json +4 -5
  40. package/locales/fa-IR/setting.json +11 -0
  41. package/locales/fa-IR/subscription.json +2 -0
  42. package/locales/fa-IR/tool.json +2 -0
  43. package/locales/fr-FR/agentGroup.json +5 -0
  44. package/locales/fr-FR/chat.json +26 -0
  45. package/locales/fr-FR/models.json +5 -5
  46. package/locales/fr-FR/plugin.json +4 -5
  47. package/locales/fr-FR/setting.json +11 -0
  48. package/locales/fr-FR/subscription.json +2 -0
  49. package/locales/fr-FR/tool.json +2 -0
  50. package/locales/it-IT/agentGroup.json +5 -0
  51. package/locales/it-IT/chat.json +26 -0
  52. package/locales/it-IT/models.json +1 -3
  53. package/locales/it-IT/plugin.json +4 -5
  54. package/locales/it-IT/setting.json +11 -0
  55. package/locales/it-IT/subscription.json +2 -0
  56. package/locales/it-IT/tool.json +2 -0
  57. package/locales/ja-JP/agentGroup.json +5 -0
  58. package/locales/ja-JP/chat.json +26 -0
  59. package/locales/ja-JP/models.json +1 -5
  60. package/locales/ja-JP/plugin.json +4 -5
  61. package/locales/ja-JP/setting.json +11 -0
  62. package/locales/ja-JP/subscription.json +2 -0
  63. package/locales/ja-JP/tool.json +2 -0
  64. package/locales/ko-KR/agentGroup.json +5 -0
  65. package/locales/ko-KR/chat.json +26 -0
  66. package/locales/ko-KR/models.json +1 -3
  67. package/locales/ko-KR/plugin.json +4 -5
  68. package/locales/ko-KR/setting.json +11 -0
  69. package/locales/ko-KR/subscription.json +2 -0
  70. package/locales/ko-KR/tool.json +2 -0
  71. package/locales/nl-NL/agentGroup.json +5 -0
  72. package/locales/nl-NL/chat.json +26 -0
  73. package/locales/nl-NL/models.json +35 -3
  74. package/locales/nl-NL/plugin.json +4 -5
  75. package/locales/nl-NL/setting.json +11 -0
  76. package/locales/nl-NL/subscription.json +2 -0
  77. package/locales/nl-NL/tool.json +2 -0
  78. package/locales/pl-PL/agentGroup.json +5 -0
  79. package/locales/pl-PL/chat.json +26 -0
  80. package/locales/pl-PL/models.json +1 -3
  81. package/locales/pl-PL/plugin.json +4 -5
  82. package/locales/pl-PL/setting.json +11 -0
  83. package/locales/pl-PL/subscription.json +2 -0
  84. package/locales/pl-PL/tool.json +2 -0
  85. package/locales/pt-BR/agentGroup.json +5 -0
  86. package/locales/pt-BR/chat.json +26 -0
  87. package/locales/pt-BR/models.json +50 -5
  88. package/locales/pt-BR/plugin.json +4 -5
  89. package/locales/pt-BR/setting.json +11 -0
  90. package/locales/pt-BR/subscription.json +2 -0
  91. package/locales/pt-BR/tool.json +2 -0
  92. package/locales/ru-RU/agentGroup.json +5 -0
  93. package/locales/ru-RU/chat.json +26 -0
  94. package/locales/ru-RU/models.json +22 -3
  95. package/locales/ru-RU/plugin.json +4 -5
  96. package/locales/ru-RU/setting.json +11 -0
  97. package/locales/ru-RU/subscription.json +2 -0
  98. package/locales/ru-RU/tool.json +2 -0
  99. package/locales/tr-TR/agentGroup.json +5 -0
  100. package/locales/tr-TR/chat.json +26 -0
  101. package/locales/tr-TR/models.json +36 -3
  102. package/locales/tr-TR/plugin.json +4 -5
  103. package/locales/tr-TR/setting.json +11 -0
  104. package/locales/tr-TR/subscription.json +2 -0
  105. package/locales/tr-TR/tool.json +2 -0
  106. package/locales/vi-VN/agentGroup.json +5 -0
  107. package/locales/vi-VN/chat.json +26 -0
  108. package/locales/vi-VN/models.json +1 -1
  109. package/locales/vi-VN/plugin.json +4 -5
  110. package/locales/vi-VN/setting.json +11 -0
  111. package/locales/vi-VN/subscription.json +2 -0
  112. package/locales/vi-VN/tool.json +2 -0
  113. package/locales/zh-CN/models.json +52 -5
  114. package/locales/zh-CN/plugin.json +5 -7
  115. package/locales/zh-CN/setting.json +10 -11
  116. package/locales/zh-CN/tool.json +2 -2
  117. package/locales/zh-TW/agentGroup.json +5 -0
  118. package/locales/zh-TW/chat.json +26 -0
  119. package/locales/zh-TW/models.json +54 -5
  120. package/locales/zh-TW/plugin.json +4 -5
  121. package/locales/zh-TW/setting.json +11 -0
  122. package/locales/zh-TW/subscription.json +2 -0
  123. package/locales/zh-TW/tool.json +2 -0
  124. package/package.json +2 -2
  125. package/packages/builtin-agents/src/agents/group-supervisor/index.ts +1 -7
  126. package/packages/builtin-tool-group-agent-builder/src/ExecutionRuntime/index.ts +29 -0
  127. package/packages/builtin-tool-group-agent-builder/src/executor.ts +18 -0
  128. package/packages/builtin-tool-group-agent-builder/src/manifest.ts +17 -0
  129. package/packages/builtin-tool-group-agent-builder/src/types.ts +10 -0
  130. package/packages/builtin-tool-group-management/src/client/Inspector/ExecuteAgentTask/index.tsx +52 -8
  131. package/packages/builtin-tool-group-management/src/client/Render/ExecuteTask/index.tsx +2 -21
  132. package/packages/builtin-tool-group-management/src/executor.test.ts +6 -16
  133. package/packages/builtin-tool-group-management/src/executor.ts +8 -47
  134. package/packages/builtin-tool-group-management/src/manifest.ts +5 -18
  135. package/packages/builtin-tool-group-management/src/systemRole.ts +1 -8
  136. package/packages/builtin-tool-group-management/src/types.ts +2 -10
  137. package/packages/builtin-tool-local-system/src/ExecutionRuntime/index.ts +70 -31
  138. package/packages/builtin-tool-local-system/src/client/Render/WriteFile/index.tsx +48 -5
  139. package/packages/builtin-tool-local-system/src/client/Streaming/WriteFile/index.tsx +39 -0
  140. package/packages/builtin-tool-local-system/src/client/Streaming/index.ts +2 -0
  141. package/packages/builtin-tool-local-system/src/executor/index.ts +94 -60
  142. package/packages/database/src/repositories/agentGroup/index.ts +23 -0
  143. package/packages/model-bank/src/aiModels/qiniu.ts +24 -0
  144. package/packages/prompts/src/prompts/fileSystem/formatCommandOutput.test.ts +61 -0
  145. package/packages/prompts/src/prompts/fileSystem/formatCommandOutput.ts +21 -0
  146. package/packages/prompts/src/prompts/fileSystem/formatCommandResult.test.ts +87 -0
  147. package/packages/prompts/src/prompts/fileSystem/formatCommandResult.ts +35 -0
  148. package/packages/prompts/src/prompts/fileSystem/formatEditResult.test.ts +57 -0
  149. package/packages/prompts/src/prompts/fileSystem/formatEditResult.ts +17 -0
  150. package/packages/prompts/src/prompts/fileSystem/formatFileContent.test.ts +59 -0
  151. package/packages/prompts/src/prompts/fileSystem/formatFileContent.ts +14 -0
  152. package/packages/prompts/src/prompts/fileSystem/formatFileList.test.ts +62 -0
  153. package/packages/prompts/src/prompts/fileSystem/formatFileList.ts +13 -0
  154. package/packages/prompts/src/prompts/fileSystem/formatFileSearchResults.test.ts +34 -0
  155. package/packages/prompts/src/prompts/fileSystem/formatFileSearchResults.ts +12 -0
  156. package/packages/prompts/src/prompts/fileSystem/formatGlobResults.test.ts +64 -0
  157. package/packages/prompts/src/prompts/fileSystem/formatGlobResults.ts +23 -0
  158. package/packages/prompts/src/prompts/fileSystem/formatGrepResults.test.ts +85 -0
  159. package/packages/prompts/src/prompts/fileSystem/formatGrepResults.ts +24 -0
  160. package/packages/prompts/src/prompts/fileSystem/formatKillResult.test.ts +30 -0
  161. package/packages/prompts/src/prompts/fileSystem/formatKillResult.ts +9 -0
  162. package/packages/prompts/src/prompts/fileSystem/formatMoveResults.test.ts +37 -0
  163. package/packages/prompts/src/prompts/fileSystem/formatMoveResults.ts +20 -0
  164. package/packages/prompts/src/prompts/fileSystem/formatMultipleFiles.test.ts +54 -0
  165. package/packages/prompts/src/prompts/fileSystem/formatMultipleFiles.ts +9 -0
  166. package/packages/prompts/src/prompts/fileSystem/formatRenameResult.test.ts +35 -0
  167. package/packages/prompts/src/prompts/fileSystem/formatRenameResult.ts +17 -0
  168. package/packages/prompts/src/prompts/fileSystem/formatWriteResult.test.ts +30 -0
  169. package/packages/prompts/src/prompts/fileSystem/formatWriteResult.ts +11 -0
  170. package/packages/prompts/src/prompts/fileSystem/index.ts +13 -0
  171. package/packages/prompts/src/prompts/index.ts +1 -0
  172. package/src/app/[variants]/(auth)/_layout/index.tsx +0 -2
  173. package/src/app/[variants]/(auth)/layout.tsx +0 -2
  174. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -3
  175. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -3
  176. package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/index.tsx +0 -2
  177. package/src/app/[variants]/(main)/_layout/index.tsx +0 -2
  178. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/Actions.tsx +4 -3
  179. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/useDropdownMenu.tsx +12 -2
  180. package/src/app/[variants]/(main)/agent/_layout/index.tsx +0 -2
  181. package/src/app/[variants]/(main)/agent/features/Portal/index.tsx +0 -2
  182. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/AddGroupAgent.tsx +69 -17
  183. package/src/app/[variants]/(main)/community/(list)/_layout/index.tsx +0 -2
  184. package/src/app/[variants]/(main)/community/(list)/assistant/_layout/index.tsx +0 -2
  185. package/src/app/[variants]/(main)/community/(list)/mcp/_layout/index.tsx +0 -2
  186. package/src/app/[variants]/(main)/community/(list)/model/_layout/index.tsx +0 -2
  187. package/src/app/[variants]/(main)/community/_layout/index.tsx +0 -2
  188. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/Actions.tsx +4 -3
  189. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/useDropdownMenu.tsx +12 -2
  190. package/src/app/[variants]/(main)/group/_layout/index.tsx +0 -2
  191. package/src/app/[variants]/(main)/group/features/Conversation/Header/index.tsx +4 -2
  192. package/src/app/[variants]/(main)/group/features/Portal/index.tsx +0 -2
  193. package/src/app/[variants]/(main)/home/_layout/index.tsx +0 -2
  194. package/src/app/[variants]/(main)/home/index.tsx +0 -2
  195. package/src/app/[variants]/(main)/image/_layout/Topics/TopicUrlSync.tsx +0 -2
  196. package/src/app/[variants]/(main)/image/_layout/index.tsx +0 -2
  197. package/src/app/[variants]/(main)/memory/_layout/index.tsx +0 -2
  198. package/src/app/[variants]/(main)/page/_layout/index.tsx +0 -2
  199. package/src/app/[variants]/(main)/resource/(home)/_layout/index.tsx +0 -2
  200. package/src/app/[variants]/(main)/resource/_layout/index.tsx +0 -2
  201. package/src/app/[variants]/(main)/resource/library/_layout/index.tsx +0 -2
  202. package/src/app/[variants]/(main)/resource/library/features/Container.tsx +0 -2
  203. package/src/app/[variants]/(main)/settings/_layout/index.tsx +0 -2
  204. package/src/app/[variants]/(main)/settings/about/index.tsx +0 -2
  205. package/src/app/[variants]/(main)/settings/agent/index.tsx +0 -2
  206. package/src/app/[variants]/(main)/settings/apikey/index.tsx +0 -2
  207. package/src/app/[variants]/(main)/settings/chat-appearance/index.tsx +0 -2
  208. package/src/app/[variants]/(main)/settings/common/index.tsx +0 -2
  209. package/src/app/[variants]/(main)/settings/hotkey/index.tsx +0 -2
  210. package/src/app/[variants]/(main)/settings/image/index.tsx +0 -2
  211. package/src/app/[variants]/(main)/settings/memory/index.tsx +0 -2
  212. package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +0 -2
  213. package/src/app/[variants]/(main)/settings/proxy/index.tsx +0 -2
  214. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -3
  215. package/src/app/[variants]/(main)/settings/storage/index.tsx +0 -2
  216. package/src/app/[variants]/(main)/settings/tts/index.tsx +0 -2
  217. package/src/app/[variants]/(mobile)/(home)/_layout/index.tsx +0 -2
  218. package/src/app/[variants]/(mobile)/_layout/index.tsx +1 -3
  219. package/src/app/[variants]/(mobile)/chat/_layout/index.tsx +0 -2
  220. package/src/app/[variants]/(mobile)/chat/settings/_layout/index.tsx +0 -2
  221. package/src/app/[variants]/(mobile)/community/(detail)/_layout/index.tsx +0 -2
  222. package/src/app/[variants]/(mobile)/community/(list)/_layout/index.tsx +0 -2
  223. package/src/app/[variants]/(mobile)/community/_layout/index.tsx +0 -2
  224. package/src/app/[variants]/(mobile)/router/MobileClientRouter.tsx +0 -2
  225. package/src/app/[variants]/(mobile)/settings/index.tsx +0 -2
  226. package/src/app/[variants]/onboarding/_layout/index.tsx +0 -2
  227. package/src/app/[variants]/router/DesktopClientRouter.tsx +0 -2
  228. package/src/components/ModelSelect/index.tsx +6 -56
  229. package/src/components/server/MobileNavLayout.tsx +0 -2
  230. package/src/components/server/ServerLayout.tsx +0 -2
  231. package/src/features/ChatInput/ActionBar/Upload/ServerMode.tsx +13 -3
  232. package/src/features/ChatInput/ActionBar/components/ActionDropdown.tsx +26 -3
  233. package/src/features/ModelSwitchPanel/components/Footer.tsx +0 -2
  234. package/src/features/ModelSwitchPanel/components/List/MultipleProvidersModelItem.tsx +0 -1
  235. package/src/features/ModelSwitchPanel/components/List/SingleProviderModelItem.tsx +0 -1
  236. package/src/features/ModelSwitchPanel/components/List/VirtualItemRenderer.tsx +0 -1
  237. package/src/features/ModelSwitchPanel/components/List/index.tsx +15 -13
  238. package/src/features/ModelSwitchPanel/components/PanelContent.tsx +0 -2
  239. package/src/features/ModelSwitchPanel/index.tsx +21 -23
  240. package/src/features/ResourceManager/components/Explorer/MasonryView/index.tsx +0 -2
  241. package/src/features/ResourceManager/components/Header/AddButton.tsx +20 -3
  242. package/src/features/User/UserAvatar.tsx +0 -2
  243. package/src/locales/default/plugin.ts +2 -1
  244. package/src/server/routers/lambda/__tests__/agentGroup.test.ts +1 -0
  245. package/src/server/routers/lambda/agentGroup.ts +22 -0
  246. package/src/services/chat/index.ts +1 -0
  247. package/src/services/chat/mecha/agentConfigResolver.test.ts +62 -45
  248. package/src/services/chat/mecha/agentConfigResolver.ts +29 -27
  249. package/src/services/chatGroup/index.ts +14 -0
  250. package/src/store/chat/agents/GroupOrchestration/__tests__/call-supervisor.test.ts +305 -0
  251. package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +2 -1
  252. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +6 -2
  253. package/src/store/chat/slices/plugin/actions/exector.ts +92 -0
  254. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +82 -177
@@ -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,
@@ -160,14 +160,16 @@ export const streamingExecutor: StateCreator<
160
160
  // - agentId is used for session ID (message storage location)
161
161
  const effectiveAgentId = paramSubAgentId || agentId;
162
162
 
163
- // Get scope from operation context if available
163
+ // Get scope and groupId from operation context if available
164
164
  const operation = operationId ? get().operations[operationId] : undefined;
165
165
  const scope = operation?.context.scope;
166
+ const groupId = operation?.context.groupId;
166
167
 
167
168
  // Resolve agent config with builtin agent runtime config merged
168
169
  // This ensures runtime plugins (e.g., 'lobe-agent-builder' for Agent Builder) are included
169
170
  const { agentConfig: agentConfigData, plugins: pluginIds } = resolveAgentConfig({
170
171
  agentId: effectiveAgentId || '',
172
+ groupId, // Pass groupId for supervisor detection
171
173
  scope, // Pass scope from operation context
172
174
  });
173
175
 
@@ -341,6 +343,7 @@ export const streamingExecutor: StateCreator<
341
343
  // - max_tokens/reasoning_effort based on chatConfig settings
342
344
  const resolved = resolveAgentConfig({
343
345
  agentId: effectiveAgentId,
346
+ groupId, // Pass groupId for supervisor detection
344
347
  scope, // scope is already available from line 329
345
348
  });
346
349
  const finalAgentConfig = agentConfig || resolved.agentConfig;
@@ -594,7 +597,8 @@ export const streamingExecutor: StateCreator<
594
597
  // - max_tokens/reasoning_effort based on chatConfig settings
595
598
  const { agentConfig: agentConfigData } = resolveAgentConfig({
596
599
  agentId: effectiveAgentId || '',
597
- scope: context.scope, // Pass scope from context parameter (available at line 883)
600
+ groupId, // Pass groupId for supervisor detection
601
+ scope: context.scope, // Pass scope from context parameter
598
602
  });
599
603
 
600
604
  // Use agent config from agentId
@@ -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
+ };