@lobehub/chat 1.141.7 → 1.141.9
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/CHANGELOG.md +50 -0
- package/apps/desktop/package.json +1 -0
- package/apps/desktop/src/main/controllers/LocalFileCtr.ts +279 -52
- package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +392 -0
- package/changelog/v1.json +18 -0
- package/docs/usage/features/{group-chat.mdx → agent-team.mdx} +14 -14
- package/docs/usage/features/agent-team.zh-CN.mdx +52 -0
- package/locales/ar/chat.json +17 -17
- package/locales/ar/setting.json +15 -19
- package/locales/ar/welcome.json +1 -1
- package/locales/bg-BG/chat.json +17 -17
- package/locales/bg-BG/setting.json +15 -19
- package/locales/de-DE/chat.json +17 -17
- package/locales/de-DE/setting.json +15 -19
- package/locales/de-DE/welcome.json +1 -1
- package/locales/en-US/chat.json +17 -17
- package/locales/en-US/setting.json +15 -19
- package/locales/en-US/welcome.json +1 -1
- package/locales/es-ES/chat.json +17 -17
- package/locales/es-ES/setting.json +15 -19
- package/locales/es-ES/welcome.json +1 -1
- package/locales/fa-IR/chat.json +17 -17
- package/locales/fa-IR/setting.json +15 -19
- package/locales/fa-IR/welcome.json +1 -1
- package/locales/fr-FR/chat.json +16 -16
- package/locales/fr-FR/setting.json +15 -19
- package/locales/fr-FR/welcome.json +1 -1
- package/locales/it-IT/chat.json +17 -17
- package/locales/it-IT/setting.json +15 -19
- package/locales/it-IT/welcome.json +1 -1
- package/locales/ja-JP/chat.json +17 -17
- package/locales/ja-JP/setting.json +15 -19
- package/locales/ja-JP/welcome.json +1 -1
- package/locales/ko-KR/chat.json +17 -17
- package/locales/ko-KR/setting.json +15 -19
- package/locales/ko-KR/welcome.json +1 -1
- package/locales/nl-NL/chat.json +17 -17
- package/locales/nl-NL/setting.json +15 -19
- package/locales/nl-NL/welcome.json +1 -1
- package/locales/pl-PL/chat.json +17 -17
- package/locales/pl-PL/setting.json +15 -19
- package/locales/pt-BR/chat.json +17 -17
- package/locales/pt-BR/setting.json +15 -19
- package/locales/pt-BR/welcome.json +1 -1
- package/locales/ru-RU/chat.json +17 -17
- package/locales/ru-RU/setting.json +15 -19
- package/locales/ru-RU/welcome.json +1 -1
- package/locales/tr-TR/chat.json +17 -17
- package/locales/tr-TR/setting.json +15 -19
- package/locales/vi-VN/chat.json +15 -15
- package/locales/vi-VN/setting.json +15 -19
- package/locales/zh-CN/chat.json +17 -17
- package/locales/zh-CN/setting.json +15 -19
- package/locales/zh-CN/welcome.json +1 -1
- package/locales/zh-TW/chat.json +17 -17
- package/locales/zh-TW/setting.json +15 -19
- package/locales/zh-TW/welcome.json +1 -1
- package/package.json +1 -1
- package/packages/agent-runtime/src/core/InterventionChecker.ts +173 -0
- package/packages/agent-runtime/src/core/UsageCounter.ts +248 -0
- package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +334 -0
- package/packages/agent-runtime/src/core/__tests__/UsageCounter.test.ts +873 -0
- package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +32 -26
- package/packages/agent-runtime/src/core/index.ts +2 -0
- package/packages/agent-runtime/src/core/runtime.ts +31 -18
- package/packages/agent-runtime/src/types/instruction.ts +1 -1
- package/packages/agent-runtime/src/types/state.ts +3 -3
- package/packages/agent-runtime/src/types/usage.ts +34 -25
- package/packages/const/src/settings/systemAgent.ts +0 -1
- package/packages/context-engine/src/index.ts +1 -0
- package/packages/context-engine/src/tools/ToolNameResolver.ts +2 -2
- package/packages/context-engine/src/tools/ToolsEngine.ts +37 -8
- package/packages/context-engine/src/tools/__tests__/ToolsEngine.test.ts +149 -5
- package/packages/context-engine/src/tools/__tests__/utils.test.ts +2 -2
- package/packages/context-engine/src/tools/index.ts +1 -0
- package/packages/context-engine/src/tools/types.ts +18 -3
- package/packages/context-engine/src/tools/utils.ts +4 -4
- package/packages/types/src/tool/builtin.ts +54 -1
- package/packages/types/src/tool/index.ts +1 -0
- package/packages/types/src/tool/intervention.ts +114 -0
- package/packages/types/src/user/settings/systemAgent.ts +0 -1
- package/packages/types/src/user/settings/tool.ts +37 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/OrchestratorThinking.tsx +2 -3
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +2 -2
- package/src/app/[variants]/(main)/chat/(workspace)/@topic/features/GroupConfig/GroupMember.tsx +34 -2
- package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +1 -1
- package/src/app/[variants]/(main)/chat/(workspace)/features/{GroupChatSettings → AgentTeamSettings}/index.tsx +4 -5
- package/src/app/[variants]/(main)/chat/(workspace)/features/SettingButton.tsx +2 -2
- package/src/app/[variants]/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx +2 -0
- package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/CollapseGroup/Actions.tsx +18 -1
- package/src/components/ChatGroupWizard/ChatGroupWizard.tsx +33 -5
- package/src/components/MemberSelectionModal/MemberSelectionModal.tsx +170 -26
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +7 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +4 -2
- package/src/features/Conversation/Messages/User/Actions.tsx +8 -2
- package/src/features/GroupChatSettings/{ChatGroupSettings.tsx → AgentTeamChatSettings.tsx} +6 -5
- package/src/features/GroupChatSettings/{GroupMembers.tsx → AgentTeamMembersSettings.tsx} +64 -19
- package/src/features/GroupChatSettings/{ChatGroupMeta.tsx → AgentTeamMetaSettings.tsx} +2 -2
- package/src/features/GroupChatSettings/AgentTeamSettings.tsx +54 -0
- package/src/features/GroupChatSettings/index.ts +4 -5
- package/src/locales/default/chat.ts +17 -17
- package/src/locales/default/setting.ts +15 -19
- package/src/locales/default/welcome.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +2 -1
- package/src/store/chat/slices/builtinTool/actions/{dalle.test.ts → __tests__/dalle.test.ts} +2 -5
- package/src/store/chat/slices/builtinTool/actions/__tests__/{localFile.test.ts → localSystem.test.ts} +4 -4
- package/src/store/chat/slices/builtinTool/actions/index.ts +2 -2
- package/src/store/chat/slices/builtinTool/actions/{localFile.ts → localSystem.ts} +183 -69
- package/src/store/chatGroup/action.ts +36 -1
- package/src/store/electron/selectors/__tests__/desktopState.test.ts +3 -3
- package/src/store/electron/selectors/desktopState.ts +11 -2
- package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +0 -4
- package/src/store/user/slices/settings/selectors/systemAgent.ts +0 -2
- package/src/tools/local-system/Placeholder/ListFiles.tsx +10 -8
- package/src/tools/local-system/Placeholder/SearchFiles.tsx +12 -10
- package/src/tools/local-system/Placeholder/index.tsx +1 -1
- package/src/tools/local-system/Render/ReadLocalFile/ReadFileSkeleton.tsx +8 -18
- package/src/tools/local-system/Render/ReadLocalFile/ReadFileView.tsx +21 -6
- package/src/tools/local-system/Render/SearchFiles/Result.tsx +5 -4
- package/src/tools/local-system/Render/SearchFiles/SearchQuery/SearchView.tsx +4 -15
- package/src/tools/local-system/Render/SearchFiles/index.tsx +3 -2
- package/src/tools/local-system/type.ts +39 -0
- package/docs/usage/features/group-chat.zh-CN.mdx +0 -52
- package/src/features/GroupChatSettings/GroupSettings.tsx +0 -30
- package/src/features/GroupChatSettings/GroupSettingsContent.tsx +0 -24
- package/src/tools/local-system/Placeholder/ReadLocalFile.tsx +0 -9
- package/src/tools/local-system/Render/ReadLocalFile/style.ts +0 -37
- /package/src/store/chat/slices/builtinTool/actions/{search.test.ts → __tests__/search.test.ts} +0 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
EditLocalFileParams,
|
|
3
|
+
GetCommandOutputParams,
|
|
4
|
+
GlobFilesParams,
|
|
5
|
+
GrepContentParams,
|
|
6
|
+
KillCommandParams,
|
|
2
7
|
ListLocalFileParams,
|
|
3
8
|
LocalMoveFilesResultItem,
|
|
4
9
|
LocalReadFileParams,
|
|
@@ -6,6 +11,7 @@ import {
|
|
|
6
11
|
LocalSearchFilesParams,
|
|
7
12
|
MoveLocalFilesParams,
|
|
8
13
|
RenameLocalFileParams,
|
|
14
|
+
RunCommandParams,
|
|
9
15
|
WriteLocalFileParams,
|
|
10
16
|
} from '@lobechat/electron-client-ipc';
|
|
11
17
|
import { StateCreator } from 'zustand/vanilla';
|
|
@@ -13,67 +19,91 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
13
19
|
import { localFileService } from '@/services/electron/localFileService';
|
|
14
20
|
import { ChatStore } from '@/store/chat/store';
|
|
15
21
|
import {
|
|
22
|
+
EditLocalFileState,
|
|
23
|
+
GetCommandOutputState,
|
|
24
|
+
GlobFilesState,
|
|
25
|
+
GrepContentState,
|
|
26
|
+
KillCommandState,
|
|
16
27
|
LocalFileListState,
|
|
17
28
|
LocalFileSearchState,
|
|
18
29
|
LocalMoveFilesState,
|
|
19
30
|
LocalReadFileState,
|
|
20
31
|
LocalReadFilesState,
|
|
21
32
|
LocalRenameFileState,
|
|
33
|
+
RunCommandState,
|
|
22
34
|
} from '@/tools/local-system/type';
|
|
23
35
|
|
|
36
|
+
/* eslint-disable typescript-sort-keys/interface */
|
|
24
37
|
export interface LocalFileAction {
|
|
25
38
|
internal_triggerLocalFileToolCalling: <T = any>(
|
|
26
39
|
id: string,
|
|
27
40
|
callingService: () => Promise<{ content: any; state?: T }>,
|
|
28
41
|
) => Promise<boolean>;
|
|
29
42
|
|
|
43
|
+
// File Operations
|
|
30
44
|
listLocalFiles: (id: string, params: ListLocalFileParams) => Promise<boolean>;
|
|
31
45
|
moveLocalFiles: (id: string, params: MoveLocalFilesParams) => Promise<boolean>;
|
|
32
|
-
reSearchLocalFiles: (id: string, params: LocalSearchFilesParams) => Promise<boolean>;
|
|
33
46
|
readLocalFile: (id: string, params: LocalReadFileParams) => Promise<boolean>;
|
|
34
47
|
readLocalFiles: (id: string, params: LocalReadFilesParams) => Promise<boolean>;
|
|
35
48
|
renameLocalFile: (id: string, params: RenameLocalFileParams) => Promise<boolean>;
|
|
36
|
-
|
|
49
|
+
reSearchLocalFiles: (id: string, params: LocalSearchFilesParams) => Promise<boolean>;
|
|
37
50
|
searchLocalFiles: (id: string, params: LocalSearchFilesParams) => Promise<boolean>;
|
|
38
51
|
toggleLocalFileLoading: (id: string, loading: boolean) => void;
|
|
39
|
-
|
|
40
52
|
writeLocalFile: (id: string, params: WriteLocalFileParams) => Promise<boolean>;
|
|
53
|
+
|
|
54
|
+
// Shell Commands
|
|
55
|
+
editLocalFile: (id: string, params: EditLocalFileParams) => Promise<boolean>;
|
|
56
|
+
getCommandOutput: (id: string, params: GetCommandOutputParams) => Promise<boolean>;
|
|
57
|
+
killCommand: (id: string, params: KillCommandParams) => Promise<boolean>;
|
|
58
|
+
runCommand: (id: string, params: RunCommandParams) => Promise<boolean>;
|
|
59
|
+
|
|
60
|
+
// Search & Find
|
|
61
|
+
globLocalFiles: (id: string, params: GlobFilesParams) => Promise<boolean>;
|
|
62
|
+
grepContent: (id: string, params: GrepContentParams) => Promise<boolean>;
|
|
41
63
|
}
|
|
64
|
+
/* eslint-enable typescript-sort-keys/interface */
|
|
42
65
|
|
|
43
|
-
|
|
66
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
67
|
+
export const localSystemSlice: StateCreator<
|
|
44
68
|
ChatStore,
|
|
45
69
|
[['zustand/devtools', never]],
|
|
46
70
|
[],
|
|
47
71
|
LocalFileAction
|
|
48
72
|
> = (set, get) => ({
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
if (state) {
|
|
54
|
-
await get().updatePluginState(id, state as any);
|
|
55
|
-
}
|
|
56
|
-
await get().internal_updateMessageContent(id, JSON.stringify(content));
|
|
57
|
-
} catch (error) {
|
|
58
|
-
await get().internal_updateMessagePluginError(id, {
|
|
59
|
-
body: error,
|
|
60
|
-
message: (error as Error).message,
|
|
61
|
-
type: 'PluginServerError',
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
get().toggleLocalFileLoading(id, false);
|
|
73
|
+
// ==================== File Editing ====================
|
|
74
|
+
editLocalFile: async (id, params) => {
|
|
75
|
+
return get().internal_triggerLocalFileToolCalling<EditLocalFileState>(id, async () => {
|
|
76
|
+
const result = await localFileService.editLocalFile(params);
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
const message = result.success
|
|
79
|
+
? `Successfully replaced ${result.replacements} occurrence(s) in ${params.file_path}`
|
|
80
|
+
: `Edit failed: ${result.error}`;
|
|
81
|
+
|
|
82
|
+
const state: EditLocalFileState = { message, result };
|
|
68
83
|
|
|
69
|
-
listLocalFiles: async (id, params) => {
|
|
70
|
-
return get().internal_triggerLocalFileToolCalling<LocalFileListState>(id, async () => {
|
|
71
|
-
const result = await localFileService.listLocalFiles(params);
|
|
72
|
-
const state: LocalFileListState = { listResults: result };
|
|
73
84
|
return { content: result, state };
|
|
74
85
|
});
|
|
75
86
|
},
|
|
76
87
|
|
|
88
|
+
writeLocalFile: async (id, params) => {
|
|
89
|
+
return get().internal_triggerLocalFileToolCalling(id, async () => {
|
|
90
|
+
const result = await localFileService.writeFile(params);
|
|
91
|
+
|
|
92
|
+
let content: { message: string; success: boolean };
|
|
93
|
+
|
|
94
|
+
if (result.success) {
|
|
95
|
+
content = {
|
|
96
|
+
message: `成功写入文件 ${params.path}`,
|
|
97
|
+
success: true,
|
|
98
|
+
};
|
|
99
|
+
} else {
|
|
100
|
+
const errorMessage = result.error;
|
|
101
|
+
|
|
102
|
+
content = { message: errorMessage || '写入文件失败', success: false };
|
|
103
|
+
}
|
|
104
|
+
return { content };
|
|
105
|
+
});
|
|
106
|
+
},
|
|
77
107
|
moveLocalFiles: async (id, params) => {
|
|
78
108
|
return get().internal_triggerLocalFileToolCalling<LocalMoveFilesState>(id, async () => {
|
|
79
109
|
const results: LocalMoveFilesResultItem[] = await localFileService.moveLocalFiles(params);
|
|
@@ -100,31 +130,6 @@ export const localFileSlice: StateCreator<
|
|
|
100
130
|
return { content: { message, results }, state };
|
|
101
131
|
});
|
|
102
132
|
},
|
|
103
|
-
|
|
104
|
-
reSearchLocalFiles: async (id, params) => {
|
|
105
|
-
get().toggleLocalFileLoading(id, true);
|
|
106
|
-
|
|
107
|
-
await get().updatePluginArguments(id, params);
|
|
108
|
-
|
|
109
|
-
return get().searchLocalFiles(id, params);
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
readLocalFile: async (id, params) => {
|
|
113
|
-
return get().internal_triggerLocalFileToolCalling<LocalReadFileState>(id, async () => {
|
|
114
|
-
const result = await localFileService.readLocalFile(params);
|
|
115
|
-
const state: LocalReadFileState = { fileContent: result };
|
|
116
|
-
return { content: result, state };
|
|
117
|
-
});
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
readLocalFiles: async (id, params) => {
|
|
121
|
-
return get().internal_triggerLocalFileToolCalling<LocalReadFilesState>(id, async () => {
|
|
122
|
-
const results = await localFileService.readLocalFiles(params);
|
|
123
|
-
const state: LocalReadFilesState = { filesContent: results };
|
|
124
|
-
return { content: results, state };
|
|
125
|
-
});
|
|
126
|
-
},
|
|
127
|
-
|
|
128
133
|
renameLocalFile: async (id, params) => {
|
|
129
134
|
return get().internal_triggerLocalFileToolCalling<LocalRenameFileState>(id, async () => {
|
|
130
135
|
const { path: currentPath, newName } = params;
|
|
@@ -169,6 +174,33 @@ export const localFileSlice: StateCreator<
|
|
|
169
174
|
});
|
|
170
175
|
},
|
|
171
176
|
|
|
177
|
+
// ==================== Search & Find ====================
|
|
178
|
+
grepContent: async (id, params) => {
|
|
179
|
+
return get().internal_triggerLocalFileToolCalling<GrepContentState>(id, async () => {
|
|
180
|
+
const result = await localFileService.grepContent(params);
|
|
181
|
+
|
|
182
|
+
const message = result.success
|
|
183
|
+
? `Found ${result.total_matches} matches in ${result.matches.length} locations`
|
|
184
|
+
: 'Search failed';
|
|
185
|
+
|
|
186
|
+
const state: GrepContentState = { message, result };
|
|
187
|
+
|
|
188
|
+
return { content: result, state };
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
globLocalFiles: async (id, params) => {
|
|
193
|
+
return get().internal_triggerLocalFileToolCalling<GlobFilesState>(id, async () => {
|
|
194
|
+
const result = await localFileService.globFiles(params);
|
|
195
|
+
|
|
196
|
+
const message = result.success ? `Found ${result.total_files} files` : 'Glob search failed';
|
|
197
|
+
|
|
198
|
+
const state: GlobFilesState = { message, result };
|
|
199
|
+
|
|
200
|
+
return { content: result, state };
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
|
|
172
204
|
searchLocalFiles: async (id, params) => {
|
|
173
205
|
return get().internal_triggerLocalFileToolCalling<LocalFileSearchState>(id, async () => {
|
|
174
206
|
const result = await localFileService.searchLocalFiles(params);
|
|
@@ -177,6 +209,89 @@ export const localFileSlice: StateCreator<
|
|
|
177
209
|
});
|
|
178
210
|
},
|
|
179
211
|
|
|
212
|
+
listLocalFiles: async (id, params) => {
|
|
213
|
+
return get().internal_triggerLocalFileToolCalling<LocalFileListState>(id, async () => {
|
|
214
|
+
const result = await localFileService.listLocalFiles(params);
|
|
215
|
+
const state: LocalFileListState = { listResults: result };
|
|
216
|
+
return { content: result, state };
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
reSearchLocalFiles: async (id, params) => {
|
|
221
|
+
get().toggleLocalFileLoading(id, true);
|
|
222
|
+
|
|
223
|
+
await get().updatePluginArguments(id, params);
|
|
224
|
+
|
|
225
|
+
return get().searchLocalFiles(id, params);
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
readLocalFile: async (id, params) => {
|
|
229
|
+
return get().internal_triggerLocalFileToolCalling<LocalReadFileState>(id, async () => {
|
|
230
|
+
const result = await localFileService.readLocalFile(params);
|
|
231
|
+
const state: LocalReadFileState = { fileContent: result };
|
|
232
|
+
return { content: result, state };
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
readLocalFiles: async (id, params) => {
|
|
237
|
+
return get().internal_triggerLocalFileToolCalling<LocalReadFilesState>(id, async () => {
|
|
238
|
+
const results = await localFileService.readLocalFiles(params);
|
|
239
|
+
const state: LocalReadFilesState = { filesContent: results };
|
|
240
|
+
return { content: results, state };
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// ==================== Shell Commands ====================
|
|
245
|
+
runCommand: async (id, params) => {
|
|
246
|
+
return get().internal_triggerLocalFileToolCalling<RunCommandState>(id, async () => {
|
|
247
|
+
const result = await localFileService.runCommand(params);
|
|
248
|
+
|
|
249
|
+
let message: string;
|
|
250
|
+
|
|
251
|
+
if (result.success) {
|
|
252
|
+
if (result.shell_id) {
|
|
253
|
+
message = `Command started in background with shell_id: ${result.shell_id}`;
|
|
254
|
+
} else {
|
|
255
|
+
message = `Command completed successfully. Exit code: ${result.exit_code}`;
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
message = `Command failed: ${result.error}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const state: RunCommandState = { message, result };
|
|
262
|
+
|
|
263
|
+
return { content: result, state };
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
killCommand: async (id, params) => {
|
|
267
|
+
return get().internal_triggerLocalFileToolCalling<KillCommandState>(id, async () => {
|
|
268
|
+
const result = await localFileService.killCommand(params);
|
|
269
|
+
|
|
270
|
+
const message = result.success
|
|
271
|
+
? `Successfully killed shell: ${params.shell_id}`
|
|
272
|
+
: `Failed to kill shell: ${result.error}`;
|
|
273
|
+
|
|
274
|
+
const state: KillCommandState = { message, result };
|
|
275
|
+
|
|
276
|
+
return { content: result, state };
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
getCommandOutput: async (id, params) => {
|
|
280
|
+
return get().internal_triggerLocalFileToolCalling<GetCommandOutputState>(id, async () => {
|
|
281
|
+
const result = await localFileService.getCommandOutput(params);
|
|
282
|
+
|
|
283
|
+
const message = result.success
|
|
284
|
+
? `Output retrieved. Running: ${result.running}`
|
|
285
|
+
: `Failed: ${result.error}`;
|
|
286
|
+
|
|
287
|
+
const state: GetCommandOutputState = { message, result };
|
|
288
|
+
|
|
289
|
+
return { content: result, state };
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
// ==================== utils ====================
|
|
294
|
+
|
|
180
295
|
toggleLocalFileLoading: (id, loading) => {
|
|
181
296
|
// Assuming a loading state structure similar to searchLoading
|
|
182
297
|
set(
|
|
@@ -187,24 +302,23 @@ export const localFileSlice: StateCreator<
|
|
|
187
302
|
`toggleLocalFileLoading/${loading ? 'start' : 'end'}`,
|
|
188
303
|
);
|
|
189
304
|
},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (result.success) {
|
|
198
|
-
content = {
|
|
199
|
-
message: `成功写入文件 ${params.path}`,
|
|
200
|
-
success: true,
|
|
201
|
-
};
|
|
202
|
-
} else {
|
|
203
|
-
const errorMessage = result.error;
|
|
204
|
-
|
|
205
|
-
content = { message: errorMessage || '写入文件失败', success: false };
|
|
305
|
+
internal_triggerLocalFileToolCalling: async (id, callingService) => {
|
|
306
|
+
get().toggleLocalFileLoading(id, true);
|
|
307
|
+
try {
|
|
308
|
+
const { state, content } = await callingService();
|
|
309
|
+
if (state) {
|
|
310
|
+
await get().updatePluginState(id, state as any);
|
|
206
311
|
}
|
|
207
|
-
|
|
208
|
-
})
|
|
312
|
+
await get().internal_updateMessageContent(id, JSON.stringify(content));
|
|
313
|
+
} catch (error) {
|
|
314
|
+
await get().internal_updateMessagePluginError(id, {
|
|
315
|
+
body: error,
|
|
316
|
+
message: (error as Error).message,
|
|
317
|
+
type: 'PluginServerError',
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
get().toggleLocalFileLoading(id, false);
|
|
321
|
+
|
|
322
|
+
return true;
|
|
209
323
|
},
|
|
210
324
|
});
|
|
@@ -98,14 +98,49 @@ export const chatGroupAction: StateCreator<
|
|
|
98
98
|
return group.id;
|
|
99
99
|
},
|
|
100
100
|
deleteGroup: async (id) => {
|
|
101
|
+
// First, get all group members to identify virtual members
|
|
102
|
+
// Note: ChatGroupAgentItem type is incorrectly defined in schema as agents table type
|
|
103
|
+
// but getGroupAgents actually returns chatGroupsAgents junction table entries
|
|
104
|
+
const groupAgents = (await chatGroupService.getGroupAgents(id)) as unknown as Array<{
|
|
105
|
+
agentId: string;
|
|
106
|
+
chatGroupId: string;
|
|
107
|
+
}>;
|
|
108
|
+
|
|
109
|
+
// Delete the group first (this will cascade delete the chat_groups_agents entries)
|
|
101
110
|
await chatGroupService.deleteGroup(id);
|
|
102
111
|
dispatch({ payload: id, type: 'deleteGroup' });
|
|
103
112
|
|
|
113
|
+
// Now delete virtual members (agents with virtual: true)
|
|
114
|
+
const sessionStore = getSessionStoreState();
|
|
115
|
+
const sessions = sessionStore.sessions || [];
|
|
116
|
+
|
|
117
|
+
// Find and delete all virtual sessions that were members of this group
|
|
118
|
+
const virtualMemberDeletions = groupAgents
|
|
119
|
+
.map((groupAgent) => {
|
|
120
|
+
// groupAgent has agentId property from the junction table
|
|
121
|
+
const session = sessions.find((s) => {
|
|
122
|
+
// Type guard: check if it's an agent session
|
|
123
|
+
if (s.type === 'agent') {
|
|
124
|
+
return s.config?.id === groupAgent.agentId;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Only delete if the session exists and has virtual flag set to true
|
|
130
|
+
if (session && session.type === 'agent' && session.config?.virtual) {
|
|
131
|
+
return sessionStore.removeSession(session.id);
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
})
|
|
135
|
+
.filter(Boolean);
|
|
136
|
+
|
|
137
|
+
// Wait for all virtual member deletions to complete
|
|
138
|
+
await Promise.all(virtualMemberDeletions);
|
|
139
|
+
|
|
104
140
|
await get().loadGroups();
|
|
105
141
|
await getSessionStoreState().refreshSessions();
|
|
106
142
|
|
|
107
143
|
// If the active session is the deleted group, switch to the inbox session
|
|
108
|
-
const sessionStore = getSessionStoreState();
|
|
109
144
|
if (sessionStore.activeId === id) {
|
|
110
145
|
sessionStore.switchSession(INBOX_SESSION_ID);
|
|
111
146
|
}
|
|
@@ -6,7 +6,7 @@ import { merge } from '@/utils/merge';
|
|
|
6
6
|
import { desktopStateSelectors } from '../desktopState';
|
|
7
7
|
|
|
8
8
|
describe('desktopStateSelectors', () => {
|
|
9
|
-
describe('
|
|
9
|
+
describe('userPath', () => {
|
|
10
10
|
it('should return userPath from appState', () => {
|
|
11
11
|
const state: ElectronState = merge(initialState, {
|
|
12
12
|
appState: {
|
|
@@ -23,7 +23,7 @@ describe('desktopStateSelectors', () => {
|
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
expect(desktopStateSelectors.
|
|
26
|
+
expect(desktopStateSelectors.userPath(state)).toEqual({
|
|
27
27
|
desktop: '/test/desktop',
|
|
28
28
|
documents: '/test/documents',
|
|
29
29
|
downloads: '/test/downloads',
|
|
@@ -40,7 +40,7 @@ describe('desktopStateSelectors', () => {
|
|
|
40
40
|
appState: {},
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
expect(desktopStateSelectors.
|
|
43
|
+
expect(desktopStateSelectors.userPath(state)).toBeUndefined();
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
46
|
});
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { ElectronState } from '@/store/electron/initialState';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const userPath = (s: ElectronState) => s.appState.userPath;
|
|
4
|
+
const userHomePath = (s: ElectronState) => userPath(s)?.home || '';
|
|
5
|
+
|
|
6
|
+
const displayRelativePath = (path: string) => (s: ElectronState) => {
|
|
7
|
+
const basePath = userHomePath(s);
|
|
8
|
+
|
|
9
|
+
return !!basePath ? path.replaceAll(basePath, '~') : path;
|
|
10
|
+
};
|
|
4
11
|
|
|
5
12
|
export const desktopStateSelectors = {
|
|
6
|
-
|
|
13
|
+
displayRelativePath,
|
|
14
|
+
userHomePath,
|
|
15
|
+
userPath,
|
|
7
16
|
};
|
|
@@ -59,10 +59,6 @@ exports[`settingsSelectors > currentSystemAgent > should merge DEFAULT_SYSTEM_AG
|
|
|
59
59
|
"model": "gpt-5-mini",
|
|
60
60
|
"provider": "openai",
|
|
61
61
|
},
|
|
62
|
-
"groupChatSupervisor": {
|
|
63
|
-
"model": "gpt-5-mini",
|
|
64
|
-
"provider": "openai",
|
|
65
|
-
},
|
|
66
62
|
"historyCompress": {
|
|
67
63
|
"model": "gpt-5-mini",
|
|
68
64
|
"provider": "openai",
|
|
@@ -14,12 +14,10 @@ const agentMeta = (s: UserStore) => currentSystemAgent(s).agentMeta;
|
|
|
14
14
|
const queryRewrite = (s: UserStore) => currentSystemAgent(s).queryRewrite;
|
|
15
15
|
const historyCompress = (s: UserStore) => currentSystemAgent(s).historyCompress;
|
|
16
16
|
const generationTopic = (s: UserStore) => currentSystemAgent(s).generationTopic;
|
|
17
|
-
const groupChatSupervisor = (s: UserStore) => currentSystemAgent(s).groupChatSupervisor;
|
|
18
17
|
|
|
19
18
|
export const systemAgentSelectors = {
|
|
20
19
|
agentMeta,
|
|
21
20
|
generationTopic,
|
|
22
|
-
groupChatSupervisor,
|
|
23
21
|
historyCompress,
|
|
24
22
|
queryRewrite,
|
|
25
23
|
thread,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
|
|
2
2
|
import { Skeleton } from 'antd';
|
|
3
3
|
import React, { memo } from 'react';
|
|
4
|
-
import { Flexbox } from 'react-layout-kit';
|
|
4
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
|
6
6
|
import { LocalFolder } from '@/features/LocalFile';
|
|
7
7
|
|
|
@@ -10,14 +10,16 @@ interface ListFilesProps {
|
|
|
10
10
|
}
|
|
11
11
|
export const ListFiles = memo<ListFilesProps>(({ args }) => {
|
|
12
12
|
return (
|
|
13
|
-
<Flexbox gap={
|
|
13
|
+
<Flexbox gap={12}>
|
|
14
14
|
<LocalFolder path={args.path} />
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
<Center height={140}>
|
|
16
|
+
<Flexbox gap={4} width={'90%'}>
|
|
17
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
|
18
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
|
19
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
|
20
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
|
21
|
+
</Flexbox>
|
|
22
|
+
</Center>
|
|
21
23
|
</Flexbox>
|
|
22
24
|
);
|
|
23
25
|
});
|
|
@@ -3,8 +3,8 @@ import { Icon } from '@lobehub/ui';
|
|
|
3
3
|
import { Skeleton } from 'antd';
|
|
4
4
|
import { createStyles } from 'antd-style';
|
|
5
5
|
import { SearchIcon } from 'lucide-react';
|
|
6
|
-
import { memo } from 'react';
|
|
7
|
-
import { Flexbox } from 'react-layout-kit';
|
|
6
|
+
import React, { memo } from 'react';
|
|
7
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
8
8
|
|
|
9
9
|
const useStyles = createStyles(({ css, token, cx }) => ({
|
|
10
10
|
query: cx(css`
|
|
@@ -29,8 +29,8 @@ const SearchFiles = memo<SearchFilesProps>(({ args }) => {
|
|
|
29
29
|
const { styles } = useStyles();
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
|
-
<Flexbox gap={
|
|
33
|
-
<Flexbox align={'center'} distribution={'space-between'} gap={40} height={
|
|
32
|
+
<Flexbox gap={4}>
|
|
33
|
+
<Flexbox align={'center'} distribution={'space-between'} gap={40} height={26} horizontal>
|
|
34
34
|
<Flexbox align={'center'} className={styles.query} gap={8} horizontal>
|
|
35
35
|
<Icon icon={SearchIcon} />
|
|
36
36
|
{args.keywords ? (
|
|
@@ -42,12 +42,14 @@ const SearchFiles = memo<SearchFilesProps>(({ args }) => {
|
|
|
42
42
|
|
|
43
43
|
<Skeleton.Node active style={{ height: 20, width: 40 }} />
|
|
44
44
|
</Flexbox>
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
<Center height={140}>
|
|
46
|
+
<Flexbox gap={4} width={'90%'}>
|
|
47
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
|
48
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
|
49
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
|
50
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
|
51
|
+
</Flexbox>
|
|
52
|
+
</Center>
|
|
51
53
|
</Flexbox>
|
|
52
54
|
);
|
|
53
55
|
});
|
|
@@ -3,8 +3,8 @@ import { memo } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { LocalSystemApiName } from '@/tools/local-system';
|
|
5
5
|
|
|
6
|
+
import ReadLocalFile from '../Render/ReadLocalFile/ReadFileSkeleton';
|
|
6
7
|
import { ListFiles } from './ListFiles';
|
|
7
|
-
import ReadLocalFile from './ReadLocalFile';
|
|
8
8
|
import SearchFiles from './SearchFiles';
|
|
9
9
|
|
|
10
10
|
const RenderMap = {
|
|
@@ -9,40 +9,30 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
9
9
|
border: 1px solid ${token.colorBorderSecondary};
|
|
10
10
|
border-radius: ${token.borderRadiusLG}px;
|
|
11
11
|
`,
|
|
12
|
-
|
|
13
|
-
margin-block-end: 4px;
|
|
14
|
-
`,
|
|
12
|
+
|
|
15
13
|
meta: css`
|
|
16
14
|
font-size: 12px;
|
|
17
15
|
`,
|
|
18
|
-
path: css`
|
|
19
|
-
margin-block-start: 4px;
|
|
20
|
-
`,
|
|
21
16
|
}));
|
|
22
17
|
|
|
23
18
|
const ReadFileSkeleton = memo(() => {
|
|
24
19
|
const { styles } = useStyles();
|
|
25
20
|
|
|
26
21
|
return (
|
|
27
|
-
<Flexbox className={styles.container}>
|
|
28
|
-
<Flexbox
|
|
29
|
-
align={'center'}
|
|
30
|
-
className={styles.header}
|
|
31
|
-
gap={24}
|
|
32
|
-
horizontal
|
|
33
|
-
justify={'space-between'}
|
|
34
|
-
>
|
|
22
|
+
<Flexbox className={styles.container} gap={2}>
|
|
23
|
+
<Flexbox align={'center'} gap={24} horizontal justify={'space-between'}>
|
|
35
24
|
<Flexbox align={'center'} flex={1} gap={8} horizontal style={{ overflow: 'hidden' }}>
|
|
36
|
-
<Skeleton.
|
|
37
|
-
|
|
25
|
+
<Skeleton.Node active style={{ flex: 1, height: 16, width: 20 }} />
|
|
26
|
+
|
|
27
|
+
<Skeleton.Node active style={{ flex: 1, height: 16, minWidth: 100 }} />
|
|
38
28
|
</Flexbox>
|
|
39
29
|
<Flexbox align={'center'} className={styles.meta} gap={16}>
|
|
40
|
-
<Skeleton.
|
|
30
|
+
<Skeleton.Node active style={{ height: 16, maxWidth: 40 }} />
|
|
41
31
|
</Flexbox>
|
|
42
32
|
</Flexbox>
|
|
43
33
|
|
|
44
34
|
{/* Path */}
|
|
45
|
-
<Skeleton.
|
|
35
|
+
<Skeleton.Node active style={{ height: 16, width: '100%' }} />
|
|
46
36
|
</Flexbox>
|
|
47
37
|
);
|
|
48
38
|
});
|
|
@@ -8,6 +8,8 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
8
8
|
|
|
9
9
|
import FileIcon from '@/components/FileIcon';
|
|
10
10
|
import { localFileService } from '@/services/electron/localFileService';
|
|
11
|
+
import { useElectronStore } from '@/store/electron';
|
|
12
|
+
import { desktopStateSelectors } from '@/store/electron/selectors';
|
|
11
13
|
|
|
12
14
|
const useStyles = createStyles(({ css, token, cx }) => ({
|
|
13
15
|
actions: cx(
|
|
@@ -20,11 +22,19 @@ const useStyles = createStyles(({ css, token, cx }) => ({
|
|
|
20
22
|
`,
|
|
21
23
|
),
|
|
22
24
|
container: css`
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
|
|
27
|
+
height: 64px;
|
|
23
28
|
padding: 8px;
|
|
24
29
|
border: 1px solid ${token.colorBorderSecondary};
|
|
25
30
|
border-radius: ${token.borderRadiusLG}px;
|
|
31
|
+
|
|
26
32
|
transition: all 0.2s ${token.motionEaseInOut};
|
|
27
33
|
|
|
34
|
+
.local-file-actions {
|
|
35
|
+
opacity: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
&:hover {
|
|
29
39
|
border-color: ${token.colorBorder};
|
|
30
40
|
|
|
@@ -48,10 +58,13 @@ const useStyles = createStyles(({ css, token, cx }) => ({
|
|
|
48
58
|
lineCount: css`
|
|
49
59
|
color: ${token.colorTextQuaternary};
|
|
50
60
|
`,
|
|
51
|
-
meta:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
meta: cx(
|
|
62
|
+
'local-file-actions',
|
|
63
|
+
css`
|
|
64
|
+
font-size: 12px;
|
|
65
|
+
color: ${token.colorTextTertiary};
|
|
66
|
+
`,
|
|
67
|
+
),
|
|
55
68
|
path: css`
|
|
56
69
|
margin-block-start: 4px;
|
|
57
70
|
padding-inline: 4px;
|
|
@@ -104,6 +117,8 @@ const ReadFileView = memo<ReadFileViewProps>(
|
|
|
104
117
|
localFileService.openLocalFolder({ isDirectory: false, path });
|
|
105
118
|
};
|
|
106
119
|
|
|
120
|
+
const displayPath = useElectronStore(desktopStateSelectors.displayRelativePath(path));
|
|
121
|
+
|
|
107
122
|
return (
|
|
108
123
|
<Flexbox className={styles.container}>
|
|
109
124
|
<Flexbox
|
|
@@ -115,7 +130,7 @@ const ReadFileView = memo<ReadFileViewProps>(
|
|
|
115
130
|
onClick={handleToggleExpand}
|
|
116
131
|
>
|
|
117
132
|
<Flexbox align={'center'} flex={1} gap={0} horizontal style={{ overflow: 'hidden' }}>
|
|
118
|
-
<FileIcon fileName={filename} fileType={fileType} size={
|
|
133
|
+
<FileIcon fileName={filename} fileType={fileType} size={16} variant={'raw'} />
|
|
119
134
|
<Flexbox horizontal>
|
|
120
135
|
<Text className={styles.fileName} ellipsis>
|
|
121
136
|
{filename}
|
|
@@ -174,7 +189,7 @@ const ReadFileView = memo<ReadFileViewProps>(
|
|
|
174
189
|
|
|
175
190
|
{/* Path */}
|
|
176
191
|
<Text className={styles.path} ellipsis type={'secondary'}>
|
|
177
|
-
{
|
|
192
|
+
{displayPath}
|
|
178
193
|
</Text>
|
|
179
194
|
|
|
180
195
|
{isExpanded && (
|