@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
@@ -0,0 +1,357 @@
1
+ 'use client';
2
+
3
+ import { Accordion, AccordionItem, Avatar, Flexbox, Tag, Text } from '@lobehub/ui';
4
+ import { Steps } from 'antd';
5
+ import { createStaticStyles, cssVar } from 'antd-style';
6
+ import { memo } from 'react';
7
+
8
+ import BubblesLoading from '@/components/BubblesLoading';
9
+ import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
10
+ import StreamingMarkdown from '@/components/StreamingMarkdown';
11
+ import { highlightTextStyles } from '@/styles';
12
+
13
+ import type { AddPreferenceMemoryParams } from '../../types';
14
+
15
+ const styles = createStaticStyles(({ css, cssVar }) => ({
16
+ container: css`
17
+ overflow: hidden;
18
+
19
+ width: 100%;
20
+ border: 1px solid ${cssVar.colorBorderSecondary};
21
+ border-radius: 16px;
22
+
23
+ background: ${cssVar.colorBgContainer};
24
+ `,
25
+ content: css`
26
+ padding-block: 12px;
27
+ padding-inline: 16px;
28
+ `,
29
+ detail: css`
30
+ font-size: 13px;
31
+ line-height: 1.6;
32
+ color: ${cssVar.colorTextSecondary};
33
+ `,
34
+ directive: css`
35
+ font-size: 14px;
36
+ line-height: 1.6;
37
+ color: ${cssVar.colorText};
38
+ `,
39
+ header: css`
40
+ padding-block: 10px;
41
+ padding-inline: 12px;
42
+ border-block-end: 1px solid ${cssVar.colorBorderSecondary};
43
+ `,
44
+ section: css`
45
+ padding: 4px;
46
+ border-block-start: 1px solid ${cssVar.colorBorderSecondary};
47
+ `,
48
+ stepContent: css`
49
+ font-size: 13px;
50
+ line-height: 1.6;
51
+ color: ${cssVar.colorTextSecondary};
52
+ white-space: pre-wrap;
53
+ `,
54
+ stepsContainer: css`
55
+ .ant-steps-item-content {
56
+ min-height: auto;
57
+ }
58
+
59
+ .ant-steps-item-description {
60
+ padding-block-end: 12px !important;
61
+ }
62
+ `,
63
+ suggestion: css`
64
+ padding-block: 8px;
65
+ padding-inline: 12px;
66
+ border-radius: 8px;
67
+
68
+ font-size: 13px;
69
+ line-height: 1.5;
70
+ color: ${cssVar.colorTextSecondary};
71
+
72
+ background: ${cssVar.colorFillQuaternary};
73
+ `,
74
+ summary: css`
75
+ font-size: 14px;
76
+ font-weight: 500;
77
+ color: ${cssVar.colorTextSecondary};
78
+ `,
79
+ tags: css`
80
+ padding-block-start: 8px;
81
+ border-block-start: 1px dashed ${cssVar.colorBorderSecondary};
82
+ `,
83
+ title: css`
84
+ overflow: hidden;
85
+ display: -webkit-box;
86
+ -webkit-box-orient: vertical;
87
+ -webkit-line-clamp: 1;
88
+
89
+ font-weight: 500;
90
+ color: ${cssVar.colorText};
91
+ `,
92
+ }));
93
+
94
+ export interface PreferenceMemoryCardProps {
95
+ data?: AddPreferenceMemoryParams;
96
+ loading?: boolean;
97
+ }
98
+
99
+ export const PreferenceMemoryCard = memo<PreferenceMemoryCardProps>(({ data, loading }) => {
100
+ const { summary, details, tags, title, withPreference } = data || {};
101
+ const { conclusionDirectives, originContext, appContext, suggestions, type } =
102
+ withPreference || {};
103
+
104
+ const hasContextContent =
105
+ originContext?.actor ||
106
+ originContext?.scenario ||
107
+ originContext?.trigger ||
108
+ originContext?.applicableWhen ||
109
+ originContext?.notApplicableWhen;
110
+
111
+ const hasAppContext = appContext?.app || appContext?.feature || appContext?.surface;
112
+
113
+ const hasSuggestions = suggestions && suggestions.length > 0;
114
+
115
+ if (
116
+ !summary &&
117
+ !details &&
118
+ !tags?.length &&
119
+ !title &&
120
+ !conclusionDirectives &&
121
+ !hasContextContent &&
122
+ !hasSuggestions
123
+ )
124
+ return null;
125
+
126
+ const contextItems = [
127
+ { avatar: '👤', content: originContext?.actor, title: 'Actor' },
128
+ { avatar: '🎯', content: originContext?.scenario, title: 'Scenario' },
129
+ { avatar: '⚡', content: originContext?.trigger, title: 'Trigger' },
130
+ { avatar: '✅', content: originContext?.applicableWhen, title: 'Applicable When' },
131
+ { avatar: '❌', content: originContext?.notApplicableWhen, title: 'Not Applicable When' },
132
+ ].filter((item) => item.content);
133
+
134
+ const appContextItems = [
135
+ { avatar: '📱', content: appContext?.app, title: 'App' },
136
+ { avatar: '🔧', content: appContext?.feature, title: 'Feature' },
137
+ { avatar: '📍', content: appContext?.surface, title: 'Surface' },
138
+ ].filter((item) => item.content);
139
+
140
+ return (
141
+ <Flexbox className={styles.container}>
142
+ {/* Header */}
143
+ <Flexbox align={'center'} className={styles.header} gap={8} horizontal>
144
+ <Flexbox flex={1}>
145
+ <div className={styles.title}>{title || 'Preference Memory'}</div>
146
+ </Flexbox>
147
+ {type && <Tag>{type}</Tag>}
148
+ {loading && <NeuralNetworkLoading size={20} />}
149
+ </Flexbox>
150
+
151
+ {/* When has context content: collapse summary */}
152
+ {hasContextContent || hasAppContext ? (
153
+ <>
154
+ {/* Collapsed Summary */}
155
+ {(summary || tags?.length) && (
156
+ <Accordion gap={0}>
157
+ <AccordionItem
158
+ itemKey="summary"
159
+ paddingBlock={8}
160
+ paddingInline={8}
161
+ styles={{
162
+ base: { marginBlock: 4, marginInline: 4 },
163
+ }}
164
+ title={
165
+ <Text fontSize={12} type={'secondary'} weight={500}>
166
+ Summary
167
+ </Text>
168
+ }
169
+ >
170
+ <Flexbox gap={8} paddingBlock={'8px 12px'} paddingInline={8}>
171
+ {summary && <div className={styles.summary}>{summary}</div>}
172
+ {details && <div className={styles.detail}>{details}</div>}
173
+ {tags && tags.length > 0 && (
174
+ <Flexbox className={styles.tags} gap={8} horizontal wrap={'wrap'}>
175
+ {tags.map((tag, index) => (
176
+ <Tag key={index}>{tag}</Tag>
177
+ ))}
178
+ </Flexbox>
179
+ )}
180
+ </Flexbox>
181
+ </AccordionItem>
182
+ </Accordion>
183
+ )}
184
+
185
+ {/* Origin Context Steps */}
186
+ {hasContextContent && (
187
+ <Accordion className={styles.section} defaultExpandedKeys={['context']} gap={0}>
188
+ <AccordionItem
189
+ itemKey="context"
190
+ paddingBlock={8}
191
+ paddingInline={8}
192
+ title={
193
+ <Text fontSize={12} type={'secondary'} weight={500}>
194
+ Origin Context
195
+ </Text>
196
+ }
197
+ >
198
+ <Flexbox paddingBlock={'8px 12px'} paddingInline={8}>
199
+ <Steps
200
+ className={styles.stepsContainer}
201
+ current={null as any}
202
+ direction="vertical"
203
+ items={contextItems.map((item) => ({
204
+ description: <div className={styles.stepContent}>{item.content}</div>,
205
+ icon: (
206
+ <Avatar
207
+ avatar={item.avatar}
208
+ shadow
209
+ shape={'square'}
210
+ size={20}
211
+ style={{
212
+ border: `1px solid ${cssVar.colorBorderSecondary}`,
213
+ fontSize: 11,
214
+ }}
215
+ />
216
+ ),
217
+ title: (
218
+ <Text as={'span'} fontSize={12} type={'secondary'} weight={500}>
219
+ {item.title}
220
+ </Text>
221
+ ),
222
+ }))}
223
+ size="small"
224
+ />
225
+ </Flexbox>
226
+ </AccordionItem>
227
+ </Accordion>
228
+ )}
229
+
230
+ {/* App Context */}
231
+ {hasAppContext && (
232
+ <Accordion className={styles.section} gap={0}>
233
+ <AccordionItem
234
+ itemKey="appContext"
235
+ paddingBlock={8}
236
+ paddingInline={8}
237
+ title={
238
+ <Text fontSize={12} type={'secondary'} weight={500}>
239
+ App Context
240
+ </Text>
241
+ }
242
+ >
243
+ <Flexbox paddingBlock={'8px 12px'} paddingInline={8}>
244
+ <Steps
245
+ className={styles.stepsContainer}
246
+ current={null as any}
247
+ direction="vertical"
248
+ items={appContextItems.map((item) => ({
249
+ description: <div className={styles.stepContent}>{item.content}</div>,
250
+ icon: (
251
+ <Avatar
252
+ avatar={item.avatar}
253
+ shadow
254
+ shape={'square'}
255
+ size={20}
256
+ style={{
257
+ border: `1px solid ${cssVar.colorBorderSecondary}`,
258
+ fontSize: 11,
259
+ }}
260
+ />
261
+ ),
262
+ title: (
263
+ <Text as={'span'} fontSize={12} type={'secondary'} weight={500}>
264
+ {item.title}
265
+ </Text>
266
+ ),
267
+ }))}
268
+ size="small"
269
+ />
270
+ </Flexbox>
271
+ </AccordionItem>
272
+ </Accordion>
273
+ )}
274
+
275
+ {/* Conclusion Directive */}
276
+ {conclusionDirectives && (
277
+ <Flexbox
278
+ className={styles.section}
279
+ gap={8}
280
+ style={{ paddingBlock: 16, paddingInline: 12 }}
281
+ >
282
+ <Text fontSize={12} weight={500}>
283
+ <span className={highlightTextStyles.primary}>Directive</span>
284
+ </Text>
285
+ <div className={styles.directive}>{conclusionDirectives}</div>
286
+ </Flexbox>
287
+ )}
288
+
289
+ {/* Suggestions */}
290
+ {hasSuggestions && (
291
+ <Flexbox
292
+ className={styles.section}
293
+ gap={8}
294
+ style={{ paddingBlock: 16, paddingInline: 12 }}
295
+ >
296
+ <Text fontSize={12} weight={500}>
297
+ <span className={highlightTextStyles.info}>Suggestions</span>
298
+ </Text>
299
+ <Flexbox gap={8}>
300
+ {suggestions.map((suggestion, index) => (
301
+ <div className={styles.suggestion} key={index}>
302
+ {suggestion}
303
+ </div>
304
+ ))}
305
+ </Flexbox>
306
+ </Flexbox>
307
+ )}
308
+ </>
309
+ ) : (
310
+ /* When no context content: show summary and details */
311
+ <Flexbox className={styles.content} gap={8}>
312
+ {!summary && loading ? (
313
+ <BubblesLoading />
314
+ ) : (
315
+ <>
316
+ {summary && <div className={styles.summary}>{summary}</div>}
317
+ {details && <StreamingMarkdown>{details}</StreamingMarkdown>}
318
+ {conclusionDirectives && (
319
+ <Flexbox gap={4} paddingBlock={8}>
320
+ <Text fontSize={12} weight={500}>
321
+ <span className={highlightTextStyles.primary}>Directive</span>
322
+ </Text>
323
+ <div className={styles.directive}>{conclusionDirectives}</div>
324
+ </Flexbox>
325
+ )}
326
+ {hasSuggestions && (
327
+ <Flexbox gap={8} paddingBlock={8}>
328
+ <Text fontSize={12} weight={500}>
329
+ <span className={highlightTextStyles.info}>Suggestions</span>
330
+ </Text>
331
+ <Flexbox gap={8}>
332
+ {suggestions.map((suggestion, index) => (
333
+ <div className={styles.suggestion} key={index}>
334
+ {suggestion}
335
+ </div>
336
+ ))}
337
+ </Flexbox>
338
+ </Flexbox>
339
+ )}
340
+ {tags && tags.length > 0 && (
341
+ <Flexbox className={styles.tags} gap={8} horizontal wrap={'wrap'}>
342
+ {tags.map((tag, index) => (
343
+ <Tag key={index}>{tag}</Tag>
344
+ ))}
345
+ </Flexbox>
346
+ )}
347
+ </>
348
+ )}
349
+ </Flexbox>
350
+ )}
351
+ </Flexbox>
352
+ );
353
+ });
354
+
355
+ PreferenceMemoryCard.displayName = 'PreferenceMemoryCard';
356
+
357
+ export default PreferenceMemoryCard;
@@ -1 +1,2 @@
1
1
  export { ExperienceMemoryCard, type ExperienceMemoryCardProps } from './ExperienceMemoryCard';
2
+ export { PreferenceMemoryCard, type PreferenceMemoryCardProps } from './PreferenceMemoryCard';
@@ -74,7 +74,7 @@ class MemoryExecutor extends BaseExecutor<typeof MemoryApiName> {
74
74
  }
75
75
 
76
76
  return {
77
- content: `🧠 Context memory saved: "${params.title}"`,
77
+ content: `Context memory "${params.title}" saved with memoryId: "${result.memoryId}" and contextId: "${result.contextId}"`,
78
78
  state: { contextId: result.contextId, memoryId: result.memoryId },
79
79
  success: true,
80
80
  };
@@ -151,7 +151,7 @@ class MemoryExecutor extends BaseExecutor<typeof MemoryApiName> {
151
151
  }
152
152
 
153
153
  return {
154
- content: `🧠 Identity memory saved: "${params.title}"`,
154
+ content: `Identity memory "${params.title}" saved with memoryId: "${result.memoryId}" and identityId: "${result.identityId}"`,
155
155
  state: { identityId: result.identityId, memoryId: result.memoryId },
156
156
  success: true,
157
157
  };
@@ -189,7 +189,7 @@ class MemoryExecutor extends BaseExecutor<typeof MemoryApiName> {
189
189
  }
190
190
 
191
191
  return {
192
- content: `🧠 Preference memory saved: "${params.title}"`,
192
+ content: `Preference memory "${params.title}" saved with memoryId: "${result.memoryId}" and preferenceId: "${result.preferenceId}"`,
193
193
  state: { memoryId: result.memoryId, preferenceId: result.preferenceId },
194
194
  success: true,
195
195
  };
@@ -54,4 +54,5 @@ Conversation language: {{language}}
54
54
  - When memory activity is warranted, explain which layers are affected, cite any matching memories you found, and justify why extraction or updates are needed.
55
55
  - When nothing qualifies, explicitly state that no memory action is required after reviewing the context.
56
56
  - Keep your reasoning concise, structured, and aligned with the conversation language.
57
+ - **Never expose internal memory IDs** (e.g., mem_xxx, id: xxx) to users in your responses. Refer to memories by their descriptive titles or summaries instead.
57
58
  </response_expectations>`;
@@ -0,0 +1,129 @@
1
+ import type { LobeToolManifest } from './types';
2
+
3
+ /**
4
+ * JSON Schema type for tool parameters
5
+ */
6
+ export interface ToolParameterSchema {
7
+ properties?: Record<string, unknown>;
8
+ required?: string[];
9
+ type?: string;
10
+ }
11
+
12
+ /**
13
+ * Safe JSON parse utility
14
+ */
15
+ const safeParseJSON = <T = Record<string, unknown>>(text?: string): T | undefined => {
16
+ if (typeof text !== 'string') return undefined;
17
+ try {
18
+ return JSON.parse(text) as T;
19
+ } catch {
20
+ return undefined;
21
+ }
22
+ };
23
+
24
+ /**
25
+ * Tool Arguments Repairer
26
+ *
27
+ * Handles repair of malformed tool call arguments caused by LLM string escape issues.
28
+ *
29
+ * When some LLMs (like Claude haiku-4.5) output tool calls, they may produce malformed JSON
30
+ * where the entire content gets stuffed into the first field with escaped quotes.
31
+ *
32
+ * @example Malformed data:
33
+ * ```javascript
34
+ * { description: 'real desc", "instruction": "real instruction", "timeout": 120}' }
35
+ * ```
36
+ *
37
+ * @example Expected data:
38
+ * ```javascript
39
+ * { description: 'real desc', instruction: 'real instruction', timeout: 120 }
40
+ * ```
41
+ *
42
+ * @example Usage:
43
+ * ```typescript
44
+ * const repairer = new ToolArgumentsRepairer(manifest);
45
+ * const args = repairer.parse('execTask', argumentsString);
46
+ * ```
47
+ */
48
+ export class ToolArgumentsRepairer {
49
+ private manifest?: LobeToolManifest;
50
+
51
+ /**
52
+ * Create a new ToolArgumentsRepairer
53
+ * @param manifest - Tool manifest for schema lookup
54
+ */
55
+ constructor(manifest?: LobeToolManifest) {
56
+ this.manifest = manifest;
57
+ }
58
+
59
+ /**
60
+ * Parse and repair tool call arguments
61
+ *
62
+ * @param apiName - API name
63
+ * @param argumentsStr - Raw arguments string from LLM
64
+ * @returns Parsed and repaired arguments object
65
+ */
66
+ parse(apiName: string, argumentsStr: string): Record<string, unknown> {
67
+ const parsed = safeParseJSON<Record<string, unknown>>(argumentsStr) || {};
68
+
69
+ // Get API schema for repair
70
+ const apiSchema = this.manifest?.api?.find((a) => a.name === apiName)?.parameters;
71
+
72
+ return this.repair(parsed, apiSchema);
73
+ }
74
+
75
+ /**
76
+ * Repair malformed arguments using schema
77
+ *
78
+ * @param parsed - The parsed (but potentially malformed) arguments object
79
+ * @param schema - The JSON schema for the tool's parameters (with required fields)
80
+ * @returns The repaired arguments object
81
+ */
82
+ repair(parsed: Record<string, unknown>, schema?: ToolParameterSchema): Record<string, unknown> {
83
+ // If no schema or no required fields, skip repair
84
+ if (!schema?.required || !Array.isArray(schema.required) || schema.required.length === 0) {
85
+ return parsed;
86
+ }
87
+
88
+ const keys = Object.keys(parsed);
89
+ const missingFields = schema.required.filter((f) => !(f in parsed));
90
+
91
+ // If no missing required fields, no need to repair
92
+ if (missingFields.length === 0) {
93
+ return parsed;
94
+ }
95
+
96
+ // Check if any existing field's value contains the missing field patterns
97
+ // This indicates the string escape issue
98
+ for (const key of keys) {
99
+ const value = parsed[key];
100
+ if (typeof value !== 'string') continue;
101
+
102
+ // Check if value contains patterns like `", "missingField":` or `", \"missingField\":`
103
+ const hasMissingFieldPattern = missingFields.some(
104
+ (field) => value.includes(`", "${field}":`) || value.includes(`", \\"${field}\\":`),
105
+ );
106
+
107
+ if (hasMissingFieldPattern) {
108
+ // Try to reconstruct the correct JSON
109
+ // The value is actually: 'realValue", "field2": "value2", ...}'
110
+ // So we rebuild: '{"key": "realValue", "field2": "value2", ...}'
111
+ try {
112
+ const reconstructed = `{"${key}": "${value}`;
113
+ const repaired = JSON.parse(reconstructed) as Record<string, unknown>;
114
+
115
+ // Verify the repair was successful - all required fields should be present
116
+ const stillMissing = schema.required.filter((f) => !(f in repaired));
117
+ if (stillMissing.length === 0) {
118
+ return repaired;
119
+ }
120
+ } catch {
121
+ // Repair failed, continue to try other approaches or return original
122
+ }
123
+ }
124
+ }
125
+
126
+ // Could not repair, return original parsed data
127
+ return parsed;
128
+ }
129
+ }