@opensumi/ide-ai-native 3.7.2-next-1740064823.0 → 3.7.2-next-1740107209.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 (196) 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/ChatEditor.d.ts +1 -0
  22. package/lib/browser/components/ChatEditor.d.ts.map +1 -1
  23. package/lib/browser/components/ChatEditor.js +3 -3
  24. package/lib/browser/components/ChatEditor.js.map +1 -1
  25. package/lib/browser/components/ChatMarkdown.d.ts +1 -0
  26. package/lib/browser/components/ChatMarkdown.d.ts.map +1 -1
  27. package/lib/browser/components/ChatMarkdown.js +2 -2
  28. package/lib/browser/components/ChatMarkdown.js.map +1 -1
  29. package/lib/browser/components/ChatReply.d.ts.map +1 -1
  30. package/lib/browser/components/ChatReply.js +6 -8
  31. package/lib/browser/components/ChatReply.js.map +1 -1
  32. package/lib/browser/components/ChatToolRender.d.ts +2 -1
  33. package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
  34. package/lib/browser/components/ChatToolRender.js +40 -19
  35. package/lib/browser/components/ChatToolRender.js.map +1 -1
  36. package/lib/browser/components/components.module.less +3 -2
  37. package/lib/browser/index.d.ts.map +1 -1
  38. package/lib/browser/index.js +7 -0
  39. package/lib/browser/index.js.map +1 -1
  40. package/lib/browser/mcp/base-apply.service.d.ts +67 -0
  41. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -0
  42. package/lib/browser/mcp/base-apply.service.js +290 -0
  43. package/lib/browser/mcp/base-apply.service.js.map +1 -0
  44. package/lib/browser/mcp/mcp-server-proxy.service.d.ts +6 -1
  45. package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
  46. package/lib/browser/mcp/mcp-server-proxy.service.js +2 -1
  47. package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -1
  48. package/lib/browser/mcp/mcp-server.feature.registry.d.ts +5 -1
  49. package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -1
  50. package/lib/browser/mcp/mcp-server.feature.registry.js +15 -0
  51. package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -1
  52. package/lib/browser/mcp/tools/components/EditFile.d.ts +3 -0
  53. package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -0
  54. package/lib/browser/mcp/tools/components/EditFile.js +101 -0
  55. package/lib/browser/mcp/tools/components/EditFile.js.map +1 -0
  56. package/lib/browser/mcp/tools/components/index.module.less +67 -0
  57. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  58. package/lib/browser/mcp/tools/createNewFileWithText.js +1 -2
  59. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  60. package/lib/browser/mcp/tools/editFile.d.ts +8 -0
  61. package/lib/browser/mcp/tools/editFile.d.ts.map +1 -0
  62. package/lib/browser/mcp/tools/editFile.js +95 -0
  63. package/lib/browser/mcp/tools/editFile.js.map +1 -0
  64. package/lib/browser/mcp/tools/findFilesByNameSubstring.d.ts.map +1 -1
  65. package/lib/browser/mcp/tools/findFilesByNameSubstring.js +1 -2
  66. package/lib/browser/mcp/tools/findFilesByNameSubstring.js.map +1 -1
  67. package/lib/browser/mcp/tools/getCurrentFilePath.d.ts.map +1 -1
  68. package/lib/browser/mcp/tools/getCurrentFilePath.js +1 -2
  69. package/lib/browser/mcp/tools/getCurrentFilePath.js.map +1 -1
  70. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
  71. package/lib/browser/mcp/tools/getDiagnosticsByPath.js +1 -2
  72. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  73. package/lib/browser/mcp/tools/getFileTextByPath.d.ts.map +1 -1
  74. package/lib/browser/mcp/tools/getFileTextByPath.js +1 -2
  75. package/lib/browser/mcp/tools/getFileTextByPath.js.map +1 -1
  76. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.d.ts.map +1 -1
  77. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js +1 -2
  78. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
  79. package/lib/browser/mcp/tools/getOpenEditorFileText.d.ts.map +1 -1
  80. package/lib/browser/mcp/tools/getOpenEditorFileText.js +1 -2
  81. package/lib/browser/mcp/tools/getOpenEditorFileText.js.map +1 -1
  82. package/lib/browser/mcp/tools/getSelectedText.d.ts.map +1 -1
  83. package/lib/browser/mcp/tools/getSelectedText.js +1 -2
  84. package/lib/browser/mcp/tools/getSelectedText.js.map +1 -1
  85. package/lib/browser/mcp/tools/handlers/EditFile.d.ts +10 -0
  86. package/lib/browser/mcp/tools/handlers/EditFile.d.ts.map +1 -0
  87. package/lib/browser/mcp/tools/handlers/EditFile.js +28 -0
  88. package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -0
  89. package/lib/browser/mcp/tools/handlers/ReadFile.d.ts +6 -0
  90. package/lib/browser/mcp/tools/handlers/ReadFile.d.ts.map +1 -1
  91. package/lib/browser/mcp/tools/handlers/ReadFile.js +14 -0
  92. package/lib/browser/mcp/tools/handlers/ReadFile.js.map +1 -1
  93. package/lib/browser/mcp/tools/handlers/utils.d.ts +2 -0
  94. package/lib/browser/mcp/tools/handlers/utils.d.ts.map +1 -0
  95. package/lib/browser/mcp/tools/handlers/utils.js +7 -0
  96. package/lib/browser/mcp/tools/handlers/utils.js.map +1 -0
  97. package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
  98. package/lib/browser/mcp/tools/listDir.js +2 -4
  99. package/lib/browser/mcp/tools/listDir.js.map +1 -1
  100. package/lib/browser/mcp/tools/readFile.d.ts.map +1 -1
  101. package/lib/browser/mcp/tools/readFile.js +2 -4
  102. package/lib/browser/mcp/tools/readFile.js.map +1 -1
  103. package/lib/browser/mcp/tools/replaceOpenEditorFile.d.ts.map +1 -1
  104. package/lib/browser/mcp/tools/replaceOpenEditorFile.js +6 -5
  105. package/lib/browser/mcp/tools/replaceOpenEditorFile.js.map +1 -1
  106. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.d.ts.map +1 -1
  107. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.js +1 -2
  108. package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.js.map +1 -1
  109. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
  110. package/lib/browser/mcp/tools/runTerminalCmd.js +1 -2
  111. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  112. package/lib/browser/preferences/schema.d.ts.map +1 -1
  113. package/lib/browser/preferences/schema.js +5 -0
  114. package/lib/browser/preferences/schema.js.map +1 -1
  115. package/lib/browser/types.d.ts +14 -10
  116. package/lib/browser/types.d.ts.map +1 -1
  117. package/lib/browser/types.js +1 -2
  118. package/lib/browser/types.js.map +1 -1
  119. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +1 -0
  120. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  121. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +3 -0
  122. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  123. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts +4 -0
  124. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  125. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  126. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
  127. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +1 -0
  128. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
  129. package/lib/common/index.d.ts +1 -0
  130. package/lib/common/index.d.ts.map +1 -1
  131. package/lib/common/index.js.map +1 -1
  132. package/lib/common/prompts/context-prompt-provider.d.ts +14 -0
  133. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -0
  134. package/lib/common/prompts/context-prompt-provider.js +38 -0
  135. package/lib/common/prompts/context-prompt-provider.js.map +1 -0
  136. package/lib/common/utils.d.ts +1 -0
  137. package/lib/common/utils.d.ts.map +1 -1
  138. package/lib/common/utils.js +3 -1
  139. package/lib/common/utils.js.map +1 -1
  140. package/lib/node/base-language-model.d.ts +2 -2
  141. package/lib/node/base-language-model.d.ts.map +1 -1
  142. package/lib/node/base-language-model.js +8 -4
  143. package/lib/node/base-language-model.js.map +1 -1
  144. package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
  145. package/lib/node/mcp-server-manager-impl.js +2 -1
  146. package/lib/node/mcp-server-manager-impl.js.map +1 -1
  147. package/lib/node/openai/openai-language-model.d.ts +1 -1
  148. package/lib/node/openai/openai-language-model.d.ts.map +1 -1
  149. package/lib/node/openai/openai-language-model.js +2 -2
  150. package/lib/node/openai/openai-language-model.js.map +1 -1
  151. package/package.json +23 -25
  152. package/src/browser/ai-core.contribution.ts +4 -0
  153. package/src/browser/chat/chat-agent.service.ts +7 -0
  154. package/src/browser/chat/chat-model.ts +0 -2
  155. package/src/browser/chat/chat-proxy.service.ts +12 -2
  156. package/src/browser/chat/chat.internal.service.ts +12 -1
  157. package/src/browser/chat/chat.view.tsx +2 -1
  158. package/src/browser/components/ChatEditor.tsx +13 -10
  159. package/src/browser/components/ChatMarkdown.tsx +3 -1
  160. package/src/browser/components/ChatReply.tsx +8 -15
  161. package/src/browser/components/ChatToolRender.tsx +41 -20
  162. package/src/browser/components/components.module.less +3 -2
  163. package/src/browser/index.ts +7 -0
  164. package/src/browser/mcp/base-apply.service.ts +349 -0
  165. package/src/browser/mcp/mcp-server-proxy.service.ts +4 -2
  166. package/src/browser/mcp/mcp-server.feature.registry.ts +25 -3
  167. package/src/browser/mcp/tools/components/EditFile.tsx +144 -0
  168. package/src/browser/mcp/tools/components/index.module.less +67 -0
  169. package/src/browser/mcp/tools/createNewFileWithText.ts +1 -2
  170. package/src/browser/mcp/tools/editFile.ts +100 -0
  171. package/src/browser/mcp/tools/findFilesByNameSubstring.ts +1 -2
  172. package/src/browser/mcp/tools/getCurrentFilePath.ts +1 -2
  173. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -2
  174. package/src/browser/mcp/tools/getFileTextByPath.ts +1 -2
  175. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -2
  176. package/src/browser/mcp/tools/getOpenEditorFileText.ts +1 -2
  177. package/src/browser/mcp/tools/getSelectedText.ts +1 -2
  178. package/src/browser/mcp/tools/handlers/EditFile.ts +21 -0
  179. package/src/browser/mcp/tools/handlers/ReadFile.ts +19 -1
  180. package/src/browser/mcp/tools/handlers/utils.ts +3 -0
  181. package/src/browser/mcp/tools/listDir.ts +2 -4
  182. package/src/browser/mcp/tools/readFile.ts +2 -4
  183. package/src/browser/mcp/tools/replaceOpenEditorFile.ts +8 -7
  184. package/src/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.ts +1 -2
  185. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -2
  186. package/src/browser/preferences/schema.ts +5 -0
  187. package/src/browser/types.ts +15 -11
  188. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +4 -0
  189. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +4 -0
  190. package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +1 -0
  191. package/src/common/index.ts +1 -0
  192. package/src/common/prompts/context-prompt-provider.ts +46 -0
  193. package/src/common/utils.ts +2 -0
  194. package/src/node/base-language-model.ts +17 -4
  195. package/src/node/mcp-server-manager-impl.ts +2 -1
  196. package/src/node/openai/openai-language-model.ts +2 -2
@@ -40,6 +40,7 @@ import {
40
40
  TokenMCPServerProxyService,
41
41
  } from '../../common';
42
42
  import { LLMContextService, LLMContextServiceToken } from '../../common/llm-context';
43
+ import { ChatAgentPromptProvider } from '../../common/prompts/context-prompt-provider';
43
44
  import { ChatContext } from '../components/ChatContext';
44
45
  import { CodeBlockWrapperInput } from '../components/ChatEditor';
45
46
  import { ChatInput } from '../components/ChatInput';
@@ -50,7 +51,7 @@ import { MessageData, createMessageByAI, createMessageByUser } from '../componen
50
51
  import { WelcomeMessage } from '../components/WelcomeMsg';
51
52
  import { MCPServerProxyService } from '../mcp/mcp-server-proxy.service';
52
53
  import { MCPToolsDialog } from '../mcp/mcp-tools-dialog.view';
53
- import { ChatAgentPromptProvider, ChatViewHeaderRender, TSlashCommandCustomRender } from '../types';
54
+ import { ChatViewHeaderRender, TSlashCommandCustomRender } from '../types';
54
55
 
55
56
  import { ChatRequestModel, ChatSlashCommandItemModel } from './chat-model';
56
57
  import { ChatProxyService } from './chat-proxy.service';
@@ -31,9 +31,10 @@ interface Props {
31
31
  language?: string;
32
32
  agentId?: string;
33
33
  command?: string;
34
+ hideInsert?: boolean;
34
35
  }
35
36
  export const CodeEditorWithHighlight = (props: Props) => {
36
- const { input, language, relationId, agentId, command } = props;
37
+ const { input, language, relationId, agentId, command, hideInsert } = props;
37
38
  const ref = React.useRef<HTMLDivElement | null>(null);
38
39
  const monacoCommandRegistry = useInjectable<MonacoCommandRegistry>(MonacoCommandRegistry);
39
40
  const clipboardService = useInjectable<IClipboardService>(IClipboardService);
@@ -101,15 +102,17 @@ export const CodeEditorWithHighlight = (props: Props) => {
101
102
  return (
102
103
  <div className={styles.monaco_wrapper}>
103
104
  <div className={styles.action_toolbar}>
104
- <Popover id={`ai-chat-inser-${useUUID}`} title={localize('aiNative.chat.code.insert')}>
105
- <EnhanceIcon
106
- className={getIcon('insert')}
107
- onClick={() => handleInsert()}
108
- tabIndex={0}
109
- role='button'
110
- ariaLabel={localize('aiNative.chat.code.insert')}
111
- />
112
- </Popover>
105
+ {!hideInsert && (
106
+ <Popover id={`ai-chat-inser-${useUUID}`} title={localize('aiNative.chat.code.insert')}>
107
+ <EnhanceIcon
108
+ className={getIcon('insert')}
109
+ onClick={() => handleInsert()}
110
+ tabIndex={0}
111
+ role='button'
112
+ ariaLabel={localize('aiNative.chat.code.insert')}
113
+ />
114
+ </Popover>
115
+ )}
113
116
  <Popover
114
117
  id={`ai-chat-copy-${useUUID}`}
115
118
  title={localize(isCoping ? 'aiNative.chat.code.copy.success' : 'aiNative.chat.code.copy')}
@@ -17,6 +17,7 @@ interface MarkdownProps {
17
17
  className?: string;
18
18
  fillInIncompleteTokens?: boolean; // 补齐不完整的 token,如代码块或表格
19
19
  markedOptions?: IMarkedOptions;
20
+ hideInsert?: boolean;
20
21
  }
21
22
 
22
23
  export const ChatMarkdown = (props: MarkdownProps) => {
@@ -42,13 +43,14 @@ export const ChatMarkdown = (props: MarkdownProps) => {
42
43
  <div className={styles.code}>
43
44
  <ConfigProvider value={appConfig}>
44
45
  <div className={styles.code_block}>
45
- <div className={styles.code_language}>{language}</div>
46
+ <div className={cls(styles.code_language, 'language-badge')}>{language}</div>
46
47
  <CodeEditorWithHighlight
47
48
  input={code as string}
48
49
  language={language}
49
50
  relationId={props.relationId || ''}
50
51
  agentId={props.agentId}
51
52
  command={props.command}
53
+ hideInsert={props.hideInsert}
52
54
  />
53
55
  </div>
54
56
  </ConfigProvider>
@@ -149,8 +149,8 @@ const TreeRenderer = (props: { treeData: IChatResponseProgressFileTreeData }) =>
149
149
  );
150
150
  };
151
151
 
152
- const ToolCallRender = (props: { toolCall: IChatToolContent['content'] }) => {
153
- const { toolCall } = props;
152
+ const ToolCallRender = (props: { toolCall: IChatToolContent['content']; messageId?: string }) => {
153
+ const { toolCall, messageId } = props;
154
154
  const chatAgentViewService = useInjectable<IChatAgentViewService>(ChatAgentViewServiceToken);
155
155
  const [node, setNode] = useState<React.JSX.Element | null>(null);
156
156
 
@@ -158,7 +158,7 @@ const ToolCallRender = (props: { toolCall: IChatToolContent['content'] }) => {
158
158
  const config = chatAgentViewService.getChatComponent('toolCall');
159
159
  if (config) {
160
160
  const { component: Component, initialProps } = config;
161
- setNode(<Component {...initialProps} value={toolCall} />);
161
+ setNode(<Component {...initialProps} value={toolCall} messageId={messageId} />);
162
162
  return;
163
163
  }
164
164
  setNode(
@@ -169,14 +169,14 @@ const ToolCallRender = (props: { toolCall: IChatToolContent['content'] }) => {
169
169
  );
170
170
  const deferred = chatAgentViewService.getChatComponentDeferred('toolCall')!;
171
171
  deferred.promise.then(({ component: Component, initialProps }) => {
172
- setNode(<Component {...initialProps} value={toolCall} />);
172
+ setNode(<Component {...initialProps} value={toolCall} messageId={messageId} />);
173
173
  });
174
174
  }, [toolCall.state]);
175
175
 
176
176
  return node;
177
177
  };
178
178
 
179
- const ComponentRender = (props: { component: string; value?: unknown }) => {
179
+ const ComponentRender = (props: { component: string; value?: unknown; messageId?: string }) => {
180
180
  const chatAgentViewService = useInjectable<IChatAgentViewService>(ChatAgentViewServiceToken);
181
181
  const [node, setNode] = useState<React.JSX.Element | null>(null);
182
182
 
@@ -184,7 +184,7 @@ const ComponentRender = (props: { component: string; value?: unknown }) => {
184
184
  const config = chatAgentViewService.getChatComponent(props.component);
185
185
  if (config) {
186
186
  const { component: Component, initialProps } = config;
187
- setNode(<Component {...initialProps} value={props.value} />);
187
+ setNode(<Component {...initialProps} value={props.value} messageId={props.messageId} />);
188
188
  return;
189
189
  }
190
190
  setNode(
@@ -224,7 +224,6 @@ export const ChatReply = (props: IChatReplyProps) => {
224
224
  const chatApiService = useInjectable<ChatService>(ChatServiceToken);
225
225
  const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
226
226
  const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
227
-
228
227
  useEffect(() => {
229
228
  const disposableCollection = new DisposableCollection();
230
229
 
@@ -298,12 +297,6 @@ export const ChatReply = (props: IChatReplyProps) => {
298
297
  </div>
299
298
  );
300
299
 
301
- const renderComponent = (componentId: string, value: unknown) => (
302
- <ComponentRender component={componentId} value={value} />
303
- );
304
-
305
- const renderToolCall = (toolCall: IChatToolContent['content']) => <ToolCallRender toolCall={toolCall} />;
306
-
307
300
  const contentNode = React.useMemo(
308
301
  () =>
309
302
  request.response.responseContents.map((item, index) => {
@@ -313,9 +306,9 @@ export const ChatReply = (props: IChatReplyProps) => {
313
306
  } else if (item.kind === 'treeData') {
314
307
  node = renderTreeData(item.treeData);
315
308
  } else if (item.kind === 'component') {
316
- node = renderComponent(item.component, item.value);
309
+ node = <ComponentRender component={item.component} value={item.value} messageId={msgId} />;
317
310
  } else if (item.kind === 'toolCall') {
318
- node = renderToolCall(item.content);
311
+ node = <ToolCallRender toolCall={item.content} messageId={msgId} />;
319
312
  } else {
320
313
  node = renderMarkdown(item.content);
321
314
  }
@@ -1,20 +1,27 @@
1
1
  import cls from 'classnames';
2
2
  import React, { useState } from 'react';
3
3
 
4
+ import { useInjectable } from '@opensumi/ide-core-browser';
4
5
  import { Icon } from '@opensumi/ide-core-browser/lib/components';
5
6
  import { Loading } from '@opensumi/ide-core-browser/lib/components/ai-native';
6
7
  import { IChatToolContent, uuid } from '@opensumi/ide-core-common';
7
8
 
9
+ import { IMCPServerRegistry, TokenMCPServerRegistry } from '../types';
10
+
8
11
  import { CodeEditorWithHighlight } from './ChatEditor';
9
12
  import styles from './ChatToolRender.module.less';
10
13
 
11
- export const ChatToolRender = (props: { value: IChatToolContent['content'] }) => {
12
- const { value } = props;
14
+ export const ChatToolRender = (props: { value: IChatToolContent['content']; messageId?: string }) => {
15
+ const { value, messageId } = props;
13
16
  const [isExpanded, setIsExpanded] = useState(false);
17
+ const mcpServerFeatureRegistry = useInjectable<IMCPServerRegistry>(TokenMCPServerRegistry);
14
18
 
15
19
  if (!value || !value.function || !value.id) {
16
20
  return null;
17
21
  }
22
+ const label = mcpServerFeatureRegistry.getMCPTool(value.function.name)?.label || value.function.name;
23
+
24
+ const ToolComponent = mcpServerFeatureRegistry.getToolComponent(value.function.name);
18
25
 
19
26
  const getStateInfo = (state?: string): { label: string; icon: React.ReactNode } => {
20
27
  switch (state) {
@@ -22,11 +29,22 @@ export const ChatToolRender = (props: { value: IChatToolContent['content'] }) =>
22
29
  case 'streaming':
23
30
  return { label: 'Generating', icon: <Loading /> };
24
31
  case 'complete':
25
- return { label: 'Complete', icon: <Icon iconClass="codicon codicon-check" /> };
32
+ return { label: 'Complete', icon: <Icon iconClass='codicon codicon-check' /> };
26
33
  case 'result':
27
- return { label: 'Result Ready', icon: <Icon iconClass="codicon codicon-check-all" /> };
34
+ return { label: 'Result Ready', icon: <Icon iconClass='codicon codicon-check-all' /> };
28
35
  default:
29
- return { label: state || 'Unknown', icon: <Icon iconClass="codicon codicon-question" /> };
36
+ return { label: state || 'Unknown', icon: <Icon iconClass='codicon codicon-question' /> };
37
+ }
38
+ };
39
+ const getParsedArgs = () => {
40
+ try {
41
+ // TODO: 流式输出中function_call的参数还不完整,需要等待complete状态
42
+ if (value.state !== 'complete' && value.state !== 'result') {
43
+ return {};
44
+ }
45
+ return JSON.parse(value.function?.arguments || '{}');
46
+ } catch (error) {
47
+ return {};
30
48
  }
31
49
  };
32
50
 
@@ -36,12 +54,12 @@ export const ChatToolRender = (props: { value: IChatToolContent['content'] }) =>
36
54
 
37
55
  const stateInfo = getStateInfo(value.state);
38
56
 
39
- return (
40
- <div className={styles['chat-tool-render']}>
57
+ return [
58
+ <div className={styles['chat-tool-render']} key='chat-tool-render'>
41
59
  <div className={styles['tool-header']} onClick={toggleExpand}>
42
60
  <div className={styles['tool-name']}>
43
61
  <span className={cls(styles['expand-icon'], { [styles.expanded]: isExpanded })}>▶</span>
44
- {value?.function?.name}
62
+ {label}
45
63
  </div>
46
64
  {value.state && (
47
65
  <div className={styles['tool-state']}>
@@ -54,24 +72,27 @@ export const ChatToolRender = (props: { value: IChatToolContent['content'] }) =>
54
72
  {value?.function?.arguments && (
55
73
  <div className={styles['tool-arguments']}>
56
74
  <div className={styles['section-label']}>Arguments</div>
57
- <CodeEditorWithHighlight
58
- input={value?.function?.arguments}
59
- language={'json'}
60
- relationId={uuid(4)}
61
- />
75
+ <CodeEditorWithHighlight input={value?.function?.arguments} language={'json'} relationId={uuid(4)} />
62
76
  </div>
63
77
  )}
64
78
  {value?.result && (
65
79
  <div className={styles['tool-result']}>
66
80
  <div className={styles['section-label']}>Result</div>
67
- <CodeEditorWithHighlight
68
- input={value.result}
69
- language={'json'}
70
- relationId={uuid(4)}
71
- />
81
+ <CodeEditorWithHighlight input={value.result} language={'json'} relationId={uuid(4)} />
72
82
  </div>
73
83
  )}
74
84
  </div>
75
- </div>
76
- );
85
+ </div>,
86
+ ToolComponent && (
87
+ <ToolComponent
88
+ key='tool-component'
89
+ state={value.state}
90
+ args={getParsedArgs()}
91
+ result={value.result}
92
+ index={value.index}
93
+ messageId={messageId}
94
+ toolCallId={value.id}
95
+ />
96
+ ),
97
+ ];
77
98
  };
@@ -253,7 +253,7 @@
253
253
  .editor {
254
254
  border-radius: 8px;
255
255
  font-size: 12px;
256
- padding: 32px 8px 8px 8px;
256
+ padding: 28px 8px 8px 8px;
257
257
  line-height: 18px;
258
258
  &::-webkit-scrollbar {
259
259
  width: auto;
@@ -265,7 +265,7 @@
265
265
  display: flex;
266
266
  position: absolute;
267
267
  right: 8px;
268
- top: 6px;
268
+ top: 2px;
269
269
  z-index: 100;
270
270
  height: 20px;
271
271
  align-items: center;
@@ -300,6 +300,7 @@
300
300
  background-color: var(--design-language-background);
301
301
  border-radius: 8px 0px 8px 0;
302
302
  color: var(--design-text-foreground);
303
+ font-size: 12px;
303
304
  }
304
305
  }
305
306
 
@@ -30,6 +30,7 @@ import {
30
30
  } from '../common';
31
31
  import { LLMContextServiceToken } from '../common/llm-context';
32
32
  import { MCPServerManager, MCPServerManagerPath } from '../common/mcp-server-manager';
33
+ import { ChatAgentPromptProvider, DefaultChatAgentPromptProvider } from '../common/prompts/context-prompt-provider';
33
34
 
34
35
  import { AINativeBrowserContribution } from './ai-core.contribution';
35
36
  import { ChatAgentService } from './chat/chat-agent.service';
@@ -57,6 +58,7 @@ import { LanguageParserService } from './languages/service';
57
58
  import { MCPServerProxyService } from './mcp/mcp-server-proxy.service';
58
59
  import { MCPServerRegistry } from './mcp/mcp-server.feature.registry';
59
60
  import { CreateNewFileWithTextTool } from './mcp/tools/createNewFileWithText';
61
+ import { EditFileTool } from './mcp/tools/editFile';
60
62
  import { FindFilesByNameSubstringTool } from './mcp/tools/findFilesByNameSubstring';
61
63
  import { GetCurrentFilePathTool } from './mcp/tools/getCurrentFilePath';
62
64
  import { GetDiagnosticsByPathTool } from './mcp/tools/getDiagnosticsByPath';
@@ -97,6 +99,7 @@ export class AINativeModule extends BrowserModule {
97
99
  // MCP Server Contributions START
98
100
  ListDirTool,
99
101
  ReadFileTool,
102
+ EditFileTool,
100
103
  CreateNewFileWithTextTool,
101
104
  GetSelectedTextTool,
102
105
  GetOpenEditorFileDiagnosticsTool,
@@ -196,6 +199,10 @@ export class AINativeModule extends BrowserModule {
196
199
  token: InlineDiffService,
197
200
  useClass: InlineDiffService,
198
201
  },
202
+ {
203
+ token: ChatAgentPromptProvider,
204
+ useValue: DefaultChatAgentPromptProvider,
205
+ },
199
206
  ];
200
207
 
201
208
  backServices = [
@@ -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';