@lobehub/lobehub 2.0.0-next.335 → 2.0.0-next.337

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 (53) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/changelog/v1.json +21 -0
  3. package/package.json +1 -1
  4. package/packages/builtin-tool-agent-builder/src/manifest.ts +0 -2
  5. package/packages/builtin-tool-group-management/src/manifest.ts +54 -53
  6. package/packages/builtin-tool-group-management/src/systemRole.ts +43 -111
  7. package/packages/builtin-tool-memory/src/client/Render/AddPreferenceMemory/index.tsx +17 -0
  8. package/packages/builtin-tool-memory/src/client/Render/index.ts +2 -0
  9. package/packages/builtin-tool-memory/src/client/Streaming/AddPreferenceMemory/index.tsx +17 -0
  10. package/packages/builtin-tool-memory/src/client/Streaming/index.ts +4 -3
  11. package/packages/builtin-tool-memory/src/client/components/PreferenceMemoryCard.tsx +357 -0
  12. package/packages/builtin-tool-memory/src/client/components/index.ts +1 -0
  13. package/packages/builtin-tool-memory/src/executor/index.ts +3 -3
  14. package/packages/builtin-tool-memory/src/systemRole.ts +1 -0
  15. package/packages/context-engine/src/engine/tools/ToolArgumentsRepairer.ts +129 -0
  16. package/packages/context-engine/src/engine/tools/__tests__/ToolArgumentsRepairer.test.ts +186 -0
  17. package/packages/context-engine/src/engine/tools/index.ts +3 -0
  18. package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/index.ts +2 -0
  19. package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/with-assistant-group.json +156 -0
  20. package/packages/conversation-flow/src/__tests__/parse.test.ts +22 -0
  21. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +88 -11
  22. package/packages/database/src/models/userMemory/model.ts +1 -1
  23. package/packages/memory-user-memory/src/extractors/context.test.ts +0 -1
  24. package/packages/memory-user-memory/src/extractors/experience.test.ts +0 -1
  25. package/packages/memory-user-memory/src/extractors/identity.test.ts +0 -1
  26. package/packages/memory-user-memory/src/extractors/preference.test.ts +0 -1
  27. package/packages/memory-user-memory/src/schemas/context.ts +0 -2
  28. package/packages/memory-user-memory/src/schemas/experience.ts +0 -2
  29. package/packages/memory-user-memory/src/schemas/identity.ts +1 -2
  30. package/packages/memory-user-memory/src/schemas/preference.ts +0 -2
  31. package/packages/types/src/openai/chat.ts +0 -4
  32. package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +5 -1
  33. package/src/app/[variants]/(main)/community/(detail)/user/features/UserAgentCard.tsx +8 -8
  34. package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupCard.tsx +142 -15
  35. package/src/app/[variants]/(main)/community/(detail)/user/features/useUserDetail.ts +45 -20
  36. package/src/server/routers/lambda/market/agentGroup.ts +179 -1
  37. package/src/server/routers/lambda/userMemories/tools.ts +5 -4
  38. package/src/server/routers/lambda/userMemories.ts +4 -4
  39. package/src/server/services/discover/index.ts +4 -0
  40. package/src/server/services/memory/userMemory/extract.ts +3 -3
  41. package/src/services/chat/chat.test.ts +109 -104
  42. package/src/services/chat/index.ts +13 -32
  43. package/src/services/chat/mecha/agentConfigResolver.test.ts +113 -0
  44. package/src/services/chat/mecha/agentConfigResolver.ts +15 -5
  45. package/src/services/marketApi.ts +14 -0
  46. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +13 -0
  47. package/src/store/chat/agents/createAgentExecutors.ts +13 -1
  48. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +5 -1
  49. package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +14 -0
  50. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +131 -7
  51. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +61 -62
  52. package/src/store/chat/slices/plugin/action.test.ts +71 -0
  53. package/src/store/chat/slices/plugin/actions/internals.ts +14 -5
@@ -28,7 +28,7 @@ import { type StateCreator } from 'zustand/vanilla';
28
28
 
29
29
  import { createAgentToolsEngine } from '@/helpers/toolEngineering';
30
30
  import { chatService } from '@/services/chat';
31
- import { resolveAgentConfig } from '@/services/chat/mecha';
31
+ import { type ResolvedAgentConfig, resolveAgentConfig } from '@/services/chat/mecha';
32
32
  import { messageService } from '@/services/message';
33
33
  import { createAgentExecutors } from '@/store/chat/agents/createAgentExecutors';
34
34
  import { type ChatStore } from '@/store/chat/store';
@@ -72,9 +72,15 @@ export interface StreamingExecutorAction {
72
72
  * Used to get Agent config (model, provider, plugins) instead of agentId
73
73
  */
74
74
  subAgentId?: string;
75
+ /**
76
+ * Whether this is a sub-task execution (disables lobe-gtd tools to prevent nested sub-tasks)
77
+ */
78
+ isSubTask?: boolean;
75
79
  }) => {
76
80
  state: AgentState;
77
81
  context: AgentRuntimeContext;
82
+ /** Resolved agent config with isSubTask filtering applied */
83
+ agentConfig: ResolvedAgentConfig;
78
84
  };
79
85
  /**
80
86
  * Retrieves an AI-generated chat message from the backend service with streaming
@@ -85,7 +91,8 @@ export interface StreamingExecutorAction {
85
91
  model: string;
86
92
  provider: string;
87
93
  operationId?: string;
88
- agentConfig?: any;
94
+ /** Pre-resolved agent config (from internal_createAgentState) with isSubTask filtering applied */
95
+ agentConfig: ResolvedAgentConfig;
89
96
  traceId?: string;
90
97
  /** Initial context for page editor (captured at operation start) */
91
98
  initialContext?: RuntimeInitialContext;
@@ -132,6 +139,10 @@ export interface StreamingExecutorAction {
132
139
  */
133
140
  parentOperationId?: string;
134
141
  skipCreateFirstMessage?: boolean;
142
+ /**
143
+ * Whether this is a sub-task execution (disables lobe-gtd tools to prevent nested sub-tasks)
144
+ */
145
+ isSubTask?: boolean;
135
146
  }) => Promise<{ cost?: Cost; usage?: Usage } | void>;
136
147
  }
137
148
 
@@ -151,6 +162,7 @@ export const streamingExecutor: StateCreator<
151
162
  initialContext,
152
163
  operationId,
153
164
  subAgentId: paramSubAgentId,
165
+ isSubTask,
154
166
  }) => {
155
167
  // Use provided agentId/topicId or fallback to global state
156
168
  const { activeAgentId, activeTopicId } = get();
@@ -169,11 +181,16 @@ export const streamingExecutor: StateCreator<
169
181
 
170
182
  // Resolve agent config with builtin agent runtime config merged
171
183
  // This ensures runtime plugins (e.g., 'lobe-agent-builder' for Agent Builder) are included
172
- const { agentConfig: agentConfigData, plugins: pluginIds } = resolveAgentConfig({
184
+ // isSubTask is passed to filter out lobe-gtd tools to prevent nested sub-task creation
185
+ const agentConfig = resolveAgentConfig({
173
186
  agentId: effectiveAgentId || '',
174
187
  groupId, // Pass groupId for supervisor detection
188
+ isSubTask, // Filter out lobe-gtd in sub-task context
175
189
  scope, // Pass scope from operation context
176
190
  });
191
+ const { agentConfig: agentConfigData, plugins: pluginIds } = agentConfig;
192
+
193
+ log('[internal_createAgentState] resolved plugins=%o, isSubTask=%s', pluginIds, isSubTask);
177
194
 
178
195
  // Get tools manifest map
179
196
  const toolsEngine = createAgentToolsEngine({
@@ -260,7 +277,7 @@ export const streamingExecutor: StateCreator<
260
277
  initialContext: runtimeInitialContext,
261
278
  };
262
279
 
263
- return { state, context };
280
+ return { state, context, agentConfig };
264
281
  },
265
282
 
266
283
  internal_fetchAIChatMessage: async ({
@@ -333,23 +350,10 @@ export const streamingExecutor: StateCreator<
333
350
  // Create base context for child operations and message queries
334
351
  const fetchContext = { agentId, topicId, threadId, groupId, scope };
335
352
 
336
- // For group orchestration scenarios:
337
- // - subAgentId is used for agent config retrieval (model, provider, plugins)
338
- // - agentId is used for session ID (message storage location)
339
- const effectiveAgentId = subAgentId || agentId;
340
-
341
- // Resolve agent config with params adjusted based on chatConfig
342
- // If agentConfig is passed in, use it directly (it's already resolved)
343
- // Otherwise, resolve from mecha layer which handles:
344
- // - Builtin agent runtime config merging
345
- // - max_tokens/reasoning_effort based on chatConfig settings
346
- const resolved = resolveAgentConfig({
347
- agentId: effectiveAgentId,
348
- groupId, // Pass groupId for supervisor detection
349
- scope, // scope is already available from line 329
350
- });
351
- const finalAgentConfig = agentConfig || resolved.agentConfig;
352
- const chatConfig = resolved.chatConfig;
353
+ // Use pre-resolved agent config (from internal_createAgentState)
354
+ // This ensures isSubTask filtering and other runtime modifications are preserved
355
+ const { agentConfig: agentConfigData, chatConfig, plugins: pluginIds } = agentConfig;
356
+ log('[internal_fetchAIChatMessage] using pre-resolved config, plugins=%o', pluginIds);
353
357
 
354
358
  let finalUsage: ModelUsage | undefined;
355
359
  let finalToolCalls: MessageToolCall[] | undefined;
@@ -437,18 +441,18 @@ export const streamingExecutor: StateCreator<
437
441
  await chatService.createAssistantMessageStream({
438
442
  abortController,
439
443
  params: {
440
- // Use effectiveAgentId for agent config resolution (system role, tools, etc.)
441
- // In group orchestration: subAgentId for the actual speaking agent
442
- // In normal chat: agentId for the main agent
443
- agentId: effectiveAgentId || undefined,
444
+ // agentId is used for context, not for config resolution (config is pre-resolved)
445
+ agentId: agentId || undefined,
444
446
  groupId,
445
447
  messages,
446
448
  model,
447
449
  provider,
450
+ // Pass pre-resolved config to avoid duplicate resolveAgentConfig calls
451
+ // This ensures isSubTask filtering and other runtime modifications are preserved
452
+ resolvedAgentConfig: agentConfig,
448
453
  scope, // Pass scope to chat service for page-agent injection
449
- topicId, // Pass topicId for GTD context injection
450
- ...finalAgentConfig.params,
451
- plugins: finalAgentConfig.plugins,
454
+ topicId: topicId ?? undefined, // Pass topicId for GTD context injection
455
+ ...agentConfigData.params,
452
456
  },
453
457
  historySummary: historySummary?.content,
454
458
  // Pass page editor context from agent runtime
@@ -544,7 +548,13 @@ export const streamingExecutor: StateCreator<
544
548
  },
545
549
 
546
550
  internal_execAgentRuntime: async (params) => {
547
- const { messages: originalMessages, parentMessageId, parentMessageType, context } = params;
551
+ const {
552
+ messages: originalMessages,
553
+ parentMessageId,
554
+ parentMessageType,
555
+ context,
556
+ isSubTask,
557
+ } = params;
548
558
 
549
559
  // Extract values from context
550
560
  const { agentId, topicId, threadId, subAgentId, groupId } = context;
@@ -593,30 +603,32 @@ export const streamingExecutor: StateCreator<
593
603
  // Create a new array to avoid modifying the original messages
594
604
  let messages = [...originalMessages];
595
605
 
596
- // Use effectiveAgentId to get agent config (subAgentId in group orchestration, agentId otherwise)
597
- // resolveAgentConfig handles:
598
- // - Builtin agent runtime config merging
599
- // - max_tokens/reasoning_effort based on chatConfig settings
600
- const { agentConfig: agentConfigData } = resolveAgentConfig({
601
- agentId: effectiveAgentId || '',
602
- groupId, // Pass groupId for supervisor detection
603
- scope: context.scope, // Pass scope from context parameter
606
+ // ===========================================
607
+ // Step 1: Create Agent State (resolves config once)
608
+ // ===========================================
609
+ // agentConfig contains isSubTask filtering and is passed to callLLM executor
610
+ const {
611
+ state: initialAgentState,
612
+ context: initialAgentContext,
613
+ agentConfig,
614
+ } = get().internal_createAgentState({
615
+ messages,
616
+ parentMessageId: params.parentMessageId,
617
+ agentId,
618
+ topicId,
619
+ threadId: threadId ?? undefined,
620
+ initialState: params.initialState,
621
+ initialContext: params.initialContext,
622
+ operationId,
623
+ subAgentId, // Pass subAgentId for agent config retrieval
624
+ isSubTask, // Pass isSubTask to filter out lobe-gtd tools in sub-task context
604
625
  });
605
626
 
606
- // Use agent config from agentId
627
+ // Use model/provider from resolved agentConfig
628
+ const { agentConfig: agentConfigData } = agentConfig;
607
629
  const model = agentConfigData.model;
608
630
  const provider = agentConfigData.provider;
609
631
 
610
- // ===========================================
611
- // Step 1: Knowledge Base Tool Integration
612
- // ===========================================
613
- // RAG retrieval is now handled by the Knowledge Base Tool
614
- // The AI will decide when to call searchKnowledgeBase and readKnowledge tools
615
- // based on the conversation context and available knowledge bases
616
-
617
- // TODO: Implement selected files full-text injection if needed
618
- // User-selected files should be handled differently from knowledge base files
619
-
620
632
  // ===========================================
621
633
  // Step 2: Create and Execute Agent Runtime
622
634
  // ===========================================
@@ -633,6 +645,7 @@ export const streamingExecutor: StateCreator<
633
645
 
634
646
  const runtime = new AgentRuntime(agent, {
635
647
  executors: createAgentExecutors({
648
+ agentConfig, // Pass pre-resolved config to callLLM executor
636
649
  get,
637
650
  messageKey,
638
651
  operationId,
@@ -650,20 +663,6 @@ export const streamingExecutor: StateCreator<
650
663
  operationId,
651
664
  });
652
665
 
653
- // Create agent state and context with user intervention config
654
- const { state: initialAgentState, context: initialAgentContext } =
655
- get().internal_createAgentState({
656
- messages,
657
- parentMessageId: params.parentMessageId,
658
- agentId,
659
- topicId,
660
- threadId: threadId ?? undefined,
661
- initialState: params.initialState,
662
- initialContext: params.initialContext,
663
- operationId,
664
- subAgentId, // Pass subAgentId for agent config retrieval
665
- });
666
-
667
666
  let state = initialAgentState;
668
667
  let nextContext = initialAgentContext;
669
668
 
@@ -1060,6 +1060,77 @@ describe('ChatPluginAction', () => {
1060
1060
 
1061
1061
  expect(transformed[0].apiName).toBe(longApiName);
1062
1062
  });
1063
+
1064
+ it('should repair malformed JSON arguments with escaped string issue', () => {
1065
+ // This is the malformed data from haiku-4.5 model
1066
+ // The entire JSON got stuffed into the "description" field with escaped quotes
1067
+ const malformedArguments = JSON.stringify({
1068
+ description:
1069
+ 'Synthesize all 10 batch analyses into 10 most important themes for product builders", "instruction": "You have access to 10 batch analysis files", "runInClient": true, "timeout": 120000}',
1070
+ });
1071
+
1072
+ const toolCalls: MessageToolCall[] = [
1073
+ {
1074
+ id: 'tool1',
1075
+ function: {
1076
+ name: ['lobe-gtd', 'execTask', 'default'].join(PLUGIN_SCHEMA_SEPARATOR),
1077
+ arguments: malformedArguments,
1078
+ },
1079
+ type: 'function',
1080
+ },
1081
+ ];
1082
+
1083
+ // Setup builtin tool manifest with schema that has required fields
1084
+ act(() => {
1085
+ useToolStore.setState({
1086
+ builtinTools: [
1087
+ {
1088
+ type: 'builtin',
1089
+ identifier: 'lobe-gtd',
1090
+ manifest: {
1091
+ identifier: 'lobe-gtd',
1092
+ api: [
1093
+ {
1094
+ name: 'execTask',
1095
+ description: 'Execute async task',
1096
+ parameters: {
1097
+ type: 'object',
1098
+ required: ['description', 'instruction'],
1099
+ properties: {
1100
+ description: { type: 'string' },
1101
+ instruction: { type: 'string' },
1102
+ runInClient: { type: 'boolean' },
1103
+ timeout: { type: 'number' },
1104
+ },
1105
+ },
1106
+ },
1107
+ ],
1108
+ type: 'builtin',
1109
+ } as any,
1110
+ },
1111
+ ],
1112
+ });
1113
+ });
1114
+
1115
+ const { result } = renderHook(() => useChatStore());
1116
+
1117
+ const transformed = result.current.internal_transformToolCalls(toolCalls);
1118
+
1119
+ // Parse the transformed arguments
1120
+ const repairedArgs = JSON.parse(transformed[0].arguments);
1121
+
1122
+ // Verify all fields are correctly extracted
1123
+ expect(repairedArgs).toHaveProperty('description');
1124
+ expect(repairedArgs).toHaveProperty('instruction');
1125
+ expect(repairedArgs).toHaveProperty('runInClient', true);
1126
+ expect(repairedArgs).toHaveProperty('timeout', 120000);
1127
+
1128
+ // Verify description is the correct short value, not the entire malformed string
1129
+ expect(repairedArgs.description).toBe(
1130
+ 'Synthesize all 10 batch analyses into 10 most important themes for product builders',
1131
+ );
1132
+ expect(repairedArgs.instruction).toBe('You have access to 10 batch analysis files');
1133
+ });
1063
1134
  });
1064
1135
 
1065
1136
  describe('internal_updatePluginError', () => {
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
2
- import { ToolNameResolver } from '@lobechat/context-engine';
2
+ import { ToolArgumentsRepairer, ToolNameResolver } from '@lobechat/context-engine';
3
3
  import { type ChatToolPayload, type MessageToolCall } from '@lobechat/types';
4
4
  import { type LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
5
5
  import { type StateCreator } from 'zustand/vanilla';
@@ -78,9 +78,18 @@ export const pluginInternals: StateCreator<
78
78
 
79
79
  // Resolve tool calls and add source field
80
80
  const resolved = toolNameResolver.resolve(toolCalls, manifests);
81
- return resolved.map((payload) => ({
82
- ...payload,
83
- source: sourceMap[payload.identifier],
84
- }));
81
+
82
+ return resolved.map((payload) => {
83
+ // Parse and repair arguments if needed
84
+ const manifest = manifests[payload.identifier];
85
+ const repairer = new ToolArgumentsRepairer(manifest);
86
+ const repairedArgs = repairer.parse(payload.apiName, payload.arguments);
87
+
88
+ return {
89
+ ...payload,
90
+ arguments: JSON.stringify(repairedArgs),
91
+ source: sourceMap[payload.identifier],
92
+ };
93
+ });
85
94
  },
86
95
  });