@lobehub/lobehub 2.0.0-next.308 → 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.
Files changed (27) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/locales/en-US/plugin.json +1 -0
  4. package/locales/zh-CN/plugin.json +1 -0
  5. package/package.json +1 -1
  6. package/packages/agent-runtime/src/types/state.ts +2 -0
  7. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/GetAgentInfo/index.tsx +68 -0
  8. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/index.ts +3 -0
  9. package/packages/builtin-tool-memory/src/manifest.ts +581 -19
  10. package/packages/types/src/topic/thread.ts +2 -2
  11. package/packages/types/src/userMemory/layers.ts +19 -8
  12. package/packages/types/src/userMemory/shared.ts +7 -1
  13. package/src/locales/default/plugin.ts +1 -0
  14. package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +11 -3
  15. package/src/server/modules/Mecha/AgentToolsEngine/index.ts +14 -6
  16. package/src/server/modules/Mecha/AgentToolsEngine/types.ts +4 -3
  17. package/src/server/routers/lambda/aiAgent.ts +10 -0
  18. package/src/server/services/agent/index.test.ts +155 -0
  19. package/src/server/services/agent/index.ts +25 -0
  20. package/src/server/services/agentRuntime/AgentRuntimeService.ts +2 -0
  21. package/src/server/services/agentRuntime/types.ts +1 -0
  22. package/src/server/services/aiAgent/__tests__/execAgent.threadId.test.ts +29 -9
  23. package/src/server/services/aiAgent/index.ts +175 -6
  24. package/src/server/services/lobehubSkill/index.ts +109 -0
  25. package/src/server/services/toolExecution/builtin.ts +28 -2
  26. package/src/server/services/toolExecution/types.ts +3 -0
  27. package/src/store/chat/agents/createAgentExecutors.ts +4 -2
@@ -1,4 +1,5 @@
1
1
  import type { AgentRuntimeContext, AgentState } from '@lobechat/agent-runtime';
2
+ import type { LobeToolManifest } from '@lobechat/context-engine';
2
3
  import { type LobeChatDatabase } from '@lobechat/database';
3
4
  import type {
4
5
  ExecAgentParams,
@@ -10,6 +11,7 @@ import type {
10
11
  } from '@lobechat/types';
11
12
  import { ThreadStatus, ThreadType } from '@lobechat/types';
12
13
  import { nanoid } from '@lobechat/utils';
14
+ import { MarketSDK } from '@lobehub/market-sdk';
13
15
  import debug from 'debug';
14
16
 
15
17
  import { LOADING_FLAT } from '@/const/message';
@@ -18,16 +20,43 @@ import { MessageModel } from '@/database/models/message';
18
20
  import { PluginModel } from '@/database/models/plugin';
19
21
  import { ThreadModel } from '@/database/models/thread';
20
22
  import { TopicModel } from '@/database/models/topic';
23
+ import { UserModel } from '@/database/models/user';
24
+ import { generateTrustedClientToken } from '@/libs/trusted-client';
21
25
  import {
22
26
  type ServerAgentToolsContext,
23
27
  createServerAgentToolsEngine,
24
28
  serverMessagesEngine,
25
29
  } from '@/server/modules/Mecha';
30
+ import { AgentService } from '@/server/services/agent';
26
31
  import { AgentRuntimeService } from '@/server/services/agentRuntime';
27
32
  import type { StepLifecycleCallbacks } from '@/server/services/agentRuntime/types';
28
33
 
29
34
  const log = debug('lobe-server:ai-agent-service');
30
35
 
36
+ /**
37
+ * Format error for storage in thread metadata
38
+ * Handles Error objects which don't serialize properly with JSON.stringify
39
+ */
40
+ function formatErrorForMetadata(error: unknown): Record<string, any> | undefined {
41
+ if (!error) return undefined;
42
+
43
+ // Handle Error objects
44
+ if (error instanceof Error) {
45
+ return {
46
+ message: error.message,
47
+ name: error.name,
48
+ };
49
+ }
50
+
51
+ // Handle objects with message property (like ChatMessageError)
52
+ if (typeof error === 'object' && 'message' in error) {
53
+ return error as Record<string, any>;
54
+ }
55
+
56
+ // Fallback: wrap in object
57
+ return { message: String(error) };
58
+ }
59
+
31
60
  /**
32
61
  * Internal params for execAgent with step lifecycle callbacks
33
62
  * This extends the public ExecAgentParams with server-side only options
@@ -53,6 +82,7 @@ export class AiAgentService {
53
82
  private readonly userId: string;
54
83
  private readonly db: LobeChatDatabase;
55
84
  private readonly agentModel: AgentModel;
85
+ private readonly agentService: AgentService;
56
86
  private readonly messageModel: MessageModel;
57
87
  private readonly pluginModel: PluginModel;
58
88
  private readonly threadModel: ThreadModel;
@@ -63,6 +93,7 @@ export class AiAgentService {
63
93
  this.userId = userId;
64
94
  this.db = db;
65
95
  this.agentModel = new AgentModel(db, userId);
96
+ this.agentService = new AgentService(db, userId);
66
97
  this.messageModel = new MessageModel(db, userId);
67
98
  this.pluginModel = new PluginModel(db, userId);
68
99
  this.threadModel = new ThreadModel(db, userId);
@@ -106,8 +137,8 @@ export class AiAgentService {
106
137
 
107
138
  log('execAgent: identifier=%s, prompt=%s', identifier, prompt.slice(0, 50));
108
139
 
109
- // 1. Get agent configuration from database (supports both id and slug)
110
- const agentConfig = await this.agentModel.getAgentConfig(identifier);
140
+ // 1. Get agent configuration with default config merged (supports both id and slug)
141
+ const agentConfig = await this.agentService.getAgentConfig(identifier);
111
142
  if (!agentConfig) {
112
143
  throw new Error(`Agent not found: ${identifier}`);
113
144
  }
@@ -158,7 +189,11 @@ export class AiAgentService {
158
189
  return info?.abilities?.functionCall ?? true;
159
190
  };
160
191
 
161
- // 5. Create tools using Server AgentToolsEngine
192
+ // 5. Fetch LobeHub Skills manifests (temporary solution until LOBE-3517 is implemented)
193
+ const lobehubSkillManifests = await this.fetchLobehubSkillManifests();
194
+ log('execAgent: got %d lobehub skill manifests', lobehubSkillManifests.length);
195
+
196
+ // 6. Create tools using Server AgentToolsEngine
162
197
  const hasEnabledKnowledgeBases =
163
198
  agentConfig.knowledgeBases?.some((kb: { enabled?: boolean | null }) => kb.enabled === true) ??
164
199
  false;
@@ -169,6 +204,7 @@ export class AiAgentService {
169
204
  };
170
205
 
171
206
  const toolsEngine = createServerAgentToolsEngine(toolsContext, {
207
+ additionalManifests: lobehubSkillManifests,
172
208
  agentConfig: {
173
209
  chatConfig: agentConfig.chatConfig ?? undefined,
174
210
  plugins: agentConfig.plugins ?? undefined,
@@ -180,6 +216,8 @@ export class AiAgentService {
180
216
 
181
217
  // Generate tools and manifest map
182
218
  const pluginIds = agentConfig.plugins || [];
219
+ log('execAgent: agent configured plugins: %O', pluginIds);
220
+
183
221
  const toolsResult = toolsEngine.generateToolsDetailed({
184
222
  model,
185
223
  provider,
@@ -188,6 +226,12 @@ export class AiAgentService {
188
226
 
189
227
  const tools = toolsResult.tools;
190
228
 
229
+ // Log detailed tools generation result
230
+ if (toolsResult.filteredTools && toolsResult.filteredTools.length > 0) {
231
+ log('execAgent: filtered tools: %O', toolsResult.filteredTools);
232
+ }
233
+ log('execAgent: enabled tool ids: %O', toolsResult.enabledToolIds);
234
+
191
235
  // Get manifest map and convert from Map to Record
192
236
  const manifestMap = toolsEngine.getEnabledPluginManifests(pluginIds);
193
237
  const toolManifestMap: Record<string, any> = {};
@@ -195,7 +239,20 @@ export class AiAgentService {
195
239
  toolManifestMap[id] = manifest;
196
240
  });
197
241
 
198
- log('execAgent: generated %d tools', tools?.length ?? 0);
242
+ // Build toolSourceMap for routing tool execution
243
+ const toolSourceMap: Record<string, 'builtin' | 'plugin' | 'mcp' | 'klavis' | 'lobehubSkill'> =
244
+ {};
245
+ // Mark lobehub skills
246
+ for (const manifest of lobehubSkillManifests) {
247
+ toolSourceMap[manifest.identifier] = 'lobehubSkill';
248
+ }
249
+
250
+ log(
251
+ 'execAgent: generated %d tools from %d configured plugins, %d lobehub skills',
252
+ tools?.length ?? 0,
253
+ pluginIds.length,
254
+ lobehubSkillManifests.length,
255
+ );
199
256
 
200
257
  // 6. Get existing messages if provided
201
258
  let historyMessages: any[] = [];
@@ -301,7 +358,18 @@ export class AiAgentService {
301
358
  },
302
359
  };
303
360
 
304
- // 12. Create operation using AgentRuntimeService
361
+ // 12. Log final operation parameters summary
362
+ log(
363
+ 'execAgent: creating operation %s with params: model=%s, provider=%s, tools=%d, messages=%d, manifests=%d',
364
+ operationId,
365
+ model,
366
+ provider,
367
+ tools?.length ?? 0,
368
+ processedMessages.length,
369
+ Object.keys(toolManifestMap).length,
370
+ );
371
+
372
+ // 13. Create operation using AgentRuntimeService
305
373
  // Wrap in try-catch to handle operation startup failures (e.g., QStash unavailable)
306
374
  // If createOperation fails, we still have valid messages that need error info
307
375
  try {
@@ -320,6 +388,7 @@ export class AiAgentService {
320
388
  operationId,
321
389
  stepCallbacks,
322
390
  toolManifestMap,
391
+ toolSourceMap,
323
392
  tools,
324
393
  userId: this.userId,
325
394
  });
@@ -616,6 +685,11 @@ export class AiAgentService {
616
685
  }
617
686
  }
618
687
 
688
+ // Log error when task fails
689
+ if (reason === 'error' && finalState.error) {
690
+ console.error('execSubAgentTask: task failed for thread %s:', threadId, finalState.error);
691
+ }
692
+
619
693
  try {
620
694
  // Extract summary from last assistant message and update task message content
621
695
  const lastAssistantMessage = finalState.messages
@@ -630,12 +704,15 @@ export class AiAgentService {
630
704
  log('execSubAgentTask: updated task message %s with summary', sourceMessageId);
631
705
  }
632
706
 
707
+ // Format error for proper serialization (Error objects don't serialize with JSON.stringify)
708
+ const formattedError = formatErrorForMetadata(finalState.error);
709
+
633
710
  // Update Thread metadata
634
711
  await this.threadModel.update(threadId, {
635
712
  metadata: {
636
713
  completedAt,
637
714
  duration,
638
- error: finalState.error,
715
+ error: formattedError,
639
716
  operationId: finalState.operationId,
640
717
  startedAt,
641
718
  totalCost: finalState.cost?.total,
@@ -659,6 +736,98 @@ export class AiAgentService {
659
736
  };
660
737
  }
661
738
 
739
+ /**
740
+ * Fetch LobeHub Skills manifests from Market API
741
+ * This is a temporary solution until LOBE-3517 is implemented (store skills in DB)
742
+ */
743
+ private async fetchLobehubSkillManifests(): Promise<LobeToolManifest[]> {
744
+ try {
745
+ // 1. Get user info for trusted client token
746
+ const user = await UserModel.findById(this.db, this.userId);
747
+ if (!user?.email) {
748
+ log('fetchLobehubSkillManifests: user email not found, skipping');
749
+ return [];
750
+ }
751
+
752
+ // 2. Generate trusted client token
753
+ const trustedClientToken = generateTrustedClientToken({
754
+ email: user.email,
755
+ name: user.fullName || user.firstName || undefined,
756
+ userId: this.userId,
757
+ });
758
+
759
+ if (!trustedClientToken) {
760
+ log('fetchLobehubSkillManifests: trusted client not configured, skipping');
761
+ return [];
762
+ }
763
+
764
+ // 3. Create MarketSDK instance
765
+ const marketSDK = new MarketSDK({
766
+ baseURL: process.env.NEXT_PUBLIC_MARKET_BASE_URL,
767
+ trustedClientToken,
768
+ });
769
+
770
+ // 4. Get user's connected skills
771
+ const { connections } = await marketSDK.connect.listConnections();
772
+ if (!connections || connections.length === 0) {
773
+ log('fetchLobehubSkillManifests: no connected skills found');
774
+ return [];
775
+ }
776
+
777
+ log('fetchLobehubSkillManifests: found %d connected skills', connections.length);
778
+
779
+ // 5. Fetch tools for each connection and build manifests
780
+ const manifests: LobeToolManifest[] = [];
781
+
782
+ for (const connection of connections) {
783
+ try {
784
+ // Connection returns providerId (e.g., 'twitter', 'linear'), not numeric id
785
+ const providerId = (connection as any).providerId;
786
+ if (!providerId) {
787
+ log('fetchLobehubSkillManifests: connection missing providerId: %O', connection);
788
+ continue;
789
+ }
790
+ const providerName =
791
+ (connection as any).providerName || (connection as any).name || providerId;
792
+ const icon = (connection as any).icon;
793
+
794
+ const { tools } = await marketSDK.skills.listTools(providerId);
795
+ if (!tools || tools.length === 0) continue;
796
+
797
+ const manifest: LobeToolManifest = {
798
+ api: tools.map((tool: any) => ({
799
+ description: tool.description || '',
800
+ name: tool.name,
801
+ parameters: tool.inputSchema || { properties: {}, type: 'object' },
802
+ })),
803
+ identifier: providerId,
804
+ meta: {
805
+ avatar: icon || '🔗',
806
+ description: `LobeHub Skill: ${providerName}`,
807
+ tags: ['lobehub-skill', providerId],
808
+ title: providerName,
809
+ },
810
+ type: 'builtin',
811
+ };
812
+
813
+ manifests.push(manifest);
814
+ log(
815
+ 'fetchLobehubSkillManifests: built manifest for %s with %d tools',
816
+ providerId,
817
+ tools.length,
818
+ );
819
+ } catch (error) {
820
+ log('fetchLobehubSkillManifests: failed to fetch tools for connection: %O', error);
821
+ }
822
+ }
823
+
824
+ return manifests;
825
+ } catch (error) {
826
+ log('fetchLobehubSkillManifests: error fetching skills: %O', error);
827
+ return [];
828
+ }
829
+ }
830
+
662
831
  /**
663
832
  * Calculate total tokens from AgentState usage object
664
833
  * AgentState.usage is of type Usage from @lobechat/agent-runtime
@@ -0,0 +1,109 @@
1
+ import { type LobeChatDatabase } from '@lobechat/database';
2
+ import { MarketSDK } from '@lobehub/market-sdk';
3
+ import debug from 'debug';
4
+
5
+ import { UserModel } from '@/database/models/user';
6
+ import { generateTrustedClientToken } from '@/libs/trusted-client';
7
+
8
+ const log = debug('lobe-server:lobehub-skill-service');
9
+
10
+ export interface LobehubSkillExecuteParams {
11
+ args: Record<string, any>;
12
+ provider: string;
13
+ toolName: string;
14
+ }
15
+
16
+ export interface LobehubSkillExecuteResult {
17
+ content: string;
18
+ error?: { code: string; message?: string };
19
+ success: boolean;
20
+ }
21
+
22
+ export class LobehubSkillService {
23
+ private db: LobeChatDatabase;
24
+ private userId: string;
25
+ private marketSDK?: MarketSDK;
26
+
27
+ constructor(db: LobeChatDatabase, userId: string) {
28
+ this.db = db;
29
+ this.userId = userId;
30
+ }
31
+
32
+ /**
33
+ * Initialize MarketSDK with trusted client token
34
+ */
35
+ private async getMarketSDK(): Promise<MarketSDK | null> {
36
+ if (this.marketSDK) return this.marketSDK;
37
+
38
+ try {
39
+ const user = await UserModel.findById(this.db, this.userId);
40
+ if (!user?.email) {
41
+ log('getMarketSDK: user email not found');
42
+ return null;
43
+ }
44
+
45
+ const trustedClientToken = generateTrustedClientToken({
46
+ email: user.email,
47
+ name: user.fullName || user.firstName || undefined,
48
+ userId: this.userId,
49
+ });
50
+
51
+ if (!trustedClientToken) {
52
+ log('getMarketSDK: trusted client not configured');
53
+ return null;
54
+ }
55
+
56
+ this.marketSDK = new MarketSDK({
57
+ baseURL: process.env.NEXT_PUBLIC_MARKET_BASE_URL,
58
+ trustedClientToken,
59
+ });
60
+
61
+ return this.marketSDK;
62
+ } catch (error) {
63
+ log('getMarketSDK: error creating SDK: %O', error);
64
+ return null;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Execute a LobeHub Skill tool
70
+ */
71
+ async execute(params: LobehubSkillExecuteParams): Promise<LobehubSkillExecuteResult> {
72
+ const { provider, toolName, args } = params;
73
+
74
+ log('execute: %s/%s with args: %O', provider, toolName, args);
75
+
76
+ const sdk = await this.getMarketSDK();
77
+ if (!sdk) {
78
+ return {
79
+ content:
80
+ 'MarketSDK not available. Please ensure you are authenticated with LobeHub Market.',
81
+ error: { code: 'MARKET_SDK_NOT_AVAILABLE' },
82
+ success: false,
83
+ };
84
+ }
85
+
86
+ try {
87
+ const response = await sdk.skills.callTool(provider, {
88
+ args,
89
+ tool: toolName,
90
+ });
91
+
92
+ log('execute: response: %O', response);
93
+
94
+ return {
95
+ content: typeof response.data === 'string' ? response.data : JSON.stringify(response.data),
96
+ success: response.success,
97
+ };
98
+ } catch (error) {
99
+ const err = error as Error;
100
+ console.error('LobehubSkillService.execute error %s/%s: %O', provider, toolName, err);
101
+
102
+ return {
103
+ content: err.message,
104
+ error: { code: 'LOBEHUB_SKILL_ERROR', message: err.message },
105
+ success: false,
106
+ };
107
+ }
108
+ }
109
+ }
@@ -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('Executing builtin tool: %s:%s with args: %O', identifier, apiName, args, context);
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
  }
@@ -1087,12 +1087,14 @@ export const createAgentExecutors = (context: {
1087
1087
  }
1088
1088
 
1089
1089
  if (status.status === 'failed') {
1090
- log('[%s] Task failed: %s', taskLogId, status.error);
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: ${status.error}`,
1097
+ `Task failed: ${errorMessage}`,
1096
1098
  undefined,
1097
1099
  { operationId: state.operationId },
1098
1100
  );