@opensumi/ide-ai-native 3.7.2-next-1739859371.0 → 3.7.2-next-1739945875.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 (243) hide show
  1. package/lib/browser/ai-core.contribution.d.ts +3 -0
  2. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contribution.js +68 -2
  4. package/lib/browser/ai-core.contribution.js.map +1 -1
  5. package/lib/browser/chat/chat-model.d.ts +2 -2
  6. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  7. package/lib/browser/chat/chat-model.js +16 -5
  8. package/lib/browser/chat/chat-model.js.map +1 -1
  9. package/lib/browser/chat/chat-proxy.service.d.ts +4 -0
  10. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  11. package/lib/browser/chat/chat-proxy.service.js +43 -0
  12. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  13. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  14. package/lib/browser/chat/chat.view.js +29 -2
  15. package/lib/browser/chat/chat.view.js.map +1 -1
  16. package/lib/browser/components/ChatContext/ContextSelector.d.ts +12 -0
  17. package/lib/browser/components/ChatContext/ContextSelector.d.ts.map +1 -0
  18. package/lib/browser/components/ChatContext/ContextSelector.js +113 -0
  19. package/lib/browser/components/ChatContext/ContextSelector.js.map +1 -0
  20. package/lib/browser/components/ChatContext/index.d.ts +4 -0
  21. package/lib/browser/components/ChatContext/index.d.ts.map +1 -0
  22. package/lib/browser/components/ChatContext/index.js +84 -0
  23. package/lib/browser/components/ChatContext/index.js.map +1 -0
  24. package/lib/browser/components/ChatContext/style.module.less +189 -0
  25. package/lib/browser/components/ChatInput.d.ts.map +1 -1
  26. package/lib/browser/components/ChatInput.js.map +1 -1
  27. package/lib/browser/components/ChatReply.d.ts.map +1 -1
  28. package/lib/browser/components/ChatReply.js +25 -0
  29. package/lib/browser/components/ChatReply.js.map +1 -1
  30. package/lib/browser/components/ChatToolRender.d.ts +6 -0
  31. package/lib/browser/components/ChatToolRender.d.ts.map +1 -0
  32. package/lib/browser/components/ChatToolRender.js +53 -0
  33. package/lib/browser/components/ChatToolRender.js.map +1 -0
  34. package/lib/browser/components/ChatToolRender.module.less +86 -0
  35. package/lib/browser/components/components.module.less +32 -31
  36. package/lib/browser/components/utils.d.ts +2 -2
  37. package/lib/browser/context/llm-context.contribution.d.ts +7 -0
  38. package/lib/browser/context/llm-context.contribution.d.ts.map +1 -0
  39. package/lib/browser/context/llm-context.contribution.js +21 -0
  40. package/lib/browser/context/llm-context.contribution.js.map +1 -0
  41. package/lib/browser/context/llm-context.service.d.ts +24 -0
  42. package/lib/browser/context/llm-context.service.d.ts.map +1 -0
  43. package/lib/browser/context/llm-context.service.js +136 -0
  44. package/lib/browser/context/llm-context.service.js.map +1 -0
  45. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts +2 -2
  46. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts.map +1 -1
  47. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  48. package/lib/browser/contrib/intelligent-completions/intelligent-completions.feature.registry.d.ts +4 -0
  49. package/lib/browser/contrib/intelligent-completions/intelligent-completions.feature.registry.d.ts.map +1 -1
  50. package/lib/browser/contrib/intelligent-completions/intelligent-completions.feature.registry.js +7 -0
  51. package/lib/browser/contrib/intelligent-completions/intelligent-completions.feature.registry.js.map +1 -1
  52. package/lib/browser/index.d.ts +11 -3
  53. package/lib/browser/index.d.ts.map +1 -1
  54. package/lib/browser/index.js +56 -3
  55. package/lib/browser/index.js.map +1 -1
  56. package/lib/browser/mcp/mcp-server-proxy.service.d.ts +25 -0
  57. package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -0
  58. package/lib/browser/mcp/mcp-server-proxy.service.js +56 -0
  59. package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -0
  60. package/lib/browser/mcp/mcp-server.feature.registry.d.ts +16 -0
  61. package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -0
  62. package/lib/browser/mcp/mcp-server.feature.registry.js +53 -0
  63. package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -0
  64. package/lib/browser/mcp/mcp-tools-dialog.module.less +44 -0
  65. package/lib/browser/mcp/mcp-tools-dialog.view.d.ts +8 -0
  66. package/lib/browser/mcp/mcp-tools-dialog.view.d.ts.map +1 -0
  67. package/lib/browser/mcp/mcp-tools-dialog.view.js +16 -0
  68. package/lib/browser/mcp/mcp-tools-dialog.view.js.map +1 -0
  69. package/lib/browser/mcp/tools/createNewFileWithText.d.ts +9 -0
  70. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -0
  71. package/lib/browser/mcp/tools/createNewFileWithText.js +83 -0
  72. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -0
  73. package/lib/browser/mcp/tools/findFilesByNameSubstring.d.ts +9 -0
  74. package/lib/browser/mcp/tools/findFilesByNameSubstring.d.ts.map +1 -0
  75. package/lib/browser/mcp/tools/findFilesByNameSubstring.js +92 -0
  76. package/lib/browser/mcp/tools/findFilesByNameSubstring.js.map +1 -0
  77. package/lib/browser/mcp/tools/getCurrentFilePath.d.ts +8 -0
  78. package/lib/browser/mcp/tools/getCurrentFilePath.d.ts.map +1 -0
  79. package/lib/browser/mcp/tools/getCurrentFilePath.js +49 -0
  80. package/lib/browser/mcp/tools/getCurrentFilePath.js.map +1 -0
  81. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts +10 -0
  82. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -0
  83. package/lib/browser/mcp/tools/getDiagnosticsByPath.js +119 -0
  84. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -0
  85. package/lib/browser/mcp/tools/getFileTextByPath.d.ts +9 -0
  86. package/lib/browser/mcp/tools/getFileTextByPath.d.ts.map +1 -0
  87. package/lib/browser/mcp/tools/getFileTextByPath.js +97 -0
  88. package/lib/browser/mcp/tools/getFileTextByPath.js.map +1 -0
  89. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.d.ts +11 -0
  90. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.d.ts.map +1 -0
  91. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js +119 -0
  92. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -0
  93. package/lib/browser/mcp/tools/getOpenEditorFileText.d.ts +8 -0
  94. package/lib/browser/mcp/tools/getOpenEditorFileText.d.ts.map +1 -0
  95. package/lib/browser/mcp/tools/getOpenEditorFileText.js +50 -0
  96. package/lib/browser/mcp/tools/getOpenEditorFileText.js.map +1 -0
  97. package/lib/browser/mcp/tools/getSelectedText.d.ts +8 -0
  98. package/lib/browser/mcp/tools/getSelectedText.d.ts.map +1 -0
  99. package/lib/browser/mcp/tools/getSelectedText.js +57 -0
  100. package/lib/browser/mcp/tools/getSelectedText.js.map +1 -0
  101. package/lib/browser/mcp/tools/handlers/ListDir.d.ts +21 -0
  102. package/lib/browser/mcp/tools/handlers/ListDir.d.ts.map +1 -0
  103. package/lib/browser/mcp/tools/handlers/ListDir.js +112 -0
  104. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -0
  105. package/lib/browser/mcp/tools/handlers/ReadFile.d.ts +47 -0
  106. package/lib/browser/mcp/tools/handlers/ReadFile.d.ts.map +1 -0
  107. package/lib/browser/mcp/tools/handlers/ReadFile.js +147 -0
  108. package/lib/browser/mcp/tools/handlers/ReadFile.js.map +1 -0
  109. package/lib/browser/mcp/tools/listDir.d.ts +8 -0
  110. package/lib/browser/mcp/tools/listDir.d.ts.map +1 -0
  111. package/lib/browser/mcp/tools/listDir.js +65 -0
  112. package/lib/browser/mcp/tools/listDir.js.map +1 -0
  113. package/lib/browser/mcp/tools/readFile.d.ts +8 -0
  114. package/lib/browser/mcp/tools/readFile.d.ts.map +1 -0
  115. package/lib/browser/mcp/tools/readFile.js +82 -0
  116. package/lib/browser/mcp/tools/readFile.js.map +1 -0
  117. package/lib/browser/mcp/tools/replaceOpenEditorFile.d.ts +8 -0
  118. package/lib/browser/mcp/tools/replaceOpenEditorFile.d.ts.map +1 -0
  119. package/lib/browser/mcp/tools/replaceOpenEditorFile.js +79 -0
  120. package/lib/browser/mcp/tools/replaceOpenEditorFile.js.map +1 -0
  121. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.d.ts +8 -0
  122. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.d.ts.map +1 -0
  123. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.js +84 -0
  124. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.js.map +1 -0
  125. package/lib/browser/mcp/tools/runTerminalCmd.d.ts +18 -0
  126. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -0
  127. package/lib/browser/mcp/tools/runTerminalCmd.js +96 -0
  128. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -0
  129. package/lib/browser/preferences/schema.d.ts.map +1 -1
  130. package/lib/browser/preferences/schema.js +60 -0
  131. package/lib/browser/preferences/schema.js.map +1 -1
  132. package/lib/browser/types.d.ts +64 -3
  133. package/lib/browser/types.d.ts.map +1 -1
  134. package/lib/browser/types.js +5 -1
  135. package/lib/browser/types.js.map +1 -1
  136. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.js +1 -1
  137. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.js.map +1 -1
  138. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts.map +1 -1
  139. package/lib/browser/widget/inline-input/inline-input.controller.d.ts.map +1 -1
  140. package/lib/common/index.d.ts +9 -0
  141. package/lib/common/index.d.ts.map +1 -1
  142. package/lib/common/index.js +4 -1
  143. package/lib/common/index.js.map +1 -1
  144. package/lib/common/llm-context.d.ts +37 -0
  145. package/lib/common/llm-context.d.ts.map +1 -0
  146. package/lib/common/llm-context.js +5 -0
  147. package/lib/common/llm-context.js.map +1 -0
  148. package/lib/common/mcp-server-manager.d.ts +40 -0
  149. package/lib/common/mcp-server-manager.d.ts.map +1 -0
  150. package/lib/common/mcp-server-manager.js +6 -0
  151. package/lib/common/mcp-server-manager.js.map +1 -0
  152. package/lib/common/tool-invocation-registry.d.ts +91 -0
  153. package/lib/common/tool-invocation-registry.d.ts.map +1 -0
  154. package/lib/common/tool-invocation-registry.js +90 -0
  155. package/lib/common/tool-invocation-registry.js.map +1 -0
  156. package/lib/common/types.d.ts +17 -0
  157. package/lib/common/types.d.ts.map +1 -1
  158. package/lib/node/anthropic/anthropic-language-model.d.ts +9 -0
  159. package/lib/node/anthropic/anthropic-language-model.d.ts.map +1 -0
  160. package/lib/node/anthropic/anthropic-language-model.js +26 -0
  161. package/lib/node/anthropic/anthropic-language-model.js.map +1 -0
  162. package/lib/node/base-language-model.d.ts +14 -0
  163. package/lib/node/base-language-model.d.ts.map +1 -0
  164. package/lib/node/base-language-model.js +136 -0
  165. package/lib/node/base-language-model.js.map +1 -0
  166. package/lib/node/deepseek/deepseek-language-model.d.ts +9 -0
  167. package/lib/node/deepseek/deepseek-language-model.d.ts.map +1 -0
  168. package/lib/node/deepseek/deepseek-language-model.js +26 -0
  169. package/lib/node/deepseek/deepseek-language-model.js.map +1 -0
  170. package/lib/node/index.d.ts.map +1 -1
  171. package/lib/node/index.js +19 -0
  172. package/lib/node/index.js.map +1 -1
  173. package/lib/node/mcp/sumi-mcp-server.d.ts +91 -0
  174. package/lib/node/mcp/sumi-mcp-server.d.ts.map +1 -0
  175. package/lib/node/mcp/sumi-mcp-server.js +172 -0
  176. package/lib/node/mcp/sumi-mcp-server.js.map +1 -0
  177. package/lib/node/mcp-server-manager-impl.d.ts +27 -0
  178. package/lib/node/mcp-server-manager-impl.d.ts.map +1 -0
  179. package/lib/node/mcp-server-manager-impl.js +127 -0
  180. package/lib/node/mcp-server-manager-impl.js.map +1 -0
  181. package/lib/node/mcp-server.d.ts +207 -0
  182. package/lib/node/mcp-server.d.ts.map +1 -0
  183. package/lib/node/mcp-server.js +91 -0
  184. package/lib/node/mcp-server.js.map +1 -0
  185. package/lib/node/openai/openai-language-model.d.ts +9 -0
  186. package/lib/node/openai/openai-language-model.d.ts.map +1 -0
  187. package/lib/node/openai/openai-language-model.js +29 -0
  188. package/lib/node/openai/openai-language-model.js.map +1 -0
  189. package/package.json +34 -22
  190. package/src/browser/ai-core.contribution.ts +77 -1
  191. package/src/browser/chat/chat-model.ts +24 -6
  192. package/src/browser/chat/chat-proxy.service.ts +42 -0
  193. package/src/browser/chat/chat.view.tsx +59 -6
  194. package/src/browser/components/ChatContext/ContextSelector.tsx +177 -0
  195. package/src/browser/components/ChatContext/index.tsx +135 -0
  196. package/src/browser/components/ChatContext/style.module.less +189 -0
  197. package/src/browser/components/ChatInput.tsx +1 -0
  198. package/src/browser/components/ChatReply.tsx +32 -0
  199. package/src/browser/components/ChatToolRender.module.less +86 -0
  200. package/src/browser/components/ChatToolRender.tsx +77 -0
  201. package/src/browser/components/components.module.less +32 -31
  202. package/src/browser/context/llm-context.contribution.ts +14 -0
  203. package/src/browser/context/llm-context.service.ts +156 -0
  204. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +2 -3
  205. package/src/browser/contrib/intelligent-completions/intelligent-completions.feature.registry.ts +11 -0
  206. package/src/browser/index.ts +68 -4
  207. package/src/browser/mcp/mcp-server-proxy.service.ts +53 -0
  208. package/src/browser/mcp/mcp-server.feature.registry.ts +54 -0
  209. package/src/browser/mcp/mcp-tools-dialog.module.less +44 -0
  210. package/src/browser/mcp/mcp-tools-dialog.view.tsx +24 -0
  211. package/src/browser/mcp/tools/createNewFileWithText.ts +83 -0
  212. package/src/browser/mcp/tools/findFilesByNameSubstring.ts +93 -0
  213. package/src/browser/mcp/tools/getCurrentFilePath.ts +49 -0
  214. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +123 -0
  215. package/src/browser/mcp/tools/getFileTextByPath.ts +97 -0
  216. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +121 -0
  217. package/src/browser/mcp/tools/getOpenEditorFileText.ts +50 -0
  218. package/src/browser/mcp/tools/getSelectedText.ts +57 -0
  219. package/src/browser/mcp/tools/handlers/ListDir.ts +117 -0
  220. package/src/browser/mcp/tools/handlers/ReadFile.ts +174 -0
  221. package/src/browser/mcp/tools/listDir.ts +66 -0
  222. package/src/browser/mcp/tools/readFile.ts +82 -0
  223. package/src/browser/mcp/tools/replaceOpenEditorFile.ts +80 -0
  224. package/src/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.ts +91 -0
  225. package/src/browser/mcp/tools/runTerminalCmd.ts +107 -0
  226. package/src/browser/preferences/schema.ts +60 -0
  227. package/src/browser/types.ts +92 -3
  228. package/src/browser/widget/inline-chat/inline-chat-editor.controller.ts +1 -1
  229. package/src/browser/widget/inline-diff/inline-diff.controller.ts +1 -1
  230. package/src/browser/widget/inline-input/inline-input.controller.ts +1 -1
  231. package/src/common/index.ts +14 -0
  232. package/src/common/llm-context.ts +41 -0
  233. package/src/common/mcp-server-manager.ts +46 -0
  234. package/src/common/tool-invocation-registry.ts +170 -0
  235. package/src/common/types.ts +22 -0
  236. package/src/node/anthropic/anthropic-language-model.ts +25 -0
  237. package/src/node/base-language-model.ts +163 -0
  238. package/src/node/deepseek/deepseek-language-model.ts +25 -0
  239. package/src/node/index.ts +21 -0
  240. package/src/node/mcp/sumi-mcp-server.ts +197 -0
  241. package/src/node/mcp-server-manager-impl.ts +148 -0
  242. package/src/node/mcp-server.ts +126 -0
  243. package/src/node/openai/openai-language-model.ts +25 -0
@@ -73,7 +73,10 @@ import {
73
73
  AI_CHAT_VIEW_ID,
74
74
  AI_MENU_BAR_DEBUG_TOOLBAR,
75
75
  ChatProxyServiceToken,
76
+ ISumiMCPServerBackend,
77
+ SumiMCPServerProxyServicePath,
76
78
  } from '../common';
79
+ import { MCPServerDescription } from '../common/mcp-server-manager';
77
80
 
78
81
  import { ChatProxyService } from './chat/chat-proxy.service';
79
82
  import { AIChatView } from './chat/chat.view';
@@ -97,10 +100,13 @@ import {
97
100
  IChatFeatureRegistry,
98
101
  IChatRenderRegistry,
99
102
  IIntelligentCompletionsRegistry,
103
+ IMCPServerRegistry,
100
104
  IProblemFixProviderRegistry,
101
105
  IRenameCandidatesProviderRegistry,
102
106
  IResolveConflictRegistry,
103
107
  ITerminalProviderRegistry,
108
+ MCPServerContribution,
109
+ TokenMCPServerRegistry,
104
110
  } from './types';
105
111
  import { InlineChatEditorController } from './widget/inline-chat/inline-chat-editor.controller';
106
112
  import { InlineChatFeatureRegistry } from './widget/inline-chat/inline-chat.feature.registry';
@@ -145,6 +151,12 @@ export class AINativeBrowserContribution
145
151
  @Autowired(AINativeCoreContribution)
146
152
  private readonly contributions: ContributionProvider<AINativeCoreContribution>;
147
153
 
154
+ @Autowired(MCPServerContribution)
155
+ private readonly mcpServerContributions: ContributionProvider<MCPServerContribution>;
156
+
157
+ @Autowired(TokenMCPServerRegistry)
158
+ private readonly mcpServerRegistry: IMCPServerRegistry;
159
+
148
160
  @Autowired(InlineChatFeatureRegistryToken)
149
161
  private readonly inlineChatFeatureRegistry: InlineChatFeatureRegistry;
150
162
 
@@ -208,6 +220,9 @@ export class AINativeBrowserContribution
208
220
  @Autowired(CodeActionSingleHandler)
209
221
  private readonly codeActionSingleHandler: CodeActionSingleHandler;
210
222
 
223
+ @Autowired(SumiMCPServerProxyServicePath)
224
+ private readonly sumiMCPServerBackendProxy: ISumiMCPServerBackend;
225
+
211
226
  @Autowired(WorkbenchEditorService)
212
227
  private readonly workbenchEditorService: WorkbenchEditorServiceImpl;
213
228
 
@@ -279,7 +294,7 @@ export class AINativeBrowserContribution
279
294
 
280
295
  onDidStart() {
281
296
  runWhenIdle(() => {
282
- const { supportsRenameSuggestions, supportsInlineChat } = this.aiNativeConfigService.capabilities;
297
+ const { supportsRenameSuggestions, supportsInlineChat, supportsMCP } = this.aiNativeConfigService.capabilities;
283
298
  const prefChatVisibleType = this.preferenceService.getValid(AINativeSettingSectionsId.ChatVisibleType);
284
299
 
285
300
  if (prefChatVisibleType === 'always') {
@@ -295,6 +310,19 @@ export class AINativeBrowserContribution
295
310
  if (supportsInlineChat) {
296
311
  this.codeActionSingleHandler.load();
297
312
  }
313
+
314
+ if (supportsMCP) {
315
+ // 初始化内置 MCP Server
316
+ this.sumiMCPServerBackendProxy.initBuiltinMCPServer();
317
+
318
+ // 从 preferences 获取并初始化外部 MCP Servers
319
+ const mcpServers = this.preferenceService.getValid<MCPServerDescription[]>(
320
+ AINativeSettingSectionsId.MCPServers,
321
+ );
322
+ if (mcpServers && mcpServers.length > 0) {
323
+ this.sumiMCPServerBackendProxy.initExternalMCPServers(mcpServers);
324
+ }
325
+ }
298
326
  });
299
327
  }
300
328
 
@@ -308,6 +336,12 @@ export class AINativeBrowserContribution
308
336
  contribution.registerTerminalProvider?.(this.terminalProviderRegistry);
309
337
  contribution.registerIntelligentCompletionFeature?.(this.intelligentCompletionsRegistry);
310
338
  contribution.registerProblemFixFeature?.(this.problemFixProviderRegistry);
339
+ contribution.registerChatAgentPromptProvider?.();
340
+ });
341
+
342
+ // 注册 Opensumi 框架提供的 MCP Server Tools 能力 (此时的 Opensumi 作为 MCP Server)
343
+ this.mcpServerContributions.getContributions().forEach((contribution) => {
344
+ contribution.registerMCPServer(this.mcpServerRegistry);
311
345
  });
312
346
  }
313
347
 
@@ -379,6 +413,48 @@ export class AINativeBrowserContribution
379
413
  });
380
414
  }
381
415
 
416
+ // Register language model API key settings
417
+ if (this.aiNativeConfigService.capabilities.supportsCustomLLMSettings) {
418
+ registry.registerSettingSection(AI_NATIVE_SETTING_GROUP_ID, {
419
+ title: localize('preference.ai.native.llm.apiSettings.title'),
420
+ preferences: [
421
+ {
422
+ id: AINativeSettingSectionsId.LLMModelSelection,
423
+ localized: 'preference.ai.native.llm.model.selection',
424
+ },
425
+ {
426
+ id: AINativeSettingSectionsId.DeepseekApiKey,
427
+ localized: 'preference.ai.native.deepseek.apiKey',
428
+ },
429
+ {
430
+ id: AINativeSettingSectionsId.AnthropicApiKey,
431
+ localized: 'preference.ai.native.anthropic.apiKey',
432
+ },
433
+ {
434
+ id: AINativeSettingSectionsId.OpenaiApiKey,
435
+ localized: 'preference.ai.native.openai.apiKey',
436
+ },
437
+ {
438
+ id: AINativeSettingSectionsId.OpenaiBaseURL,
439
+ localized: 'preference.ai.native.openai.baseURL',
440
+ },
441
+ ],
442
+ });
443
+ }
444
+
445
+ // Register MCP server settings
446
+ if (this.aiNativeConfigService.capabilities.supportsMCP) {
447
+ registry.registerSettingSection(AI_NATIVE_SETTING_GROUP_ID, {
448
+ title: localize('preference.ai.native.mcp.settings.title'),
449
+ preferences: [
450
+ {
451
+ id: AINativeSettingSectionsId.MCPServers,
452
+ localized: 'preference.ai.native.mcp.servers',
453
+ },
454
+ ],
455
+ });
456
+ }
457
+
382
458
  if (this.aiNativeConfigService.capabilities.supportsInlineChat) {
383
459
  registry.registerSettingSection(AI_NATIVE_SETTING_GROUP_ID, {
384
460
  title: localize('preference.ai.native.inlineChat.title'),
@@ -6,6 +6,7 @@ import {
6
6
  IChatComponent,
7
7
  IChatMarkdownContent,
8
8
  IChatProgress,
9
+ IChatToolContent,
9
10
  IChatTreeData,
10
11
  ILogger,
11
12
  memoize,
@@ -26,7 +27,12 @@ import {
26
27
  import { MsgHistoryManager } from '../model/msg-history-manager';
27
28
  import { IChatSlashCommandItem } from '../types';
28
29
 
29
- export type IChatProgressResponseContent = IChatMarkdownContent | IChatAsyncContent | IChatTreeData | IChatComponent;
30
+ export type IChatProgressResponseContent =
31
+ | IChatMarkdownContent
32
+ | IChatAsyncContent
33
+ | IChatTreeData
34
+ | IChatComponent
35
+ | IChatToolContent;
30
36
 
31
37
  @Injectable({ multiple: true })
32
38
  export class ChatResponseModel extends Disposable {
@@ -81,8 +87,8 @@ export class ChatResponseModel extends Disposable {
81
87
  }
82
88
 
83
89
  updateContent(progress: IChatProgress, quiet?: boolean): void {
90
+ const responsePartLength = this.#responseParts.length - 1;
84
91
  if (progress.kind === 'content' || progress.kind === 'markdownContent') {
85
- const responsePartLength = this.#responseParts.length - 1;
86
92
  const lastResponsePart = this.#responseParts[responsePartLength];
87
93
 
88
94
  if (!lastResponsePart || lastResponsePart.kind !== 'markdownContent') {
@@ -120,11 +126,20 @@ export class ChatResponseModel extends Disposable {
120
126
  }
121
127
  this.#updateResponseText(quiet);
122
128
  });
123
- } else if (progress.kind === 'treeData') {
129
+ } else if (progress.kind === 'treeData' || progress.kind === 'component') {
124
130
  this.#responseParts.push(progress);
125
131
  this.#updateResponseText(quiet);
126
- } else if (progress.kind === 'component') {
127
- this.#responseParts.push(progress);
132
+ } else if (progress.kind === 'toolCall') {
133
+ const find = this.#responseParts.find(
134
+ (item) => item.kind === 'toolCall' && item.content.id === progress.content.id,
135
+ );
136
+ if (find) {
137
+ // @ts-ignore
138
+ find.content = progress.content;
139
+ // this.#responseParts[responsePartLength] = find;
140
+ } else {
141
+ this.#responseParts.push(progress);
142
+ }
128
143
  this.#updateResponseText(quiet);
129
144
  }
130
145
  }
@@ -141,6 +156,9 @@ export class ChatResponseModel extends Disposable {
141
156
  if (part.kind === 'component') {
142
157
  return '';
143
158
  }
159
+ if (part.kind === 'toolCall') {
160
+ return part.content.function.name;
161
+ }
144
162
  return part.content.value;
145
163
  })
146
164
  .join('\n\n');
@@ -258,7 +276,7 @@ export class ChatModel extends Disposable implements IChatModel {
258
276
 
259
277
  const { kind } = progress;
260
278
 
261
- const basicKind = ['content', 'markdownContent', 'asyncContent', 'treeData', 'component'];
279
+ const basicKind = ['content', 'markdownContent', 'asyncContent', 'treeData', 'component', 'toolCall'];
262
280
 
263
281
  if (basicKind.includes(kind)) {
264
282
  request.response.updateContent(progress, quiet);
@@ -1,18 +1,23 @@
1
1
  import { Autowired, Injectable } from '@opensumi/di';
2
+ import { PreferenceService } from '@opensumi/ide-core-browser';
2
3
  import {
3
4
  AIBackSerivcePath,
4
5
  CancellationToken,
6
+ ChatAgentViewServiceToken,
5
7
  ChatFeatureRegistryToken,
6
8
  ChatServiceToken,
7
9
  Deferred,
8
10
  Disposable,
9
11
  IAIBackService,
10
12
  IAIReporter,
13
+ IApplicationService,
11
14
  IChatProgress,
12
15
  uuid,
13
16
  } from '@opensumi/ide-core-common';
17
+ import { AINativeSettingSectionsId } from '@opensumi/ide-core-common/lib/settings/ai-native';
14
18
  import { IChatMessage } from '@opensumi/ide-core-common/lib/types/ai-native';
15
19
  import { MonacoCommandRegistry } from '@opensumi/ide-editor/lib/browser/monaco-contrib/command/command.service';
20
+ import { IMessageService } from '@opensumi/ide-overlay';
16
21
  import { listenReadable } from '@opensumi/ide-utils/lib/stream';
17
22
 
18
23
  import {
@@ -22,6 +27,8 @@ import {
22
27
  IChatAgentService,
23
28
  IChatAgentWelcomeMessage,
24
29
  } from '../../common';
30
+ import { ChatToolRender } from '../components/ChatToolRender';
31
+ import { IChatAgentViewService } from '../types';
25
32
 
26
33
  import { ChatService } from './chat.api.service';
27
34
  import { ChatFeatureRegistry } from './chat.feature.registry';
@@ -52,9 +59,27 @@ export class ChatProxyService extends Disposable {
52
59
  @Autowired(IAIReporter)
53
60
  private readonly aiReporter: IAIReporter;
54
61
 
62
+ @Autowired(ChatAgentViewServiceToken)
63
+ private readonly chatAgentViewService: IChatAgentViewService;
64
+
65
+ @Autowired(PreferenceService)
66
+ private readonly preferenceService: PreferenceService;
67
+
68
+ @Autowired(IApplicationService)
69
+ private readonly applicationService: IApplicationService;
70
+
71
+ @Autowired(IMessageService)
72
+ private readonly messageService: IMessageService;
73
+
55
74
  private chatDeferred: Deferred<void> = new Deferred<void>();
56
75
 
57
76
  public registerDefaultAgent() {
77
+ this.chatAgentViewService.registerChatComponent({
78
+ id: 'toolCall',
79
+ component: ChatToolRender,
80
+ initialProps: {},
81
+ });
82
+
58
83
  this.addDispose(
59
84
  this.chatAgentService.registerAgent({
60
85
  id: ChatProxyService.AGENT_ID,
@@ -79,12 +104,28 @@ export class ChatProxyService extends Disposable {
79
104
  }
80
105
  }
81
106
 
107
+ const model = this.preferenceService.get<string>(AINativeSettingSectionsId.LLMModelSelection);
108
+ let apiKey: string = '';
109
+ let baseURL: string = '';
110
+ if (model === 'deepseek') {
111
+ apiKey = this.preferenceService.get<string>(AINativeSettingSectionsId.DeepseekApiKey, '');
112
+ } else if (model === 'openai') {
113
+ apiKey = this.preferenceService.get<string>(AINativeSettingSectionsId.OpenaiApiKey, '');
114
+ baseURL = this.preferenceService.get<string>(AINativeSettingSectionsId.OpenaiBaseURL, '');
115
+ } else {
116
+ apiKey = this.preferenceService.get<string>(AINativeSettingSectionsId.AnthropicApiKey, '');
117
+ }
118
+
82
119
  const stream = await this.aiBackService.requestStream(
83
120
  prompt,
84
121
  {
85
122
  requestId: request.requestId,
86
123
  sessionId: request.sessionId,
87
124
  history: this.aiChatService.getHistoryMessages(),
125
+ clientId: this.applicationService.clientId,
126
+ apiKey,
127
+ model,
128
+ baseURL,
88
129
  },
89
130
  token,
90
131
  );
@@ -97,6 +138,7 @@ export class ChatProxyService extends Disposable {
97
138
  this.chatDeferred.resolve();
98
139
  },
99
140
  onError: (error) => {
141
+ this.messageService.error(error.message);
100
142
  this.aiReporter.end(request.sessionId + '_' + request.requestId, {
101
143
  message: error.message,
102
144
  success: false,
@@ -1,7 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { MessageList } from 'react-chat-elements';
3
3
 
4
- import { getIcon, useInjectable, useUpdateOnEvent } from '@opensumi/ide-core-browser';
4
+ import {
5
+ AINativeConfigService,
6
+ getIcon,
7
+ useEventEffect,
8
+ useInjectable,
9
+ useUpdateOnEvent,
10
+ } from '@opensumi/ide-core-browser';
5
11
  import { Popover, PopoverPosition } from '@opensumi/ide-core-browser/lib/components';
6
12
  import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
7
13
  import {
@@ -18,13 +24,23 @@ import {
18
24
  IAIReporter,
19
25
  IChatComponent,
20
26
  IChatContent,
27
+ MessageType,
21
28
  localize,
22
29
  uuid,
23
30
  } from '@opensumi/ide-core-common';
24
31
  import { IMainLayoutService } from '@opensumi/ide-main-layout';
32
+ import { IDialogService } from '@opensumi/ide-overlay';
25
33
 
26
34
  import 'react-chat-elements/dist/main.css';
27
- import { AI_CHAT_VIEW_ID, IChatAgentService, IChatInternalService, IChatMessageStructure } from '../../common';
35
+ import {
36
+ AI_CHAT_VIEW_ID,
37
+ IChatAgentService,
38
+ IChatInternalService,
39
+ IChatMessageStructure,
40
+ TokenMCPServerProxyService,
41
+ } from '../../common';
42
+ import { LLMContextService, LLMContextServiceToken } from '../../common/llm-context';
43
+ import { ChatContext } from '../components/ChatContext';
28
44
  import { CodeBlockWrapperInput } from '../components/ChatEditor';
29
45
  import { ChatInput } from '../components/ChatInput';
30
46
  import { ChatMarkdown } from '../components/ChatMarkdown';
@@ -32,7 +48,9 @@ import { ChatNotify, ChatReply } from '../components/ChatReply';
32
48
  import { SlashCustomRender } from '../components/SlashCustomRender';
33
49
  import { MessageData, createMessageByAI, createMessageByUser } from '../components/utils';
34
50
  import { WelcomeMessage } from '../components/WelcomeMsg';
35
- import { ChatViewHeaderRender, TSlashCommandCustomRender } from '../types';
51
+ import { MCPServerProxyService } from '../mcp/mcp-server-proxy.service';
52
+ import { MCPToolsDialog } from '../mcp/mcp-tools-dialog.view';
53
+ import { ChatAgentPromptProvider, ChatViewHeaderRender, TSlashCommandCustomRender } from '../types';
36
54
 
37
55
  import { ChatRequestModel, ChatSlashCommandItemModel } from './chat-model';
38
56
  import { ChatProxyService } from './chat-proxy.service';
@@ -41,7 +59,6 @@ import { ChatFeatureRegistry } from './chat.feature.registry';
41
59
  import { ChatInternalService } from './chat.internal.service';
42
60
  import styles from './chat.module.less';
43
61
  import { ChatRenderRegistry } from './chat.render.registry';
44
-
45
62
  const SCROLL_CLASSNAME = 'chat_scroll';
46
63
 
47
64
  interface TDispatchAction {
@@ -56,10 +73,16 @@ export const AIChatView = () => {
56
73
  const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
57
74
  const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
58
75
  const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
76
+ const contextService = useInjectable<LLMContextService>(LLMContextServiceToken);
77
+ const promptProvider = useInjectable<ChatAgentPromptProvider>(ChatAgentPromptProvider);
78
+
59
79
  const layoutService = useInjectable<IMainLayoutService>(IMainLayoutService);
80
+ const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
60
81
  const msgHistoryManager = aiChatService.sessionModel.history;
61
82
  const containerRef = React.useRef<HTMLDivElement>(null);
62
83
  const chatInputRef = React.useRef<{ setInputValue: (v: string) => void } | null>(null);
84
+ const dialogService = useInjectable<IDialogService>(IDialogService);
85
+ const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
63
86
 
64
87
  const [shortcutCommands, setShortcutCommands] = React.useState<ChatSlashCommandItemModel[]>([]);
65
88
 
@@ -81,6 +104,7 @@ export const AIChatView = () => {
81
104
  const [defaultAgentId, setDefaultAgentId] = React.useState<string>('');
82
105
  const [command, setCommand] = React.useState('');
83
106
  const [theme, setTheme] = React.useState<string | null>(null);
107
+ const [mcpToolsCount, setMcpToolsCount] = React.useState<number>(0);
84
108
 
85
109
  React.useEffect(() => {
86
110
  const featureSlashCommands = chatFeatureRegistry.getAllShortcutSlashCommand();
@@ -478,7 +502,10 @@ export const AIChatView = () => {
478
502
  const { message, agentId, command, reportExtra } = value;
479
503
  const { actionType, actionSource } = reportExtra || {};
480
504
 
481
- const request = aiChatService.createRequest(message, agentId!, command);
505
+ const context = contextService.serialize();
506
+ const fullMessage = await promptProvider.provideContextPrompt(context, message);
507
+
508
+ const request = aiChatService.createRequest(fullMessage, agentId!, command);
482
509
  if (!request) {
483
510
  return;
484
511
  }
@@ -626,6 +653,25 @@ export const AIChatView = () => {
626
653
  };
627
654
  }, [aiChatService.sessionModel]);
628
655
 
656
+ useEventEffect(
657
+ mcpServerProxyService.onChangeMCPServers,
658
+ () => {
659
+ mcpServerProxyService.getAllMCPTools().then((tools) => {
660
+ setMcpToolsCount(tools.length);
661
+ });
662
+ },
663
+ [mcpServerProxyService],
664
+ );
665
+
666
+ const handleShowMCPTools = React.useCallback(async () => {
667
+ const tools = await mcpServerProxyService.getAllMCPTools();
668
+ dialogService.open({
669
+ message: <MCPToolsDialog tools={tools} />,
670
+ type: MessageType.Empty,
671
+ buttons: ['关闭'],
672
+ });
673
+ }, [mcpServerProxyService, dialogService]);
674
+
629
675
  return (
630
676
  <div id={styles.ai_chat_view}>
631
677
  <div className={styles.header_container}>
@@ -643,6 +689,7 @@ export const AIChatView = () => {
643
689
  />
644
690
  </div>
645
691
  <div className={styles.chat_input_wrap}>
692
+ <ChatContext />
646
693
  <div className={styles.header_operate}>
647
694
  <div className={styles.header_operate_left}>
648
695
  {shortcutCommands.map((command) => (
@@ -657,7 +704,13 @@ export const AIChatView = () => {
657
704
  </Popover>
658
705
  ))}
659
706
  </div>
660
- <div className={styles.header_operate_right}></div>
707
+ <div className={styles.header_operate_right}>
708
+ {aiNativeConfigService.capabilities.supportsMCP && (
709
+ <div className={styles.tag} onClick={handleShowMCPTools}>
710
+ {`MCP Tools: ${mcpToolsCount}`}
711
+ </div>
712
+ )}
713
+ </div>
661
714
  </div>
662
715
  <ChatInputWrapperRender
663
716
  onSend={(value, agentId, command) =>
@@ -0,0 +1,177 @@
1
+ import cls from 'classnames';
2
+ import { debounce } from 'lodash';
3
+ import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
4
+
5
+ import { ClickOutside } from '@opensumi/ide-components/lib/click-outside';
6
+ import { AppConfig, LabelService } from '@opensumi/ide-core-browser';
7
+ import { Icon, Input, Scrollbars } from '@opensumi/ide-core-browser/lib/components';
8
+ import { RecentFilesManager } from '@opensumi/ide-core-browser/lib/quick-open/recent-files';
9
+ import { useInjectable } from '@opensumi/ide-core-browser/lib/react-hooks/injectable-hooks';
10
+ import { FileSearchServicePath, IFileSearchService } from '@opensumi/ide-file-search/lib/common/file-search';
11
+ import { URI } from '@opensumi/ide-utils';
12
+
13
+ import { FileContext } from '../../../common/llm-context';
14
+
15
+ import styles from './style.module.less';
16
+
17
+ interface CandidateFileProps {
18
+ uri: URI;
19
+ active: boolean;
20
+ selected: boolean;
21
+ onDidSelect: (val: URI) => void;
22
+ onDidDeselect: (val: URI) => void;
23
+ }
24
+
25
+ const CandidateFile = memo(({ uri, active, selected, onDidSelect, onDidDeselect }: CandidateFileProps) => {
26
+ const labelService = useInjectable<LabelService>(LabelService);
27
+ const appConfig = useInjectable<AppConfig>(AppConfig);
28
+ const itemsRef = useRef<HTMLDivElement | null>(null);
29
+
30
+ useEffect(() => {
31
+ if (active && itemsRef.current) {
32
+ const scrollBehavior: ScrollIntoViewOptions = {
33
+ behavior: 'instant',
34
+ block: 'end',
35
+ };
36
+ itemsRef.current.scrollIntoView(scrollBehavior);
37
+ }
38
+ }, [active, itemsRef.current]);
39
+
40
+ return (
41
+ <div
42
+ className={cls(styles.candidate_file, active && styles.active)}
43
+ ref={(ele) => (itemsRef.current = ele)}
44
+ onClick={() => (selected ? onDidDeselect(uri) : onDidSelect(uri))}
45
+ >
46
+ <Icon iconClass={labelService.getIcon(uri)} />
47
+ <span className={styles.basename}>{uri.path.base}</span>
48
+ <span className={styles.dir}>{URI.file(appConfig.workspaceDir).relative(uri.parent)?.toString()}</span>
49
+ {selected && <Icon icon='check' style={{ marginLeft: 'auto', color: 'var(--editorGutter-addedBackground)' }} />}
50
+ </div>
51
+ );
52
+ });
53
+
54
+ interface ContextSelectorProps {
55
+ addedFiles: FileContext[];
56
+ onDidSelect: (val: URI) => void;
57
+ onDidDeselect: (val: URI) => void;
58
+ onDidClose: () => void;
59
+ }
60
+
61
+ export const ContextSelector = memo(({ addedFiles, onDidDeselect, onDidSelect, onDidClose }: ContextSelectorProps) => {
62
+ const [candidateFiles, updateCandidateFiles] = useState<URI[]>([]);
63
+ const [activeFile, setActiveFile] = useState<URI | null>(null);
64
+ const [searching, toggleSearching] = useState(false);
65
+ const [searchResults, updateSearchResults] = useState<URI[]>([]);
66
+
67
+ const recentFilesManager: RecentFilesManager = useInjectable(RecentFilesManager);
68
+ const appConfig = useInjectable<AppConfig>(AppConfig);
69
+ const searchService = useInjectable<IFileSearchService>(FileSearchServicePath);
70
+
71
+ const container = useRef<HTMLDivElement | null>();
72
+
73
+ useEffect(() => {
74
+ if (candidateFiles.length === 0) {
75
+ recentFilesManager.getMostRecentlyOpenedFiles().then((files) => {
76
+ const addedUris = addedFiles.map((val) => val.uri);
77
+ const recentFiles = files.filter((file) => !addedUris.includes(new URI(file))).map((file) => new URI(file));
78
+ updateCandidateFiles(recentFiles);
79
+ setActiveFile(recentFiles[0] || null);
80
+ });
81
+ }
82
+ }, [addedFiles]);
83
+
84
+ const onDidInput = useCallback(
85
+ debounce((ev) => {
86
+ if (ev.target.value.trim() === '') {
87
+ updateSearchResults([]);
88
+ setActiveFile(candidateFiles[0]);
89
+ return;
90
+ }
91
+
92
+ toggleSearching(true);
93
+ searchService
94
+ .find(ev.target.value, {
95
+ rootUris: [appConfig.workspaceDir],
96
+ limit: 200,
97
+ useGitIgnore: true,
98
+ noIgnoreParent: true,
99
+ fuzzyMatch: true,
100
+ })
101
+ .then((res) => {
102
+ const results = res.map((val) => new URI(val));
103
+ updateSearchResults(results);
104
+ setActiveFile(results[0]);
105
+ })
106
+ .finally(() => {
107
+ toggleSearching(false);
108
+ });
109
+ }, 500),
110
+ [],
111
+ );
112
+
113
+ const onDidKeyDown = useCallback(
114
+ (event) => {
115
+ const { key } = event;
116
+ if (key === 'Escape') {
117
+ onDidClose();
118
+ return;
119
+ }
120
+
121
+ if (key === 'Enter' && activeFile) {
122
+ onDidSelect(activeFile);
123
+ return;
124
+ }
125
+
126
+ const validKeys = ['ArrowUp', 'ArrowDown'];
127
+
128
+ if (!validKeys.includes(key)) {
129
+ return;
130
+ }
131
+
132
+ const files = searchResults.length > 0 ? searchResults : candidateFiles;
133
+
134
+ if (files.length === 0) {
135
+ return;
136
+ }
137
+
138
+ const currentIndex = files.indexOf(activeFile!);
139
+ const safeIndex = currentIndex === -1 ? 0 : currentIndex;
140
+ const lastIndex = files.length - 1;
141
+
142
+ const nextIndex =
143
+ key === 'ArrowUp' ? (safeIndex > 0 ? safeIndex - 1 : lastIndex) : safeIndex < lastIndex ? safeIndex + 1 : 0;
144
+
145
+ setActiveFile(files[nextIndex]);
146
+ },
147
+ [activeFile, searchResults, candidateFiles],
148
+ );
149
+
150
+ return (
151
+ <ClickOutside mouseEvents={['click', 'contextmenu']} onOutsideClick={() => onDidClose()}>
152
+ <div className={styles.context_selector} onKeyDown={onDidKeyDown} tabIndex={-1}>
153
+ <div style={{ padding: '4px' }}>
154
+ <Input placeholder='Search files by name' autoFocus onInput={onDidInput} />
155
+ </div>
156
+ <Scrollbars forwardedRef={(el) => (el ? (container.current = el.ref) : null)}>
157
+ <div className={styles.context_list}>
158
+ {searching && <div className={styles.context_search_layer} />}
159
+ <span className={styles.list_desc}>
160
+ {searchResults.length > 0 ? 'Search Results' : 'Recent Opened Files'}
161
+ </span>
162
+ {(searchResults.length > 0 ? searchResults : candidateFiles).map((file) => (
163
+ <CandidateFile
164
+ key={file.toString()}
165
+ uri={file}
166
+ active={activeFile === file}
167
+ onDidSelect={onDidSelect}
168
+ onDidDeselect={onDidDeselect}
169
+ selected={!!addedFiles.find((val) => val.uri.isEqual(file))}
170
+ />
171
+ ))}
172
+ </div>
173
+ </Scrollbars>
174
+ </div>
175
+ </ClickOutside>
176
+ );
177
+ });