@lobehub/lobehub 2.0.0-next.85 → 2.0.0-next.87

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 (95) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/apps/desktop/src/main/modules/networkProxy/dispatcher.ts +16 -16
  3. package/apps/desktop/src/main/modules/networkProxy/tester.ts +11 -11
  4. package/apps/desktop/src/main/modules/networkProxy/urlBuilder.ts +3 -3
  5. package/apps/desktop/src/main/modules/networkProxy/validator.ts +10 -10
  6. package/changelog/v1.json +18 -0
  7. package/package.json +1 -1
  8. package/packages/agent-runtime/src/core/runtime.ts +36 -1
  9. package/packages/agent-runtime/src/types/event.ts +1 -0
  10. package/packages/agent-runtime/src/types/generalAgent.ts +16 -0
  11. package/packages/agent-runtime/src/types/instruction.ts +30 -0
  12. package/packages/agent-runtime/src/types/runtime.ts +7 -0
  13. package/packages/types/src/message/common/metadata.ts +3 -0
  14. package/packages/types/src/message/common/tools.ts +2 -2
  15. package/packages/types/src/tool/search/index.ts +8 -2
  16. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
  17. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +7 -2
  18. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +15 -14
  19. package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/Item/index.tsx +2 -2
  20. package/src/components/Analytics/MainInterfaceTracker.tsx +2 -2
  21. package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
  22. package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
  23. package/src/features/Conversation/MarkdownElements/LobeThinking/Render.tsx +3 -3
  24. package/src/features/Conversation/MarkdownElements/Thinking/Render.tsx +3 -3
  25. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +1 -1
  26. package/src/features/Conversation/Messages/User/index.tsx +3 -3
  27. package/src/features/Conversation/Messages/index.tsx +3 -3
  28. package/src/features/Conversation/components/AutoScroll.tsx +2 -2
  29. package/src/features/PluginsUI/Render/StandaloneType/Iframe.tsx +3 -3
  30. package/src/features/Portal/Home/Body/Plugins/ArtifactList/index.tsx +3 -3
  31. package/src/features/ShareModal/ShareText/index.tsx +3 -3
  32. package/src/services/search.ts +2 -2
  33. package/src/store/chat/agents/GeneralChatAgent.ts +98 -0
  34. package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +366 -0
  35. package/src/store/chat/agents/__tests__/createAgentExecutors/call-llm.test.ts +1217 -0
  36. package/src/store/chat/agents/__tests__/createAgentExecutors/call-tool.test.ts +1976 -0
  37. package/src/store/chat/agents/__tests__/createAgentExecutors/finish.test.ts +453 -0
  38. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/index.ts +4 -0
  39. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockInstructions.ts +126 -0
  40. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockMessages.ts +94 -0
  41. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockOperations.ts +96 -0
  42. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockStore.ts +138 -0
  43. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/assertions.ts +185 -0
  44. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/index.ts +3 -0
  45. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/operationTestUtils.ts +94 -0
  46. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +139 -0
  47. package/src/store/chat/agents/__tests__/createAgentExecutors/request-human-approve.test.ts +545 -0
  48. package/src/store/chat/agents/__tests__/createAgentExecutors/resolve-aborted-tools.test.ts +686 -0
  49. package/src/store/chat/agents/createAgentExecutors.ts +313 -80
  50. package/src/store/chat/selectors.ts +1 -0
  51. package/src/store/chat/slices/aiChat/__tests__/ai-chat.integration.test.ts +667 -0
  52. package/src/store/chat/slices/aiChat/actions/__tests__/cancel-functionality.test.ts +137 -27
  53. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +163 -125
  54. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +12 -2
  55. package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +0 -2
  56. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +0 -2
  57. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +286 -19
  58. package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +0 -112
  59. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +42 -99
  60. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +90 -57
  61. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +5 -25
  62. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +220 -98
  63. package/src/store/chat/slices/aiChat/actions/streamingStates.ts +0 -34
  64. package/src/store/chat/slices/aiChat/initialState.ts +0 -28
  65. package/src/store/chat/slices/aiChat/selectors.test.ts +280 -0
  66. package/src/store/chat/slices/aiChat/selectors.ts +31 -7
  67. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +21 -30
  68. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +29 -49
  69. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +83 -48
  70. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +78 -28
  71. package/src/store/chat/slices/builtinTool/actions/search.ts +146 -59
  72. package/src/store/chat/slices/builtinTool/selectors.test.ts +258 -0
  73. package/src/store/chat/slices/builtinTool/selectors.ts +25 -4
  74. package/src/store/chat/slices/message/action.test.ts +134 -16
  75. package/src/store/chat/slices/message/actions/internals.ts +33 -7
  76. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +85 -52
  77. package/src/store/chat/slices/message/initialState.ts +0 -10
  78. package/src/store/chat/slices/message/selectors/messageState.ts +34 -12
  79. package/src/store/chat/slices/operation/__tests__/actions.test.ts +712 -16
  80. package/src/store/chat/slices/operation/__tests__/integration.test.ts +342 -0
  81. package/src/store/chat/slices/operation/__tests__/selectors.test.ts +257 -17
  82. package/src/store/chat/slices/operation/actions.ts +218 -11
  83. package/src/store/chat/slices/operation/selectors.ts +135 -6
  84. package/src/store/chat/slices/operation/types.ts +29 -3
  85. package/src/store/chat/slices/plugin/action.test.ts +30 -322
  86. package/src/store/chat/slices/plugin/actions/internals.ts +0 -14
  87. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +21 -19
  88. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +45 -27
  89. package/src/store/chat/slices/plugin/actions/publicApi.ts +3 -4
  90. package/src/store/chat/slices/plugin/actions/workflow.ts +0 -55
  91. package/src/store/chat/slices/thread/selectors/index.ts +4 -2
  92. package/src/store/chat/slices/topic/action.ts +3 -3
  93. package/src/store/chat/slices/translate/action.ts +54 -41
  94. package/src/tools/web-browsing/ExecutionRuntime/index.ts +5 -2
  95. package/src/tools/web-browsing/Portal/Search/Footer.tsx +11 -9
@@ -16,12 +16,12 @@ describe('Operation Selectors', () => {
16
16
 
17
17
  act(() => {
18
18
  result.current.startOperation({
19
- type: 'generateAI',
19
+ type: 'execAgentRuntime',
20
20
  context: { sessionId: 'session1' },
21
21
  });
22
22
 
23
23
  result.current.startOperation({
24
- type: 'generateAI',
24
+ type: 'execAgentRuntime',
25
25
  context: { sessionId: 'session1' },
26
26
  });
27
27
 
@@ -31,7 +31,9 @@ describe('Operation Selectors', () => {
31
31
  });
32
32
  });
33
33
 
34
- const generateOps = operationSelectors.getOperationsByType('generateAI')(result.current);
34
+ const generateOps = operationSelectors.getOperationsByType('execAgentRuntime')(
35
+ result.current,
36
+ );
35
37
  const reasoningOps = operationSelectors.getOperationsByType('reasoning')(result.current);
36
38
 
37
39
  expect(generateOps).toHaveLength(2);
@@ -48,7 +50,7 @@ describe('Operation Selectors', () => {
48
50
  useChatStore.setState({ activeId: 'session1', activeTopicId: 'topic1' });
49
51
 
50
52
  result.current.startOperation({
51
- type: 'generateAI',
53
+ type: 'execAgentRuntime',
52
54
  context: { sessionId: 'session1', topicId: 'topic1' },
53
55
  });
54
56
 
@@ -59,7 +61,7 @@ describe('Operation Selectors', () => {
59
61
 
60
62
  // Operation in different context
61
63
  result.current.startOperation({
62
- type: 'generateAI',
64
+ type: 'execAgentRuntime',
63
65
  context: { sessionId: 'session2', topicId: 'topic2' },
64
66
  });
65
67
  });
@@ -82,7 +84,7 @@ describe('Operation Selectors', () => {
82
84
 
83
85
  act(() => {
84
86
  opId = result.current.startOperation({
85
- type: 'generateAI',
87
+ type: 'execAgentRuntime',
86
88
  context: { sessionId: 'session1' },
87
89
  }).operationId;
88
90
  });
@@ -103,12 +105,14 @@ describe('Operation Selectors', () => {
103
105
 
104
106
  act(() => {
105
107
  result.current.startOperation({
106
- type: 'generateAI',
108
+ type: 'execAgentRuntime',
107
109
  context: { sessionId: 'session1' },
108
110
  });
109
111
  });
110
112
 
111
- expect(operationSelectors.hasRunningOperationType('generateAI')(result.current)).toBe(true);
113
+ expect(operationSelectors.hasRunningOperationType('execAgentRuntime')(result.current)).toBe(
114
+ true,
115
+ );
112
116
  expect(operationSelectors.hasRunningOperationType('reasoning')(result.current)).toBe(false);
113
117
  });
114
118
  });
@@ -125,7 +129,7 @@ describe('Operation Selectors', () => {
125
129
 
126
130
  act(() => {
127
131
  result.current.startOperation({
128
- type: 'generateAI',
132
+ type: 'execAgentRuntime',
129
133
  context: { sessionId: 'session1', topicId: 'topic1' },
130
134
  });
131
135
  });
@@ -146,7 +150,7 @@ describe('Operation Selectors', () => {
146
150
 
147
151
  act(() => {
148
152
  result.current.startOperation({
149
- type: 'generateAI',
153
+ type: 'execAgentRuntime',
150
154
  context: { sessionId: 'session1', topicId: 'topic1' },
151
155
  });
152
156
  });
@@ -163,7 +167,7 @@ describe('Operation Selectors', () => {
163
167
  useChatStore.setState({ activeId: 'session1', activeTopicId: 'topic1' });
164
168
 
165
169
  result.current.startOperation({
166
- type: 'generateAI',
170
+ type: 'execAgentRuntime',
167
171
  context: { sessionId: 'session1', topicId: 'topic1' },
168
172
  label: 'Generating response...',
169
173
  });
@@ -190,7 +194,7 @@ describe('Operation Selectors', () => {
190
194
 
191
195
  act(() => {
192
196
  result.current.startOperation({
193
- type: 'generateAI',
197
+ type: 'execAgentRuntime',
194
198
  context: { sessionId: 'session1', messageId: 'msg1' },
195
199
  });
196
200
  });
@@ -200,6 +204,122 @@ describe('Operation Selectors', () => {
200
204
  });
201
205
  });
202
206
 
207
+ describe('isMessageCreating', () => {
208
+ it('should return true for user message during sendMessage operation', () => {
209
+ const { result } = renderHook(() => useChatStore());
210
+
211
+ let opId: string;
212
+
213
+ act(() => {
214
+ opId = result.current.startOperation({
215
+ type: 'sendMessage',
216
+ context: { sessionId: 'session1', messageId: 'user_msg_1' },
217
+ }).operationId;
218
+
219
+ // Associate message with operation
220
+ result.current.associateMessageWithOperation('user_msg_1', opId!);
221
+ });
222
+
223
+ expect(operationSelectors.isMessageCreating('user_msg_1')(result.current)).toBe(true);
224
+ expect(operationSelectors.isMessageCreating('other_msg')(result.current)).toBe(false);
225
+ });
226
+
227
+ it('should return true for assistant message during createAssistantMessage operation', () => {
228
+ const { result } = renderHook(() => useChatStore());
229
+
230
+ let opId: string;
231
+
232
+ act(() => {
233
+ opId = result.current.startOperation({
234
+ type: 'createAssistantMessage',
235
+ context: { sessionId: 'session1', messageId: 'assistant_msg_1' },
236
+ }).operationId;
237
+
238
+ // Associate message with operation
239
+ result.current.associateMessageWithOperation('assistant_msg_1', opId!);
240
+ });
241
+
242
+ expect(operationSelectors.isMessageCreating('assistant_msg_1')(result.current)).toBe(true);
243
+ expect(operationSelectors.isMessageCreating('other_msg')(result.current)).toBe(false);
244
+ });
245
+
246
+ it('should return false when operation completes', () => {
247
+ const { result } = renderHook(() => useChatStore());
248
+
249
+ let opId: string;
250
+
251
+ act(() => {
252
+ opId = result.current.startOperation({
253
+ type: 'sendMessage',
254
+ context: { sessionId: 'session1', messageId: 'msg1' },
255
+ }).operationId;
256
+
257
+ result.current.associateMessageWithOperation('msg1', opId!);
258
+ });
259
+
260
+ expect(operationSelectors.isMessageCreating('msg1')(result.current)).toBe(true);
261
+
262
+ act(() => {
263
+ result.current.completeOperation(opId!);
264
+ });
265
+
266
+ expect(operationSelectors.isMessageCreating('msg1')(result.current)).toBe(false);
267
+ });
268
+
269
+ it('should return false for other operation types', () => {
270
+ const { result } = renderHook(() => useChatStore());
271
+
272
+ let opId: string;
273
+
274
+ act(() => {
275
+ // execAgentRuntime should not be considered as "creating"
276
+ opId = result.current.startOperation({
277
+ type: 'execAgentRuntime',
278
+ context: { sessionId: 'session1', messageId: 'msg1' },
279
+ }).operationId;
280
+
281
+ result.current.associateMessageWithOperation('msg1', opId!);
282
+ });
283
+
284
+ expect(operationSelectors.isMessageCreating('msg1')(result.current)).toBe(false);
285
+ });
286
+
287
+ it('should only check sendMessage and createAssistantMessage operations', () => {
288
+ const { result } = renderHook(() => useChatStore());
289
+
290
+ let sendMsgOpId: string;
291
+ let createAssistantOpId: string;
292
+ let toolCallOpId: string;
293
+
294
+ act(() => {
295
+ // sendMessage - should be creating
296
+ sendMsgOpId = result.current.startOperation({
297
+ type: 'sendMessage',
298
+ context: { sessionId: 'session1', messageId: 'user_msg' },
299
+ }).operationId;
300
+ result.current.associateMessageWithOperation('user_msg', sendMsgOpId!);
301
+
302
+ // createAssistantMessage - should be creating
303
+ createAssistantOpId = result.current.startOperation({
304
+ type: 'createAssistantMessage',
305
+ context: { sessionId: 'session1', messageId: 'assistant_msg' },
306
+ }).operationId;
307
+ result.current.associateMessageWithOperation('assistant_msg', createAssistantOpId!);
308
+
309
+ // toolCalling - should NOT be creating
310
+ toolCallOpId = result.current.startOperation({
311
+ type: 'toolCalling',
312
+ context: { sessionId: 'session1', messageId: 'tool_msg' },
313
+ }).operationId;
314
+ result.current.associateMessageWithOperation('tool_msg', toolCallOpId!);
315
+ });
316
+
317
+ expect(operationSelectors.isMessageCreating('user_msg')(result.current)).toBe(true);
318
+ expect(operationSelectors.isMessageCreating('assistant_msg')(result.current)).toBe(true);
319
+ expect(operationSelectors.isMessageCreating('tool_msg')(result.current)).toBe(false);
320
+ });
321
+ });
322
+
203
323
  describe('getOperationContextFromMessage', () => {
204
324
  it('should return operation context from message ID', () => {
205
325
  const { result } = renderHook(() => useChatStore());
@@ -208,7 +328,7 @@ describe('Operation Selectors', () => {
208
328
 
209
329
  act(() => {
210
330
  opId = result.current.startOperation({
211
- type: 'generateAI',
331
+ type: 'execAgentRuntime',
212
332
  context: { sessionId: 'session1', topicId: 'topic1', messageId: 'msg1' },
213
333
  }).operationId;
214
334
 
@@ -225,19 +345,19 @@ describe('Operation Selectors', () => {
225
345
  });
226
346
 
227
347
  describe('backward compatibility selectors', () => {
228
- it('isAIGenerating should work', () => {
348
+ it('isAgentRuntimeRunning should work', () => {
229
349
  const { result } = renderHook(() => useChatStore());
230
350
 
231
- expect(operationSelectors.isAIGenerating(result.current)).toBe(false);
351
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(false);
232
352
 
233
353
  act(() => {
234
354
  result.current.startOperation({
235
- type: 'generateAI',
355
+ type: 'execAgentRuntime',
236
356
  context: { sessionId: 'session1' },
237
357
  });
238
358
  });
239
359
 
240
- expect(operationSelectors.isAIGenerating(result.current)).toBe(true);
360
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(true);
241
361
  });
242
362
 
243
363
  it('isSendingMessage should work', () => {
@@ -269,5 +389,125 @@ describe('Operation Selectors', () => {
269
389
 
270
390
  expect(operationSelectors.isInRAGFlow(result.current)).toBe(true);
271
391
  });
392
+
393
+ it('isMainWindowAgentRuntimeRunning should only detect main window operations', () => {
394
+ const { result } = renderHook(() => useChatStore());
395
+
396
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
397
+
398
+ // Start a main window operation (inThread: false)
399
+ let mainOpId: string;
400
+ act(() => {
401
+ mainOpId = result.current.startOperation({
402
+ type: 'execAgentRuntime',
403
+ context: { sessionId: 'session1' },
404
+ metadata: { inThread: false },
405
+ }).operationId;
406
+ });
407
+
408
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(true);
409
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(true);
410
+
411
+ // Complete main window operation
412
+ act(() => {
413
+ result.current.completeOperation(mainOpId!);
414
+ });
415
+
416
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
417
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(false);
418
+ });
419
+
420
+ it('isMainWindowAgentRuntimeRunning should exclude thread operations', () => {
421
+ const { result } = renderHook(() => useChatStore());
422
+
423
+ // Start a thread operation (inThread: true)
424
+ let threadOpId: string;
425
+ act(() => {
426
+ threadOpId = result.current.startOperation({
427
+ type: 'execAgentRuntime',
428
+ context: { sessionId: 'session1', threadId: 'thread1' },
429
+ metadata: { inThread: true },
430
+ }).operationId;
431
+ });
432
+
433
+ // Thread operation should not affect main window state
434
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
435
+ // But should be detected by general isAgentRuntimeRunning
436
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(true);
437
+
438
+ // Complete thread operation
439
+ act(() => {
440
+ result.current.completeOperation(threadOpId!);
441
+ });
442
+
443
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
444
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(false);
445
+ });
446
+
447
+ it('isMainWindowAgentRuntimeRunning should distinguish between main and thread operations', () => {
448
+ const { result } = renderHook(() => useChatStore());
449
+
450
+ let mainOpId: string;
451
+ let threadOpId: string;
452
+
453
+ // Start both main window and thread operations
454
+ act(() => {
455
+ mainOpId = result.current.startOperation({
456
+ type: 'execAgentRuntime',
457
+ context: { sessionId: 'session1' },
458
+ metadata: { inThread: false },
459
+ }).operationId;
460
+
461
+ threadOpId = result.current.startOperation({
462
+ type: 'execAgentRuntime',
463
+ context: { sessionId: 'session1', threadId: 'thread1' },
464
+ metadata: { inThread: true },
465
+ }).operationId;
466
+ });
467
+
468
+ // Both selectors should detect their respective operations
469
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(true);
470
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(true);
471
+
472
+ // Complete main window operation, thread operation still running
473
+ act(() => {
474
+ result.current.completeOperation(mainOpId!);
475
+ });
476
+
477
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
478
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(true);
479
+
480
+ // Complete thread operation
481
+ act(() => {
482
+ result.current.completeOperation(threadOpId!);
483
+ });
484
+
485
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
486
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(false);
487
+ });
488
+
489
+ it('isMainWindowAgentRuntimeRunning should exclude aborting operations', () => {
490
+ const { result } = renderHook(() => useChatStore());
491
+
492
+ let opId: string;
493
+ act(() => {
494
+ opId = result.current.startOperation({
495
+ type: 'execAgentRuntime',
496
+ context: { sessionId: 'session1' },
497
+ metadata: { inThread: false },
498
+ }).operationId;
499
+ });
500
+
501
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(true);
502
+
503
+ // Mark as aborting
504
+ act(() => {
505
+ result.current.updateOperationMetadata(opId!, { isAborting: true });
506
+ });
507
+
508
+ // Should exclude aborting operations
509
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
510
+ expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(false);
511
+ });
272
512
  });
273
513
  });