@opensumi/ide-ai-native 3.8.2-next-1741418699.0 → 3.8.2-next-1741622293.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 (53) hide show
  1. package/lib/browser/chat/apply.service.js +1 -1
  2. package/lib/browser/chat/apply.service.js.map +1 -1
  3. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  4. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  5. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  6. package/lib/browser/chat/chat.view.js +3 -2
  7. package/lib/browser/chat/chat.view.js.map +1 -1
  8. package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
  9. package/lib/browser/components/ChatToolRender.js +12 -18
  10. package/lib/browser/components/ChatToolRender.js.map +1 -1
  11. package/lib/browser/components/ChatToolRender.module.less +15 -27
  12. package/lib/browser/contrib/inline-completions/prompt/matcher.js +2 -2
  13. package/lib/browser/contrib/inline-completions/prompt/similarSnippets.d.ts +1 -1
  14. package/lib/browser/contrib/inline-completions/prompt/similarSnippets.js +2 -2
  15. package/lib/browser/contrib/intelligent-completions/view/default.d.ts.map +1 -1
  16. package/lib/browser/contrib/intelligent-completions/view/default.js.map +1 -1
  17. package/lib/browser/mcp/base-apply.service.d.ts +1 -1
  18. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  19. package/lib/browser/mcp/base-apply.service.js +5 -3
  20. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  21. package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -1
  22. package/lib/browser/mcp/config/components/mcp-config.view.js +7 -21
  23. package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -1
  24. package/lib/browser/mcp/config/components/mcp-server-form.d.ts.map +1 -1
  25. package/lib/browser/mcp/config/components/mcp-server-form.js +25 -33
  26. package/lib/browser/mcp/config/components/mcp-server-form.js.map +1 -1
  27. package/lib/browser/mcp/config/mcp-config.contribution.js.map +1 -1
  28. package/lib/browser/mcp/tools/handlers/EditFile.js +2 -2
  29. package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -1
  30. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  31. package/lib/browser/model/msg-history-manager.js.map +1 -1
  32. package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts.map +1 -1
  33. package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +1 -1
  34. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  35. package/lib/common/prompts/context-prompt-provider.js +4 -2
  36. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  37. package/package.json +23 -23
  38. package/src/browser/chat/apply.service.ts +1 -1
  39. package/src/browser/chat/chat-manager.service.ts +1 -2
  40. package/src/browser/chat/chat.view.tsx +5 -2
  41. package/src/browser/components/ChatToolRender.module.less +15 -27
  42. package/src/browser/components/ChatToolRender.tsx +12 -14
  43. package/src/browser/contrib/inline-completions/prompt/matcher.ts +2 -2
  44. package/src/browser/contrib/inline-completions/prompt/similarSnippets.ts +2 -2
  45. package/src/browser/contrib/intelligent-completions/view/default.ts +0 -1
  46. package/src/browser/mcp/base-apply.service.ts +10 -3
  47. package/src/browser/mcp/config/components/mcp-config.view.tsx +9 -24
  48. package/src/browser/mcp/config/components/mcp-server-form.tsx +54 -68
  49. package/src/browser/mcp/config/mcp-config.contribution.ts +1 -1
  50. package/src/browser/mcp/tools/handlers/EditFile.ts +2 -2
  51. package/src/browser/model/msg-history-manager.ts +1 -3
  52. package/src/browser/widget/inline-diff/inline-diff-manager.tsx +0 -1
  53. package/src/common/prompts/context-prompt-provider.ts +6 -2
@@ -5,7 +5,6 @@ import { useInjectable } from '@opensumi/ide-core-browser';
5
5
  import { Icon } from '@opensumi/ide-core-browser/lib/components';
6
6
  import { Loading } from '@opensumi/ide-core-browser/lib/components/ai-native';
7
7
  import { IChatToolContent, uuid } from '@opensumi/ide-core-common';
8
- import { localize } from '@opensumi/ide-core-common/lib/localize';
9
8
 
10
9
  import { IMCPServerRegistry, TokenMCPServerRegistry } from '../types';
11
10
 
@@ -65,29 +64,28 @@ export const ChatToolRender = (props: { value: IChatToolContent['content']; mess
65
64
  toolCallId={value.id}
66
65
  />
67
66
  ) : (
68
- <div className={styles.chat_tool_render}>
69
- <div className={styles.tool_header} onClick={toggleExpand}>
70
- <div className={styles.tool_name}>
71
- <Icon iconClass={`codicon codicon-chevron-${isExpanded ? 'down' : 'right'}`} />
72
- <Icon size='small' iconClass={cls('codicon codicon-tools', styles.tool_icon)} />
73
- <span className={styles.tool_label}>{label}</span>
67
+ <div className={styles['chat-tool-render']}>
68
+ <div className={styles['tool-header']} onClick={toggleExpand}>
69
+ <div className={styles['tool-name']}>
70
+ <Icon iconClass={`codicon codicon-triangle-${isExpanded ? 'down' : 'right'}`} />
71
+ {label}
74
72
  </div>
75
73
  {value.state && (
76
- <div className={styles.tool_state}>
77
- <span className={styles.state_icon}>{stateInfo.icon}</span>
74
+ <div className={styles['tool-state']}>
75
+ <span className={styles['state-icon']}>{stateInfo.icon}</span>
78
76
  </div>
79
77
  )}
80
78
  </div>
81
- <div className={cls(styles.tool_content, { [styles.expanded]: isExpanded })}>
79
+ <div className={cls(styles['tool-content'], { [styles.expanded]: isExpanded })}>
82
80
  {value?.function?.arguments && (
83
- <div className={styles.tool_arguments}>
84
- <div className={styles.section_label}>{localize('ai.native.mcp.tool.arguments')}:</div>
81
+ <div className={styles['tool-arguments']}>
82
+ <div className={styles['section-label']}>Arguments</div>
85
83
  <CodeEditorWithHighlight input={value?.function?.arguments} language={'json'} relationId={uuid(4)} />
86
84
  </div>
87
85
  )}
88
86
  {value?.result && (
89
- <div className={styles.tool_result}>
90
- <div className={styles.section_label}>{localize('ai.native.mcp.tool.results')}:</div>
87
+ <div className={styles['tool-result']}>
88
+ <div className={styles['section-label']}>Result</div>
91
89
  <CodeEditorWithHighlight input={value.result} language={'json'} relationId={uuid(4)} />
92
90
  </div>
93
91
  )}
@@ -244,8 +244,8 @@ export abstract class WindowedMatcher {
244
244
  tokens.slice(startLine, endLine).forEach((token) => token.forEach((word) => size.add(word)));
245
245
  cache.push(size);
246
246
  }
247
- const traget = cache[index];
248
- const score = this.similarityScore(traget, this.referenceTokens);
247
+ const target = cache[index];
248
+ const score = this.similarityScore(target, this.referenceTokens);
249
249
  snippets.push({
250
250
  score,
251
251
  startLine,
@@ -15,9 +15,9 @@ import {
15
15
  import { MAX_NEIGHBOR_AGGREGATE_LENGTH } from './const';
16
16
  import { FixedWindowSizeJaccardMatcher } from './jaccardMatcher';
17
17
 
18
- export const getOpenedTabFileList = (docuemnts: IEditorDocumentModel[]) => {
18
+ export const getOpenedTabFileList = (documents: IEditorDocumentModel[]) => {
19
19
  // 过滤超大文档
20
- const recentFiles = docuemnts.filter((document) => isDocumentValid(document));
20
+ const recentFiles = documents.filter((document) => isDocumentValid(document));
21
21
  return recentFiles;
22
22
  };
23
23
 
@@ -29,7 +29,6 @@ import {
29
29
 
30
30
  import { CodeEditsResultValue, ICodeEdit, ICodeEditsResult } from '../index';
31
31
 
32
-
33
32
  import { BaseCodeEditsView } from './base';
34
33
 
35
34
  /**
@@ -225,7 +225,12 @@ export abstract class BaseApplyService extends WithEventBus {
225
225
  this.onCodeBlockUpdateEmitter.fire(codeBlock);
226
226
  }
227
227
 
228
- async registerCodeBlock(relativePath: string, content: string, toolCallId: string): Promise<CodeBlockData> {
228
+ async registerCodeBlock(
229
+ relativePath: string,
230
+ content: string,
231
+ toolCallId: string,
232
+ instructions?: string,
233
+ ): Promise<CodeBlockData> {
229
234
  const lastMessageId = this.chatInternalService.sessionModel.history.lastMessageId!;
230
235
  const uriCodeBlocks = this.getUriCodeBlocks(URI.file(path.join(this.appConfig.workspaceDir, relativePath)));
231
236
  const originalModelRef = await this.editorDocumentModelService.createModelReference(
@@ -240,6 +245,7 @@ export abstract class BaseApplyService extends WithEventBus {
240
245
  createdAt: Date.now(),
241
246
  toolCallId,
242
247
  messageId: lastMessageId,
248
+ instructions,
243
249
  // TODO: 支持range
244
250
  originalCode: originalModelRef.instance.getText(),
245
251
  };
@@ -565,8 +571,8 @@ export abstract class BaseApplyService extends WithEventBus {
565
571
  .split('\n')
566
572
  .map((line) => {
567
573
  if (line.startsWith('@@')) {
568
- const [, , , start, end] = line.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
569
- return new Range(parseInt(start, 10), 0, parseInt(end, 10), 0);
574
+ const [, , , start, lineCount] = line.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
575
+ return new Range(parseInt(start, 10), 0, parseInt(start, 10) + parseInt(lineCount, 10) - 1, 0);
570
576
  }
571
577
  return null;
572
578
  })
@@ -587,6 +593,7 @@ export abstract class BaseApplyService extends WithEventBus {
587
593
  result?: string;
588
594
  }>;
589
595
 
596
+ // FIXME: 貌似筛选逻辑不太对,需要重构
590
597
  // TODO: 支持使用内存中的document获取诊断信息,实现并行apply accept
591
598
  protected getDiagnosticInfos(uri: string, ranges: Range[]) {
592
599
  const markers = this.markerService.getManager().getMarkers({ resource: uri });
@@ -4,8 +4,7 @@ import React, { useCallback } from 'react';
4
4
  import { Badge } from '@opensumi/ide-components';
5
5
  import { AINativeSettingSectionsId, ILogger, useInjectable } from '@opensumi/ide-core-browser';
6
6
  import { PreferenceService } from '@opensumi/ide-core-browser/lib/preferences';
7
- import { PreferenceScope, localize } from '@opensumi/ide-core-common';
8
- import { IMessageService } from '@opensumi/ide-overlay';
7
+ import { localize } from '@opensumi/ide-core-common';
9
8
 
10
9
  import { BUILTIN_MCP_SERVER_NAME, ISumiMCPServerBackend, SumiMCPServerProxyServicePath } from '../../../../common';
11
10
  import { MCPServerDescription } from '../../../../common/mcp-server-manager';
@@ -18,13 +17,12 @@ import { MCPServerForm, MCPServerFormData } from './mcp-server-form';
18
17
  export const MCPConfigView: React.FC = () => {
19
18
  const mcpServerProxyService = useInjectable<MCPServerProxyService>(MCPServerProxyService);
20
19
  const preferenceService = useInjectable<PreferenceService>(PreferenceService);
21
- const messageService = useInjectable<IMessageService>(IMessageService);
22
20
  const sumiMCPServerBackendProxy = useInjectable<ISumiMCPServerBackend>(SumiMCPServerProxyServicePath);
23
21
  const logger = useInjectable<ILogger>(ILogger);
24
22
  const [servers, setServers] = React.useState<MCPServer[]>([]);
25
23
  const [formVisible, setFormVisible] = React.useState(false);
26
24
  const [editingServer, setEditingServer] = React.useState<MCPServerFormData | undefined>();
27
- const [loadingServer, setLoadingServer] = React.useState<string | undefined>();
25
+
28
26
  const loadServers = useCallback(async () => {
29
27
  const allServers = await mcpServerProxyService.$getServers();
30
28
  setServers(allServers);
@@ -44,7 +42,6 @@ export const MCPConfigView: React.FC = () => {
44
42
  const handleServerControl = useCallback(
45
43
  async (serverName: string, start: boolean) => {
46
44
  try {
47
- setLoadingServer(serverName);
48
45
  if (start) {
49
46
  await mcpServerProxyService.$startServer(serverName);
50
47
  } else {
@@ -88,14 +85,10 @@ export const MCPConfigView: React.FC = () => {
88
85
  });
89
86
  }
90
87
 
91
- await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers, PreferenceScope.User);
88
+ await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers);
92
89
  await loadServers();
93
- setLoadingServer(undefined);
94
90
  } catch (error) {
95
- const msg = error.message || error;
96
91
  logger.error(`Failed to ${start ? 'start' : 'stop'} server ${serverName}:`, error);
97
- messageService.error(error.message);
98
- setLoadingServer(undefined);
99
92
  }
100
93
  },
101
94
  [mcpServerProxyService, preferenceService, sumiMCPServerBackendProxy, loadServers],
@@ -124,7 +117,7 @@ export const MCPConfigView: React.FC = () => {
124
117
  const servers = preferenceService.get<MCPServerFormData[]>(AINativeSettingSectionsId.MCPServers, []);
125
118
  const updatedServers = servers.filter((s) => s.name !== serverName);
126
119
  sumiMCPServerBackendProxy.removeServer(serverName);
127
- await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers, PreferenceScope.User);
120
+ await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers);
128
121
  await loadServers();
129
122
  },
130
123
  [editingServer, formVisible],
@@ -143,7 +136,7 @@ export const MCPConfigView: React.FC = () => {
143
136
  setServers(servers as MCPServer[]);
144
137
  setFormVisible(false);
145
138
  await sumiMCPServerBackendProxy.addOrUpdateServer(data as MCPServerDescription);
146
- await preferenceService.set(AINativeSettingSectionsId.MCPServers, servers, PreferenceScope.User);
139
+ await preferenceService.set(AINativeSettingSectionsId.MCPServers, servers);
147
140
  await loadServers();
148
141
  },
149
142
  [servers, formVisible, loadServers],
@@ -167,10 +160,10 @@ export const MCPConfigView: React.FC = () => {
167
160
  <div className={styles.header}>
168
161
  <div>
169
162
  <h2 className={styles.title}>MCP Servers</h2>
170
- <p className={styles.description}>{localize('ai.native.mcp.manage.connections')}</p>
163
+ <p className={styles.description}>Manage your MCP server connections.</p>
171
164
  </div>
172
165
  <button className={styles.addButton} onClick={handleAddServer}>
173
- + {localize('ai.native.mcp.addMCPServer.title')}
166
+ + Add new MCP server
174
167
  </button>
175
168
  </div>
176
169
  <div className={styles.serversList}>
@@ -191,15 +184,7 @@ export const MCPConfigView: React.FC = () => {
191
184
  title={server.isStarted ? 'Stop' : 'Start'}
192
185
  onClick={() => handleServerControl(server.name, !server.isStarted)}
193
186
  >
194
- <i
195
- className={`codicon ${
196
- loadingServer === server.name
197
- ? 'codicon-loading kt-icon-loading'
198
- : server.isStarted
199
- ? 'codicon-debug-stop'
200
- : 'codicon-debug-start'
201
- }`}
202
- />
187
+ <i className={`codicon ${server.isStarted ? 'codicon-debug-stop' : 'codicon-debug-start'}`} />
203
188
  </button>
204
189
  {server.name !== BUILTIN_MCP_SERVER_NAME && (
205
190
  <button className={styles.iconButton} title='Delete' onClick={() => handleDeleteServer(server.name)}>
@@ -212,7 +197,7 @@ export const MCPConfigView: React.FC = () => {
212
197
  <div className={styles.detailRow}>
213
198
  <span className={styles.detailLabel}>Status:</span>
214
199
  <span className={`${styles.serverStatus} ${server.isStarted ? styles.running : styles.stopped}`}>
215
- {server.isStarted ? localize('ai.native.mcp.running') : localize('ai.native.mcp.stopped')}
200
+ {server.isStarted ? 'Running' : 'Stopped'}
216
201
  </span>
217
202
  </div>
218
203
  {server.type && (
@@ -67,75 +67,35 @@ export const MCPServerForm: FC<Props> = ({ visible, initialData, onSave, onCance
67
67
  );
68
68
  }, [initialData]);
69
69
 
70
- const validateForm = useCallback(
71
- (formData: MCPServerFormData) => {
72
- if (formData.name.trim() === '') {
73
- messageService.error(localize('ai.native.mcp.name.isRequired'));
74
- return false;
75
- }
76
- if (
77
- !initialData &&
78
- existingServers.some((server) => server.name.toLocaleLowerCase() === formData.name.toLocaleLowerCase())
79
- ) {
80
- messageService.error(formatLocalize('ai.native.mcp.serverNameExists', formData.name));
81
- return false;
82
- }
83
- if (formData.type === MCP_SERVER_TYPE.SSE) {
84
- const isServerHostValid = formData.serverHost?.trim() !== '';
85
- if (!isServerHostValid) {
86
- messageService.error(localize('ai.native.mcp.serverHost.isRequired'));
87
- }
88
- return isServerHostValid;
89
- }
90
- const isCommandValid = formData.command?.trim() !== '';
91
- if (!isCommandValid) {
92
- messageService.error(localize('ai.native.mcp.command.isRequired'));
93
- }
94
- return isCommandValid;
95
- },
96
- [existingServers, initialData],
97
- );
98
-
99
- const handleSubmit = useCallback(
100
- (e: FormEvent) => {
101
- e.preventDefault();
102
- const isValid = validateForm(formData);
103
- if (!isValid) {
104
- return;
105
- }
106
- const form = {
107
- ...formData,
108
- };
109
- if (formData.type === MCP_SERVER_TYPE.SSE) {
110
- form.serverHost = form.serverHost?.trim();
111
- } else {
112
- const args = argsText.split(' ').filter(Boolean);
113
- const env = envText
114
- .split('\n')
115
- .filter(Boolean)
116
- .reduce((acc, line) => {
117
- const [key, value] = line.split('=');
118
- if (key && value) {
119
- acc[key.trim()] = value.trim();
120
- }
121
- return acc;
122
- }, {} as Record<string, string>);
123
- form.args = args;
124
- form.env = env;
125
- }
126
-
127
- setFormData({
128
- ...formData,
129
- command: '',
130
- serverHost: '',
131
- args: [],
132
- env: {},
133
- });
70
+ const handleSubmit = (e: FormEvent) => {
71
+ e.preventDefault();
72
+ const isValid = validateForm(formData);
73
+ if (!isValid) {
74
+ return;
75
+ }
76
+ const form = {
77
+ ...formData,
78
+ };
79
+ if (formData.type === MCP_SERVER_TYPE.SSE) {
80
+ form.serverHost = form.serverHost?.trim();
81
+ } else {
82
+ const args = argsText.split(' ').filter(Boolean);
83
+ const env = envText
84
+ .split('\n')
85
+ .filter(Boolean)
86
+ .reduce((acc, line) => {
87
+ const [key, value] = line.split('=');
88
+ if (key && value) {
89
+ acc[key.trim()] = value.trim();
90
+ }
91
+ return acc;
92
+ }, {} as Record<string, string>);
93
+ form.args = args;
94
+ form.env = env;
95
+ }
134
96
 
135
- onSave(form);
136
- },
137
- [formData, argsText, envText, onSave, validateForm],
138
- );
97
+ onSave(form);
98
+ };
139
99
 
140
100
  const handleCommandChange = useCallback(
141
101
  (e: ChangeEvent<HTMLInputElement>) => {
@@ -223,6 +183,32 @@ export const MCPServerForm: FC<Props> = ({ visible, initialData, onSave, onCance
223
183
  }
224
184
  }, [formData, argsText, envText]);
225
185
 
186
+ const validateForm = useCallback(
187
+ (formData: MCPServerFormData) => {
188
+ if (formData.name.trim() === '') {
189
+ messageService.error(localize('ai.native.mcp.name.isRequired'));
190
+ return false;
191
+ }
192
+ if (existingServers.some((server) => server.name.toLocaleLowerCase() === formData.name.toLocaleLowerCase())) {
193
+ messageService.error(formatLocalize('ai.native.mcp.serverNameExists', formData.name));
194
+ return false;
195
+ }
196
+ if (formData.type === MCP_SERVER_TYPE.SSE) {
197
+ const isServerHostValid = formData.serverHost?.trim() !== '';
198
+ if (!isServerHostValid) {
199
+ messageService.error(localize('ai.native.mcp.serverHost.isRequired'));
200
+ }
201
+ return isServerHostValid;
202
+ }
203
+ const isCommandValid = formData.command?.trim() !== '';
204
+ if (!isCommandValid) {
205
+ messageService.error(localize('ai.native.mcp.command.isRequired'));
206
+ }
207
+ return isCommandValid;
208
+ },
209
+ [existingServers],
210
+ );
211
+
226
212
  return (
227
213
  <Modal
228
214
  title={initialData ? localize('ai.native.mcp.editMCPServer.title') : localize('ai.native.mcp.addMCPServer.title')}
@@ -1,6 +1,6 @@
1
1
  import { Autowired } from '@opensumi/di';
2
2
  import { LabelService } from '@opensumi/ide-core-browser/lib/services';
3
- import { Domain, URI } from '@opensumi/ide-core-common';
3
+ import { Domain, Schemes, URI } from '@opensumi/ide-core-common';
4
4
  import {
5
5
  BrowserEditorContribution,
6
6
  EditorComponentRegistry,
@@ -13,8 +13,8 @@ export class EditFileHandler {
13
13
  private applyService: BaseApplyService;
14
14
 
15
15
  async handler(params: { targetFile: string; codeEdit: string; instructions?: string }, toolCallId: string) {
16
- const { targetFile, codeEdit } = params;
17
- const block = await this.applyService.registerCodeBlock(targetFile, codeEdit, toolCallId);
16
+ const { targetFile, codeEdit, instructions } = params;
17
+ const block = await this.applyService.registerCodeBlock(targetFile, codeEdit, toolCallId, instructions);
18
18
  const blockData = await this.applyService.apply(block);
19
19
  return blockData;
20
20
  }
@@ -1,7 +1,5 @@
1
- import { Injectable } from '@opensumi/di';
2
1
  import { Disposable, Emitter, Event, uuid } from '@opensumi/ide-core-common';
3
- import { ChatMessageRole } from '@opensumi/ide-core-common/lib/types/ai-native';
4
- import { IHistoryChatMessage } from '@opensumi/ide-core-common/lib/types/ai-native';
2
+ import { ChatMessageRole, IHistoryChatMessage } from '@opensumi/ide-core-common/lib/types/ai-native';
5
3
 
6
4
  type IExcludeMessage = Omit<IHistoryChatMessage, 'id' | 'order'>;
7
5
 
@@ -3,7 +3,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { Icon, Popover } from '@opensumi/ide-components';
4
4
  import { AppConfig, IDisposable, URI, localize, path, useInjectable } from '@opensumi/ide-core-browser';
5
5
  import { IResource, WorkbenchEditorService } from '@opensumi/ide-editor';
6
- import { Path } from '@opensumi/ide-utils/lib/path';
7
6
 
8
7
  import { BaseApplyService } from '../../mcp/base-apply.service';
9
8
 
@@ -42,11 +42,15 @@ ${context.recentlyViewFiles.map((file, idx) => ` ${idx + 1}: ${file}`).join('
42
42
  `,
43
43
  )}
44
44
  </attached_files>
45
- ${currentModel ? `<current_opened_file>
45
+ ${
46
+ currentModel
47
+ ? `<current_opened_file>
46
48
  \`\`\`${currentModel.languageId} ${currentModel.uri.toString()}
47
49
  ${currentModel.getText()}
48
50
  \`\`\`
49
- </current_opened_file>` : ''}
51
+ </current_opened_file>`
52
+ : ''
53
+ }
50
54
  </additional_data>
51
55
  <user_query>
52
56
  ${userMessage}