@lobehub/lobehub 2.0.0-next.305 → 2.0.0-next.307
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +18 -3
- package/CHANGELOG.md +53 -0
- package/changelog/v1.json +18 -0
- package/e2e/src/steps/community/detail-pages.steps.ts +3 -1
- package/e2e/src/steps/community/interactions.steps.ts +4 -4
- package/package.json +2 -2
- package/packages/builtin-agents/src/agents/group-supervisor/index.ts +1 -7
- package/packages/builtin-tool-group-agent-builder/src/ExecutionRuntime/index.ts +29 -0
- package/packages/builtin-tool-group-agent-builder/src/executor.ts +18 -0
- package/packages/builtin-tool-group-agent-builder/src/manifest.ts +17 -0
- package/packages/builtin-tool-group-agent-builder/src/types.ts +10 -0
- package/packages/builtin-tool-group-management/src/executor.test.ts +0 -12
- package/packages/builtin-tool-group-management/src/executor.ts +8 -47
- package/packages/builtin-tool-group-management/src/manifest.ts +0 -17
- package/packages/builtin-tool-group-management/src/systemRole.ts +1 -8
- package/packages/builtin-tool-group-management/src/types.ts +0 -10
- package/packages/builtin-tool-local-system/src/ExecutionRuntime/index.ts +70 -31
- package/packages/builtin-tool-local-system/src/executor/index.ts +94 -60
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +9 -6
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +103 -0
- package/packages/context-engine/src/providers/GroupAgentBuilderContextInjector.ts +18 -31
- package/packages/context-engine/src/providers/__tests__/GroupAgentBuilderContextInjector.test.ts +307 -0
- package/packages/database/src/repositories/agentGroup/index.ts +23 -0
- package/packages/prompts/src/prompts/fileSystem/formatCommandOutput.test.ts +61 -0
- package/packages/prompts/src/prompts/fileSystem/formatCommandOutput.ts +21 -0
- package/packages/prompts/src/prompts/fileSystem/formatCommandResult.test.ts +87 -0
- package/packages/prompts/src/prompts/fileSystem/formatCommandResult.ts +35 -0
- package/packages/prompts/src/prompts/fileSystem/formatEditResult.test.ts +57 -0
- package/packages/prompts/src/prompts/fileSystem/formatEditResult.ts +17 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileContent.test.ts +59 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileContent.ts +14 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileList.test.ts +62 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileList.ts +13 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileSearchResults.test.ts +34 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileSearchResults.ts +12 -0
- package/packages/prompts/src/prompts/fileSystem/formatGlobResults.test.ts +64 -0
- package/packages/prompts/src/prompts/fileSystem/formatGlobResults.ts +23 -0
- package/packages/prompts/src/prompts/fileSystem/formatGrepResults.test.ts +85 -0
- package/packages/prompts/src/prompts/fileSystem/formatGrepResults.ts +24 -0
- package/packages/prompts/src/prompts/fileSystem/formatKillResult.test.ts +30 -0
- package/packages/prompts/src/prompts/fileSystem/formatKillResult.ts +9 -0
- package/packages/prompts/src/prompts/fileSystem/formatMoveResults.test.ts +37 -0
- package/packages/prompts/src/prompts/fileSystem/formatMoveResults.ts +20 -0
- package/packages/prompts/src/prompts/fileSystem/formatMultipleFiles.test.ts +54 -0
- package/packages/prompts/src/prompts/fileSystem/formatMultipleFiles.ts +9 -0
- package/packages/prompts/src/prompts/fileSystem/formatRenameResult.test.ts +35 -0
- package/packages/prompts/src/prompts/fileSystem/formatRenameResult.ts +17 -0
- package/packages/prompts/src/prompts/fileSystem/formatWriteResult.test.ts +30 -0
- package/packages/prompts/src/prompts/fileSystem/formatWriteResult.ts +11 -0
- package/packages/prompts/src/prompts/fileSystem/index.ts +13 -0
- package/packages/prompts/src/prompts/index.ts +1 -0
- package/packages/prompts/src/prompts/userMemory/__snapshots__/index.test.ts.snap +14 -38
- package/packages/prompts/src/prompts/userMemory/index.ts +5 -24
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/Actions.tsx +4 -3
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/useDropdownMenu.tsx +12 -2
- package/src/app/[variants]/(main)/community/(detail)/assistant/index.tsx +1 -1
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/AddGroupAgent.tsx +69 -17
- package/src/app/[variants]/(main)/community/(detail)/mcp/index.tsx +1 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/Actions.tsx +4 -3
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/useDropdownMenu.tsx +12 -2
- package/src/app/[variants]/(main)/group/features/Conversation/MainChatInput/index.tsx +2 -2
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +2 -2
- package/src/features/ChatInput/ActionBar/Upload/ServerMode.tsx +13 -3
- package/src/features/ChatInput/ActionBar/components/ActionDropdown.tsx +26 -3
- package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -1
- package/src/features/Conversation/Messages/components/ContentLoading.tsx +8 -2
- package/src/features/ResourceManager/components/Header/AddButton.tsx +20 -3
- package/src/server/routers/lambda/__tests__/agentGroup.test.ts +1 -0
- package/src/server/routers/lambda/agentGroup.ts +22 -0
- package/src/services/chat/index.ts +1 -0
- package/src/services/chat/mecha/agentConfigResolver.test.ts +62 -45
- package/src/services/chat/mecha/agentConfigResolver.ts +77 -10
- package/src/services/chat/mecha/modelParamsResolver.test.ts +211 -0
- package/src/services/chatGroup/index.ts +14 -0
- package/src/store/agentGroup/action.ts +30 -0
- package/src/store/agentGroup/slices/lifecycle.test.ts +77 -18
- package/src/store/agentGroup/slices/lifecycle.ts +7 -9
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +6 -2
- package/src/store/chat/slices/operation/__tests__/selectors.test.ts +124 -0
- package/src/store/chat/slices/operation/selectors.ts +22 -0
|
@@ -71,8 +71,14 @@ export class LocalSystemExecutionRuntime {
|
|
|
71
71
|
|
|
72
72
|
const state: LocalFileListState = { listResults: result };
|
|
73
73
|
|
|
74
|
+
const fileList = result.map((f) => ` ${f.isDirectory ? '[D]' : '[F]'} ${f.name}`).join('\n');
|
|
75
|
+
const content =
|
|
76
|
+
result.length > 0
|
|
77
|
+
? `Found ${result.length} item(s) in ${args.path}:\n${fileList}`
|
|
78
|
+
: `Directory ${args.path} is empty`;
|
|
79
|
+
|
|
74
80
|
return {
|
|
75
|
-
content
|
|
81
|
+
content,
|
|
76
82
|
state,
|
|
77
83
|
success: true,
|
|
78
84
|
};
|
|
@@ -91,8 +97,11 @@ export class LocalSystemExecutionRuntime {
|
|
|
91
97
|
|
|
92
98
|
const state: LocalReadFileState = { fileContent: result };
|
|
93
99
|
|
|
100
|
+
const lineInfo = args.loc ? ` (lines ${args.loc[0]}-${args.loc[1]})` : '';
|
|
101
|
+
const content = `File: ${args.path}${lineInfo}\n\n${result.content}`;
|
|
102
|
+
|
|
94
103
|
return {
|
|
95
|
-
content
|
|
104
|
+
content,
|
|
96
105
|
state,
|
|
97
106
|
success: true,
|
|
98
107
|
};
|
|
@@ -111,8 +120,11 @@ export class LocalSystemExecutionRuntime {
|
|
|
111
120
|
|
|
112
121
|
const state: LocalReadFilesState = { filesContent: results };
|
|
113
122
|
|
|
123
|
+
const fileContents = results.map((r) => `=== ${r.filename} ===\n${r.content}`).join('\n\n');
|
|
124
|
+
const content = `Read ${results.length} file(s):\n\n${fileContents}`;
|
|
125
|
+
|
|
114
126
|
return {
|
|
115
|
-
content
|
|
127
|
+
content,
|
|
116
128
|
state,
|
|
117
129
|
success: true,
|
|
118
130
|
};
|
|
@@ -131,8 +143,12 @@ export class LocalSystemExecutionRuntime {
|
|
|
131
143
|
|
|
132
144
|
const state: LocalFileSearchState = { searchResults: result };
|
|
133
145
|
|
|
146
|
+
const fileList = result.map((f) => ` ${f.path}`).join('\n');
|
|
147
|
+
const content =
|
|
148
|
+
result.length > 0 ? `Found ${result.length} file(s):\n${fileList}` : 'No files found';
|
|
149
|
+
|
|
134
150
|
return {
|
|
135
|
-
content
|
|
151
|
+
content,
|
|
136
152
|
state,
|
|
137
153
|
success: true,
|
|
138
154
|
};
|
|
@@ -154,14 +170,14 @@ export class LocalSystemExecutionRuntime {
|
|
|
154
170
|
const successCount = results.filter((r) => r.success).length;
|
|
155
171
|
const failedCount = results.length - successCount;
|
|
156
172
|
|
|
157
|
-
let
|
|
173
|
+
let content = '';
|
|
158
174
|
|
|
159
175
|
if (allSucceeded) {
|
|
160
|
-
|
|
176
|
+
content = `Successfully moved ${results.length} item(s).`;
|
|
161
177
|
} else if (someFailed) {
|
|
162
|
-
|
|
178
|
+
content = `Moved ${successCount} item(s) successfully. Failed to move ${failedCount} item(s).`;
|
|
163
179
|
} else {
|
|
164
|
-
|
|
180
|
+
content = `Failed to move all ${results.length} item(s).`;
|
|
165
181
|
}
|
|
166
182
|
|
|
167
183
|
const state: LocalMoveFilesState = {
|
|
@@ -171,7 +187,7 @@ export class LocalSystemExecutionRuntime {
|
|
|
171
187
|
};
|
|
172
188
|
|
|
173
189
|
return {
|
|
174
|
-
content
|
|
190
|
+
content,
|
|
175
191
|
state,
|
|
176
192
|
success: true,
|
|
177
193
|
};
|
|
@@ -197,7 +213,7 @@ export class LocalSystemExecutionRuntime {
|
|
|
197
213
|
};
|
|
198
214
|
|
|
199
215
|
return {
|
|
200
|
-
content:
|
|
216
|
+
content: `Failed to rename file: ${result.error}`,
|
|
201
217
|
state,
|
|
202
218
|
success: false,
|
|
203
219
|
};
|
|
@@ -210,10 +226,7 @@ export class LocalSystemExecutionRuntime {
|
|
|
210
226
|
};
|
|
211
227
|
|
|
212
228
|
return {
|
|
213
|
-
content:
|
|
214
|
-
message: `Successfully renamed file ${args.path} to ${args.newName}.`,
|
|
215
|
-
success: true,
|
|
216
|
-
}),
|
|
229
|
+
content: `Successfully renamed file ${args.path} to ${args.newName}`,
|
|
217
230
|
state,
|
|
218
231
|
success: true,
|
|
219
232
|
};
|
|
@@ -232,20 +245,14 @@ export class LocalSystemExecutionRuntime {
|
|
|
232
245
|
|
|
233
246
|
if (!result.success) {
|
|
234
247
|
return {
|
|
235
|
-
content:
|
|
236
|
-
message: result.error || '写入文件失败',
|
|
237
|
-
success: false,
|
|
238
|
-
}),
|
|
248
|
+
content: `Failed to write file: ${result.error || 'Unknown error'}`,
|
|
239
249
|
error: result.error,
|
|
240
250
|
success: false,
|
|
241
251
|
};
|
|
242
252
|
}
|
|
243
253
|
|
|
244
254
|
return {
|
|
245
|
-
content:
|
|
246
|
-
message: `成功写入文件 ${args.path}`,
|
|
247
|
-
success: true,
|
|
248
|
-
}),
|
|
255
|
+
content: `Successfully wrote to ${args.path}`,
|
|
249
256
|
success: true,
|
|
250
257
|
};
|
|
251
258
|
} catch (error) {
|
|
@@ -301,22 +308,28 @@ export class LocalSystemExecutionRuntime {
|
|
|
301
308
|
try {
|
|
302
309
|
const result: RunCommandResult = await this.localFileService.runCommand(args);
|
|
303
310
|
|
|
304
|
-
|
|
311
|
+
const parts: string[] = [];
|
|
305
312
|
|
|
306
313
|
if (result.success) {
|
|
307
314
|
if (result.shell_id) {
|
|
308
|
-
|
|
315
|
+
parts.push(`Command started in background with shell_id: ${result.shell_id}`);
|
|
309
316
|
} else {
|
|
310
|
-
|
|
317
|
+
parts.push('Command completed successfully.');
|
|
311
318
|
}
|
|
312
319
|
} else {
|
|
313
|
-
|
|
320
|
+
parts.push(`Command failed: ${result.error}`);
|
|
314
321
|
}
|
|
315
322
|
|
|
323
|
+
if (result.stdout) parts.push(`Output:\n${result.stdout}`);
|
|
324
|
+
if (result.stderr) parts.push(`Stderr:\n${result.stderr}`);
|
|
325
|
+
if (result.exit_code !== undefined) parts.push(`Exit code: ${result.exit_code}`);
|
|
326
|
+
|
|
327
|
+
const message = parts[0];
|
|
328
|
+
const content = parts.join('\n\n');
|
|
316
329
|
const state: RunCommandState = { message, result };
|
|
317
330
|
|
|
318
331
|
return {
|
|
319
|
-
content
|
|
332
|
+
content,
|
|
320
333
|
state,
|
|
321
334
|
success: result.success,
|
|
322
335
|
};
|
|
@@ -337,10 +350,14 @@ export class LocalSystemExecutionRuntime {
|
|
|
337
350
|
? `Output retrieved. Running: ${result.running}`
|
|
338
351
|
: `Failed: ${result.error}`;
|
|
339
352
|
|
|
353
|
+
const parts: string[] = [message];
|
|
354
|
+
if (result.output) parts.push(`Output:\n${result.output}`);
|
|
355
|
+
if (result.error) parts.push(`Error: ${result.error}`);
|
|
356
|
+
|
|
340
357
|
const state: GetCommandOutputState = { message, result };
|
|
341
358
|
|
|
342
359
|
return {
|
|
343
|
-
content:
|
|
360
|
+
content: parts.join('\n\n'),
|
|
344
361
|
state,
|
|
345
362
|
success: result.success,
|
|
346
363
|
};
|
|
@@ -364,7 +381,7 @@ export class LocalSystemExecutionRuntime {
|
|
|
364
381
|
const state: KillCommandState = { message, result };
|
|
365
382
|
|
|
366
383
|
return {
|
|
367
|
-
content:
|
|
384
|
+
content: message,
|
|
368
385
|
state,
|
|
369
386
|
success: result.success,
|
|
370
387
|
};
|
|
@@ -389,8 +406,19 @@ export class LocalSystemExecutionRuntime {
|
|
|
389
406
|
|
|
390
407
|
const state: GrepContentState = { message, result };
|
|
391
408
|
|
|
409
|
+
let content = message;
|
|
410
|
+
if (result.success && result.matches.length > 0) {
|
|
411
|
+
const matchList = result.matches
|
|
412
|
+
.slice(0, 20)
|
|
413
|
+
.map((m) => ` ${m}`)
|
|
414
|
+
.join('\n');
|
|
415
|
+
const moreInfo =
|
|
416
|
+
result.matches.length > 20 ? `\n ... and ${result.matches.length - 20} more` : '';
|
|
417
|
+
content = `${message}:\n${matchList}${moreInfo}`;
|
|
418
|
+
}
|
|
419
|
+
|
|
392
420
|
return {
|
|
393
|
-
content
|
|
421
|
+
content,
|
|
394
422
|
state,
|
|
395
423
|
success: result.success,
|
|
396
424
|
};
|
|
@@ -411,8 +439,19 @@ export class LocalSystemExecutionRuntime {
|
|
|
411
439
|
|
|
412
440
|
const state: GlobFilesState = { message, result };
|
|
413
441
|
|
|
442
|
+
let content = message;
|
|
443
|
+
if (result.success && result.files.length > 0) {
|
|
444
|
+
const fileList = result.files
|
|
445
|
+
.slice(0, 50)
|
|
446
|
+
.map((f) => ` ${f}`)
|
|
447
|
+
.join('\n');
|
|
448
|
+
const moreInfo =
|
|
449
|
+
result.files.length > 50 ? `\n ... and ${result.files.length - 50} more` : '';
|
|
450
|
+
content = `${message}:\n${fileList}${moreInfo}`;
|
|
451
|
+
}
|
|
452
|
+
|
|
414
453
|
return {
|
|
415
|
-
content
|
|
454
|
+
content,
|
|
416
455
|
state,
|
|
417
456
|
success: result.success,
|
|
418
457
|
};
|
|
@@ -23,6 +23,21 @@ import type {
|
|
|
23
23
|
RunCommandResult,
|
|
24
24
|
WriteLocalFileParams,
|
|
25
25
|
} from '@lobechat/electron-client-ipc';
|
|
26
|
+
import {
|
|
27
|
+
formatCommandOutput,
|
|
28
|
+
formatCommandResult,
|
|
29
|
+
formatEditResult,
|
|
30
|
+
formatFileContent,
|
|
31
|
+
formatFileList,
|
|
32
|
+
formatFileSearchResults,
|
|
33
|
+
formatGlobResults,
|
|
34
|
+
formatGrepResults,
|
|
35
|
+
formatKillResult,
|
|
36
|
+
formatMoveResults,
|
|
37
|
+
formatMultipleFiles,
|
|
38
|
+
formatRenameResult,
|
|
39
|
+
formatWriteResult,
|
|
40
|
+
} from '@lobechat/prompts';
|
|
26
41
|
import { BaseExecutor, type BuiltinToolResult } from '@lobechat/types';
|
|
27
42
|
|
|
28
43
|
import { localFileService } from '@/services/electron/localFileService';
|
|
@@ -76,8 +91,10 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
76
91
|
|
|
77
92
|
const state: LocalFileListState = { listResults: result };
|
|
78
93
|
|
|
94
|
+
const content = formatFileList(result, params.path);
|
|
95
|
+
|
|
79
96
|
return {
|
|
80
|
-
content
|
|
97
|
+
content,
|
|
81
98
|
state,
|
|
82
99
|
success: true,
|
|
83
100
|
};
|
|
@@ -96,8 +113,14 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
96
113
|
|
|
97
114
|
const state: LocalReadFileState = { fileContent: result };
|
|
98
115
|
|
|
116
|
+
const content = formatFileContent({
|
|
117
|
+
content: result.content,
|
|
118
|
+
lineRange: params.loc,
|
|
119
|
+
path: params.path,
|
|
120
|
+
});
|
|
121
|
+
|
|
99
122
|
return {
|
|
100
|
-
content
|
|
123
|
+
content,
|
|
101
124
|
state,
|
|
102
125
|
success: true,
|
|
103
126
|
};
|
|
@@ -116,8 +139,10 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
116
139
|
|
|
117
140
|
const state: LocalReadFilesState = { filesContent: results };
|
|
118
141
|
|
|
142
|
+
const content = formatMultipleFiles(results);
|
|
143
|
+
|
|
119
144
|
return {
|
|
120
|
-
content
|
|
145
|
+
content,
|
|
121
146
|
state,
|
|
122
147
|
success: true,
|
|
123
148
|
};
|
|
@@ -136,8 +161,10 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
136
161
|
|
|
137
162
|
const state: LocalFileSearchState = { searchResults: result };
|
|
138
163
|
|
|
164
|
+
const content = formatFileSearchResults(result);
|
|
165
|
+
|
|
139
166
|
return {
|
|
140
|
-
content
|
|
167
|
+
content,
|
|
141
168
|
state,
|
|
142
169
|
success: true,
|
|
143
170
|
};
|
|
@@ -154,20 +181,9 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
154
181
|
try {
|
|
155
182
|
const results: LocalMoveFilesResultItem[] = await localFileService.moveLocalFiles(params);
|
|
156
183
|
|
|
157
|
-
const allSucceeded = results.every((r) => r.success);
|
|
158
|
-
const someFailed = results.some((r) => !r.success);
|
|
159
184
|
const successCount = results.filter((r) => r.success).length;
|
|
160
|
-
const failedCount = results.length - successCount;
|
|
161
185
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (allSucceeded) {
|
|
165
|
-
message = `Successfully moved ${results.length} item(s).`;
|
|
166
|
-
} else if (someFailed) {
|
|
167
|
-
message = `Moved ${successCount} item(s) successfully. Failed to move ${failedCount} item(s).`;
|
|
168
|
-
} else {
|
|
169
|
-
message = `Failed to move all ${results.length} item(s).`;
|
|
170
|
-
}
|
|
186
|
+
const content = formatMoveResults(results);
|
|
171
187
|
|
|
172
188
|
const state: LocalMoveFilesState = {
|
|
173
189
|
results,
|
|
@@ -176,7 +192,7 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
176
192
|
};
|
|
177
193
|
|
|
178
194
|
return {
|
|
179
|
-
content
|
|
195
|
+
content,
|
|
180
196
|
state,
|
|
181
197
|
success: true,
|
|
182
198
|
};
|
|
@@ -202,7 +218,12 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
202
218
|
};
|
|
203
219
|
|
|
204
220
|
return {
|
|
205
|
-
content:
|
|
221
|
+
content: formatRenameResult({
|
|
222
|
+
error: result.error,
|
|
223
|
+
newName: params.newName,
|
|
224
|
+
oldPath: params.path,
|
|
225
|
+
success: false,
|
|
226
|
+
}),
|
|
206
227
|
state,
|
|
207
228
|
success: false,
|
|
208
229
|
};
|
|
@@ -215,8 +236,9 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
215
236
|
};
|
|
216
237
|
|
|
217
238
|
return {
|
|
218
|
-
content:
|
|
219
|
-
|
|
239
|
+
content: formatRenameResult({
|
|
240
|
+
newName: params.newName,
|
|
241
|
+
oldPath: params.path,
|
|
220
242
|
success: true,
|
|
221
243
|
}),
|
|
222
244
|
state,
|
|
@@ -237,8 +259,9 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
237
259
|
|
|
238
260
|
if (!result.success) {
|
|
239
261
|
return {
|
|
240
|
-
content:
|
|
241
|
-
|
|
262
|
+
content: formatWriteResult({
|
|
263
|
+
error: result.error,
|
|
264
|
+
path: params.path,
|
|
242
265
|
success: false,
|
|
243
266
|
}),
|
|
244
267
|
error: { message: result.error || 'Failed to write file', type: 'PluginServerError' },
|
|
@@ -247,8 +270,8 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
247
270
|
}
|
|
248
271
|
|
|
249
272
|
return {
|
|
250
|
-
content:
|
|
251
|
-
|
|
273
|
+
content: formatWriteResult({
|
|
274
|
+
path: params.path,
|
|
252
275
|
success: true,
|
|
253
276
|
}),
|
|
254
277
|
success: true,
|
|
@@ -273,11 +296,12 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
273
296
|
};
|
|
274
297
|
}
|
|
275
298
|
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
299
|
+
const content = formatEditResult({
|
|
300
|
+
filePath: params.file_path,
|
|
301
|
+
linesAdded: result.linesAdded,
|
|
302
|
+
linesDeleted: result.linesDeleted,
|
|
303
|
+
replacements: result.replacements,
|
|
304
|
+
});
|
|
281
305
|
|
|
282
306
|
const state: EditLocalFileState = {
|
|
283
307
|
diffText: result.diffText,
|
|
@@ -287,7 +311,7 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
287
311
|
};
|
|
288
312
|
|
|
289
313
|
return {
|
|
290
|
-
content
|
|
314
|
+
content,
|
|
291
315
|
state,
|
|
292
316
|
success: true,
|
|
293
317
|
};
|
|
@@ -306,22 +330,19 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
306
330
|
try {
|
|
307
331
|
const result: RunCommandResult = await localFileService.runCommand(params);
|
|
308
332
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
} else {
|
|
318
|
-
message = `Command failed: ${result.error}`;
|
|
319
|
-
}
|
|
333
|
+
const content = formatCommandResult({
|
|
334
|
+
error: result.error,
|
|
335
|
+
exitCode: result.exit_code,
|
|
336
|
+
shellId: result.shell_id,
|
|
337
|
+
stderr: result.stderr,
|
|
338
|
+
stdout: result.stdout,
|
|
339
|
+
success: result.success,
|
|
340
|
+
});
|
|
320
341
|
|
|
321
|
-
const state: RunCommandState = { message, result };
|
|
342
|
+
const state: RunCommandState = { message: content.split('\n\n')[0], result };
|
|
322
343
|
|
|
323
344
|
return {
|
|
324
|
-
content
|
|
345
|
+
content,
|
|
325
346
|
state,
|
|
326
347
|
success: result.success,
|
|
327
348
|
};
|
|
@@ -338,14 +359,17 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
338
359
|
try {
|
|
339
360
|
const result: GetCommandOutputResult = await localFileService.getCommandOutput(params);
|
|
340
361
|
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
:
|
|
362
|
+
const content = formatCommandOutput({
|
|
363
|
+
error: result.error,
|
|
364
|
+
output: result.output,
|
|
365
|
+
running: result.running,
|
|
366
|
+
success: result.success,
|
|
367
|
+
});
|
|
344
368
|
|
|
345
|
-
const state: GetCommandOutputState = { message, result };
|
|
369
|
+
const state: GetCommandOutputState = { message: content.split('\n\n')[0], result };
|
|
346
370
|
|
|
347
371
|
return {
|
|
348
|
-
content
|
|
372
|
+
content,
|
|
349
373
|
state,
|
|
350
374
|
success: result.success,
|
|
351
375
|
};
|
|
@@ -362,14 +386,16 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
362
386
|
try {
|
|
363
387
|
const result: KillCommandResult = await localFileService.killCommand(params);
|
|
364
388
|
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
:
|
|
389
|
+
const content = formatKillResult({
|
|
390
|
+
error: result.error,
|
|
391
|
+
shellId: params.shell_id,
|
|
392
|
+
success: result.success,
|
|
393
|
+
});
|
|
368
394
|
|
|
369
|
-
const state: KillCommandState = { message, result };
|
|
395
|
+
const state: KillCommandState = { message: content, result };
|
|
370
396
|
|
|
371
397
|
return {
|
|
372
|
-
content
|
|
398
|
+
content,
|
|
373
399
|
state,
|
|
374
400
|
success: result.success,
|
|
375
401
|
};
|
|
@@ -388,14 +414,17 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
388
414
|
try {
|
|
389
415
|
const result: GrepContentResult = await localFileService.grepContent(params);
|
|
390
416
|
|
|
391
|
-
const
|
|
392
|
-
?
|
|
417
|
+
const content = result.success
|
|
418
|
+
? formatGrepResults({
|
|
419
|
+
matches: result.matches,
|
|
420
|
+
totalMatches: result.total_matches,
|
|
421
|
+
})
|
|
393
422
|
: 'Search failed';
|
|
394
423
|
|
|
395
|
-
const state: GrepContentState = { message, result };
|
|
424
|
+
const state: GrepContentState = { message: content.split('\n')[0], result };
|
|
396
425
|
|
|
397
426
|
return {
|
|
398
|
-
content
|
|
427
|
+
content,
|
|
399
428
|
state,
|
|
400
429
|
success: result.success,
|
|
401
430
|
};
|
|
@@ -412,12 +441,17 @@ class LocalSystemExecutor extends BaseExecutor<typeof LocalSystemApiEnum> {
|
|
|
412
441
|
try {
|
|
413
442
|
const result: GlobFilesResult = await localFileService.globFiles(params);
|
|
414
443
|
|
|
415
|
-
const
|
|
444
|
+
const content = result.success
|
|
445
|
+
? formatGlobResults({
|
|
446
|
+
files: result.files,
|
|
447
|
+
totalFiles: result.total_files,
|
|
448
|
+
})
|
|
449
|
+
: 'Glob search failed';
|
|
416
450
|
|
|
417
|
-
const state: GlobFilesState = { message, result };
|
|
451
|
+
const state: GlobFilesState = { message: content.split('\n')[0], result };
|
|
418
452
|
|
|
419
453
|
return {
|
|
420
|
-
content
|
|
454
|
+
content,
|
|
421
455
|
state,
|
|
422
456
|
success: result.success,
|
|
423
457
|
};
|
|
@@ -7,9 +7,9 @@ const log = debug('context-engine:processor:GroupMessageFlattenProcessor');
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Group Message Flatten Processor
|
|
10
|
-
* Responsible for flattening role=assistantGroup messages into standard assistant + tool message sequences
|
|
10
|
+
* Responsible for flattening role=assistantGroup and role=supervisor messages into standard assistant + tool message sequences
|
|
11
11
|
*
|
|
12
|
-
* AssistantGroup messages are created when assistant messages with tools are merged with their tool results.
|
|
12
|
+
* AssistantGroup/Supervisor messages are created when assistant messages with tools are merged with their tool results.
|
|
13
13
|
* This processor converts them back to a flat structure that AI models can understand.
|
|
14
14
|
*/
|
|
15
15
|
export class GroupMessageFlattenProcessor extends BaseProcessor {
|
|
@@ -31,8 +31,11 @@ export class GroupMessageFlattenProcessor extends BaseProcessor {
|
|
|
31
31
|
|
|
32
32
|
// Process each message
|
|
33
33
|
for (const message of clonedContext.messages) {
|
|
34
|
-
// Check if this is an assistantGroup message with children field
|
|
35
|
-
if (
|
|
34
|
+
// Check if this is an assistantGroup or supervisor message with children field
|
|
35
|
+
if (
|
|
36
|
+
(message.role === 'assistantGroup' || message.role === 'supervisor') &&
|
|
37
|
+
message.children
|
|
38
|
+
) {
|
|
36
39
|
// If children array is empty, skip this message entirely (no content to flatten)
|
|
37
40
|
if (message.children.length === 0) {
|
|
38
41
|
continue;
|
|
@@ -42,7 +45,7 @@ export class GroupMessageFlattenProcessor extends BaseProcessor {
|
|
|
42
45
|
groupMessagesFlattened++;
|
|
43
46
|
|
|
44
47
|
log(
|
|
45
|
-
`Flattening
|
|
48
|
+
`Flattening ${message.role} message ${message.id} with ${message.children.length} children`,
|
|
46
49
|
);
|
|
47
50
|
|
|
48
51
|
// Flatten each child
|
|
@@ -148,7 +151,7 @@ export class GroupMessageFlattenProcessor extends BaseProcessor {
|
|
|
148
151
|
clonedContext.metadata.toolMessagesCreated = toolMessagesCreated;
|
|
149
152
|
|
|
150
153
|
log(
|
|
151
|
-
`AssistantGroup message flatten processing completed: ${groupMessagesFlattened} groups flattened, ${assistantMessagesCreated} assistant messages created, ${toolMessagesCreated} tool messages created`,
|
|
154
|
+
`AssistantGroup/Supervisor message flatten processing completed: ${groupMessagesFlattened} groups flattened, ${assistantMessagesCreated} assistant messages created, ${toolMessagesCreated} tool messages created`,
|
|
152
155
|
);
|
|
153
156
|
|
|
154
157
|
return this.markAsExecuted(clonedContext);
|
|
@@ -489,6 +489,109 @@ describe('GroupMessageFlattenProcessor', () => {
|
|
|
489
489
|
});
|
|
490
490
|
});
|
|
491
491
|
|
|
492
|
+
describe('Supervisor Messages', () => {
|
|
493
|
+
it('should flatten supervisor message with children', async () => {
|
|
494
|
+
const processor = new GroupMessageFlattenProcessor();
|
|
495
|
+
|
|
496
|
+
const input: any[] = [
|
|
497
|
+
{
|
|
498
|
+
id: 'msg-supervisor-1',
|
|
499
|
+
role: 'supervisor',
|
|
500
|
+
content: '',
|
|
501
|
+
createdAt: '2025-10-27T10:00:00.000Z',
|
|
502
|
+
updatedAt: '2025-10-27T10:00:10.000Z',
|
|
503
|
+
meta: { title: 'Supervisor Agent' },
|
|
504
|
+
children: [
|
|
505
|
+
{
|
|
506
|
+
id: 'msg-1',
|
|
507
|
+
content: 'Let me coordinate the agents',
|
|
508
|
+
tools: [
|
|
509
|
+
{
|
|
510
|
+
id: 'tool-1',
|
|
511
|
+
type: 'builtin',
|
|
512
|
+
apiName: 'broadcast',
|
|
513
|
+
arguments: '{"message":"Hello agents"}',
|
|
514
|
+
identifier: 'lobe-group-management',
|
|
515
|
+
result: {
|
|
516
|
+
id: 'msg-tool-1',
|
|
517
|
+
content: 'Broadcast sent',
|
|
518
|
+
error: null,
|
|
519
|
+
state: {},
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
],
|
|
523
|
+
usage: { totalTokens: 100 },
|
|
524
|
+
},
|
|
525
|
+
],
|
|
526
|
+
},
|
|
527
|
+
];
|
|
528
|
+
|
|
529
|
+
const context = createContext(input);
|
|
530
|
+
const result = await processor.process(context);
|
|
531
|
+
|
|
532
|
+
// Should create 2 messages: 1 assistant + 1 tool
|
|
533
|
+
expect(result.messages).toHaveLength(2);
|
|
534
|
+
|
|
535
|
+
// Check assistant message (supervisor gets flattened to assistant)
|
|
536
|
+
const assistantMsg = result.messages[0];
|
|
537
|
+
expect(assistantMsg.role).toBe('assistant');
|
|
538
|
+
expect(assistantMsg.id).toBe('msg-1');
|
|
539
|
+
expect(assistantMsg.content).toBe('Let me coordinate the agents');
|
|
540
|
+
expect(assistantMsg.tools).toHaveLength(1);
|
|
541
|
+
|
|
542
|
+
// Check tool message
|
|
543
|
+
const toolMsg = result.messages[1];
|
|
544
|
+
expect(toolMsg.role).toBe('tool');
|
|
545
|
+
expect(toolMsg.id).toBe('msg-tool-1');
|
|
546
|
+
expect(toolMsg.content).toBe('Broadcast sent');
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('should flatten supervisor message with content only (no tools)', async () => {
|
|
550
|
+
const processor = new GroupMessageFlattenProcessor();
|
|
551
|
+
|
|
552
|
+
const input: any[] = [
|
|
553
|
+
{
|
|
554
|
+
id: 'msg-supervisor-1',
|
|
555
|
+
role: 'supervisor',
|
|
556
|
+
content: '',
|
|
557
|
+
children: [
|
|
558
|
+
{
|
|
559
|
+
id: 'msg-1',
|
|
560
|
+
content: 'Anthropic cowork',
|
|
561
|
+
},
|
|
562
|
+
],
|
|
563
|
+
},
|
|
564
|
+
];
|
|
565
|
+
|
|
566
|
+
const context = createContext(input);
|
|
567
|
+
const result = await processor.process(context);
|
|
568
|
+
|
|
569
|
+
// Should create 1 assistant message
|
|
570
|
+
expect(result.messages).toHaveLength(1);
|
|
571
|
+
expect(result.messages[0].role).toBe('assistant');
|
|
572
|
+
expect(result.messages[0].content).toBe('Anthropic cowork');
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('should handle supervisor message with empty children', async () => {
|
|
576
|
+
const processor = new GroupMessageFlattenProcessor();
|
|
577
|
+
|
|
578
|
+
const input: any[] = [
|
|
579
|
+
{
|
|
580
|
+
id: 'msg-supervisor-1',
|
|
581
|
+
role: 'supervisor',
|
|
582
|
+
content: '',
|
|
583
|
+
children: [],
|
|
584
|
+
},
|
|
585
|
+
];
|
|
586
|
+
|
|
587
|
+
const context = createContext(input);
|
|
588
|
+
const result = await processor.process(context);
|
|
589
|
+
|
|
590
|
+
// Empty children means no messages created
|
|
591
|
+
expect(result.messages).toHaveLength(0);
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
|
|
492
595
|
describe('Real-world Test Case', () => {
|
|
493
596
|
it('should flatten the provided real-world group message', async () => {
|
|
494
597
|
const processor = new GroupMessageFlattenProcessor();
|