@opensumi/ide-ai-native 3.7.2-next-1739859371.0 → 3.7.2-next-1740013940.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
@@ -0,0 +1,174 @@
1
+ import { Autowired, Injectable } from '@opensumi/di';
2
+ import { FileSearchQuickCommandHandler } from '@opensumi/ide-addons/lib/browser/file-search.contribution';
3
+ import { AppConfig } from '@opensumi/ide-core-browser';
4
+ import { CancellationToken, URI } from '@opensumi/ide-core-common';
5
+ import { IEditorDocumentModelRef, IEditorDocumentModelService } from '@opensumi/ide-editor/lib/browser';
6
+ import { IFileServiceClient } from '@opensumi/ide-file-service';
7
+
8
+ @Injectable()
9
+ export class FileHandler {
10
+ private static readonly MAX_FILE_SIZE_BYTES = 2e6;
11
+ private static readonly MAX_LINES = 250;
12
+ private static readonly MAX_CHARS = 1e5;
13
+ private static readonly NEWLINE = '\n';
14
+
15
+ @Autowired(IEditorDocumentModelService)
16
+ protected modelService: IEditorDocumentModelService;
17
+
18
+ @Autowired(FileSearchQuickCommandHandler)
19
+ protected fileSearchQuickCommandHandler: FileSearchQuickCommandHandler;
20
+
21
+ @Autowired(AppConfig)
22
+ protected appConfig: AppConfig;
23
+
24
+ @Autowired(IFileServiceClient)
25
+ protected fileSystemService: IFileServiceClient;
26
+
27
+ async findSimilarFiles(filePath: string, maxResults: number): Promise<string[]> {
28
+ const items = await this.fileSearchQuickCommandHandler.getQueryFiles(filePath, new Set(), CancellationToken.None);
29
+ return items
30
+ .slice(0, maxResults)
31
+ .map((item) => item.getUri()?.codeUri.fsPath)
32
+ .filter(Boolean) as string[];
33
+ }
34
+ // TODO: 错误应该给模型?
35
+ private createFileNotFoundError(filePath: string, similarFiles: string[]): Error {
36
+ const errorMessage =
37
+ similarFiles.length > 0
38
+ ? `Could not find file '${filePath}'. Did you mean one of:\n${similarFiles
39
+ .map((file) => `- ${file}`)
40
+ .join('\n')}`
41
+ : `Could not find file '${filePath}' in the workspace.`;
42
+
43
+ return new Error(
44
+ JSON.stringify({
45
+ clientVisibleErrorMessage: errorMessage,
46
+ modelVisibleErrorMessage: errorMessage,
47
+ actualErrorMessage: `File not found: ${filePath}`,
48
+ }),
49
+ );
50
+ }
51
+
52
+ private createFileTooLargeError(fileSizeMB: string, fileStatsSize: number): Error {
53
+ return new Error(
54
+ JSON.stringify({
55
+ clientVisibleErrorMessage: `File is too large, >${fileSizeMB}MB`,
56
+ modelVisibleErrorMessage: `The file is too large to read, was >${fileSizeMB}MB`,
57
+ actualErrorMessage: `File is too large to read, was >${fileSizeMB}MB, size: ${fileStatsSize} bytes`,
58
+ }),
59
+ );
60
+ }
61
+
62
+ private trimContent(content: string, maxChars: number): string {
63
+ return content.slice(0, maxChars).split(FileHandler.NEWLINE).slice(0, -1).join(FileHandler.NEWLINE);
64
+ }
65
+
66
+ private getLineRange(
67
+ fileParams: {
68
+ startLineOneIndexed?: number;
69
+ endLineOneIndexedInclusive?: number;
70
+ },
71
+ forceLimit: boolean,
72
+ ): { start: number; end: number; didShorten: boolean; didSetDefault: boolean } {
73
+ let start = fileParams.startLineOneIndexed ?? 1;
74
+ let end = fileParams.endLineOneIndexedInclusive ?? start + FileHandler.MAX_LINES - 1;
75
+ let didShorten = false;
76
+ let didSetDefault = false;
77
+
78
+ if (forceLimit) {
79
+ return { start, end, didShorten, didSetDefault };
80
+ }
81
+
82
+ if (fileParams.endLineOneIndexedInclusive === undefined || fileParams.startLineOneIndexed === undefined) {
83
+ start = 1;
84
+ end = FileHandler.MAX_LINES;
85
+ didSetDefault = true;
86
+ } else if (fileParams.endLineOneIndexedInclusive - fileParams.startLineOneIndexed > FileHandler.MAX_LINES) {
87
+ end = fileParams.startLineOneIndexed + FileHandler.MAX_LINES;
88
+ didShorten = true;
89
+ }
90
+
91
+ return { start, end, didShorten, didSetDefault };
92
+ }
93
+
94
+ async readFile(fileParams: {
95
+ relativeWorkspacePath: string;
96
+ readEntireFile: boolean;
97
+ fileIsAllowedToBeReadEntirely?: boolean;
98
+ startLineOneIndexed?: number;
99
+ endLineOneIndexedInclusive?: number;
100
+ }) {
101
+ if (!fileParams) {
102
+ throw new Error('No read file parameters provided. Need to give at least the path.');
103
+ }
104
+
105
+ const uri = new URI(`${this.appConfig.workspaceDir}/${fileParams.relativeWorkspacePath}`);
106
+ if (!uri) {
107
+ const similarFiles = await this.findSimilarFiles(fileParams.relativeWorkspacePath, 3);
108
+ throw this.createFileNotFoundError(fileParams.relativeWorkspacePath, similarFiles);
109
+ }
110
+
111
+ const fileSizeMB = (FileHandler.MAX_FILE_SIZE_BYTES / 1e6).toFixed(2);
112
+ const fileStats = await this.fileSystemService.getFileStat(uri.toString());
113
+
114
+ if (fileStats?.size && fileStats.size > FileHandler.MAX_FILE_SIZE_BYTES) {
115
+ throw this.createFileTooLargeError(fileSizeMB, fileStats.size);
116
+ }
117
+
118
+ let modelReference: IEditorDocumentModelRef | undefined;
119
+ try {
120
+ modelReference = await this.modelService.createModelReference(uri);
121
+ const fileContent = modelReference.instance.getMonacoModel().getValue();
122
+ const fileLines = fileContent.split(FileHandler.NEWLINE);
123
+
124
+ const shouldLimitLines = !(fileParams.readEntireFile && fileParams.fileIsAllowedToBeReadEntirely);
125
+ const shouldForceLimitLines = fileParams.readEntireFile && !fileParams.fileIsAllowedToBeReadEntirely;
126
+ let didShortenCharRange = false;
127
+
128
+ if (shouldLimitLines) {
129
+ const {
130
+ start,
131
+ end,
132
+ didShorten: didShortenLineRange,
133
+ didSetDefault: didSetDefaultLineRange,
134
+ } = this.getLineRange(fileParams, shouldForceLimitLines);
135
+
136
+ const adjustedStart = Math.max(start, 1);
137
+ const adjustedEnd = Math.min(end, fileLines.length);
138
+ let selectedContent = fileLines.slice(adjustedStart - 1, adjustedEnd).join(FileHandler.NEWLINE);
139
+
140
+ if (selectedContent.length > FileHandler.MAX_CHARS) {
141
+ didShortenCharRange = true;
142
+ selectedContent = this.trimContent(selectedContent, FileHandler.MAX_CHARS);
143
+ }
144
+
145
+ return {
146
+ contents: selectedContent,
147
+ didDowngradeToLineRange: shouldForceLimitLines,
148
+ didShortenLineRange,
149
+ didShortenCharRange,
150
+ didSetDefaultLineRange,
151
+ fullFileContents: fileContent,
152
+ startLineOneIndexed: adjustedStart,
153
+ endLineOneIndexedInclusive: adjustedEnd,
154
+ relativeWorkspacePath: fileParams.relativeWorkspacePath,
155
+ };
156
+ }
157
+
158
+ let fullContent = fileContent;
159
+ if (fullContent.length > FileHandler.MAX_CHARS) {
160
+ didShortenCharRange = true;
161
+ fullContent = this.trimContent(fullContent, FileHandler.MAX_CHARS);
162
+ }
163
+
164
+ return {
165
+ contents: fullContent,
166
+ fullFileContents: fileContent,
167
+ didDowngradeToLineRange: false,
168
+ didShortenCharRange,
169
+ };
170
+ } finally {
171
+ modelReference?.dispose();
172
+ }
173
+ }
174
+ }
@@ -0,0 +1,66 @@
1
+ import { z } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+
4
+ import { Autowired } from '@opensumi/di';
5
+ import { Domain } from '@opensumi/ide-core-common';
6
+
7
+ import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
8
+
9
+ import { ListDirHandler } from './handlers/ListDir';
10
+
11
+ const inputSchema = z
12
+ .object({
13
+ relative_workspace_path: z
14
+ .string()
15
+ .describe("Path to list contents of, relative to the workspace root. Ex: './' is the root of the workspace"),
16
+ explanation: z
17
+ .string()
18
+ .describe('One sentence explanation as to why this tool is being used, and how it contributes to the goal.'),
19
+ })
20
+ .transform((data) => ({
21
+ relativeWorkspacePath: data.relative_workspace_path,
22
+ }));
23
+
24
+ @Domain(MCPServerContribution)
25
+ export class ListDirTool implements MCPServerContribution {
26
+ @Autowired(ListDirHandler)
27
+ private readonly listDirHandler: ListDirHandler;
28
+
29
+ registerMCPServer(registry: IMCPServerRegistry): void {
30
+ registry.registerMCPTool(this.getToolDefinition());
31
+ }
32
+
33
+ getToolDefinition(): MCPToolDefinition {
34
+ return {
35
+ name: 'list_dir',
36
+ description:
37
+ 'List the contents of a directory. The quick tool to use for discovery, before using more targeted tools like semantic search or file reading. Useful to try to understand the file structure before diving deeper into specific files. Can be used to explore the codebase.',
38
+ inputSchema: zodToJsonSchema(inputSchema),
39
+ handler: this.handler.bind(this),
40
+ };
41
+ }
42
+
43
+ private async handler(args: z.infer<typeof inputSchema>, logger: MCPLogger) {
44
+ // TODO: 应该添加统一的 validate 逻辑
45
+ args = inputSchema.parse(args);
46
+ const result = await this.listDirHandler.handler(args);
47
+ return {
48
+ content: [
49
+ {
50
+ type: 'text',
51
+ text: `Contents of directory:
52
+
53
+
54
+ ${result.files
55
+ .map(
56
+ (file) =>
57
+ `[${file.isDirectory ? 'dir' : 'file'}] ${file.name} ${
58
+ file.isDirectory ? `(${file.numChildren ?? '?'} items)` : `(${file.size}KB, ${file.numLines} lines)`
59
+ } - ${new Date(file.lastModified).toLocaleString()}`,
60
+ )
61
+ .join('\n')}`,
62
+ },
63
+ ],
64
+ };
65
+ }
66
+ }
@@ -0,0 +1,82 @@
1
+ import { z } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+
4
+ import { Autowired } from '@opensumi/di';
5
+ import { Domain } from '@opensumi/ide-core-common';
6
+
7
+ import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
8
+
9
+ import { FileHandler } from './handlers/ReadFile';
10
+
11
+ const inputSchema = z
12
+ .object({
13
+ relative_workspace_path: z.string().describe('The path of the file to read, relative to the workspace root.'),
14
+ should_read_entire_file: z.boolean().describe('Whether to read the entire file. Defaults to false.'),
15
+ start_line_one_indexed: z.number().describe('The one-indexed line number to start reading from (inclusive).'),
16
+ end_line_one_indexed_inclusive: z.number().describe('The one-indexed line number to end reading at (inclusive).'),
17
+ explanation: z
18
+ .string()
19
+ .describe('One sentence explanation as to why this tool is being used, and how it contributes to the goal.'),
20
+ })
21
+ .transform((data) => ({
22
+ relativeWorkspacePath: data.relative_workspace_path,
23
+ readEntireFile: data.should_read_entire_file,
24
+ startLineOneIndexed: data.start_line_one_indexed,
25
+ endLineOneIndexedInclusive: data.end_line_one_indexed_inclusive,
26
+ }));
27
+
28
+ @Domain(MCPServerContribution)
29
+ export class ReadFileTool implements MCPServerContribution {
30
+ @Autowired(FileHandler)
31
+ private readonly fileHandler: FileHandler;
32
+
33
+ registerMCPServer(registry: IMCPServerRegistry): void {
34
+ registry.registerMCPTool(this.getToolDefinition());
35
+ }
36
+
37
+ getToolDefinition(): MCPToolDefinition {
38
+ return {
39
+ name: 'read_file',
40
+ description: `Read the contents of a file (and the outline).
41
+
42
+ When using this tool to gather information, it's your responsibility to ensure you have the COMPLETE context. Each time you call this command you should:
43
+ 1) Assess if contents viewed are sufficient to proceed with the task.
44
+ 2) Take note of lines not shown.
45
+ 3) If file contents viewed are insufficient, and you suspect they may be in lines not shown, proactively call the tool again to view those lines.
46
+ 4) When in doubt, call this tool again to gather more information. Partial file views may miss critical dependencies, imports, or functionality.
47
+
48
+ If reading a range of lines is not enough, you may choose to read the entire file.
49
+ Reading entire files is often wasteful and slow, especially for large files (i.e. more than a few hundred lines). So you should use this option sparingly.
50
+ Reading the entire file is not allowed in most cases. You are only allowed to read the entire file if it has been edited or manually attached to the conversation by the user.`,
51
+ inputSchema: zodToJsonSchema(inputSchema),
52
+ handler: this.handler.bind(this),
53
+ };
54
+ }
55
+
56
+ private async handler(args: z.infer<typeof inputSchema>, logger: MCPLogger) {
57
+ // TODO: 应该添加统一的 validate 逻辑
58
+ args = inputSchema.parse(args);
59
+ const result = await this.fileHandler.readFile(args);
60
+ return {
61
+ content: [
62
+ {
63
+ type: 'text',
64
+ text: result.didShortenLineRange
65
+ ? `Contents of ${result.relativeWorkspacePath}, from line ${args.startLineOneIndexed}-${
66
+ args.endLineOneIndexedInclusive
67
+ }:
68
+
69
+ \`\`\`
70
+ // ${result.relativeWorkspacePath!.split('/').pop()}
71
+ ${result.contents}
72
+ \`\`\``
73
+ : `Full contents of ${args.relativeWorkspacePath}:
74
+
75
+ \`\`\`
76
+ ${result.contents}
77
+ \`\`\``,
78
+ },
79
+ ],
80
+ };
81
+ }
82
+ }
@@ -0,0 +1,80 @@
1
+ import { z } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+
4
+ import { Autowired, Injectable } from '@opensumi/di';
5
+ import { Domain } from '@opensumi/ide-core-common';
6
+ import { WorkbenchEditorService } from '@opensumi/ide-editor';
7
+
8
+ import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
9
+
10
+ const inputSchema = z.object({
11
+ text: z.string().describe('The new content to replace the entire file with'),
12
+ });
13
+
14
+ @Domain(MCPServerContribution)
15
+ export class ReplaceOpenEditorFileTool implements MCPServerContribution {
16
+ @Autowired(WorkbenchEditorService)
17
+ private readonly editorService: WorkbenchEditorService;
18
+
19
+ registerMCPServer(registry: IMCPServerRegistry): void {
20
+ registry.registerMCPTool(this.getToolDefinition());
21
+ }
22
+
23
+ getToolDefinition(): MCPToolDefinition {
24
+ return {
25
+ name: 'replace_open_in_editor_file_text',
26
+ description:
27
+ 'Replaces the entire content of the currently active file in the IDE editor with specified new text. ' +
28
+ 'Use this tool when you need to completely overwrite the current file\'s content. ' +
29
+ 'Requires a text parameter containing the new content. ' +
30
+ 'Returns one of three possible responses: ' +
31
+ '"ok" if the file content was successfully replaced, ' +
32
+ '"no file open" if no editor is active, ' +
33
+ '"unknown error" if the operation fails.',
34
+ inputSchema: zodToJsonSchema(inputSchema),
35
+ handler: this.handler.bind(this),
36
+ };
37
+ }
38
+
39
+ private async handler(args: z.infer<typeof inputSchema>, logger: MCPLogger) {
40
+ try {
41
+ const editor = this.editorService.currentEditor;
42
+ if (!editor || !editor.monacoEditor) {
43
+ logger.appendLine('Error: No active text editor found');
44
+ return {
45
+ content: [{ type: 'text', text: 'no file open' }],
46
+ isError: true,
47
+ };
48
+ }
49
+
50
+ // Get the model and its full range
51
+ const model = editor.monacoEditor.getModel();
52
+ if (!model) {
53
+ logger.appendLine('Error: No model found for current editor');
54
+ return {
55
+ content: [{ type: 'text', text: 'unknown error' }],
56
+ isError: true,
57
+ };
58
+ }
59
+
60
+ const fullRange = model.getFullModelRange();
61
+
62
+ // Execute the replacement
63
+ editor.monacoEditor.executeEdits('mcp.tool.replace-file', [{
64
+ range: fullRange,
65
+ text: args.text,
66
+ }]);
67
+
68
+ logger.appendLine('Successfully replaced file content');
69
+ return {
70
+ content: [{ type: 'text', text: 'ok' }],
71
+ };
72
+ } catch (error) {
73
+ logger.appendLine(`Error during file content replacement: ${error}`);
74
+ return {
75
+ content: [{ type: 'text', text: 'unknown error' }],
76
+ isError: true,
77
+ };
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,91 @@
1
+ import { z } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+
4
+ import { Autowired, Injectable } from '@opensumi/di';
5
+ import { Domain } from '@opensumi/ide-core-common';
6
+ import { WorkbenchEditorService } from '@opensumi/ide-editor';
7
+ import { Selection, SelectionDirection } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/selection';
8
+
9
+ import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
10
+ import { LiveInlineDiffPreviewer } from '../../widget/inline-diff/inline-diff-previewer';
11
+ import { InlineDiffController } from '../../widget/inline-diff/inline-diff.controller';
12
+
13
+ const inputSchema = z.object({
14
+ text: z.string().describe('The new content to replace the entire file with'),
15
+ });
16
+
17
+ @Domain(MCPServerContribution)
18
+ export class ReplaceOpenEditorFileByDiffPreviewerTool implements MCPServerContribution {
19
+ @Autowired(WorkbenchEditorService)
20
+ private readonly editorService: WorkbenchEditorService;
21
+
22
+ registerMCPServer(registry: IMCPServerRegistry): void {
23
+ registry.registerMCPTool(this.getToolDefinition());
24
+ }
25
+
26
+ getToolDefinition(): MCPToolDefinition {
27
+ return {
28
+ name: 'replace_open_in_editor_file_text',
29
+ description:
30
+ 'Replaces the entire content of the currently active file in the IDE editor with specified new text using diff previewer. ' +
31
+ "Use this tool when you need to completely overwrite the current file's content with diff preview. " +
32
+ 'Requires a text parameter containing the new content. ' +
33
+ 'Returns one of three possible responses: ' +
34
+ '"ok" if the file content was successfully replaced, ' +
35
+ '"no file open" if no editor is active, ' +
36
+ '"unknown error" if the operation fails.',
37
+ inputSchema: zodToJsonSchema(inputSchema),
38
+ handler: this.handler.bind(this),
39
+ };
40
+ }
41
+
42
+ private async handler(args: z.infer<typeof inputSchema>, logger: MCPLogger) {
43
+ try {
44
+ const editor = this.editorService.currentEditor;
45
+ if (!editor || !editor.monacoEditor) {
46
+ logger.appendLine('Error: No active text editor found');
47
+ return {
48
+ content: [{ type: 'text', text: 'no file open' }],
49
+ isError: true,
50
+ };
51
+ }
52
+
53
+ // Get the model and its full range
54
+ const model = editor.monacoEditor.getModel();
55
+ if (!model) {
56
+ logger.appendLine('Error: No model found for current editor');
57
+ return {
58
+ content: [{ type: 'text', text: 'unknown error' }],
59
+ isError: true,
60
+ };
61
+ }
62
+
63
+ const fullRange = model.getFullModelRange();
64
+ const inlineDiffHandler = InlineDiffController.get(editor.monacoEditor)!;
65
+
66
+ // Create diff previewer
67
+ const previewer = inlineDiffHandler.createDiffPreviewer(
68
+ editor.monacoEditor,
69
+ Selection.fromRange(fullRange, SelectionDirection.LTR),
70
+ {
71
+ disposeWhenEditorClosed: false,
72
+ renderRemovedWidgetImmediately: true,
73
+ },
74
+ ) as LiveInlineDiffPreviewer;
75
+
76
+ // Set the new content
77
+ previewer.setValue(args.text);
78
+
79
+ logger.appendLine('Successfully created diff preview with new content');
80
+ return {
81
+ content: [{ type: 'text', text: 'ok' }],
82
+ };
83
+ } catch (error) {
84
+ logger.appendLine(`Error during file content replacement: ${error}`);
85
+ return {
86
+ content: [{ type: 'text', text: 'unknown error' }],
87
+ isError: true,
88
+ };
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,107 @@
1
+ import { z } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+
4
+ import { Autowired } from '@opensumi/di';
5
+ import { AppConfig } from '@opensumi/ide-core-browser';
6
+ import { Deferred, Domain } from '@opensumi/ide-core-common';
7
+ import { ITerminalController, ITerminalGroupViewService } from '@opensumi/ide-terminal-next/lib/common/controller';
8
+
9
+ import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
10
+
11
+ const color = {
12
+ italic: '\x1b[3m',
13
+ reset: '\x1b[0m',
14
+ };
15
+
16
+ const inputSchema = z.object({
17
+ command: z.string().describe('The terminal command to execute'),
18
+ is_background: z.boolean().describe('Whether the command should be run in the background'),
19
+ explanation: z
20
+ .string()
21
+ .describe('One sentence explanation as to why this command needs to be run and how it contributes to the goal.'),
22
+ require_user_approval: z
23
+ .boolean()
24
+ .describe(
25
+ "Whether the user must approve the command before it is executed. Only set this to false if the command is safe and if it matches the user's requirements for commands that should be executed automatically.",
26
+ ),
27
+ });
28
+
29
+ @Domain(MCPServerContribution)
30
+ export class RunTerminalCommandTool implements MCPServerContribution {
31
+ @Autowired(ITerminalController)
32
+ protected readonly terminalController: ITerminalController;
33
+
34
+ @Autowired(AppConfig)
35
+ protected readonly appConfig: AppConfig;
36
+
37
+ @Autowired(ITerminalGroupViewService)
38
+ protected readonly terminalView: ITerminalGroupViewService;
39
+
40
+ private terminalId = 0;
41
+
42
+ registerMCPServer(registry: IMCPServerRegistry): void {
43
+ registry.registerMCPTool(this.getToolDefinition());
44
+ }
45
+
46
+ getToolDefinition(): MCPToolDefinition {
47
+ return {
48
+ name: 'run_terminal_cmd',
49
+ description:
50
+ "PROPOSE a command to run on behalf of the user.\nIf you have this tool, note that you DO have the ability to run commands directly on the USER's system.\n\nAdhere to these rules:\n1. Based on the contents of the conversation, you will be told if you are in the same shell as a previous step or a new shell.\n2. If in a new shell, you should `cd` to the right directory and do necessary setup in addition to running the command.\n3. If in the same shell, the state will persist, no need to do things like `cd` to the same directory.\n4. For ANY commands that would use a pager, you should append ` | cat` to the command (or whatever is appropriate). You MUST do this for: git, less, head, tail, more, etc.\n5. For commands that are long running/expected to run indefinitely until interruption, please run them in the background. To run jobs in the background, set `is_background` to true rather than changing the details of the command.\n6. Dont include any newlines in the command.",
51
+ inputSchema: zodToJsonSchema(inputSchema),
52
+ handler: this.handler.bind(this),
53
+ };
54
+ }
55
+
56
+ getShellLaunchConfig(command: string) {
57
+ return {
58
+ name: `MCP:Terminal_${this.terminalId++}`,
59
+ cwd: this.appConfig.workspaceDir,
60
+ args: ['-c', command],
61
+ };
62
+ }
63
+
64
+ private async handler(args: z.infer<typeof inputSchema>, logger: MCPLogger) {
65
+ if (args.require_user_approval) {
66
+ // FIXME: support approval
67
+ }
68
+
69
+ const terminalClient = await this.terminalController.createTerminalWithWidget({
70
+ config: this.getShellLaunchConfig(args.command),
71
+ closeWhenExited: false,
72
+ });
73
+
74
+ this.terminalController.showTerminalPanel();
75
+
76
+ const result: { type: string; text: string }[] = [];
77
+ const def = new Deferred<{ isError?: boolean; content: { type: string; text: string }[] }>();
78
+
79
+ terminalClient.onOutput((e) => {
80
+ result.push({
81
+ type: 'output',
82
+ text: e.data.toString(),
83
+ });
84
+ });
85
+
86
+ terminalClient.onExit((e) => {
87
+ const isError = e.code !== 0;
88
+ def.resolve({
89
+ isError,
90
+ content: result,
91
+ });
92
+
93
+ terminalClient.term.writeln(
94
+ `\n${color.italic}> Command ${args.command} executed successfully. Terminal will close in ${
95
+ 3000 / 1000
96
+ } seconds.${color.reset}\n`,
97
+ );
98
+
99
+ setTimeout(() => {
100
+ terminalClient.dispose();
101
+ this.terminalView.removeWidget(terminalClient.id);
102
+ }, 3000);
103
+ });
104
+
105
+ return def.promise;
106
+ }
107
+ }
@@ -58,6 +58,66 @@ export const aiNativePreferenceSchema: PreferenceSchema = {
58
58
  type: 'boolean',
59
59
  default: false,
60
60
  },
61
+ [AINativeSettingSectionsId.LLMModelSelection]: {
62
+ type: 'string',
63
+ default: 'deepseek',
64
+ enum: ['deepseek', 'anthropic', 'openai'],
65
+ description: localize('preference.ai.native.llm.model.selection.description'),
66
+ },
67
+ [AINativeSettingSectionsId.DeepseekApiKey]: {
68
+ type: 'string',
69
+ default: '',
70
+ description: localize('preference.ai.native.deepseek.apiKey.description'),
71
+ },
72
+ [AINativeSettingSectionsId.AnthropicApiKey]: {
73
+ type: 'string',
74
+ default: '',
75
+ description: localize('preference.ai.native.anthropic.apiKey.description'),
76
+ },
77
+ [AINativeSettingSectionsId.OpenaiApiKey]: {
78
+ type: 'string',
79
+ default: '',
80
+ description: localize('preference.ai.native.openai.apiKey.description'),
81
+ },
82
+ [AINativeSettingSectionsId.OpenaiBaseURL]: {
83
+ type: 'string',
84
+ default: '',
85
+ description: localize('preference.ai.native.openai.baseURL.description'),
86
+ },
87
+ [AINativeSettingSectionsId.MCPServers]: {
88
+ type: 'array',
89
+ default: [],
90
+ description: localize('preference.ai.native.mcp.servers.description'),
91
+ items: {
92
+ type: 'object',
93
+ required: ['name', 'command', 'args'],
94
+ properties: {
95
+ name: {
96
+ type: 'string',
97
+ description: localize('preference.ai.native.mcp.servers.name.description'),
98
+ },
99
+ command: {
100
+ type: 'string',
101
+ description: localize('preference.ai.native.mcp.servers.command.description'),
102
+ },
103
+ args: {
104
+ type: 'array',
105
+ items: {
106
+ type: 'string',
107
+ },
108
+ description: localize('preference.ai.native.mcp.servers.args.description'),
109
+ },
110
+ env: {
111
+ type: 'object',
112
+ additionalProperties: {
113
+ type: 'string',
114
+ },
115
+ description: localize('preference.ai.native.mcp.servers.env.description'),
116
+ default: {},
117
+ },
118
+ },
119
+ },
120
+ },
61
121
  [AINativeSettingSectionsId.CodeEditsTyping]: {
62
122
  type: 'boolean',
63
123
  default: false,