@lobehub/lobehub 2.0.0-next.104 → 2.0.0-next.105

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 (31) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/apps/desktop/package.json +2 -2
  3. package/changelog/v1.json +5 -0
  4. package/package.json +9 -3
  5. package/packages/database/src/repositories/knowledge/index.ts +5 -8
  6. package/packages/model-bank/src/aiModels/moonshot.ts +46 -0
  7. package/packages/model-runtime/src/core/contextBuilders/openai.ts +1 -1
  8. package/packages/model-runtime/src/providers/moonshot/index.ts +17 -4
  9. package/packages/types/src/user/settings/keyVaults.ts +0 -68
  10. package/packages/utils/src/client/parserPlaceholder.ts +1 -1
  11. package/src/services/__tests__/_auth.test.ts +1 -4
  12. package/src/services/_auth.ts +2 -3
  13. package/src/services/_header.ts +1 -8
  14. package/src/store/chat/agents/__tests__/createAgentExecutors/call-llm.test.ts +18 -0
  15. package/src/store/chat/agents/__tests__/createAgentExecutors/call-tool.test.ts +40 -11
  16. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/assertions.ts +3 -0
  17. package/src/store/chat/agents/__tests__/createAgentExecutors/request-human-approve.test.ts +15 -0
  18. package/src/store/chat/agents/__tests__/createAgentExecutors/resolve-aborted-tools.test.ts +37 -11
  19. package/src/store/chat/agents/createAgentExecutors.ts +22 -13
  20. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +4 -8
  21. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +16 -2
  22. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +5 -1
  23. package/src/store/chat/slices/builtinTool/actions/search.ts +5 -1
  24. package/src/store/chat/slices/message/actions/publicApi.ts +10 -2
  25. package/src/store/chat/slices/message/actions/query.ts +17 -4
  26. package/src/store/chat/slices/operation/__tests__/selectors.test.ts +93 -5
  27. package/src/store/chat/slices/operation/selectors.ts +16 -3
  28. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +24 -18
  29. package/src/store/user/slices/settings/selectors/keyVaults.ts +0 -5
  30. package/src/features/ChatList/Error/AccessCodeForm.tsx +0 -63
  31. package/src/services/__tests__/share.test.ts +0 -61
@@ -57,6 +57,9 @@ describe('resolve_aborted_tools executor', () => {
57
57
  topicId: 'test-topic',
58
58
  parentId: parentMessage.id,
59
59
  }),
60
+ expect.objectContaining({
61
+ operationId: expect.any(String),
62
+ }),
60
63
  );
61
64
  });
62
65
 
@@ -114,6 +117,9 @@ describe('resolve_aborted_tools executor', () => {
114
117
  pluginIntervention: { status: 'aborted' },
115
118
  tool_call_id: toolCall.id,
116
119
  }),
120
+ expect.objectContaining({
121
+ operationId: expect.any(String),
122
+ }),
117
123
  );
118
124
  });
119
125
  });
@@ -193,17 +199,22 @@ describe('resolve_aborted_tools executor', () => {
193
199
  });
194
200
 
195
201
  // Then
196
- expect(mockStore.optimisticCreateMessage).toHaveBeenCalledWith({
197
- role: 'tool',
198
- content: 'Tool execution was aborted by user.',
199
- plugin: toolCall,
200
- pluginIntervention: { status: 'aborted' },
201
- tool_call_id: 'tool_abc',
202
- parentId: 'msg_parent',
203
- sessionId: 'sess_123',
204
- topicId: 'topic_456',
205
- threadId: undefined,
206
- });
202
+ expect(mockStore.optimisticCreateMessage).toHaveBeenCalledWith(
203
+ expect.objectContaining({
204
+ role: 'tool',
205
+ content: 'Tool execution was aborted by user.',
206
+ plugin: toolCall,
207
+ pluginIntervention: { status: 'aborted' },
208
+ tool_call_id: 'tool_abc',
209
+ parentId: 'msg_parent',
210
+ sessionId: 'sess_123',
211
+ topicId: 'topic_456',
212
+ threadId: undefined,
213
+ }),
214
+ expect.objectContaining({
215
+ operationId: expect.any(String),
216
+ }),
217
+ );
207
218
  });
208
219
 
209
220
  it('should preserve tool payload details', async () => {
@@ -240,6 +251,9 @@ describe('resolve_aborted_tools executor', () => {
240
251
  expect.objectContaining({
241
252
  plugin: toolCall,
242
253
  }),
254
+ expect.objectContaining({
255
+ operationId: expect.any(String),
256
+ }),
243
257
  );
244
258
  });
245
259
 
@@ -264,6 +278,9 @@ describe('resolve_aborted_tools executor', () => {
264
278
  expect.objectContaining({
265
279
  topicId: undefined,
266
280
  }),
281
+ expect.objectContaining({
282
+ operationId: expect.any(String),
283
+ }),
267
284
  );
268
285
  });
269
286
  });
@@ -481,6 +498,9 @@ describe('resolve_aborted_tools executor', () => {
481
498
  expect.objectContaining({
482
499
  plugin: toolCall,
483
500
  }),
501
+ expect.objectContaining({
502
+ operationId: expect.any(String),
503
+ }),
484
504
  );
485
505
  });
486
506
 
@@ -571,6 +591,9 @@ describe('resolve_aborted_tools executor', () => {
571
591
  type: 'builtin',
572
592
  }),
573
593
  }),
594
+ expect.objectContaining({
595
+ operationId: expect.any(String),
596
+ }),
574
597
  );
575
598
  });
576
599
 
@@ -606,6 +629,9 @@ describe('resolve_aborted_tools executor', () => {
606
629
  type: 'default',
607
630
  }),
608
631
  }),
632
+ expect.objectContaining({
633
+ operationId: expect.any(String),
634
+ }),
609
635
  );
610
636
  });
611
637
 
@@ -89,16 +89,19 @@ export const createAgentExecutors = (context: {
89
89
  llmPayload.parentMessageId = context.parentId;
90
90
  }
91
91
  // Create assistant message (following server-side pattern)
92
- const assistantMessageItem = await context.get().optimisticCreateMessage({
93
- content: LOADING_FLAT,
94
- model: llmPayload.model,
95
- parentId: llmPayload.parentMessageId,
96
- provider: llmPayload.provider,
97
- role: 'assistant',
98
- sessionId: opContext.sessionId!,
99
- threadId: opContext.threadId,
100
- topicId: opContext.topicId ?? undefined,
101
- });
92
+ const assistantMessageItem = await context.get().optimisticCreateMessage(
93
+ {
94
+ content: LOADING_FLAT,
95
+ model: llmPayload.model,
96
+ parentId: llmPayload.parentMessageId,
97
+ provider: llmPayload.provider,
98
+ role: 'assistant',
99
+ sessionId: opContext.sessionId!,
100
+ threadId: opContext.threadId,
101
+ topicId: opContext.topicId ?? undefined,
102
+ },
103
+ { operationId: context.operationId },
104
+ );
102
105
 
103
106
  if (!assistantMessageItem) {
104
107
  throw new Error('Failed to create assistant message');
@@ -371,7 +374,9 @@ export const createAgentExecutors = (context: {
371
374
  topicId: opContext.topicId ?? undefined,
372
375
  };
373
376
 
374
- const createPromise = context.get().optimisticCreateMessage(toolMessageParams);
377
+ const createPromise = context
378
+ .get()
379
+ .optimisticCreateMessage(toolMessageParams, { operationId: createToolMsgOpId });
375
380
  context.get().updateOperationMetadata(createToolMsgOpId, {
376
381
  createMessagePromise: createPromise,
377
382
  });
@@ -632,7 +637,9 @@ export const createAgentExecutors = (context: {
632
637
  topicId: opContext.topicId ?? undefined,
633
638
  };
634
639
 
635
- const createResult = await context.get().optimisticCreateMessage(toolMessageParams);
640
+ const createResult = await context
641
+ .get()
642
+ .optimisticCreateMessage(toolMessageParams, { operationId: context.operationId });
636
643
 
637
644
  if (!createResult) {
638
645
  log(
@@ -709,7 +716,9 @@ export const createAgentExecutors = (context: {
709
716
  topicId: opContext.topicId ?? undefined,
710
717
  };
711
718
 
712
- const createResult = await context.get().optimisticCreateMessage(toolMessageParams);
719
+ const createResult = await context
720
+ .get()
721
+ .optimisticCreateMessage(toolMessageParams, { operationId: context.operationId });
713
722
 
714
723
  if (createResult) {
715
724
  log(
@@ -261,6 +261,10 @@ export const conversationLifecycle: StateCreator<
261
261
 
262
262
  summaryTitle().catch(console.error);
263
263
 
264
+ // Complete sendMessage operation here - message creation is done
265
+ // execAgentRuntime is a separate operation (child) that handles AI response generation
266
+ get().completeOperation(operationId);
267
+
264
268
  // Get the current messages to generate AI response
265
269
  const displayMessages = displayMessageSelectors.activeDisplayMessages(get());
266
270
 
@@ -287,16 +291,8 @@ export const conversationLifecycle: StateCreator<
287
291
  if (userFiles.length > 0) {
288
292
  await getAgentStoreState().addFilesToAgent(userFiles, false);
289
293
  }
290
-
291
- // Complete operation on success
292
- get().completeOperation(operationId);
293
294
  } catch (e) {
294
295
  console.error(e);
295
- // Fail operation on error
296
- get().failOperation(operationId, {
297
- type: e instanceof Error ? e.name : 'unknown_error',
298
- message: e instanceof Error ? e.message : 'AI generation failed',
299
- });
300
296
  } finally {
301
297
  if (data.topicId) get().internal_updateTopicLoading(data.topicId, false);
302
298
  }
@@ -241,9 +241,18 @@ describe('search actions', () => {
241
241
  it('should update arguments and perform search', async () => {
242
242
  const { result } = renderHook(() => useChatStore());
243
243
  const spy = vi.spyOn(result.current, 'search');
244
- const { triggerSearchAgain } = result.current;
245
244
 
246
245
  const messageId = 'test-message-id';
246
+ const operationId = 'op_test';
247
+
248
+ // Set up messageOperationMap so triggerSearchAgain can get operationId
249
+ useChatStore.setState({
250
+ messageOperationMap: {
251
+ [messageId]: operationId,
252
+ },
253
+ });
254
+
255
+ const { triggerSearchAgain } = result.current;
247
256
  const query: SearchQuery = {
248
257
  query: 'test query',
249
258
  };
@@ -252,7 +261,12 @@ describe('search actions', () => {
252
261
  await triggerSearchAgain(messageId, query, { aiSummary: true });
253
262
  });
254
263
 
255
- expect(result.current.optimisticUpdatePluginArguments).toHaveBeenCalledWith(messageId, query);
264
+ expect(result.current.optimisticUpdatePluginArguments).toHaveBeenCalledWith(
265
+ messageId,
266
+ query,
267
+ false,
268
+ { operationId },
269
+ );
256
270
  expect(spy).toHaveBeenCalledWith(messageId, query, true);
257
271
  });
258
272
  });
@@ -108,7 +108,11 @@ export const localSystemSlice: StateCreator<
108
108
  },
109
109
 
110
110
  reSearchLocalFiles: async (id, params) => {
111
- await get().optimisticUpdatePluginArguments(id, params);
111
+ // Get operationId from messageOperationMap to ensure proper context isolation
112
+ const operationId = get().messageOperationMap[id];
113
+ const context = operationId ? { operationId } : undefined;
114
+
115
+ await get().optimisticUpdatePluginArguments(id, params, false, context);
112
116
 
113
117
  return get().searchLocalFiles(id, params);
114
118
  },
@@ -276,7 +276,11 @@ export const searchSlice: StateCreator<
276
276
  },
277
277
 
278
278
  triggerSearchAgain: async (id, data, options) => {
279
- await get().optimisticUpdatePluginArguments(id, data);
279
+ // Get operationId from messageOperationMap to ensure proper context isolation
280
+ const operationId = get().messageOperationMap[id];
281
+ const context = operationId ? { operationId } : undefined;
282
+
283
+ await get().optimisticUpdatePluginArguments(id, data, false, context);
280
284
 
281
285
  await get().search(id, data, options?.aiSummary);
282
286
  },
@@ -167,14 +167,22 @@ export const messagePublicApi: StateCreator<
167
167
  const message = dbMessageSelectors.getDbMessageById(id)(get());
168
168
  if (!message || message.role !== 'tool') return;
169
169
 
170
+ // Get operationId from messageOperationMap to ensure proper context isolation
171
+ const operationId = get().messageOperationMap[id];
172
+ const context = operationId ? { operationId } : undefined;
173
+
170
174
  const removeToolInAssistantMessage = async () => {
171
175
  if (!message.parentId) return;
172
- await get().optimisticRemoveToolFromAssistantMessage(message.parentId, message.tool_call_id);
176
+ await get().optimisticRemoveToolFromAssistantMessage(
177
+ message.parentId,
178
+ message.tool_call_id,
179
+ context,
180
+ );
173
181
  };
174
182
 
175
183
  await Promise.all([
176
184
  // 1. remove tool message
177
- get().optimisticDeleteMessage(id),
185
+ get().optimisticDeleteMessage(id, context),
178
186
  // 2. remove the tool item in the assistant tools
179
187
  removeToolInAssistantMessage(),
180
188
  ]);
@@ -32,6 +32,7 @@ export interface MessageQueryAction {
32
32
  messages: UIChatMessage[],
33
33
  params?: {
34
34
  action?: any;
35
+ operationId?: string;
35
36
  sessionId?: string;
36
37
  topicId?: string | null;
37
38
  },
@@ -66,10 +67,22 @@ export const messageQuery: StateCreator<
66
67
  },
67
68
 
68
69
  replaceMessages: (messages, params) => {
69
- const messagesKey = messageMapKey(
70
- params?.sessionId ?? get().activeId,
71
- params?.topicId ?? get().activeTopicId,
72
- );
70
+ let sessionId: string;
71
+ let topicId: string | null | undefined;
72
+
73
+ // Priority 1: Get context from operation if operationId is provided
74
+ if (params?.operationId) {
75
+ const { sessionId: opSessionId, topicId: opTopicId } =
76
+ get().internal_getSessionContext(params);
77
+ sessionId = opSessionId;
78
+ topicId = opTopicId;
79
+ } else {
80
+ // Priority 2: Use explicit sessionId/topicId or fallback to global state
81
+ sessionId = params?.sessionId ?? get().activeId;
82
+ topicId = params?.topicId ?? get().activeTopicId;
83
+ }
84
+
85
+ const messagesKey = messageMapKey(sessionId, topicId);
73
86
 
74
87
  // Get raw messages from dbMessagesMap and apply reducer
75
88
  const nextDbMap = { ...get().dbMessagesMap, [messagesKey]: messages };
@@ -393,6 +393,11 @@ describe('Operation Selectors', () => {
393
393
  it('isMainWindowAgentRuntimeRunning should only detect main window operations', () => {
394
394
  const { result } = renderHook(() => useChatStore());
395
395
 
396
+ // Set active context
397
+ act(() => {
398
+ useChatStore.setState({ activeId: 'session1', activeTopicId: undefined });
399
+ });
400
+
396
401
  expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
397
402
 
398
403
  // Start a main window operation (inThread: false)
@@ -400,7 +405,7 @@ describe('Operation Selectors', () => {
400
405
  act(() => {
401
406
  mainOpId = result.current.startOperation({
402
407
  type: 'execAgentRuntime',
403
- context: { sessionId: 'session1' },
408
+ context: { sessionId: 'session1', topicId: null },
404
409
  metadata: { inThread: false },
405
410
  }).operationId;
406
411
  });
@@ -420,12 +425,17 @@ describe('Operation Selectors', () => {
420
425
  it('isMainWindowAgentRuntimeRunning should exclude thread operations', () => {
421
426
  const { result } = renderHook(() => useChatStore());
422
427
 
428
+ // Set active context
429
+ act(() => {
430
+ useChatStore.setState({ activeId: 'session1', activeTopicId: undefined });
431
+ });
432
+
423
433
  // Start a thread operation (inThread: true)
424
434
  let threadOpId: string;
425
435
  act(() => {
426
436
  threadOpId = result.current.startOperation({
427
437
  type: 'execAgentRuntime',
428
- context: { sessionId: 'session1', threadId: 'thread1' },
438
+ context: { sessionId: 'session1', topicId: null, threadId: 'thread1' },
429
439
  metadata: { inThread: true },
430
440
  }).operationId;
431
441
  });
@@ -447,6 +457,11 @@ describe('Operation Selectors', () => {
447
457
  it('isMainWindowAgentRuntimeRunning should distinguish between main and thread operations', () => {
448
458
  const { result } = renderHook(() => useChatStore());
449
459
 
460
+ // Set active context
461
+ act(() => {
462
+ useChatStore.setState({ activeId: 'session1', activeTopicId: undefined });
463
+ });
464
+
450
465
  let mainOpId: string;
451
466
  let threadOpId: string;
452
467
 
@@ -454,13 +469,13 @@ describe('Operation Selectors', () => {
454
469
  act(() => {
455
470
  mainOpId = result.current.startOperation({
456
471
  type: 'execAgentRuntime',
457
- context: { sessionId: 'session1' },
472
+ context: { sessionId: 'session1', topicId: null },
458
473
  metadata: { inThread: false },
459
474
  }).operationId;
460
475
 
461
476
  threadOpId = result.current.startOperation({
462
477
  type: 'execAgentRuntime',
463
- context: { sessionId: 'session1', threadId: 'thread1' },
478
+ context: { sessionId: 'session1', topicId: null, threadId: 'thread1' },
464
479
  metadata: { inThread: true },
465
480
  }).operationId;
466
481
  });
@@ -489,11 +504,16 @@ describe('Operation Selectors', () => {
489
504
  it('isMainWindowAgentRuntimeRunning should exclude aborting operations', () => {
490
505
  const { result } = renderHook(() => useChatStore());
491
506
 
507
+ // Set active context
508
+ act(() => {
509
+ useChatStore.setState({ activeId: 'session1', activeTopicId: undefined });
510
+ });
511
+
492
512
  let opId: string;
493
513
  act(() => {
494
514
  opId = result.current.startOperation({
495
515
  type: 'execAgentRuntime',
496
- context: { sessionId: 'session1' },
516
+ context: { sessionId: 'session1', topicId: null },
497
517
  metadata: { inThread: false },
498
518
  }).operationId;
499
519
  });
@@ -509,5 +529,73 @@ describe('Operation Selectors', () => {
509
529
  expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
510
530
  expect(operationSelectors.isAgentRuntimeRunning(result.current)).toBe(false);
511
531
  });
532
+
533
+ it('isMainWindowAgentRuntimeRunning should only detect operations in current active topic', () => {
534
+ const { result } = renderHook(() => useChatStore());
535
+
536
+ // Set active session and topic
537
+ act(() => {
538
+ useChatStore.setState({ activeId: 'session1', activeTopicId: 'topic1' });
539
+ });
540
+
541
+ let topic1OpId: string;
542
+ let topic2OpId: string;
543
+
544
+ // Start operation in topic1 (current active topic)
545
+ act(() => {
546
+ topic1OpId = result.current.startOperation({
547
+ type: 'execAgentRuntime',
548
+ context: { sessionId: 'session1', topicId: 'topic1' },
549
+ metadata: { inThread: false },
550
+ }).operationId;
551
+ });
552
+
553
+ // Should detect operation in current topic
554
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(true);
555
+
556
+ // Start operation in topic2 (different topic)
557
+ act(() => {
558
+ topic2OpId = result.current.startOperation({
559
+ type: 'execAgentRuntime',
560
+ context: { sessionId: 'session1', topicId: 'topic2' },
561
+ metadata: { inThread: false },
562
+ }).operationId;
563
+ });
564
+
565
+ // Should still only detect topic1 operation (current active topic)
566
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(true);
567
+
568
+ // Switch to topic2
569
+ act(() => {
570
+ useChatStore.setState({ activeTopicId: 'topic2' });
571
+ });
572
+
573
+ // Should now detect topic2 operation
574
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(true);
575
+
576
+ // Complete topic2 operation
577
+ act(() => {
578
+ result.current.completeOperation(topic2OpId!);
579
+ });
580
+
581
+ // Should not detect any operation in topic2 now
582
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
583
+
584
+ // Switch back to topic1
585
+ act(() => {
586
+ useChatStore.setState({ activeTopicId: 'topic1' });
587
+ });
588
+
589
+ // Should detect topic1 operation again
590
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(true);
591
+
592
+ // Complete topic1 operation
593
+ act(() => {
594
+ result.current.completeOperation(topic1OpId!);
595
+ });
596
+
597
+ // Should not detect any operation now
598
+ expect(operationSelectors.isMainWindowAgentRuntimeRunning(result.current)).toBe(false);
599
+ });
512
600
  });
513
601
  });
@@ -180,14 +180,27 @@ const isAgentRuntimeRunning = (s: ChatStoreState): boolean => {
180
180
  /**
181
181
  * Check if agent runtime is running in main window only
182
182
  * Used for main window UI state (e.g., send button loading)
183
- * Excludes thread operations to prevent cross-contamination
183
+ * Excludes thread operations and operations from other topics to prevent cross-contamination
184
184
  */
185
185
  const isMainWindowAgentRuntimeRunning = (s: ChatStoreState): boolean => {
186
186
  const operationIds = s.operationsByType['execAgentRuntime'] || [];
187
+
187
188
  return operationIds.some((id) => {
188
189
  const op = s.operations[id];
189
- // Only include main window operations (not thread)
190
- return op && op.status === 'running' && !op.metadata.isAborting && !op.metadata.inThread;
190
+ if (!op || op.status !== 'running' || op.metadata.isAborting || op.metadata.inThread) {
191
+ return false;
192
+ }
193
+
194
+ // Session must match
195
+ if (s.activeId !== op.context.sessionId) return false;
196
+
197
+ // Topic comparison: normalize null/undefined (both mean "default topic")
198
+ // activeTopicId can be null (initial state) or undefined (after topic operations)
199
+ // Operation context topicId can also be null or undefined
200
+ const activeTopicId = s.activeTopicId ?? null;
201
+ const opTopicId = op.context.topicId ?? null;
202
+
203
+ return activeTopicId === opTopicId;
191
204
  });
192
205
  };
193
206
 
@@ -32,6 +32,7 @@ export interface PluginOptimisticUpdateAction {
32
32
  id: string,
33
33
  value: T,
34
34
  replace?: boolean,
35
+ context?: OptimisticUpdateContext,
35
36
  ) => Promise<void>;
36
37
 
37
38
  /**
@@ -106,7 +107,7 @@ export const pluginOptimisticUpdate: StateCreator<
106
107
  }
107
108
  },
108
109
 
109
- optimisticUpdatePluginArguments: async (id, value, replace = false) => {
110
+ optimisticUpdatePluginArguments: async (id, value, replace = false, context) => {
110
111
  const { refreshMessages } = get();
111
112
  const toolMessage = displayMessageSelectors.getDisplayMessageById(id)(get());
112
113
  if (!toolMessage || !toolMessage?.tool_call_id) return;
@@ -121,20 +122,22 @@ export const pluginOptimisticUpdate: StateCreator<
121
122
  if (isEqual(prevJson, nextValue)) return;
122
123
 
123
124
  // optimistic update
124
- get().internal_dispatchMessage({
125
- id,
126
- type: 'updateMessagePlugin',
127
- value: { arguments: JSON.stringify(nextValue) },
128
- });
125
+ get().internal_dispatchMessage(
126
+ { id, type: 'updateMessagePlugin', value: { arguments: JSON.stringify(nextValue) } },
127
+ context,
128
+ );
129
129
 
130
130
  // 同样需要更新 assistantMessage 的 pluginArguments
131
131
  if (assistantMessage) {
132
- get().internal_dispatchMessage({
133
- id: assistantMessage.id,
134
- type: 'updateMessageTools',
135
- tool_call_id: toolMessage?.tool_call_id,
136
- value: { arguments: JSON.stringify(nextValue) },
137
- });
132
+ get().internal_dispatchMessage(
133
+ {
134
+ id: assistantMessage.id,
135
+ type: 'updateMessageTools',
136
+ tool_call_id: toolMessage?.tool_call_id,
137
+ value: { arguments: JSON.stringify(nextValue) },
138
+ },
139
+ context,
140
+ );
138
141
  assistantMessage = displayMessageSelectors.getDisplayMessageById(assistantMessage?.id)(get());
139
142
  }
140
143
 
@@ -183,11 +186,14 @@ export const pluginOptimisticUpdate: StateCreator<
183
186
  if (!assistantMessage) return;
184
187
 
185
188
  const { internal_dispatchMessage, internal_refreshToUpdateMessageTools } = get();
186
- internal_dispatchMessage({
187
- type: 'addMessageTool',
188
- value: tool,
189
- id: assistantMessage.id,
190
- });
189
+ internal_dispatchMessage(
190
+ {
191
+ type: 'addMessageTool',
192
+ value: tool,
193
+ id: assistantMessage.id,
194
+ },
195
+ context,
196
+ );
191
197
 
192
198
  await internal_refreshToUpdateMessageTools(id, context);
193
199
  },
@@ -199,7 +205,7 @@ export const pluginOptimisticUpdate: StateCreator<
199
205
  const { internal_dispatchMessage, internal_refreshToUpdateMessageTools } = get();
200
206
 
201
207
  // optimistic update
202
- internal_dispatchMessage({ type: 'deleteMessageTool', tool_call_id, id: message.id });
208
+ internal_dispatchMessage({ type: 'deleteMessageTool', tool_call_id, id: message.id }, context);
203
209
 
204
210
  // update the message tools
205
211
  await internal_refreshToUpdateMessageTools(id, context);
@@ -10,12 +10,7 @@ const getVaultByProvider = (provider: string) => (s: UserStore) =>
10
10
  // @ts-ignore
11
11
  (keyVaultsSettings(s)[provider] || {}) as any;
12
12
 
13
- const password = (s: UserStore) => keyVaultsSettings(s).password || '';
14
-
15
13
  export const keyVaultsConfigSelectors = {
16
14
  getVaultByProvider,
17
-
18
15
  keyVaultsSettings,
19
-
20
- password,
21
16
  };
@@ -1,63 +0,0 @@
1
- import { Button, InputPassword } from '@lobehub/ui';
2
- import { memo } from 'react';
3
- import { useTranslation } from 'react-i18next';
4
- import { Flexbox } from 'react-layout-kit';
5
-
6
- import { useChatStore } from '@/store/chat';
7
- import { useUserStore } from '@/store/user';
8
- import { keyVaultsConfigSelectors } from '@/store/user/selectors';
9
-
10
- import { FormAction } from './style';
11
-
12
- interface AccessCodeFormProps {
13
- id: string;
14
- }
15
-
16
- const AccessCodeForm = memo<AccessCodeFormProps>(({ id }) => {
17
- const { t } = useTranslation('error');
18
- const [password, updateKeyVaults] = useUserStore((s) => [
19
- keyVaultsConfigSelectors.password(s),
20
- s.updateKeyVaults,
21
- ]);
22
- const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
23
-
24
- return (
25
- <>
26
- <FormAction
27
- avatar={'🗳'}
28
- description={t('unlock.password.description')}
29
- title={t('unlock.password.title')}
30
- >
31
- <InputPassword
32
- autoComplete={'new-password'}
33
- onChange={(e) => {
34
- updateKeyVaults({ password: e.target.value });
35
- }}
36
- placeholder={t('unlock.password.placeholder')}
37
- value={password}
38
- variant={'filled'}
39
- />
40
- </FormAction>
41
- <Flexbox gap={12}>
42
- <Button
43
- onClick={() => {
44
- resend(id);
45
- deleteMessage(id);
46
- }}
47
- type={'primary'}
48
- >
49
- {t('unlock.confirm')}
50
- </Button>
51
- <Button
52
- onClick={() => {
53
- deleteMessage(id);
54
- }}
55
- >
56
- {t('unlock.closeMessage')}
57
- </Button>
58
- </Flexbox>
59
- </>
60
- );
61
- });
62
-
63
- export default AccessCodeForm;