@opensumi/ide-ai-native 3.7.2-next-1740066619.0 → 3.7.2-next-1740323956.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 (250) hide show
  1. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  2. package/lib/browser/ai-core.contribution.js +4 -0
  3. package/lib/browser/ai-core.contribution.js.map +1 -1
  4. package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
  5. package/lib/browser/chat/chat-agent.service.js +6 -0
  6. package/lib/browser/chat/chat-agent.service.js.map +1 -1
  7. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  8. package/lib/browser/chat/chat-model.js +5 -23
  9. package/lib/browser/chat/chat-model.js.map +1 -1
  10. package/lib/browser/chat/chat-proxy.service.d.ts +1 -0
  11. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  12. package/lib/browser/chat/chat-proxy.service.js +8 -1
  13. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  14. package/lib/browser/chat/chat.internal.service.d.ts +4 -0
  15. package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
  16. package/lib/browser/chat/chat.internal.service.js +10 -1
  17. package/lib/browser/chat/chat.internal.service.js.map +1 -1
  18. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  19. package/lib/browser/chat/chat.view.js +2 -2
  20. package/lib/browser/chat/chat.view.js.map +1 -1
  21. package/lib/browser/components/ChatContext/index.d.ts.map +1 -1
  22. package/lib/browser/components/ChatContext/index.js +18 -2
  23. package/lib/browser/components/ChatContext/index.js.map +1 -1
  24. package/lib/browser/components/ChatContext/style.module.less +12 -0
  25. package/lib/browser/components/ChatEditor.d.ts +1 -0
  26. package/lib/browser/components/ChatEditor.d.ts.map +1 -1
  27. package/lib/browser/components/ChatEditor.js +3 -3
  28. package/lib/browser/components/ChatEditor.js.map +1 -1
  29. package/lib/browser/components/ChatMarkdown.d.ts +1 -0
  30. package/lib/browser/components/ChatMarkdown.d.ts.map +1 -1
  31. package/lib/browser/components/ChatMarkdown.js +2 -2
  32. package/lib/browser/components/ChatMarkdown.js.map +1 -1
  33. package/lib/browser/components/ChatReply.d.ts.map +1 -1
  34. package/lib/browser/components/ChatReply.js +6 -8
  35. package/lib/browser/components/ChatReply.js.map +1 -1
  36. package/lib/browser/components/ChatToolRender.d.ts +1 -0
  37. package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
  38. package/lib/browser/components/ChatToolRender.js +25 -7
  39. package/lib/browser/components/ChatToolRender.js.map +1 -1
  40. package/lib/browser/components/components.module.less +3 -2
  41. package/lib/browser/context/llm-context.service.d.ts +1 -0
  42. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  43. package/lib/browser/context/llm-context.service.js +24 -18
  44. package/lib/browser/context/llm-context.service.js.map +1 -1
  45. package/lib/browser/index.d.ts.map +1 -1
  46. package/lib/browser/index.js +11 -12
  47. package/lib/browser/index.js.map +1 -1
  48. package/lib/browser/mcp/base-apply.service.d.ts +67 -0
  49. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -0
  50. package/lib/browser/mcp/base-apply.service.js +290 -0
  51. package/lib/browser/mcp/base-apply.service.js.map +1 -0
  52. package/lib/browser/mcp/mcp-server-proxy.service.d.ts +6 -1
  53. package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
  54. package/lib/browser/mcp/mcp-server-proxy.service.js +2 -1
  55. package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -1
  56. package/lib/browser/mcp/mcp-server.feature.registry.d.ts +5 -1
  57. package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -1
  58. package/lib/browser/mcp/mcp-server.feature.registry.js +17 -1
  59. package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -1
  60. package/lib/browser/mcp/tools/components/EditFile.d.ts +3 -0
  61. package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -0
  62. package/lib/browser/mcp/tools/components/EditFile.js +101 -0
  63. package/lib/browser/mcp/tools/components/EditFile.js.map +1 -0
  64. package/lib/browser/mcp/tools/components/SearchResult.d.ts +11 -0
  65. package/lib/browser/mcp/tools/components/SearchResult.d.ts.map +1 -0
  66. package/lib/browser/mcp/tools/components/SearchResult.js +60 -0
  67. package/lib/browser/mcp/tools/components/SearchResult.js.map +1 -0
  68. package/lib/browser/mcp/tools/components/index.module.less +132 -0
  69. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  70. package/lib/browser/mcp/tools/createNewFileWithText.js +1 -2
  71. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  72. package/lib/browser/mcp/tools/{getSelectedText.d.ts → editFile.d.ts} +3 -3
  73. package/lib/browser/mcp/tools/editFile.d.ts.map +1 -0
  74. package/lib/browser/mcp/tools/editFile.js +95 -0
  75. package/lib/browser/mcp/tools/editFile.js.map +1 -0
  76. package/lib/browser/mcp/tools/{findFilesByNameSubstring.d.ts → fileSearch.d.ts} +3 -2
  77. package/lib/browser/mcp/tools/fileSearch.d.ts.map +1 -0
  78. package/lib/browser/mcp/tools/fileSearch.js +94 -0
  79. package/lib/browser/mcp/tools/fileSearch.js.map +1 -0
  80. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
  81. package/lib/browser/mcp/tools/getDiagnosticsByPath.js +1 -2
  82. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  83. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.d.ts.map +1 -1
  84. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js +1 -2
  85. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
  86. package/lib/browser/mcp/tools/{getFileTextByPath.d.ts → grepSearch.d.ts} +4 -3
  87. package/lib/browser/mcp/tools/grepSearch.d.ts.map +1 -0
  88. package/lib/browser/mcp/tools/grepSearch.js +118 -0
  89. package/lib/browser/mcp/tools/grepSearch.js.map +1 -0
  90. package/lib/browser/mcp/tools/handlers/EditFile.d.ts +10 -0
  91. package/lib/browser/mcp/tools/handlers/EditFile.d.ts.map +1 -0
  92. package/lib/browser/mcp/tools/handlers/EditFile.js +28 -0
  93. package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -0
  94. package/lib/browser/mcp/tools/handlers/ReadFile.d.ts +6 -0
  95. package/lib/browser/mcp/tools/handlers/ReadFile.d.ts.map +1 -1
  96. package/lib/browser/mcp/tools/handlers/ReadFile.js +14 -0
  97. package/lib/browser/mcp/tools/handlers/ReadFile.js.map +1 -1
  98. package/lib/browser/mcp/tools/handlers/utils.d.ts +2 -0
  99. package/lib/browser/mcp/tools/handlers/utils.d.ts.map +1 -0
  100. package/lib/browser/mcp/tools/handlers/utils.js +7 -0
  101. package/lib/browser/mcp/tools/handlers/utils.js.map +1 -0
  102. package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
  103. package/lib/browser/mcp/tools/listDir.js +3 -4
  104. package/lib/browser/mcp/tools/listDir.js.map +1 -1
  105. package/lib/browser/mcp/tools/readFile.d.ts.map +1 -1
  106. package/lib/browser/mcp/tools/readFile.js +3 -4
  107. package/lib/browser/mcp/tools/readFile.js.map +1 -1
  108. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
  109. package/lib/browser/mcp/tools/runTerminalCmd.js +1 -2
  110. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  111. package/lib/browser/model/msg-history-manager.d.ts +5 -0
  112. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  113. package/lib/browser/model/msg-history-manager.js +14 -0
  114. package/lib/browser/model/msg-history-manager.js.map +1 -1
  115. package/lib/browser/preferences/schema.d.ts.map +1 -1
  116. package/lib/browser/preferences/schema.js +5 -0
  117. package/lib/browser/preferences/schema.js.map +1 -1
  118. package/lib/browser/types.d.ts +14 -10
  119. package/lib/browser/types.d.ts.map +1 -1
  120. package/lib/browser/types.js +1 -2
  121. package/lib/browser/types.js.map +1 -1
  122. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +1 -0
  123. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  124. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +3 -0
  125. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  126. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts +4 -0
  127. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  128. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  129. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
  130. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +1 -0
  131. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
  132. package/lib/common/index.d.ts +1 -0
  133. package/lib/common/index.d.ts.map +1 -1
  134. package/lib/common/index.js.map +1 -1
  135. package/lib/common/mcp-server-manager.d.ts +1 -1
  136. package/lib/common/mcp-server-manager.d.ts.map +1 -1
  137. package/lib/common/mcp-server-manager.js.map +1 -1
  138. package/lib/common/prompts/context-prompt-provider.d.ts +14 -0
  139. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -0
  140. package/lib/common/prompts/context-prompt-provider.js +38 -0
  141. package/lib/common/prompts/context-prompt-provider.js.map +1 -0
  142. package/lib/common/tool-invocation-registry.d.ts +2 -1
  143. package/lib/common/tool-invocation-registry.d.ts.map +1 -1
  144. package/lib/common/tool-invocation-registry.js.map +1 -1
  145. package/lib/common/utils.d.ts +1 -0
  146. package/lib/common/utils.d.ts.map +1 -1
  147. package/lib/common/utils.js +3 -1
  148. package/lib/common/utils.js.map +1 -1
  149. package/lib/node/base-language-model.d.ts +2 -2
  150. package/lib/node/base-language-model.d.ts.map +1 -1
  151. package/lib/node/base-language-model.js +9 -5
  152. package/lib/node/base-language-model.js.map +1 -1
  153. package/lib/node/mcp/sumi-mcp-server.d.ts +1 -1
  154. package/lib/node/mcp/sumi-mcp-server.d.ts.map +1 -1
  155. package/lib/node/mcp/sumi-mcp-server.js +5 -2
  156. package/lib/node/mcp/sumi-mcp-server.js.map +1 -1
  157. package/lib/node/mcp-server-manager-impl.d.ts +1 -1
  158. package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
  159. package/lib/node/mcp-server-manager-impl.js +6 -5
  160. package/lib/node/mcp-server-manager-impl.js.map +1 -1
  161. package/lib/node/mcp-server.d.ts +2 -2
  162. package/lib/node/mcp-server.d.ts.map +1 -1
  163. package/lib/node/mcp-server.js +2 -1
  164. package/lib/node/mcp-server.js.map +1 -1
  165. package/lib/node/openai/openai-language-model.d.ts +1 -1
  166. package/lib/node/openai/openai-language-model.d.ts.map +1 -1
  167. package/lib/node/openai/openai-language-model.js +2 -2
  168. package/lib/node/openai/openai-language-model.js.map +1 -1
  169. package/package.json +24 -25
  170. package/src/browser/ai-core.contribution.ts +4 -0
  171. package/src/browser/chat/chat-agent.service.ts +7 -0
  172. package/src/browser/chat/chat-model.ts +0 -2
  173. package/src/browser/chat/chat-proxy.service.ts +12 -2
  174. package/src/browser/chat/chat.internal.service.ts +12 -1
  175. package/src/browser/chat/chat.view.tsx +2 -1
  176. package/src/browser/components/ChatContext/index.tsx +19 -3
  177. package/src/browser/components/ChatContext/style.module.less +12 -0
  178. package/src/browser/components/ChatEditor.tsx +13 -10
  179. package/src/browser/components/ChatMarkdown.tsx +3 -1
  180. package/src/browser/components/ChatReply.tsx +8 -15
  181. package/src/browser/components/ChatToolRender.tsx +36 -17
  182. package/src/browser/components/components.module.less +3 -2
  183. package/src/browser/context/llm-context.service.ts +30 -18
  184. package/src/browser/index.ts +11 -12
  185. package/src/browser/mcp/base-apply.service.ts +349 -0
  186. package/src/browser/mcp/mcp-server-proxy.service.ts +4 -2
  187. package/src/browser/mcp/mcp-server.feature.registry.ts +27 -4
  188. package/src/browser/mcp/tools/components/EditFile.tsx +144 -0
  189. package/src/browser/mcp/tools/components/SearchResult.tsx +92 -0
  190. package/src/browser/mcp/tools/components/index.module.less +132 -0
  191. package/src/browser/mcp/tools/createNewFileWithText.ts +1 -2
  192. package/src/browser/mcp/tools/editFile.ts +100 -0
  193. package/src/browser/mcp/tools/fileSearch.ts +99 -0
  194. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -2
  195. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -2
  196. package/src/browser/mcp/tools/grepSearch.ts +121 -0
  197. package/src/browser/mcp/tools/handlers/EditFile.ts +21 -0
  198. package/src/browser/mcp/tools/handlers/ReadFile.ts +19 -1
  199. package/src/browser/mcp/tools/handlers/utils.ts +3 -0
  200. package/src/browser/mcp/tools/listDir.ts +3 -4
  201. package/src/browser/mcp/tools/readFile.ts +3 -4
  202. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -2
  203. package/src/browser/model/msg-history-manager.ts +19 -0
  204. package/src/browser/preferences/schema.ts +5 -0
  205. package/src/browser/types.ts +15 -11
  206. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +4 -0
  207. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +4 -0
  208. package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +1 -0
  209. package/src/common/index.ts +1 -0
  210. package/src/common/mcp-server-manager.ts +18 -13
  211. package/src/common/prompts/context-prompt-provider.ts +46 -0
  212. package/src/common/tool-invocation-registry.ts +122 -124
  213. package/src/common/utils.ts +2 -0
  214. package/src/node/base-language-model.ts +20 -6
  215. package/src/node/mcp/sumi-mcp-server.ts +5 -2
  216. package/src/node/mcp-server-manager-impl.ts +13 -5
  217. package/src/node/mcp-server.ts +3 -2
  218. package/src/node/openai/openai-language-model.ts +2 -2
  219. package/lib/browser/mcp/tools/findFilesByNameSubstring.d.ts.map +0 -1
  220. package/lib/browser/mcp/tools/findFilesByNameSubstring.js +0 -92
  221. package/lib/browser/mcp/tools/findFilesByNameSubstring.js.map +0 -1
  222. package/lib/browser/mcp/tools/getCurrentFilePath.d.ts +0 -8
  223. package/lib/browser/mcp/tools/getCurrentFilePath.d.ts.map +0 -1
  224. package/lib/browser/mcp/tools/getCurrentFilePath.js +0 -49
  225. package/lib/browser/mcp/tools/getCurrentFilePath.js.map +0 -1
  226. package/lib/browser/mcp/tools/getFileTextByPath.d.ts.map +0 -1
  227. package/lib/browser/mcp/tools/getFileTextByPath.js +0 -97
  228. package/lib/browser/mcp/tools/getFileTextByPath.js.map +0 -1
  229. package/lib/browser/mcp/tools/getOpenEditorFileText.d.ts +0 -8
  230. package/lib/browser/mcp/tools/getOpenEditorFileText.d.ts.map +0 -1
  231. package/lib/browser/mcp/tools/getOpenEditorFileText.js +0 -50
  232. package/lib/browser/mcp/tools/getOpenEditorFileText.js.map +0 -1
  233. package/lib/browser/mcp/tools/getSelectedText.d.ts.map +0 -1
  234. package/lib/browser/mcp/tools/getSelectedText.js +0 -57
  235. package/lib/browser/mcp/tools/getSelectedText.js.map +0 -1
  236. package/lib/browser/mcp/tools/replaceOpenEditorFile.d.ts +0 -8
  237. package/lib/browser/mcp/tools/replaceOpenEditorFile.d.ts.map +0 -1
  238. package/lib/browser/mcp/tools/replaceOpenEditorFile.js +0 -79
  239. package/lib/browser/mcp/tools/replaceOpenEditorFile.js.map +0 -1
  240. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.d.ts +0 -8
  241. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.d.ts.map +0 -1
  242. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.js +0 -84
  243. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.js.map +0 -1
  244. package/src/browser/mcp/tools/findFilesByNameSubstring.ts +0 -93
  245. package/src/browser/mcp/tools/getCurrentFilePath.ts +0 -49
  246. package/src/browser/mcp/tools/getFileTextByPath.ts +0 -97
  247. package/src/browser/mcp/tools/getOpenEditorFileText.ts +0 -50
  248. package/src/browser/mcp/tools/getSelectedText.ts +0 -57
  249. package/src/browser/mcp/tools/replaceOpenEditorFile.ts +0 -80
  250. package/src/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.ts +0 -91
@@ -0,0 +1,349 @@
1
+ import { createPatch } from 'diff';
2
+
3
+ import { Autowired } from '@opensumi/di';
4
+ import { AppConfig, ChatMessageRole, IMarker, MarkerSeverity, OnEvent, WithEventBus } from '@opensumi/ide-core-browser';
5
+ import { WorkbenchEditorService } from '@opensumi/ide-editor';
6
+ import { EditorGroupCloseEvent } from '@opensumi/ide-editor/lib/browser';
7
+ import { IMarkerService } from '@opensumi/ide-markers';
8
+ import { Position, Range, Selection, SelectionDirection } from '@opensumi/ide-monaco';
9
+ import { observableValue, transaction } from '@opensumi/ide-monaco/lib/common/observable';
10
+ import { Deferred, URI, path } from '@opensumi/ide-utils';
11
+
12
+ import { IChatInternalService } from '../../common';
13
+ import { ChatInternalService } from '../chat/chat.internal.service';
14
+ import {
15
+ BaseInlineDiffPreviewer,
16
+ InlineDiffController,
17
+ InlineDiffService,
18
+ LiveInlineDiffPreviewer,
19
+ } from '../widget/inline-diff';
20
+ import { InlineStreamDiffHandler } from '../widget/inline-stream-diff/inline-stream-diff.handler';
21
+
22
+ import { FileHandler } from './tools/handlers/ReadFile';
23
+
24
+ // 提供代码块的唯一索引,迭代轮次,生成状态管理(包括取消),关联文件位置这些信息的记录,后续并行 apply 的支持
25
+ export abstract class BaseApplyService extends WithEventBus {
26
+ @Autowired(FileHandler)
27
+ protected fileHandler: FileHandler;
28
+
29
+ @Autowired(IChatInternalService)
30
+ protected chatInternalService: ChatInternalService;
31
+
32
+ @Autowired(AppConfig)
33
+ protected appConfig: AppConfig;
34
+
35
+ @Autowired(WorkbenchEditorService)
36
+ protected readonly editorService: WorkbenchEditorService;
37
+
38
+ @Autowired(InlineDiffService)
39
+ private readonly inlineDiffService: InlineDiffService;
40
+
41
+ @Autowired(IMarkerService)
42
+ private readonly markerService: IMarkerService;
43
+
44
+ constructor() {
45
+ super();
46
+ this.addDispose(
47
+ this.chatInternalService.onCancelRequest(() => {
48
+ this.cancelAllApply();
49
+ }),
50
+ );
51
+ this.addDispose(
52
+ this.chatInternalService.onRegenerateRequest(() => {
53
+ const messages = this.chatInternalService.sessionModel.history.getMessages();
54
+ const messageId = messages[messages.length - 1].id;
55
+ messageId && this.disposeApplyForMessage(messageId);
56
+ }),
57
+ );
58
+ }
59
+
60
+ public readonly codeBlockMapObservable = observableValue<Map<string, CodeBlockData>>(this, new Map());
61
+
62
+ private activePreviewer: BaseInlineDiffPreviewer<InlineStreamDiffHandler> | undefined;
63
+
64
+ private pendingApplyParams:
65
+ | {
66
+ relativePath: string;
67
+ newContent: string;
68
+ range?: Range;
69
+ }
70
+ | undefined;
71
+
72
+ @OnEvent(EditorGroupCloseEvent)
73
+ onEditorGroupClose(event: EditorGroupCloseEvent) {
74
+ if (this.activePreviewer?.getNode()?.uri.path.toString() === event.payload.resource.uri.path.toString()) {
75
+ this.activePreviewer.dispose();
76
+ this.activePreviewer = undefined;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Get the code block data by relative or absolute path of the last assistant message
82
+ */
83
+ getCodeBlock(relativeOrAbsolutePath: string, messageId?: string): CodeBlockData | undefined {
84
+ if (!relativeOrAbsolutePath) {
85
+ return undefined;
86
+ }
87
+ const blockId = this.generateBlockId(relativeOrAbsolutePath, messageId);
88
+ return this.codeBlockMapObservable.get().get(blockId);
89
+ }
90
+
91
+ getCodeBlockById(id: string): CodeBlockData | undefined {
92
+ return this.codeBlockMapObservable.get().get(id);
93
+ }
94
+
95
+ protected updateCodeBlock(codeBlock: CodeBlockData) {
96
+ const codeBlockMap = new Map(this.codeBlockMapObservable.get());
97
+ codeBlockMap.set(codeBlock.id, codeBlock);
98
+ transaction((tx) => {
99
+ this.codeBlockMapObservable.set(codeBlockMap, tx);
100
+ });
101
+ }
102
+
103
+ /**
104
+ * Register a new code block and return its unique ID
105
+ */
106
+ registerCodeBlock(relativePath: string, content: string): string {
107
+ const blockId = this.generateBlockId(relativePath);
108
+
109
+ if (!this.codeBlockMapObservable.get().has(blockId)) {
110
+ this.codeBlockMapObservable.get().set(blockId, {
111
+ id: blockId,
112
+ content,
113
+ relativePath,
114
+ status: 'generating',
115
+ iterationCount: 0,
116
+ createdAt: Date.now(),
117
+ });
118
+ }
119
+
120
+ return blockId;
121
+ }
122
+
123
+ initToolCallId(blockId: string, toolCallId: string): void {
124
+ const blockData = this.getCodeBlockById(blockId);
125
+ if (blockData && !blockData.initToolCallId) {
126
+ blockData.initToolCallId = toolCallId;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Apply changes of a code block
132
+ */
133
+ async apply(relativePath: string, newContent: string, instructions?: string): Promise<CodeBlockData> {
134
+ const blockData = this.getCodeBlock(relativePath);
135
+ if (!blockData) {
136
+ throw new Error('Code block not found');
137
+ }
138
+ try {
139
+ if (++blockData.iterationCount > 3) {
140
+ throw new Error('Max iteration count exceeded');
141
+ }
142
+ blockData.status = 'generating';
143
+ blockData.content = newContent;
144
+ this.updateCodeBlock(blockData);
145
+ const applyDiffResult = await this.doApply(relativePath, newContent, instructions);
146
+ blockData.applyResult = applyDiffResult;
147
+ this.updateCodeBlock(blockData);
148
+ return blockData;
149
+ } catch (err) {
150
+ blockData.status = 'failed';
151
+ this.updateCodeBlock(blockData);
152
+ throw err;
153
+ }
154
+ }
155
+
156
+ async reRenderPendingApply() {
157
+ if (!this.pendingApplyParams) {
158
+ throw new Error('No pending apply params');
159
+ }
160
+ const result = await this.renderApplyResult(
161
+ this.pendingApplyParams.relativePath,
162
+ this.pendingApplyParams.newContent,
163
+ this.pendingApplyParams.range,
164
+ );
165
+ if (result) {
166
+ const blockData = this.getCodeBlock(this.pendingApplyParams.relativePath)!;
167
+ blockData.applyResult = result;
168
+ this.updateCodeBlock(blockData);
169
+ }
170
+ }
171
+
172
+ async renderApplyResult(
173
+ relativePath: string,
174
+ newContent: string,
175
+ range?: Range,
176
+ ): Promise<{ diff: string; diagnosticInfos: IMarker[] } | undefined> {
177
+ // 用户可能会关闭编辑器,所以需要缓存参数
178
+ this.pendingApplyParams = {
179
+ relativePath,
180
+ newContent,
181
+ range,
182
+ };
183
+ const blockData = this.getCodeBlock(relativePath);
184
+ if (!blockData) {
185
+ throw new Error('Code block not found');
186
+ }
187
+ const openResult = await this.editorService.open(URI.file(path.join(this.appConfig.workspaceDir, relativePath)));
188
+ if (!openResult) {
189
+ throw new Error('Failed to open editor');
190
+ }
191
+ const editor = openResult.group.codeEditor.monacoEditor;
192
+ const inlineDiffController = InlineDiffController.get(editor)!;
193
+ blockData.status = 'pending';
194
+ this.updateCodeBlock(blockData);
195
+
196
+ range = range || editor.getModel()?.getFullModelRange()!;
197
+ // Create diff previewer
198
+ const previewer = inlineDiffController.createDiffPreviewer(
199
+ editor,
200
+ Selection.fromRange(range, SelectionDirection.LTR),
201
+ {
202
+ disposeWhenEditorClosed: true,
203
+ renderRemovedWidgetImmediately: true,
204
+ },
205
+ ) as LiveInlineDiffPreviewer;
206
+ this.activePreviewer = previewer;
207
+
208
+ const fullOriginalContent = editor.getModel()!.getValue();
209
+ const savedContent = editor.getModel()!.getValueInRange(range);
210
+ const deferred = new Deferred<{ diff: string; diagnosticInfos: IMarker[] }>();
211
+ if (newContent === savedContent) {
212
+ blockData.status = 'success';
213
+ deferred.resolve();
214
+ } else {
215
+ previewer.setValue(newContent);
216
+ this.addDispose(
217
+ this.inlineDiffService.onPartialEdit((event) => {
218
+ // TODO 支持自动保存
219
+ if (event.totalPartialEditCount === event.resolvedPartialEditCount) {
220
+ if (event.acceptPartialEditCount > 0) {
221
+ blockData.status = 'success';
222
+ const appliedResult = editor.getModel()!.getValue();
223
+ const diffResult = createPatch(relativePath, fullOriginalContent, appliedResult)
224
+ .split('\n')
225
+ .slice(4)
226
+ .join('\n');
227
+ const rangesFromDiffHunk = diffResult
228
+ .split('\n')
229
+ .map((line) => {
230
+ if (line.startsWith('@@')) {
231
+ const [, , , start, end] = line.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
232
+ return new Range(parseInt(start, 10), 0, parseInt(end, 10), 0);
233
+ }
234
+ return null;
235
+ })
236
+ .filter((range) => range !== null);
237
+ const diagnosticInfos = this.getdiagnosticInfos(editor.getModel()!.uri.toString(), rangesFromDiffHunk);
238
+ // 移除开头的几个固定信息,避免浪费 tokens
239
+ deferred.resolve({
240
+ diff: diffResult,
241
+ diagnosticInfos,
242
+ });
243
+ } else {
244
+ // 用户全部取消
245
+ blockData.status = 'cancelled';
246
+ deferred.resolve();
247
+ }
248
+ }
249
+ }),
250
+ );
251
+ }
252
+ return deferred.promise;
253
+ }
254
+
255
+ /**
256
+ * Cancel an ongoing apply operation
257
+ */
258
+ cancelApply(relativePath: string): void {
259
+ const blockData = this.getCodeBlock(relativePath);
260
+ if (blockData && (blockData.status === 'generating' || blockData.status === 'pending')) {
261
+ if (this.activePreviewer) {
262
+ this.activePreviewer.getNode()?.livePreviewDiffDecorationModel.discardUnProcessed();
263
+ this.activePreviewer.dispose();
264
+ }
265
+ blockData.status = 'cancelled';
266
+ this.updateCodeBlock(blockData);
267
+ }
268
+ }
269
+
270
+ cancelAllApply(): void {
271
+ this.codeBlockMapObservable.get().forEach((blockData) => {
272
+ if (blockData.status === 'generating' || blockData.status === 'pending') {
273
+ this.cancelApply(blockData.relativePath);
274
+ }
275
+ });
276
+ }
277
+
278
+ disposeApplyForMessage(messageId: string): void {
279
+ this.codeBlockMapObservable.get().forEach((blockData) => {
280
+ if (blockData.id.endsWith(':' + messageId)) {
281
+ if (blockData.status === 'generating') {
282
+ this.cancelApply(blockData.relativePath);
283
+ }
284
+ this.codeBlockMapObservable.get().delete(blockData.id);
285
+ }
286
+ });
287
+ }
288
+
289
+ revealApplyPosition(blockId: string): void {
290
+ const blockData = this.codeBlockMapObservable.get().get(blockId);
291
+ if (blockData) {
292
+ const hunkInfo = blockData.applyResult?.diff.split('\n').find((line) => line.startsWith('@@'));
293
+ let startLine = 0;
294
+ let endLine = 0;
295
+ if (hunkInfo) {
296
+ // 取改动后的区间
297
+ const [, , , start, end] = hunkInfo.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
298
+ startLine = parseInt(start, 10) - 1;
299
+ endLine = parseInt(end, 10) - 1;
300
+ }
301
+ this.editorService.open(URI.file(path.join(this.appConfig.workspaceDir, blockData.relativePath)));
302
+ const editor = this.editorService.currentEditor;
303
+ if (editor) {
304
+ editor.setSelection(new Selection(startLine, 0, endLine, 0));
305
+ }
306
+ }
307
+ }
308
+
309
+ protected abstract doApply(
310
+ relativePath: string,
311
+ newContent: string,
312
+ instructions?: string,
313
+ ): Promise<{ diff: string; diagnosticInfos: IMarker[] } | undefined>;
314
+
315
+ protected generateBlockId(absoluteOrRelativePath: string, messageId?: string): string {
316
+ if (!absoluteOrRelativePath.startsWith('/')) {
317
+ absoluteOrRelativePath = path.join(this.appConfig.workspaceDir, absoluteOrRelativePath);
318
+ }
319
+ const sessionId = this.chatInternalService.sessionModel.sessionId;
320
+ const messages = this.chatInternalService.sessionModel.history.getMessages();
321
+ messageId = messageId || messages[messages.length - 1].id;
322
+ return `${sessionId}:${absoluteOrRelativePath}:${messageId || '-'}`;
323
+ }
324
+
325
+ protected getdiagnosticInfos(uri: string, ranges: Range[]) {
326
+ const markers = this.markerService.getManager().getMarkers({ resource: uri });
327
+ return markers.filter(
328
+ (marker) =>
329
+ marker.severity >= MarkerSeverity.Warning &&
330
+ ranges.some((range) => range.containsPosition(new Position(marker.startLineNumber, marker.startColumn))),
331
+ );
332
+ }
333
+ }
334
+
335
+ export interface CodeBlockData {
336
+ id: string;
337
+ initToolCallId?: string;
338
+ content: string;
339
+ relativePath: string;
340
+ status: CodeBlockStatus;
341
+ iterationCount: number;
342
+ createdAt: number;
343
+ applyResult?: {
344
+ diff: string;
345
+ diagnosticInfos: IMarker[];
346
+ };
347
+ }
348
+
349
+ export type CodeBlockStatus = 'generating' | 'pending' | 'success' | 'rejected' | 'failed' | 'cancelled';
@@ -1,3 +1,5 @@
1
+ import { zodToJsonSchema } from 'zod-to-json-schema';
2
+
1
3
  import { Autowired, Injectable } from '@opensumi/di';
2
4
  import { ILogger } from '@opensumi/ide-core-browser';
3
5
  import { Emitter, Event } from '@opensumi/ide-core-common';
@@ -29,10 +31,10 @@ export class MCPServerProxyService implements IMCPServerProxyService {
29
31
  async $getMCPTools() {
30
32
  const tools = await this.mcpServerRegistry.getMCPTools().map((tool) =>
31
33
  // 不要传递 handler
32
- ({
34
+ ({
33
35
  name: tool.name,
34
36
  description: tool.description,
35
- inputSchema: tool.inputSchema,
37
+ inputSchema: zodToJsonSchema(tool.inputSchema),
36
38
  providerName: 'sumi-builtin',
37
39
  }),
38
40
  );
@@ -1,11 +1,12 @@
1
1
  // OpenSumi as MCP Server 前端的代理服务
2
2
  import { Autowired, Injectable } from '@opensumi/di';
3
- import { IAIBackService, ILogger } from '@opensumi/ide-core-common';
3
+ import { ILogger } from '@opensumi/ide-core-common';
4
4
 
5
- import { IMCPServerRegistry, MCPLogger, MCPToolDefinition } from '../types';
5
+ import { getToolName } from '../../common/utils';
6
+ import { IMCPServerRegistry, IMCPServerToolComponentProps, MCPLogger, MCPToolDefinition } from '../types';
6
7
 
7
8
  class LoggerAdapter implements MCPLogger {
8
- constructor(private readonly logger: ILogger) { }
9
+ constructor(private readonly logger: ILogger) {}
9
10
 
10
11
  appendLine(message: string): void {
11
12
  this.logger.log(message);
@@ -15,6 +16,7 @@ class LoggerAdapter implements MCPLogger {
15
16
  @Injectable()
16
17
  export class MCPServerRegistry implements IMCPServerRegistry {
17
18
  private tools: MCPToolDefinition[] = [];
19
+ private toolComponents: Record<string, React.FC<IMCPServerToolComponentProps>> = {};
18
20
 
19
21
  @Autowired(ILogger)
20
22
  private readonly baseLogger: ILogger;
@@ -23,10 +25,26 @@ export class MCPServerRegistry implements IMCPServerRegistry {
23
25
  return new LoggerAdapter(this.baseLogger);
24
26
  }
25
27
 
28
+ getMCPTool(name: string, serverName = 'sumi-builtin'): MCPToolDefinition | undefined {
29
+ return this.tools.find((tool) => getToolName(tool.name, serverName) === name);
30
+ }
31
+
26
32
  registerMCPTool(tool: MCPToolDefinition): void {
27
33
  this.tools.push(tool);
28
34
  }
29
35
 
36
+ registerToolComponent(
37
+ name: string,
38
+ component: React.FC<IMCPServerToolComponentProps>,
39
+ serverName = 'sumi-builtin',
40
+ ): void {
41
+ this.toolComponents[getToolName(name, serverName)] = component;
42
+ }
43
+
44
+ getToolComponent(name: string): React.FC<IMCPServerToolComponentProps> | undefined {
45
+ return this.toolComponents[name];
46
+ }
47
+
30
48
  getMCPTools(): MCPToolDefinition[] {
31
49
  return this.tools;
32
50
  }
@@ -43,8 +61,13 @@ export class MCPServerRegistry implements IMCPServerRegistry {
43
61
  if (!tool) {
44
62
  throw new Error(`MCP tool ${name} not found`);
45
63
  }
46
- return await tool.handler(args, this.logger);
64
+ // 统一校验并转换
65
+ const toolCallId = args.toolCallId;
66
+ args = tool.inputSchema.parse(args);
67
+ return await tool.handler({ ...args, toolCallId }, this.logger);
47
68
  } catch (error) {
69
+ // eslint-disable-next-line no-console
70
+ console.error('callMCPTool error:', error);
48
71
  return {
49
72
  content: [{ type: 'text', text: `The tool ${name} failed to execute. Error: ${error}` }],
50
73
  isError: true,
@@ -0,0 +1,144 @@
1
+ import cls from 'classnames';
2
+ import React, { useEffect, useMemo } from 'react';
3
+
4
+ import { Icon, Popover } from '@opensumi/ide-components';
5
+ import {
6
+ AppConfig,
7
+ LabelService,
8
+ MarkerSeverity,
9
+ URI,
10
+ Uri,
11
+ detectModeId,
12
+ path,
13
+ useAutorun,
14
+ useInjectable,
15
+ } from '@opensumi/ide-core-browser';
16
+ import { Loading } from '@opensumi/ide-core-browser/lib/components/ai-native';
17
+ import { ILanguageService } from '@opensumi/monaco-editor-core/esm/vs/editor/common/languages/language';
18
+ import { IModelService } from '@opensumi/monaco-editor-core/esm/vs/editor/common/services/model';
19
+ import { StandaloneServices } from '@opensumi/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
20
+
21
+ import { ChatMarkdown } from '../../../components/ChatMarkdown';
22
+ import { IMCPServerToolComponentProps } from '../../../types';
23
+ import { BaseApplyService, CodeBlockData } from '../../base-apply.service';
24
+
25
+ import styles from './index.module.less';
26
+
27
+ const renderStatus = (codeBlockData: CodeBlockData) => {
28
+ const status = codeBlockData.status;
29
+ switch (status) {
30
+ case 'generating':
31
+ return <Loading />;
32
+ case 'pending':
33
+ return (
34
+ <Popover title={status} id={'edit-file-tool-status-pending'}>
35
+ <Icon iconClass='codicon codicon-circle-large' />
36
+ </Popover>
37
+ );
38
+ case 'success':
39
+ return (
40
+ <Popover title={status} id={'edit-file-tool-status-success'}>
41
+ <Icon iconClass='codicon codicon-check-all' />
42
+ </Popover>
43
+ );
44
+ case 'failed':
45
+ return (
46
+ <Popover title={status} id={'edit-file-tool-status-failed'}>
47
+ <Icon iconClass='codicon codicon-error' color='var(--vscode-input-errorForeground)' />
48
+ </Popover>
49
+ );
50
+ case 'cancelled':
51
+ return (
52
+ <Popover title={status} id={'edit-file-tool-status-cancelled'}>
53
+ <Icon iconClass='codicon codicon-close' color='var(--vscode-input-placeholderForeground)' />
54
+ </Popover>
55
+ );
56
+ default:
57
+ return null;
58
+ }
59
+ };
60
+
61
+ export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
62
+ const { args, messageId, toolCallId } = props;
63
+ const labelService = useInjectable(LabelService);
64
+ const appConfig = useInjectable<AppConfig>(AppConfig);
65
+ const applyService = useInjectable<BaseApplyService>(BaseApplyService);
66
+ const { target_file = '', code_edit, instructions } = args || {};
67
+ const absolutePath = path.join(appConfig.workspaceDir, target_file);
68
+
69
+ const codeBlockData = applyService.getCodeBlock(absolutePath, messageId);
70
+
71
+ useAutorun(applyService.codeBlockMapObservable);
72
+
73
+ if (toolCallId && codeBlockData) {
74
+ applyService.initToolCallId(codeBlockData.id, toolCallId);
75
+ }
76
+
77
+ const icon = useMemo(() => {
78
+ if (!target_file) {
79
+ return;
80
+ }
81
+ const icon = `file-icon ${labelService.getIcon(URI.file(absolutePath))}`;
82
+ return icon;
83
+ }, [target_file, absolutePath]);
84
+ const languageId = useMemo(() => {
85
+ if (!target_file) {
86
+ return;
87
+ }
88
+ const modelService = StandaloneServices.get(IModelService);
89
+ const languageService = StandaloneServices.get(ILanguageService);
90
+ const detectedModeId = detectModeId(modelService, languageService, Uri.file(absolutePath));
91
+ return detectedModeId;
92
+ }, [target_file, absolutePath]);
93
+
94
+ // 多次迭代时,仅在首处tool组件中展示
95
+ if (!args || !codeBlockData || (toolCallId && toolCallId !== codeBlockData.initToolCallId)) {
96
+ return null;
97
+ }
98
+
99
+ return [
100
+ instructions && <p>{instructions}</p>,
101
+ <div className={styles['edit-file-tool']} key={`edit-file-tool-${codeBlockData.id}`}>
102
+ <div
103
+ className={cls(styles['edit-file-tool-header'], {
104
+ clickable: codeBlockData.status === 'pending' || codeBlockData.status === 'success',
105
+ })}
106
+ onClick={() => {
107
+ if (codeBlockData.status === 'pending') {
108
+ applyService.reRenderPendingApply();
109
+ } else if (codeBlockData.status === 'success') {
110
+ applyService.revealApplyPosition(codeBlockData.id);
111
+ }
112
+ }}
113
+ >
114
+ {icon && <span className={icon}></span>}
115
+ <span className={styles['edit-file-tool-file-name']}>{target_file}</span>
116
+ {codeBlockData.iterationCount > 1 && (
117
+ <span className={styles['edit-file-tool-iteration-count']}>{codeBlockData.iterationCount}/3</span>
118
+ )}
119
+ {renderStatus(codeBlockData)}
120
+ </div>
121
+ <ChatMarkdown markdown={`\`\`\`${languageId || ''}\n${code_edit}\n\`\`\``} hideInsert={true} />
122
+ </div>,
123
+ codeBlockData.applyResult && codeBlockData.applyResult.diagnosticInfos.length > 0 && (
124
+ <div
125
+ className={styles['edit-file-tool-diagnostic-errors']}
126
+ key={`edit-file-tool-diagnostic-errors-${codeBlockData.id}`}
127
+ >
128
+ <div className={styles['title']}>Found Lints:</div>
129
+ {codeBlockData.applyResult?.diagnosticInfos.map((info) => (
130
+ <div
131
+ key={info.message}
132
+ className={cls({
133
+ [styles['error']]: info.severity === MarkerSeverity.Error,
134
+ [styles['warning']]: info.severity === MarkerSeverity.Warning,
135
+ })}
136
+ >
137
+ <Icon className={`codicon codicon-${info.severity === MarkerSeverity.Error ? 'error' : 'warning'}`} />
138
+ {info.message.split('\n')[0]}
139
+ </div>
140
+ ))}
141
+ </div>
142
+ ),
143
+ ];
144
+ };
@@ -0,0 +1,92 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+
3
+ import { LabelService, URI, path, useInjectable } from '@opensumi/ide-core-browser';
4
+ import { WorkbenchEditorService } from '@opensumi/ide-editor';
5
+ import { IWorkspaceService } from '@opensumi/ide-workspace';
6
+
7
+ import { IChatInternalService } from '../../../../common';
8
+ import { ChatInternalService } from '../../../chat/chat.internal.service';
9
+
10
+ import styles from './index.module.less';
11
+
12
+ interface SearchResultProps {
13
+ args: any;
14
+ toolCallId: string;
15
+ messageId: string;
16
+ toolName: string;
17
+ }
18
+
19
+ export const FileSearchToolComponent: React.FC<SearchResultProps> = ({ args, toolCallId, messageId }) => (
20
+ <SearchResult args={args} toolCallId={toolCallId} messageId={messageId} toolName='fileSearch' />
21
+ );
22
+
23
+ export const GrepSearchToolComponent: React.FC<SearchResultProps> = ({ args, toolCallId, messageId }) => (
24
+ <SearchResult args={args} toolCallId={toolCallId} messageId={messageId} toolName='grepSearch' />
25
+ );
26
+
27
+ const SearchResult: React.FC<SearchResultProps> = ({ args, toolCallId, toolName, messageId }) => {
28
+ const [isExpanded, setIsExpanded] = useState(false);
29
+ const labelService = useInjectable<LabelService>(LabelService);
30
+ const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
31
+ const workspaceService = useInjectable<IWorkspaceService>(IWorkspaceService);
32
+ const workspaceRoot = useMemo(() => URI.parse(workspaceService.tryGetRoots()?.[0]?.uri), []);
33
+
34
+ const chatService = useInjectable<ChatInternalService>(IChatInternalService);
35
+ const [files, setFiles] = useState<string[]>(
36
+ chatService.sessionModel.history.getMessageAdditional(messageId)?.[toolCallId]?.files || [],
37
+ );
38
+ useEffect(() => {
39
+ const toDispose = chatService.sessionModel.history.onMessageAdditionalChange((additional) => {
40
+ setFiles(additional[toolCallId]?.files || []);
41
+ });
42
+ return () => {
43
+ toDispose.dispose();
44
+ };
45
+ }, []);
46
+
47
+ const handleFileClick = (uri: URI) => {
48
+ // 处理文件点击跳转
49
+ editorService.open(uri);
50
+ };
51
+
52
+ const parsedFiles = useMemo(
53
+ () =>
54
+ files.map((file) => {
55
+ const uri = URI.parse(file);
56
+ const iconClass = labelService.getIcon(uri);
57
+ return {
58
+ iconClass,
59
+ name: uri.path.base,
60
+ path: path.relative(workspaceRoot.codeUri.fsPath, uri.path.dir.toString()),
61
+ };
62
+ }),
63
+ [files],
64
+ );
65
+
66
+ return (
67
+ <div className={styles.container}>
68
+ <div className={styles.header} onClick={() => setIsExpanded(!isExpanded)}>
69
+ <span style={{ transform: `rotate(${isExpanded ? '90deg' : '0deg'})` }}>▶</span>
70
+ <span>
71
+ {toolName === 'fileSearch' ? `Searched files "${args.query}"` : `Grepped codebase "${args.query}"`} ·{' '}
72
+ {files.length} files
73
+ </span>
74
+ </div>
75
+ {isExpanded && (
76
+ <ul className={styles.fileList}>
77
+ {parsedFiles.map((file, index) => (
78
+ <li
79
+ key={index}
80
+ className={styles.fileItem}
81
+ onClick={() => handleFileClick(URI.file(path.join(workspaceRoot.codeUri.fsPath, file.path, file.name)))}
82
+ >
83
+ <span className={file.iconClass}></span>
84
+ <span style={{ flex: 1 }}>{file.name}</span>
85
+ <span className={styles.filePath}>{file.path}</span>
86
+ </li>
87
+ ))}
88
+ </ul>
89
+ )}
90
+ </div>
91
+ );
92
+ };