@opensumi/ide-ai-native 3.8.1-next-1740556231.0 → 3.8.1-next-1740571693.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 (182) hide show
  1. package/lib/browser/ai-core.contextkeys.d.ts +1 -1
  2. package/lib/browser/ai-core.contextkeys.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contextkeys.js +1 -1
  4. package/lib/browser/ai-core.contextkeys.js.map +1 -1
  5. package/lib/browser/ai-core.contribution.d.ts +1 -4
  6. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  7. package/lib/browser/ai-core.contribution.js +11 -23
  8. package/lib/browser/ai-core.contribution.js.map +1 -1
  9. package/lib/browser/chat/chat-manager.service.d.ts +0 -1
  10. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  11. package/lib/browser/chat/chat-manager.service.js +0 -13
  12. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  13. package/lib/browser/chat/chat.internal.service.d.ts +0 -1
  14. package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
  15. package/lib/browser/chat/chat.internal.service.js +0 -3
  16. package/lib/browser/chat/chat.internal.service.js.map +1 -1
  17. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  18. package/lib/browser/chat/chat.view.js +30 -1
  19. package/lib/browser/chat/chat.view.js.map +1 -1
  20. package/lib/browser/components/ChatHistory.d.ts +1 -0
  21. package/lib/browser/components/ChatHistory.d.ts.map +1 -1
  22. package/lib/browser/components/ChatHistory.js +14 -14
  23. package/lib/browser/components/ChatHistory.js.map +1 -1
  24. package/lib/browser/contrib/inline-completions/inline-completions.controller.js +1 -1
  25. package/lib/browser/contrib/inline-completions/inline-completions.controller.js.map +1 -1
  26. package/lib/browser/contrib/intelligent-completions/index.d.ts +2 -1
  27. package/lib/browser/contrib/intelligent-completions/index.d.ts.map +1 -1
  28. package/lib/browser/contrib/intelligent-completions/index.js +4 -1
  29. package/lib/browser/contrib/intelligent-completions/index.js.map +1 -1
  30. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.js +2 -2
  31. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.js.map +1 -1
  32. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts.map +1 -1
  33. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +5 -4
  34. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  35. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.d.ts.map +1 -1
  36. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.js +4 -2
  37. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.js.map +1 -1
  38. package/lib/browser/contrib/intelligent-completions/view/default.d.ts.map +1 -1
  39. package/lib/browser/contrib/intelligent-completions/view/default.js +17 -11
  40. package/lib/browser/contrib/intelligent-completions/view/default.js.map +1 -1
  41. package/lib/browser/index.d.ts.map +1 -1
  42. package/lib/browser/index.js +4 -0
  43. package/lib/browser/index.js.map +1 -1
  44. package/lib/browser/mcp/base-apply.service.d.ts +40 -31
  45. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  46. package/lib/browser/mcp/base-apply.service.js +167 -233
  47. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  48. package/lib/browser/mcp/config/components/mcp-config.module.less +178 -0
  49. package/lib/browser/mcp/config/components/mcp-config.view.d.ts +3 -0
  50. package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -0
  51. package/lib/browser/mcp/config/components/mcp-config.view.js +150 -0
  52. package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -0
  53. package/lib/browser/mcp/config/components/mcp-server-form.d.ts +16 -0
  54. package/lib/browser/mcp/config/components/mcp-server-form.d.ts.map +1 -0
  55. package/lib/browser/mcp/config/components/mcp-server-form.js +84 -0
  56. package/lib/browser/mcp/config/components/mcp-server-form.js.map +1 -0
  57. package/lib/browser/mcp/config/components/mcp-server-form.module.less +78 -0
  58. package/lib/browser/mcp/config/mcp-config.commands.d.ts +10 -0
  59. package/lib/browser/mcp/config/mcp-config.commands.d.ts.map +1 -0
  60. package/lib/browser/mcp/config/mcp-config.commands.js +35 -0
  61. package/lib/browser/mcp/config/mcp-config.commands.js.map +1 -0
  62. package/lib/browser/mcp/config/mcp-config.contribution.d.ts +16 -0
  63. package/lib/browser/mcp/config/mcp-config.contribution.d.ts.map +1 -0
  64. package/lib/browser/mcp/config/mcp-config.contribution.js +62 -0
  65. package/lib/browser/mcp/config/mcp-config.contribution.js.map +1 -0
  66. package/lib/browser/mcp/mcp-server-proxy.service.d.ts +6 -0
  67. package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
  68. package/lib/browser/mcp/mcp-server-proxy.service.js +10 -1
  69. package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -1
  70. package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -1
  71. package/lib/browser/mcp/mcp-server.feature.registry.js +3 -2
  72. package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -1
  73. package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -1
  74. package/lib/browser/mcp/tools/components/EditFile.js +41 -55
  75. package/lib/browser/mcp/tools/components/EditFile.js.map +1 -1
  76. package/lib/browser/mcp/tools/components/index.module.less +3 -22
  77. package/lib/browser/mcp/tools/editFile.js +1 -1
  78. package/lib/browser/mcp/tools/editFile.js.map +1 -1
  79. package/lib/browser/mcp/tools/handlers/EditFile.d.ts +1 -5
  80. package/lib/browser/mcp/tools/handlers/EditFile.d.ts.map +1 -1
  81. package/lib/browser/mcp/tools/handlers/EditFile.js +4 -4
  82. package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -1
  83. package/lib/browser/model/msg-history-manager.d.ts +0 -1
  84. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  85. package/lib/browser/model/msg-history-manager.js +2 -12
  86. package/lib/browser/model/msg-history-manager.js.map +1 -1
  87. package/lib/browser/preferences/schema.d.ts.map +1 -1
  88. package/lib/browser/preferences/schema.js +16 -0
  89. package/lib/browser/preferences/schema.js.map +1 -1
  90. package/lib/browser/types.d.ts +1 -1
  91. package/lib/browser/types.d.ts.map +1 -1
  92. package/lib/browser/widget/inline-diff/inline-diff-widget.module.less +0 -12
  93. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +0 -2
  94. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  95. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +4 -11
  96. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  97. package/lib/common/index.d.ts +8 -1
  98. package/lib/common/index.d.ts.map +1 -1
  99. package/lib/common/index.js +3 -1
  100. package/lib/common/index.js.map +1 -1
  101. package/lib/common/mcp-server-manager.d.ts +17 -1
  102. package/lib/common/mcp-server-manager.d.ts.map +1 -1
  103. package/lib/common/mcp-server-manager.js.map +1 -1
  104. package/lib/common/tool-invocation-registry.d.ts +2 -2
  105. package/lib/common/tool-invocation-registry.d.ts.map +1 -1
  106. package/lib/common/tool-invocation-registry.js +1 -1
  107. package/lib/common/tool-invocation-registry.js.map +1 -1
  108. package/lib/common/types.d.ts +6 -17
  109. package/lib/common/types.d.ts.map +1 -1
  110. package/lib/common/types.js.map +1 -1
  111. package/lib/common/utils.d.ts.map +1 -1
  112. package/lib/common/utils.js +2 -1
  113. package/lib/common/utils.js.map +1 -1
  114. package/lib/node/base-language-model.d.ts +1 -1
  115. package/lib/node/base-language-model.d.ts.map +1 -1
  116. package/lib/node/base-language-model.js +8 -58
  117. package/lib/node/base-language-model.js.map +1 -1
  118. package/lib/node/mcp/sumi-mcp-server.d.ts +17 -3
  119. package/lib/node/mcp/sumi-mcp-server.d.ts.map +1 -1
  120. package/lib/node/mcp/sumi-mcp-server.js +59 -6
  121. package/lib/node/mcp/sumi-mcp-server.js.map +1 -1
  122. package/lib/node/mcp-server-manager-impl.d.ts +4 -3
  123. package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
  124. package/lib/node/mcp-server-manager-impl.js +26 -6
  125. package/lib/node/mcp-server-manager-impl.js.map +1 -1
  126. package/lib/node/mcp-server.d.ts +5 -16
  127. package/lib/node/mcp-server.d.ts.map +1 -1
  128. package/lib/node/mcp-server.js +12 -6
  129. package/lib/node/mcp-server.js.map +1 -1
  130. package/lib/node/openai/openai-language-model.d.ts +4 -3
  131. package/lib/node/openai/openai-language-model.d.ts.map +1 -1
  132. package/lib/node/openai/openai-language-model.js +3 -2
  133. package/lib/node/openai/openai-language-model.js.map +1 -1
  134. package/package.json +27 -27
  135. package/src/browser/ai-core.contextkeys.ts +3 -3
  136. package/src/browser/ai-core.contribution.ts +14 -29
  137. package/src/browser/chat/chat-manager.service.ts +0 -12
  138. package/src/browser/chat/chat.internal.service.ts +0 -4
  139. package/src/browser/chat/chat.view.tsx +47 -0
  140. package/src/browser/components/ChatHistory.tsx +15 -21
  141. package/src/browser/contrib/inline-completions/inline-completions.controller.ts +1 -1
  142. package/src/browser/contrib/intelligent-completions/index.ts +5 -1
  143. package/src/browser/contrib/intelligent-completions/intelligent-completions.contribution.ts +3 -3
  144. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +6 -5
  145. package/src/browser/contrib/intelligent-completions/view/code-edits-previewer.ts +4 -2
  146. package/src/browser/contrib/intelligent-completions/view/default.ts +27 -19
  147. package/src/browser/index.ts +4 -0
  148. package/src/browser/mcp/base-apply.service.ts +213 -266
  149. package/src/browser/mcp/config/components/mcp-config.module.less +178 -0
  150. package/src/browser/mcp/config/components/mcp-config.view.tsx +215 -0
  151. package/src/browser/mcp/config/components/mcp-server-form.module.less +78 -0
  152. package/src/browser/mcp/config/components/mcp-server-form.tsx +144 -0
  153. package/src/browser/mcp/config/mcp-config.commands.ts +29 -0
  154. package/src/browser/mcp/config/mcp-config.contribution.ts +65 -0
  155. package/src/browser/mcp/mcp-server-proxy.service.ts +14 -2
  156. package/src/browser/mcp/mcp-server.feature.registry.ts +3 -2
  157. package/src/browser/mcp/tools/components/EditFile.tsx +60 -82
  158. package/src/browser/mcp/tools/components/index.module.less +3 -22
  159. package/src/browser/mcp/tools/editFile.ts +2 -2
  160. package/src/browser/mcp/tools/handlers/EditFile.ts +4 -4
  161. package/src/browser/model/msg-history-manager.ts +2 -12
  162. package/src/browser/preferences/schema.ts +16 -0
  163. package/src/browser/types.ts +1 -1
  164. package/src/browser/widget/inline-diff/inline-diff-widget.module.less +0 -12
  165. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +4 -13
  166. package/src/common/index.ts +7 -1
  167. package/src/common/mcp-server-manager.ts +17 -1
  168. package/src/common/tool-invocation-registry.ts +2 -2
  169. package/src/common/types.ts +6 -20
  170. package/src/common/utils.ts +3 -1
  171. package/src/node/base-language-model.ts +8 -67
  172. package/src/node/mcp/sumi-mcp-server.ts +67 -9
  173. package/src/node/mcp-server-manager-impl.ts +30 -9
  174. package/src/node/mcp-server.ts +11 -14
  175. package/src/node/openai/openai-language-model.ts +7 -4
  176. package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts +0 -6
  177. package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts.map +0 -1
  178. package/lib/browser/widget/inline-diff/inline-diff-manager.js +0 -27
  179. package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +0 -1
  180. package/src/browser/widget/inline-diff/inline-diff-manager.tsx +0 -38
  181. /package/lib/browser/components/{chat-history.module.less → chat-history.css} +0 -0
  182. /package/src/browser/components/{chat-history.module.less → chat-history.css} +0 -0
@@ -1,22 +1,16 @@
1
1
  import { createPatch } from 'diff';
2
2
 
3
3
  import { Autowired } from '@opensumi/di';
4
- import { AppConfig, IChatProgress, IMarker, MarkerSeverity, OnEvent, WithEventBus } from '@opensumi/ide-core-browser';
4
+ import { AppConfig, ChatMessageRole, IMarker, MarkerSeverity, OnEvent, WithEventBus } from '@opensumi/ide-core-browser';
5
5
  import { WorkbenchEditorService } from '@opensumi/ide-editor';
6
- import {
7
- EditorGroupCloseEvent,
8
- EditorGroupOpenEvent,
9
- RegisterEditorSideComponentEvent,
10
- } from '@opensumi/ide-editor/lib/browser';
6
+ import { EditorGroupCloseEvent } from '@opensumi/ide-editor/lib/browser';
11
7
  import { IMarkerService } from '@opensumi/ide-markers';
12
- import { ICodeEditor, Position, Range, Selection, SelectionDirection } from '@opensumi/ide-monaco';
13
- import { Deferred, Emitter, URI, path } from '@opensumi/ide-utils';
14
- import { SumiReadableStream } from '@opensumi/ide-utils/lib/stream';
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';
15
11
 
16
12
  import { IChatInternalService } from '../../common';
17
- import { CodeBlockData, CodeBlockStatus } from '../../common/types';
18
13
  import { ChatInternalService } from '../chat/chat.internal.service';
19
- import { InlineChatController } from '../widget/inline-chat/inline-chat-controller';
20
14
  import {
21
15
  BaseInlineDiffPreviewer,
22
16
  InlineDiffController,
@@ -47,9 +41,6 @@ export abstract class BaseApplyService extends WithEventBus {
47
41
  @Autowired(IMarkerService)
48
42
  private readonly markerService: IMarkerService;
49
43
 
50
- private onCodeBlockUpdateEmitter = new Emitter<CodeBlockData>();
51
- public onCodeBlockUpdate = this.onCodeBlockUpdateEmitter.event;
52
-
53
44
  constructor() {
54
45
  super();
55
46
  this.addDispose(
@@ -61,32 +52,23 @@ export abstract class BaseApplyService extends WithEventBus {
61
52
  this.chatInternalService.onRegenerateRequest(() => {
62
53
  const messages = this.chatInternalService.sessionModel.history.getMessages();
63
54
  const messageId = messages[messages.length - 1].id;
64
- const codeBlockMap = this.getMessageCodeBlocks(messageId);
65
- if (!codeBlockMap) {
66
- return;
67
- }
68
- Object.values(codeBlockMap).forEach((blockData) => {
69
- this.cancelApply(blockData);
70
- });
55
+ messageId && this.disposeApplyForMessage(messageId);
71
56
  }),
72
57
  );
73
58
  }
74
59
 
75
- private getMessageCodeBlocks(
76
- messageId: string,
77
- sessionId?: string,
78
- ): { [toolCallId: string]: CodeBlockData } | undefined {
79
- sessionId = sessionId || this.chatInternalService.sessionModel.sessionId;
80
- const sessionModel = this.chatInternalService.getSession(sessionId);
81
- if (!sessionModel) {
82
- throw new Error(`Session ${sessionId} not found`);
83
- }
84
- const message = sessionModel.history.getMessageAdditional(messageId);
85
- return message?.codeBlockMap;
86
- }
60
+ public readonly codeBlockMapObservable = observableValue<Map<string, CodeBlockData>>(this, new Map());
87
61
 
88
62
  private activePreviewer: BaseInlineDiffPreviewer<InlineStreamDiffHandler> | undefined;
89
63
 
64
+ private pendingApplyParams:
65
+ | {
66
+ relativePath: string;
67
+ newContent: string;
68
+ range?: Range;
69
+ }
70
+ | undefined;
71
+
90
72
  @OnEvent(EditorGroupCloseEvent)
91
73
  onEditorGroupClose(event: EditorGroupCloseEvent) {
92
74
  if (this.activePreviewer?.getNode()?.uri.path.toString() === event.payload.resource.uri.path.toString()) {
@@ -95,195 +77,187 @@ export abstract class BaseApplyService extends WithEventBus {
95
77
  }
96
78
  }
97
79
 
98
- @OnEvent(EditorGroupOpenEvent)
99
- async onEditorGroupOpen(event: EditorGroupOpenEvent) {
100
- if (!this.chatInternalService.sessionModel.history.getMessages().length) {
101
- return;
102
- }
103
- const relativePath = path.relative(this.appConfig.workspaceDir, event.payload.resource.uri.path.toString());
104
- const filePendingApplies = Object.values(
105
- this.getMessageCodeBlocks(this.chatInternalService.sessionModel.history.lastMessageId!) || {},
106
- ).filter((block) => block.relativePath === relativePath && block.status === 'pending');
107
- // TODO: 刷新后重新应用,事件无法恢复 & 恢复继续请求,需要改造成批量apply形式
108
- // TODO: 暂时只支持 pending 串行的 apply,后续支持批量apply后统一accept
109
- if (filePendingApplies.length > 0) {
110
- this.renderApplyResult(filePendingApplies[0], filePendingApplies[0].updatedCode!);
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;
111
86
  }
87
+ const blockId = this.generateBlockId(relativeOrAbsolutePath, messageId);
88
+ return this.codeBlockMapObservable.get().get(blockId);
112
89
  }
113
90
 
114
- getUriPendingCodeBlock(uri: URI): CodeBlockData | undefined {
115
- const messageId = this.chatInternalService.sessionModel.history.lastMessageId;
116
- if (!messageId) {
117
- return undefined;
118
- }
119
- const codeBlockMap = this.getMessageCodeBlocks(messageId);
120
- if (!codeBlockMap) {
121
- return undefined;
122
- }
123
- return Object.values(codeBlockMap).find(
124
- (block) =>
125
- block.relativePath === path.relative(this.appConfig.workspaceDir, uri.path.toString()) &&
126
- block.status === 'pending',
127
- );
91
+ getCodeBlockById(id: string): CodeBlockData | undefined {
92
+ return this.codeBlockMapObservable.get().get(id);
128
93
  }
129
94
 
130
- getCodeBlock(toolCallId: string, messageId?: string): CodeBlockData | undefined {
131
- messageId = messageId || this.chatInternalService.sessionModel.history.lastMessageId;
132
- if (!messageId) {
133
- throw new Error('Message ID is required');
134
- }
135
- const codeBlockMap = this.getMessageCodeBlocks(messageId);
136
- if (!codeBlockMap) {
137
- return undefined;
138
- }
139
- return codeBlockMap[toolCallId];
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
+ });
140
101
  }
141
102
 
142
- protected updateCodeBlock(codeBlock: CodeBlockData, messageId?: string) {
143
- messageId = messageId || this.chatInternalService.sessionModel.history.lastMessageId;
144
- if (!messageId) {
145
- throw new Error('Message ID is required');
146
- }
147
- const codeBlockMap = this.getMessageCodeBlocks(messageId);
148
- if (!codeBlockMap) {
149
- throw new Error('Code block not found');
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
+ });
150
118
  }
151
- codeBlockMap[codeBlock.toolCallId] = codeBlock;
152
- this.chatInternalService.sessionModel.history.setMessageAdditional(messageId, {
153
- codeBlockMap,
154
- });
155
- this.onCodeBlockUpdateEmitter.fire(codeBlock);
119
+
120
+ return blockId;
156
121
  }
157
122
 
158
- registerCodeBlock(relativePath: string, content: string, toolCallId: string): CodeBlockData {
159
- const lastMessageId = this.chatInternalService.sessionModel.history.lastMessageId!;
160
- const savedCodeBlockMap = this.getMessageCodeBlocks(lastMessageId) || {};
161
- const newBlock: CodeBlockData = {
162
- codeEdit: content,
163
- relativePath,
164
- status: 'generating' as CodeBlockStatus,
165
- iterationCount: 1,
166
- version: 1,
167
- createdAt: Date.now(),
168
- toolCallId,
169
- };
170
- const samePathCodeBlocks = Object.values(savedCodeBlockMap).filter((block) => block.relativePath === relativePath);
171
- if (samePathCodeBlocks.length > 0) {
172
- newBlock.version = samePathCodeBlocks.length;
173
- for (const block of samePathCodeBlocks.sort((a, b) => b.version - a.version)) {
174
- // 如果连续的上一个同文件apply结果存在LintError,则iterationCount++
175
- if (block.relativePath === relativePath && block.applyResult?.diagnosticInfos?.length) {
176
- newBlock.iterationCount++;
177
- } else {
178
- break;
179
- }
180
- }
123
+ initToolCallId(blockId: string, toolCallId: string): void {
124
+ const blockData = this.getCodeBlockById(blockId);
125
+ if (blockData && !blockData.initToolCallId) {
126
+ blockData.initToolCallId = toolCallId;
181
127
  }
182
- savedCodeBlockMap[toolCallId] = newBlock;
183
- this.chatInternalService.sessionModel.history.setMessageAdditional(lastMessageId, {
184
- codeBlockMap: savedCodeBlockMap,
185
- });
186
- this.onCodeBlockUpdateEmitter.fire(newBlock);
187
- return newBlock;
188
128
  }
189
129
 
190
130
  /**
191
131
  * Apply changes of a code block
192
132
  */
193
- async apply(codeBlock: CodeBlockData): Promise<CodeBlockData> {
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
+ }
194
138
  try {
195
- if (codeBlock.iterationCount > 3) {
196
- throw new Error('Lint error max iteration count exceeded');
197
- }
198
- const fastApplyFileResult = await this.doApply(codeBlock);
199
- if (!fastApplyFileResult.stream && !fastApplyFileResult.result) {
200
- throw new Error('No apply content provided');
201
- }
202
-
203
- // trigger diffPreivewer & return expected diff result directly
204
- const applyResult = await this.renderApplyResult(
205
- codeBlock,
206
- (fastApplyFileResult.result || fastApplyFileResult.stream)!,
207
- fastApplyFileResult.range,
208
- );
209
- if (applyResult) {
210
- // 用户实际接受的 apply 结果
211
- codeBlock.applyResult = applyResult;
212
- this.updateCodeBlock(codeBlock);
139
+ if (++blockData.iterationCount > 3) {
140
+ throw new Error('Max iteration count exceeded');
213
141
  }
214
-
215
- return codeBlock;
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;
216
149
  } catch (err) {
217
- codeBlock.status = 'failed';
218
- this.updateCodeBlock(codeBlock);
150
+ blockData.status = 'failed';
151
+ this.updateCodeBlock(blockData);
219
152
  throw err;
220
153
  }
221
154
  }
222
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
+
223
172
  async renderApplyResult(
224
- codeBlock: CodeBlockData,
225
- updatedContentOrStream: string | SumiReadableStream<IChatProgress>,
173
+ relativePath: string,
174
+ newContent: string,
226
175
  range?: Range,
227
176
  ): Promise<{ diff: string; diagnosticInfos: IMarker[] } | undefined> {
228
- const { relativePath } = codeBlock;
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
+ }
229
187
  const openResult = await this.editorService.open(URI.file(path.join(this.appConfig.workspaceDir, relativePath)));
230
188
  if (!openResult) {
231
189
  throw new Error('Failed to open editor');
232
190
  }
233
191
  const editor = openResult.group.codeEditor.monacoEditor;
234
192
  const inlineDiffController = InlineDiffController.get(editor)!;
235
- codeBlock.status = 'pending';
236
- // 强刷展示 manager 视图
237
- this.eventBus.fire(new RegisterEditorSideComponentEvent());
238
- this.updateCodeBlock(codeBlock);
193
+ blockData.status = 'pending';
194
+ this.updateCodeBlock(blockData);
239
195
 
240
- const fullOriginalContent = editor.getModel()!.getValue();
241
196
  range = range || editor.getModel()?.getFullModelRange()!;
242
- // const savedRangeContent = editor.getModel()!.getValueInRange(range);
243
-
244
- if (typeof updatedContentOrStream === 'string') {
245
- // Create diff previewer
246
- const previewer = inlineDiffController.createDiffPreviewer(
247
- editor,
248
- Selection.fromRange(range, SelectionDirection.LTR),
249
- {
250
- disposeWhenEditorClosed: true,
251
- renderRemovedWidgetImmediately: true,
252
- },
253
- ) as LiveInlineDiffPreviewer;
254
- // TODO: 支持多个diffPreviewer
255
- this.activePreviewer = previewer;
256
- codeBlock.updatedCode = updatedContentOrStream;
257
- previewer.setValue(updatedContentOrStream);
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();
258
214
  } else {
259
- const controller = new InlineChatController();
260
- controller.mountReadable(updatedContentOrStream);
261
- const inlineDiffHandler = InlineDiffController.get(editor)!;
262
-
263
- this.activePreviewer = inlineDiffHandler.showPreviewerByStream(editor, {
264
- crossSelection: Selection.fromRange(range, SelectionDirection.LTR),
265
- chatResponse: controller,
266
- previewerOptions: {
267
- disposeWhenEditorClosed: true,
268
- renderRemovedWidgetImmediately: false,
269
- },
270
- }) as LiveInlineDiffPreviewer;
215
+ previewer.setValue(newContent);
271
216
  this.addDispose(
272
- this.activePreviewer.getNode()!.onDiffFinished((diffModel) => {
273
- codeBlock.updatedCode = diffModel.newFullRangeTextLines.join('\n');
274
- this.updateCodeBlock(codeBlock);
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
+ }
275
249
  }),
276
250
  );
277
251
  }
278
-
279
- return this.listenPartialEdit(editor, codeBlock, fullOriginalContent);
252
+ return deferred.promise;
280
253
  }
281
254
 
282
255
  /**
283
256
  * Cancel an ongoing apply operation
284
257
  */
285
- cancelApply(blockData: CodeBlockData): void {
286
- if (blockData.status === 'generating' || blockData.status === 'pending') {
258
+ cancelApply(relativePath: string): void {
259
+ const blockData = this.getCodeBlock(relativePath);
260
+ if (blockData && (blockData.status === 'generating' || blockData.status === 'pending')) {
287
261
  if (this.activePreviewer) {
288
262
  this.activePreviewer.getNode()?.livePreviewDiffDecorationModel.discardUnProcessed();
289
263
  this.activePreviewer.dispose();
@@ -293,105 +267,62 @@ export abstract class BaseApplyService extends WithEventBus {
293
267
  }
294
268
  }
295
269
 
296
- // TODO: 目前的设计下,有一个工具 apply 没返回,是不会触发下一个的(cursor 是会全部自动 apply 的),所以这个方法目前还没有必要
297
270
  cancelAllApply(): void {
298
- const messageId = this.chatInternalService.sessionModel.history.lastMessageId!;
299
- const codeBlockMap = this.getMessageCodeBlocks(messageId);
300
- if (!codeBlockMap) {
301
- return;
302
- }
303
- Object.values(codeBlockMap).forEach((blockData) => {
304
- this.cancelApply(blockData);
271
+ this.codeBlockMapObservable.get().forEach((blockData) => {
272
+ if (blockData.status === 'generating' || blockData.status === 'pending') {
273
+ this.cancelApply(blockData.relativePath);
274
+ }
305
275
  });
306
276
  }
307
277
 
308
- revealApplyPosition(blockData: CodeBlockData): void {
309
- const hunkInfo = blockData.applyResult?.diff.split('\n').find((line) => line.startsWith('@@'));
310
- let startLine = 0;
311
- let endLine = 0;
312
- if (hunkInfo) {
313
- // 取改动后的区间
314
- const [, , , start, end] = hunkInfo.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
315
- startLine = parseInt(start, 10) - 1;
316
- endLine = parseInt(end, 10) - 1;
317
- }
318
- this.editorService.open(URI.file(path.join(this.appConfig.workspaceDir, blockData.relativePath)));
319
- const editor = this.editorService.currentEditor;
320
- if (editor) {
321
- editor.setSelection(new Selection(startLine, 0, endLine, 0));
322
- }
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
+ });
323
287
  }
324
288
 
325
- processAll(uri: URI, type: 'accept' | 'reject'): void {
326
- const codeBlock = this.getUriPendingCodeBlock(uri);
327
- if (!codeBlock) {
328
- throw new Error('No pending code block found');
329
- }
330
- const decorationModel = this.activePreviewer?.getNode()?.livePreviewDiffDecorationModel;
331
- if (!decorationModel) {
332
- throw new Error('No active previewer found');
333
- }
334
- if (type === 'accept') {
335
- decorationModel.acceptUnProcessed();
336
- } else {
337
- decorationModel.discardUnProcessed();
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
+ }
338
306
  }
339
- this.editorService.save(uri);
340
- codeBlock.status = type === 'accept' ? 'success' : 'cancelled';
341
- this.updateCodeBlock(codeBlock);
342
307
  }
343
308
 
344
- protected listenPartialEdit(editor: ICodeEditor, codeBlock: CodeBlockData, fullOriginalContent: string) {
345
- const deferred = new Deferred<{ diff: string; diagnosticInfos: IMarker[] }>();
346
- const toDispose = this.inlineDiffService.onPartialEdit((event) => {
347
- // TODO 支持自动保存
348
- if (event.totalPartialEditCount === event.resolvedPartialEditCount) {
349
- if (event.acceptPartialEditCount > 0) {
350
- codeBlock.status = 'success';
351
- const appliedResult = editor.getModel()!.getValue();
352
- const diffResult = createPatch(codeBlock.relativePath, fullOriginalContent, appliedResult)
353
- .split('\n')
354
- .slice(4)
355
- .join('\n');
356
- const rangesFromDiffHunk = diffResult
357
- .split('\n')
358
- .map((line) => {
359
- if (line.startsWith('@@')) {
360
- const [, , , start, end] = line.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
361
- return new Range(parseInt(start, 10), 0, parseInt(end, 10), 0);
362
- }
363
- return null;
364
- })
365
- .filter((range) => range !== null);
366
- const diagnosticInfos = this.getDiagnosticInfos(editor.getModel()!.uri.toString(), rangesFromDiffHunk);
367
- // 移除开头的几个固定信息,避免浪费 tokens
368
- deferred.resolve({
369
- diff: diffResult,
370
- diagnosticInfos,
371
- });
372
- } else {
373
- // 用户全部取消
374
- codeBlock.status = 'cancelled';
375
- deferred.resolve();
376
- }
377
- toDispose.dispose();
378
- }
379
- });
380
- return deferred.promise;
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 || '-'}`;
381
323
  }
382
324
 
383
- /**
384
- * Apply changes of a code block, return stream to render inline diff in stream mode, result to render inline diff directly
385
- * range is optional, if not provided, the result will be applied to the the full file
386
- */
387
- protected abstract doApply(codeBlock: CodeBlockData): Promise<{
388
- range?: Range;
389
- stream?: SumiReadableStream<IChatProgress, Error>;
390
- result?: string;
391
- }>;
392
-
393
- // TODO: 支持使用内存中的document获取诊断信息,实现并行apply accept
394
- protected getDiagnosticInfos(uri: string, ranges: Range[]) {
325
+ protected getdiagnosticInfos(uri: string, ranges: Range[]) {
395
326
  const markers = this.markerService.getManager().getMarkers({ resource: uri });
396
327
  return markers.filter(
397
328
  (marker) =>
@@ -400,3 +331,19 @@ export abstract class BaseApplyService extends WithEventBus {
400
331
  );
401
332
  }
402
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';