@opensumi/ide-ai-native 3.7.2-next-1740323956.0 → 3.7.2-next-1740366031.0

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 (73) hide show
  1. package/lib/browser/ai-core.contribution.d.ts +3 -1
  2. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contribution.js +13 -1
  4. package/lib/browser/ai-core.contribution.js.map +1 -1
  5. package/lib/browser/chat/chat-manager.service.d.ts +30 -2
  6. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  7. package/lib/browser/chat/chat-manager.service.js +45 -1
  8. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  9. package/lib/browser/chat/chat-model.d.ts +33 -6
  10. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  11. package/lib/browser/chat/chat-model.js +50 -29
  12. package/lib/browser/chat/chat-model.js.map +1 -1
  13. package/lib/browser/chat/chat-proxy.service.d.ts +1 -1
  14. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  15. package/lib/browser/chat/chat-proxy.service.js +1 -1
  16. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  17. package/lib/browser/chat/chat.internal.service.d.ts +4 -2
  18. package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
  19. package/lib/browser/chat/chat.internal.service.js +31 -12
  20. package/lib/browser/chat/chat.internal.service.js.map +1 -1
  21. package/lib/browser/chat/chat.module.less +8 -42
  22. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  23. package/lib/browser/chat/chat.view.js +85 -31
  24. package/lib/browser/chat/chat.view.js.map +1 -1
  25. package/lib/browser/components/ChatHistory.d.ts +21 -0
  26. package/lib/browser/components/ChatHistory.d.ts.map +1 -0
  27. package/lib/browser/components/ChatHistory.js +148 -0
  28. package/lib/browser/components/ChatHistory.js.map +1 -0
  29. package/lib/browser/components/ChatReply.d.ts.map +1 -1
  30. package/lib/browser/components/ChatReply.js +3 -1
  31. package/lib/browser/components/ChatReply.js.map +1 -1
  32. package/lib/browser/components/ChatThinking.js +1 -1
  33. package/lib/browser/components/ChatThinking.js.map +1 -1
  34. package/lib/browser/components/chat-history.css +139 -0
  35. package/lib/browser/components/components.module.less +2 -2
  36. package/lib/browser/components/utils.d.ts +2 -2
  37. package/lib/browser/layout/layout.module.less +1 -1
  38. package/lib/browser/mcp/tools/components/Terminal.d.ts +4 -0
  39. package/lib/browser/mcp/tools/components/Terminal.d.ts.map +1 -0
  40. package/lib/browser/mcp/tools/components/Terminal.js +64 -0
  41. package/lib/browser/mcp/tools/components/Terminal.js.map +1 -0
  42. package/lib/browser/mcp/tools/components/index.module.less +40 -0
  43. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts +43 -0
  44. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -0
  45. package/lib/browser/mcp/tools/handlers/RunCommand.js +104 -0
  46. package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -0
  47. package/lib/browser/mcp/tools/runTerminalCmd.d.ts +1 -6
  48. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
  49. package/lib/browser/mcp/tools/runTerminalCmd.js +9 -55
  50. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  51. package/lib/browser/model/msg-history-manager.d.ts +10 -0
  52. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  53. package/lib/browser/model/msg-history-manager.js +14 -9
  54. package/lib/browser/model/msg-history-manager.js.map +1 -1
  55. package/package.json +23 -23
  56. package/src/browser/ai-core.contribution.ts +13 -1
  57. package/src/browser/chat/chat-manager.service.ts +83 -7
  58. package/src/browser/chat/chat-model.ts +62 -12
  59. package/src/browser/chat/chat-proxy.service.ts +1 -1
  60. package/src/browser/chat/chat.internal.service.ts +23 -5
  61. package/src/browser/chat/chat.module.less +8 -42
  62. package/src/browser/chat/chat.view.tsx +143 -60
  63. package/src/browser/components/ChatHistory.tsx +292 -0
  64. package/src/browser/components/ChatReply.tsx +5 -1
  65. package/src/browser/components/ChatThinking.tsx +1 -1
  66. package/src/browser/components/chat-history.css +139 -0
  67. package/src/browser/components/components.module.less +2 -2
  68. package/src/browser/layout/layout.module.less +1 -1
  69. package/src/browser/mcp/tools/components/Terminal.tsx +97 -0
  70. package/src/browser/mcp/tools/components/index.module.less +40 -0
  71. package/src/browser/mcp/tools/handlers/RunCommand.ts +115 -0
  72. package/src/browser/mcp/tools/runTerminalCmd.ts +7 -68
  73. package/src/browser/model/msg-history-manager.ts +15 -1
@@ -80,9 +80,29 @@ export class ChatResponseModel extends Disposable {
80
80
  return this.#onDidChange.event;
81
81
  }
82
82
 
83
- constructor(requestId: string, public readonly session: IChatModel, public readonly agentId: string) {
83
+ constructor(
84
+ requestId: string,
85
+ public readonly session: IChatModel,
86
+ public readonly agentId: string,
87
+ initParams?: {
88
+ isComplete: boolean;
89
+ isCanceled: boolean;
90
+ responseContents: IChatProgressResponseContent[];
91
+ responseText: string;
92
+ errorDetails: IChatResponseErrorDetails | undefined;
93
+ followups: IChatFollowup[] | undefined;
94
+ },
95
+ ) {
84
96
  super();
85
97
  this.#requestId = requestId;
98
+ if (initParams) {
99
+ this.#responseContents = initParams.responseContents;
100
+ this.#responseText = initParams.responseText;
101
+ this.#isComplete = initParams.isComplete;
102
+ this.#isCanceled = initParams.isCanceled;
103
+ this.#errorDetails = initParams.errorDetails;
104
+ this.#followups = initParams.followups;
105
+ }
86
106
  }
87
107
 
88
108
  updateContent(progress: IChatProgress, quiet?: boolean): void {
@@ -215,6 +235,16 @@ export class ChatResponseModel extends Disposable {
215
235
  this.#followups = followups;
216
236
  this.#onDidChange.fire();
217
237
  }
238
+
239
+ toJSON() {
240
+ return {
241
+ isCanceled: this.isCanceled,
242
+ responseContents: this.responseContents,
243
+ responseText: this.responseText,
244
+ errorDetails: this.errorDetails,
245
+ followups: this.followups,
246
+ };
247
+ }
218
248
  }
219
249
 
220
250
  export class ChatRequestModel implements IChatRequestModel {
@@ -231,19 +261,29 @@ export class ChatRequestModel implements IChatRequestModel {
231
261
  ) {
232
262
  this.#requestId = requestId;
233
263
  }
264
+
265
+ toJSON() {
266
+ return {
267
+ requestId: this.requestId,
268
+ message: this.message,
269
+ response: this.response,
270
+ };
271
+ }
234
272
  }
235
273
 
236
- @Injectable({ multiple: true })
237
274
  export class ChatModel extends Disposable implements IChatModel {
238
275
  private static requestIdPool = 0;
239
276
 
240
- @Autowired(ILogger)
241
- protected readonly logger: ILogger;
242
-
243
- @Autowired(INJECTOR_TOKEN)
244
- injector: Injector;
277
+ constructor(initParams?: { sessionId?: string; history?: MsgHistoryManager; requests?: ChatRequestModel[] }) {
278
+ super();
279
+ this.#sessionId = initParams?.sessionId ?? uuid();
280
+ this.history = initParams?.history ?? new MsgHistoryManager();
281
+ if (initParams?.requests) {
282
+ this.#requests = new Map(initParams.requests.map((r) => [r.requestId, r]));
283
+ }
284
+ }
245
285
 
246
- #sessionId: string = uuid();
286
+ #sessionId: string;
247
287
  get sessionId(): string {
248
288
  return this.#sessionId;
249
289
  }
@@ -253,11 +293,12 @@ export class ChatModel extends Disposable implements IChatModel {
253
293
  return Array.from(this.#requests.values());
254
294
  }
255
295
 
256
- @memoize
257
- get history(): MsgHistoryManager {
258
- return this.injector.get(MsgHistoryManager, []);
296
+ restoreRequests(requests: ChatRequestModel[]): void {
297
+ this.#requests = new Map(requests.map((r) => [r.requestId, r]));
259
298
  }
260
299
 
300
+ readonly history: MsgHistoryManager;
301
+
261
302
  addRequest(message: IChatRequestMessage): ChatRequestModel {
262
303
  const requestId = `${this.sessionId}_request_${ChatModel.requestIdPool++}`;
263
304
  const response = new ChatResponseModel(requestId, this, message.agentId);
@@ -279,7 +320,8 @@ export class ChatModel extends Disposable implements IChatModel {
279
320
  if (basicKind.includes(kind)) {
280
321
  request.response.updateContent(progress, quiet);
281
322
  } else {
282
- this.logger.error(`Couldn't handle progress: ${JSON.stringify(progress)}`);
323
+ // eslint-disable-next-line no-console
324
+ console.error(`Couldn't handle progress: ${JSON.stringify(progress)}`);
283
325
  }
284
326
  }
285
327
 
@@ -291,6 +333,14 @@ export class ChatModel extends Disposable implements IChatModel {
291
333
  super.dispose();
292
334
  this.#requests.forEach((r) => r.response.dispose());
293
335
  }
336
+
337
+ toJSON() {
338
+ return {
339
+ sessionId: this.sessionId,
340
+ history: this.history,
341
+ requests: this.requests,
342
+ };
343
+ }
294
344
  }
295
345
 
296
346
  @Injectable({ multiple: true })
@@ -39,7 +39,7 @@ import { ChatFeatureRegistry } from './chat.feature.registry';
39
39
  @Injectable()
40
40
  export class ChatProxyService extends Disposable {
41
41
  // 避免和插件注册的 agent id 冲突
42
- static readonly AGENT_ID = 'Default_Chat_Agent_' + uuid(6);
42
+ static readonly AGENT_ID = 'Default_Chat_Agent';
43
43
 
44
44
  @Autowired(IChatAgentService)
45
45
  private readonly chatAgentService: IChatAgentService;
@@ -43,9 +43,15 @@ export class ChatInternalService extends Disposable {
43
43
  return this.#sessionModel;
44
44
  }
45
45
 
46
- constructor() {
47
- super();
48
- this.#sessionModel = this.chatManagerService.startSession();
46
+ init() {
47
+ this.chatManagerService.onStorageInit(() => {
48
+ const sessions = this.chatManagerService.getSessions();
49
+ if (sessions.length > 0) {
50
+ this.activateSession(sessions[sessions.length - 1].sessionId);
51
+ } else {
52
+ this.createSessionModel();
53
+ }
54
+ });
49
55
  }
50
56
 
51
57
  public setLatestRequestId(id: string): void {
@@ -70,12 +76,24 @@ export class ChatInternalService extends Disposable {
70
76
  this._onCancelRequest.fire();
71
77
  }
72
78
 
73
- clearSessionModel() {
74
- this.chatManagerService.clearSession(this.#sessionModel.sessionId);
79
+ createSessionModel() {
75
80
  this.#sessionModel = this.chatManagerService.startSession();
76
81
  this._onChangeSession.fire(this.#sessionModel.sessionId);
77
82
  }
78
83
 
84
+ clearSessionModel(sessionId?: string) {
85
+ sessionId = sessionId || this.#sessionModel.sessionId;
86
+ this.chatManagerService.clearSession(sessionId);
87
+ if (sessionId === this.#sessionModel.sessionId) {
88
+ this.#sessionModel = this.chatManagerService.startSession();
89
+ }
90
+ this._onChangeSession.fire(this.#sessionModel.sessionId);
91
+ }
92
+
93
+ getSessions() {
94
+ return this.chatManagerService.getSessions();
95
+ }
96
+
79
97
  activateSession(sessionId: string) {
80
98
  const targetSession = this.chatManagerService.getSession(sessionId);
81
99
  if (!targetSession) {
@@ -4,7 +4,7 @@
4
4
  height: 100%;
5
5
  border-radius: 12px;
6
6
  overflow: hidden;
7
- font-size: 14px;
7
+ font-size: 12px;
8
8
  user-select: text;
9
9
 
10
10
  border-top-left-radius: 12px;
@@ -75,53 +75,14 @@
75
75
 
76
76
  .header_container {
77
77
  height: 36px;
78
- display: flex;
79
- justify-content: space-between;
80
- align-items: center;
81
- padding: 12px 0 12px 16px;
78
+ padding: 8px 8px 8px 16px;
82
79
  box-sizing: border-box;
83
80
  background-color: var(--editorGroupHeader-tabsBackground);
84
81
  user-select: none;
85
82
 
86
- .left {
83
+ .header {
87
84
  display: flex;
88
85
  align-items: center;
89
- height: 16px;
90
- color: var(--design-title-foreground);
91
- font-size: 12px;
92
-
93
- .ai_avatar_icon {
94
- width: 18px;
95
- height: 18px;
96
- }
97
-
98
- .avatar_icon_normal {
99
- width: 18px;
100
- height: 18px;
101
- font-size: 14px !important;
102
- }
103
-
104
- & > * {
105
- margin-right: 8px;
106
- }
107
- }
108
-
109
- .right {
110
- display: flex;
111
- align-items: center;
112
- padding-right: 10px;
113
- .popover_icon {
114
- margin-left: 2px;
115
- }
116
-
117
- .action_btn {
118
- display: flex;
119
- align-items: center;
120
- justify-content: center;
121
- width: 22px;
122
- height: 22px;
123
- padding: 0;
124
- }
125
86
  }
126
87
  }
127
88
 
@@ -317,3 +278,8 @@
317
278
  }
318
279
  }
319
280
  }
281
+
282
+ .chat_history {
283
+ width: calc(100% - 60px);
284
+ color: var(--design-text-foreground);
285
+ }
@@ -21,6 +21,7 @@ import {
21
21
  ChatRenderRegistryToken,
22
22
  ChatServiceToken,
23
23
  Disposable,
24
+ DisposableCollection,
24
25
  IAIReporter,
25
26
  IChatComponent,
26
27
  IChatContent,
@@ -43,6 +44,7 @@ import { LLMContextService, LLMContextServiceToken } from '../../common/llm-cont
43
44
  import { ChatAgentPromptProvider } from '../../common/prompts/context-prompt-provider';
44
45
  import { ChatContext } from '../components/ChatContext';
45
46
  import { CodeBlockWrapperInput } from '../components/ChatEditor';
47
+ import ChatHistory, { IChatHistoryItem } from '../components/ChatHistory';
46
48
  import { ChatInput } from '../components/ChatInput';
47
49
  import { ChatMarkdown } from '../components/ChatMarkdown';
48
50
  import { ChatNotify, ChatReply } from '../components/ChatReply';
@@ -67,6 +69,8 @@ interface TDispatchAction {
67
69
  payload?: MessageData[];
68
70
  }
69
71
 
72
+ const MAX_TITLE_LENGTH = 100;
73
+
70
74
  export const AIChatView = () => {
71
75
  const aiChatService = useInjectable<ChatInternalService>(IChatInternalService);
72
76
  const chatApiService = useInjectable<ChatService>(ChatServiceToken);
@@ -78,12 +82,9 @@ export const AIChatView = () => {
78
82
  const promptProvider = useInjectable<ChatAgentPromptProvider>(ChatAgentPromptProvider);
79
83
 
80
84
  const layoutService = useInjectable<IMainLayoutService>(IMainLayoutService);
81
- const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
82
85
  const msgHistoryManager = aiChatService.sessionModel.history;
83
86
  const containerRef = React.useRef<HTMLDivElement>(null);
84
87
  const chatInputRef = React.useRef<{ setInputValue: (v: string) => void } | null>(null);
85
- const dialogService = useInjectable<IDialogService>(IDialogService);
86
- const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
87
88
 
88
89
  const [shortcutCommands, setShortcutCommands] = React.useState<ChatSlashCommandItemModel[]>([]);
89
90
 
@@ -105,7 +106,6 @@ export const AIChatView = () => {
105
106
  const [defaultAgentId, setDefaultAgentId] = React.useState<string>('');
106
107
  const [command, setCommand] = React.useState('');
107
108
  const [theme, setTheme] = React.useState<string | null>(null);
108
- const [mcpToolsCount, setMcpToolsCount] = React.useState<number>(0);
109
109
 
110
110
  React.useEffect(() => {
111
111
  const featureSlashCommands = chatFeatureRegistry.getAllShortcutSlashCommand();
@@ -448,7 +448,9 @@ export const AIChatView = () => {
448
448
  setLoading(false);
449
449
  }}
450
450
  onRegenerate={() => {
451
- aiChatService.sendRequest(request, true);
451
+ if (request) {
452
+ aiChatService.sendRequest(request, true);
453
+ }
452
454
  }}
453
455
  msgId={msgId}
454
456
  />
@@ -611,7 +613,8 @@ export const AIChatView = () => {
611
613
  });
612
614
  } else if (msg.role === ChatMessageRole.Assistant && msg.requestId) {
613
615
  const request = aiChatService.sessionModel.getRequest(msg.requestId)!;
614
- if (!request.response.isComplete) {
616
+ // 从storage恢复时,request为undefined
617
+ if (request && !request.response.isComplete) {
615
618
  setLoading(true);
616
619
  }
617
620
  await renderReply({
@@ -621,7 +624,7 @@ export const AIChatView = () => {
621
624
  agentId: msg.agentId,
622
625
  command: msg.agentCommand,
623
626
  startTime: msg.replyStartTime!,
624
- request: aiChatService.sessionModel.getRequest(msg.requestId)!,
627
+ request,
625
628
  });
626
629
  } else if (msg.role === ChatMessageRole.Assistant && msg.content) {
627
630
  await renderSimpleMarkdownReply({
@@ -654,25 +657,6 @@ export const AIChatView = () => {
654
657
  };
655
658
  }, [aiChatService.sessionModel]);
656
659
 
657
- useEventEffect(
658
- mcpServerProxyService.onChangeMCPServers,
659
- () => {
660
- mcpServerProxyService.getAllMCPTools().then((tools) => {
661
- setMcpToolsCount(tools.length);
662
- });
663
- },
664
- [mcpServerProxyService],
665
- );
666
-
667
- const handleShowMCPTools = React.useCallback(async () => {
668
- const tools = await mcpServerProxyService.getAllMCPTools();
669
- dialogService.open({
670
- message: <MCPToolsDialog tools={tools} />,
671
- type: MessageType.Empty,
672
- buttons: ['关闭'],
673
- });
674
- }, [mcpServerProxyService, dialogService]);
675
-
676
660
  return (
677
661
  <div id={styles.ai_chat_view}>
678
662
  <div className={styles.header_container}>
@@ -705,13 +689,6 @@ export const AIChatView = () => {
705
689
  </Popover>
706
690
  ))}
707
691
  </div>
708
- <div className={styles.header_operate_right}>
709
- {aiNativeConfigService.capabilities.supportsMCP && (
710
- <div className={styles.tag} onClick={handleShowMCPTools}>
711
- {`MCP Tools: ${mcpToolsCount}`}
712
- </div>
713
- )}
714
- </div>
715
692
  </div>
716
693
  <ChatInputWrapperRender
717
694
  onSend={(value, agentId, command) =>
@@ -750,44 +727,150 @@ export function DefaultChatViewHeader({
750
727
  handleClear: () => any;
751
728
  handleCloseChatView: () => any;
752
729
  }) {
753
- const aiAssistantName = React.useMemo(() => localize('aiNative.chat.ai.assistant.name'), []);
730
+ const dialogService = useInjectable<IDialogService>(IDialogService);
731
+ const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
732
+ const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
733
+ const aiChatService = useInjectable<ChatInternalService>(IChatInternalService);
734
+ const [historyList, setHistoryList] = React.useState<IChatHistoryItem[]>([]);
735
+ const [currentTitle, setCurrentTitle] = React.useState<string>('');
736
+ const handleNewChat = React.useCallback(() => {
737
+ if (aiChatService.sessionModel.history.getMessages().length > 0) {
738
+ aiChatService.createSessionModel();
739
+ }
740
+ }, [aiChatService]);
741
+ const handleHistoryItemSelect = React.useCallback(
742
+ (item: IChatHistoryItem) => {
743
+ aiChatService.activateSession(item.id);
744
+ },
745
+ [aiChatService],
746
+ );
747
+ const handleHistoryItemDelete = React.useCallback(
748
+ (item: IChatHistoryItem) => {
749
+ aiChatService.clearSessionModel(item.id);
750
+ },
751
+ [aiChatService],
752
+ );
753
+
754
+ const handleShowMCPTools = React.useCallback(async () => {
755
+ const tools = await mcpServerProxyService.getAllMCPTools();
756
+ dialogService.open({
757
+ message: <MCPToolsDialog tools={tools} />,
758
+ type: MessageType.Empty,
759
+ buttons: ['关闭'],
760
+ });
761
+ }, [mcpServerProxyService, dialogService]);
762
+
763
+ React.useEffect(() => {
764
+ const getHistoryList = () => {
765
+ const currentMessages = aiChatService.sessionModel.history.getMessages();
766
+ setCurrentTitle(
767
+ currentMessages.length > 0
768
+ ? currentMessages[currentMessages.length - 1].content.slice(0, MAX_TITLE_LENGTH)
769
+ : '',
770
+ );
771
+ setHistoryList(
772
+ aiChatService.getSessions().map((session) => {
773
+ const history = session.history;
774
+ const messages = history.getMessages();
775
+ const title = messages.length > 0 ? messages[0].content.slice(0, MAX_TITLE_LENGTH) : '';
776
+ const updatedAt = messages.length > 0 ? messages[messages.length - 1].replyStartTime || 0 : 0;
777
+ // const loading = session.requests[session.requests.length - 1]?.response.isComplete;
778
+ return {
779
+ id: session.sessionId,
780
+ title,
781
+ updatedAt,
782
+ // TODO: 后续支持
783
+ loading: false,
784
+ };
785
+ }),
786
+ );
787
+ };
788
+ getHistoryList();
789
+ const toDispose = new DisposableCollection();
790
+ const sessionListenIds = new Set<string>();
791
+ toDispose.push(
792
+ aiChatService.onChangeSession((sessionId) => {
793
+ getHistoryList();
794
+ if (sessionListenIds.has(sessionId)) {
795
+ return;
796
+ }
797
+ sessionListenIds.add(sessionId);
798
+ toDispose.push(
799
+ aiChatService.sessionModel.history.onMessageChange(() => {
800
+ getHistoryList();
801
+ }),
802
+ );
803
+ }),
804
+ );
805
+ toDispose.push(
806
+ aiChatService.sessionModel.history.onMessageChange(() => {
807
+ getHistoryList();
808
+ }),
809
+ );
810
+ return () => {
811
+ toDispose.dispose();
812
+ };
813
+ }, [aiChatService]);
754
814
 
755
815
  return (
756
- <>
757
- <div className={styles.left}>
758
- <span className={styles.title}>{aiAssistantName}</span>
759
- </div>
760
- <div className={styles.right}>
816
+ <div className={styles.header}>
817
+ <ChatHistory
818
+ className={styles.chat_history}
819
+ // 取对话名称
820
+ currentId={aiChatService.sessionModel.sessionId}
821
+ title={currentTitle || localize('aiNative.chat.ai.assistant.name')}
822
+ historyList={historyList}
823
+ onNewChat={handleNewChat}
824
+ onHistoryItemSelect={handleHistoryItemSelect}
825
+ onHistoryItemDelete={handleHistoryItemDelete}
826
+ onHistoryItemChange={() => {}}
827
+ />
828
+ <Popover
829
+ overlayClassName={styles.popover_icon}
830
+ id={'ai-chat-header-clear'}
831
+ title={localize('aiNative.operate.clear.title')}
832
+ >
833
+ <EnhanceIcon
834
+ wrapperClassName={styles.action_btn}
835
+ className={getIcon('clear')}
836
+ onClick={handleClear}
837
+ tabIndex={0}
838
+ role='button'
839
+ ariaLabel={localize('aiNative.operate.clear.title')}
840
+ />
841
+ </Popover>
842
+ {aiNativeConfigService.capabilities.supportsMCP && (
761
843
  <Popover
762
844
  overlayClassName={styles.popover_icon}
763
- id={'ai-chat-header-clear'}
764
- title={localize('aiNative.operate.clear.title')}
765
- >
766
- <EnhanceIcon
767
- wrapperClassName={styles.action_btn}
768
- className={getIcon('clear')}
769
- onClick={handleClear}
770
- tabIndex={0}
771
- role='button'
772
- ariaLabel={localize('aiNative.operate.clear.title')}
773
- />
774
- </Popover>
775
- <Popover
776
- overlayClassName={styles.popover_icon}
777
- id={'ai-chat-header-close'}
845
+ id={'ai-chat-header-tools'}
778
846
  position={PopoverPosition.left}
779
- title={localize('aiNative.operate.close.title')}
847
+ title={localize('aiNative.operate.tools.title')}
780
848
  >
781
849
  <EnhanceIcon
782
850
  wrapperClassName={styles.action_btn}
783
- className={getIcon('window-close')}
784
- onClick={handleCloseChatView}
851
+ className={getIcon('menubar-tool')}
852
+ onClick={handleShowMCPTools}
785
853
  tabIndex={0}
786
854
  role='button'
787
- ariaLabel={localize('aiNative.operate.close.title')}
855
+ ariaLabel={localize('aiNative.operate.tools.title')}
788
856
  />
789
857
  </Popover>
790
- </div>
791
- </>
858
+ )}
859
+ <Popover
860
+ overlayClassName={styles.popover_icon}
861
+ id={'ai-chat-header-close'}
862
+ position={PopoverPosition.left}
863
+ title={localize('aiNative.operate.close.title')}
864
+ >
865
+ <EnhanceIcon
866
+ wrapperClassName={styles.action_btn}
867
+ className={getIcon('window-close')}
868
+ onClick={handleCloseChatView}
869
+ tabIndex={0}
870
+ role='button'
871
+ ariaLabel={localize('aiNative.operate.close.title')}
872
+ />
873
+ </Popover>
874
+ </div>
792
875
  );
793
876
  }