@lobehub/lobehub 2.0.0-next.302 → 2.0.0-next.304
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/src/common/routes.ts +8 -8
- package/apps/desktop/src/main/const/dir.ts +2 -2
- package/apps/desktop/src/main/const/env.ts +4 -4
- package/apps/desktop/src/main/const/store.ts +3 -3
- package/apps/desktop/src/main/controllers/AuthCtr.ts +1 -1
- package/apps/desktop/src/main/controllers/McpInstallCtr.ts +8 -8
- package/apps/desktop/src/main/controllers/NetworkProxyCtr.ts +9 -9
- package/apps/desktop/src/main/controllers/RemoteServerSyncCtr.ts +8 -8
- package/apps/desktop/src/main/core/App.ts +9 -9
- package/apps/desktop/src/main/core/infrastructure/StaticFileServerManager.ts +2 -2
- package/apps/desktop/src/main/core/ui/ShortcutManager.ts +10 -10
- package/apps/desktop/src/main/core/ui/TrayManager.ts +12 -12
- package/apps/desktop/src/main/locales/resources.ts +4 -4
- package/apps/desktop/src/main/menus/impls/macOS.ts +1 -1
- package/apps/desktop/src/main/menus/types.ts +5 -5
- package/apps/desktop/src/main/modules/updater/configs.ts +10 -10
- package/apps/desktop/src/main/modules/updater/utils.ts +9 -9
- package/apps/desktop/src/main/services/fileSrv.ts +62 -62
- package/apps/desktop/src/main/shortcuts/config.ts +3 -3
- package/apps/desktop/src/main/types/protocol.ts +12 -12
- package/apps/desktop/src/main/utils/file-system.ts +2 -2
- package/apps/desktop/src/main/utils/logger.ts +5 -5
- package/apps/desktop/src/main/utils/protocol.ts +32 -32
- package/changelog/v1.json +18 -0
- package/locales/en-US/plugin.json +1 -0
- package/locales/zh-CN/discover.json +4 -4
- package/locales/zh-CN/plugin.json +1 -0
- package/package.json +1 -1
- package/packages/builtin-tool-group-management/src/client/Inspector/ExecuteAgentTask/index.tsx +78 -0
- package/packages/builtin-tool-group-management/src/client/Inspector/{ExecuteTasks → ExecuteAgentTasks}/index.tsx +1 -5
- package/packages/builtin-tool-group-management/src/client/Inspector/index.ts +4 -2
- package/packages/database/src/schemas/relations.ts +4 -4
- package/src/features/Conversation/ChatList/components/AutoScroll.tsx +3 -9
- package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +2 -6
- package/src/features/Conversation/Messages/Assistant/index.tsx +1 -1
- package/src/features/Conversation/Messages/AssistantGroup/components/MessageContent.tsx +3 -3
- package/src/features/Conversation/Messages/Supervisor/components/MessageContent.tsx +2 -2
- package/src/features/Conversation/Messages/components/ContentLoading.tsx +5 -3
- package/src/locales/default/plugin.ts +1 -0
- package/src/store/chat/slices/operation/__tests__/selectors.test.ts +165 -0
- package/src/store/chat/slices/operation/selectors.ts +23 -0
|
@@ -188,6 +188,171 @@ describe('Operation Selectors', () => {
|
|
|
188
188
|
});
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
+
describe('getDeepestRunningOperationByMessage', () => {
|
|
192
|
+
it('should return undefined when no operations exist', () => {
|
|
193
|
+
const { result } = renderHook(() => useChatStore());
|
|
194
|
+
|
|
195
|
+
const deepestOp = operationSelectors.getDeepestRunningOperationByMessage('msg1')(
|
|
196
|
+
result.current,
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(deepestOp).toBeUndefined();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should return undefined when no running operations exist', () => {
|
|
203
|
+
const { result } = renderHook(() => useChatStore());
|
|
204
|
+
|
|
205
|
+
let opId: string;
|
|
206
|
+
act(() => {
|
|
207
|
+
opId = result.current.startOperation({
|
|
208
|
+
type: 'execAgentRuntime',
|
|
209
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
210
|
+
}).operationId;
|
|
211
|
+
result.current.associateMessageWithOperation('msg1', opId);
|
|
212
|
+
result.current.completeOperation(opId);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const deepestOp = operationSelectors.getDeepestRunningOperationByMessage('msg1')(
|
|
216
|
+
result.current,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
expect(deepestOp).toBeUndefined();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should return the only running operation when there is one', () => {
|
|
223
|
+
const { result } = renderHook(() => useChatStore());
|
|
224
|
+
|
|
225
|
+
let opId: string;
|
|
226
|
+
act(() => {
|
|
227
|
+
opId = result.current.startOperation({
|
|
228
|
+
type: 'execAgentRuntime',
|
|
229
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
230
|
+
}).operationId;
|
|
231
|
+
result.current.associateMessageWithOperation('msg1', opId);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const deepestOp = operationSelectors.getDeepestRunningOperationByMessage('msg1')(
|
|
235
|
+
result.current,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(deepestOp).toBeDefined();
|
|
239
|
+
expect(deepestOp?.type).toBe('execAgentRuntime');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should return the leaf operation in a parent-child tree', () => {
|
|
243
|
+
const { result } = renderHook(() => useChatStore());
|
|
244
|
+
|
|
245
|
+
let parentOpId: string;
|
|
246
|
+
let childOpId: string;
|
|
247
|
+
|
|
248
|
+
act(() => {
|
|
249
|
+
// Start parent operation
|
|
250
|
+
parentOpId = result.current.startOperation({
|
|
251
|
+
type: 'execAgentRuntime',
|
|
252
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
253
|
+
}).operationId;
|
|
254
|
+
result.current.associateMessageWithOperation('msg1', parentOpId);
|
|
255
|
+
|
|
256
|
+
// Start child operation
|
|
257
|
+
childOpId = result.current.startOperation({
|
|
258
|
+
type: 'reasoning',
|
|
259
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
260
|
+
parentOperationId: parentOpId,
|
|
261
|
+
}).operationId;
|
|
262
|
+
result.current.associateMessageWithOperation('msg1', childOpId);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const deepestOp = operationSelectors.getDeepestRunningOperationByMessage('msg1')(
|
|
266
|
+
result.current,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Should return the child (reasoning) not the parent (execAgentRuntime)
|
|
270
|
+
expect(deepestOp).toBeDefined();
|
|
271
|
+
expect(deepestOp?.type).toBe('reasoning');
|
|
272
|
+
expect(deepestOp?.id).toBe(childOpId!);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should return the deepest leaf in a multi-level tree', () => {
|
|
276
|
+
const { result } = renderHook(() => useChatStore());
|
|
277
|
+
|
|
278
|
+
let rootOpId: string;
|
|
279
|
+
let level1OpId: string;
|
|
280
|
+
let level2OpId: string;
|
|
281
|
+
|
|
282
|
+
act(() => {
|
|
283
|
+
// Level 0: root operation
|
|
284
|
+
rootOpId = result.current.startOperation({
|
|
285
|
+
type: 'execAgentRuntime',
|
|
286
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
287
|
+
}).operationId;
|
|
288
|
+
result.current.associateMessageWithOperation('msg1', rootOpId);
|
|
289
|
+
|
|
290
|
+
// Level 1: child of root
|
|
291
|
+
level1OpId = result.current.startOperation({
|
|
292
|
+
type: 'callLLM',
|
|
293
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
294
|
+
parentOperationId: rootOpId,
|
|
295
|
+
}).operationId;
|
|
296
|
+
result.current.associateMessageWithOperation('msg1', level1OpId);
|
|
297
|
+
|
|
298
|
+
// Level 2: grandchild (deepest)
|
|
299
|
+
level2OpId = result.current.startOperation({
|
|
300
|
+
type: 'reasoning',
|
|
301
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
302
|
+
parentOperationId: level1OpId,
|
|
303
|
+
}).operationId;
|
|
304
|
+
result.current.associateMessageWithOperation('msg1', level2OpId);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const deepestOp = operationSelectors.getDeepestRunningOperationByMessage('msg1')(
|
|
308
|
+
result.current,
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Should return the deepest leaf (reasoning at level 2)
|
|
312
|
+
expect(deepestOp).toBeDefined();
|
|
313
|
+
expect(deepestOp?.type).toBe('reasoning');
|
|
314
|
+
expect(deepestOp?.id).toBe(level2OpId!);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should return parent when child operation completes', () => {
|
|
318
|
+
const { result } = renderHook(() => useChatStore());
|
|
319
|
+
|
|
320
|
+
let parentOpId: string;
|
|
321
|
+
let childOpId: string;
|
|
322
|
+
|
|
323
|
+
act(() => {
|
|
324
|
+
parentOpId = result.current.startOperation({
|
|
325
|
+
type: 'execAgentRuntime',
|
|
326
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
327
|
+
}).operationId;
|
|
328
|
+
result.current.associateMessageWithOperation('msg1', parentOpId);
|
|
329
|
+
|
|
330
|
+
childOpId = result.current.startOperation({
|
|
331
|
+
type: 'reasoning',
|
|
332
|
+
context: { agentId: 'session1', messageId: 'msg1' },
|
|
333
|
+
parentOperationId: parentOpId,
|
|
334
|
+
}).operationId;
|
|
335
|
+
result.current.associateMessageWithOperation('msg1', childOpId);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Before completing child
|
|
339
|
+
let deepestOp = operationSelectors.getDeepestRunningOperationByMessage('msg1')(
|
|
340
|
+
result.current,
|
|
341
|
+
);
|
|
342
|
+
expect(deepestOp?.type).toBe('reasoning');
|
|
343
|
+
|
|
344
|
+
// Complete child operation
|
|
345
|
+
act(() => {
|
|
346
|
+
result.current.completeOperation(childOpId);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// After completing child, parent should be the deepest running
|
|
350
|
+
deepestOp = operationSelectors.getDeepestRunningOperationByMessage('msg1')(result.current);
|
|
351
|
+
expect(deepestOp?.type).toBe('execAgentRuntime');
|
|
352
|
+
expect(deepestOp?.id).toBe(parentOpId!);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
191
356
|
describe('isMessageProcessing', () => {
|
|
192
357
|
it('should return true if message has running operations', () => {
|
|
193
358
|
const { result } = renderHook(() => useChatStore());
|
|
@@ -355,6 +355,28 @@ const isAnyMessageLoading =
|
|
|
355
355
|
return messageIds.some((id) => isMessageProcessing(id)(s));
|
|
356
356
|
};
|
|
357
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Get the deepest running operation for a message (leaf node in operation tree)
|
|
360
|
+
* Operations form a tree structure via parentOperationId/childOperationIds
|
|
361
|
+
* This returns the most specific (deepest) running operation for UI display
|
|
362
|
+
*/
|
|
363
|
+
const getDeepestRunningOperationByMessage =
|
|
364
|
+
(messageId: string) =>
|
|
365
|
+
(s: ChatStoreState): Operation | undefined => {
|
|
366
|
+
const operations = getOperationsByMessage(messageId)(s);
|
|
367
|
+
const runningOps = operations.filter((op) => op.status === 'running');
|
|
368
|
+
|
|
369
|
+
if (runningOps.length === 0) return undefined;
|
|
370
|
+
|
|
371
|
+
const runningOpIds = new Set(runningOps.map((op) => op.id));
|
|
372
|
+
|
|
373
|
+
// A leaf running operation has no running children
|
|
374
|
+
return runningOps.find((op) => {
|
|
375
|
+
const childIds = op.childOperationIds || [];
|
|
376
|
+
return !childIds.some((childId) => runningOpIds.has(childId));
|
|
377
|
+
});
|
|
378
|
+
};
|
|
379
|
+
|
|
358
380
|
/**
|
|
359
381
|
* Check if a specific message is being regenerated
|
|
360
382
|
*/
|
|
@@ -440,6 +462,7 @@ export const operationSelectors = {
|
|
|
440
462
|
getCurrentContextOperations,
|
|
441
463
|
getCurrentOperationLabel,
|
|
442
464
|
getCurrentOperationProgress,
|
|
465
|
+
getDeepestRunningOperationByMessage,
|
|
443
466
|
getOperationById,
|
|
444
467
|
getOperationContextFromMessage,
|
|
445
468
|
getOperationsByContext,
|