@opensumi/ide-ai-native 3.9.1-next-1749115679.0 → 3.9.1-next-1749175927.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 (68) hide show
  1. package/lib/browser/chat/chat-manager.service.d.ts +1 -0
  2. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  3. package/lib/browser/chat/chat-manager.service.js +9 -3
  4. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  5. package/lib/browser/chat/chat-model.d.ts +8 -1
  6. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  7. package/lib/browser/chat/chat-model.js +113 -78
  8. package/lib/browser/chat/chat-model.js.map +1 -1
  9. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  10. package/lib/browser/chat/chat.view.js +32 -16
  11. package/lib/browser/chat/chat.view.js.map +1 -1
  12. package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -1
  13. package/lib/browser/mcp/mcp-server.feature.registry.js +1 -7
  14. package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -1
  15. package/lib/browser/mcp/tools/createNewFileWithText.d.ts +3 -1
  16. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  17. package/lib/browser/mcp/tools/createNewFileWithText.js +40 -14
  18. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  19. package/lib/browser/mcp/tools/fileSearch.d.ts.map +1 -1
  20. package/lib/browser/mcp/tools/fileSearch.js +5 -9
  21. package/lib/browser/mcp/tools/fileSearch.js.map +1 -1
  22. package/lib/browser/mcp/tools/grepSearch.d.ts.map +1 -1
  23. package/lib/browser/mcp/tools/grepSearch.js +10 -22
  24. package/lib/browser/mcp/tools/grepSearch.js.map +1 -1
  25. package/lib/browser/mcp/tools/handlers/ListDir.js +1 -1
  26. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  27. package/lib/browser/mcp/tools/handlers/ReadFile.js +1 -1
  28. package/lib/browser/mcp/tools/handlers/ReadFile.js.map +1 -1
  29. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts +1 -11
  30. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
  31. package/lib/browser/mcp/tools/handlers/RunCommand.js +4 -11
  32. package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
  33. package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
  34. package/lib/browser/mcp/tools/listDir.js +15 -19
  35. package/lib/browser/mcp/tools/listDir.js.map +1 -1
  36. package/lib/browser/model/msg-history-manager.d.ts +47 -1
  37. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  38. package/lib/browser/model/msg-history-manager.js +127 -2
  39. package/lib/browser/model/msg-history-manager.js.map +1 -1
  40. package/lib/browser/types.d.ts +4 -0
  41. package/lib/browser/types.d.ts.map +1 -1
  42. package/lib/browser/types.js.map +1 -1
  43. package/lib/node/mcp-server.sse.d.ts +1 -187
  44. package/lib/node/mcp-server.sse.d.ts.map +1 -1
  45. package/lib/node/mcp-server.sse.js +2 -2
  46. package/lib/node/mcp-server.sse.js.map +1 -1
  47. package/lib/node/mcp-server.stdio.d.ts +1 -187
  48. package/lib/node/mcp-server.stdio.d.ts.map +1 -1
  49. package/package.json +26 -26
  50. package/src/browser/chat/chat-manager.service.ts +16 -7
  51. package/src/browser/chat/chat-model.ts +130 -75
  52. package/src/browser/chat/chat.view.tsx +46 -14
  53. package/src/browser/mcp/mcp-server.feature.registry.ts +1 -6
  54. package/src/browser/mcp/tools/createNewFileWithText.ts +46 -17
  55. package/src/browser/mcp/tools/fileSearch.ts +5 -8
  56. package/src/browser/mcp/tools/grepSearch.ts +21 -32
  57. package/src/browser/mcp/tools/handlers/ListDir.ts +2 -2
  58. package/src/browser/mcp/tools/handlers/ReadFile.ts +2 -2
  59. package/src/browser/mcp/tools/handlers/RunCommand.ts +14 -21
  60. package/src/browser/mcp/tools/listDir.ts +12 -15
  61. package/src/browser/model/msg-history-manager.ts +181 -2
  62. package/src/browser/types.ts +6 -0
  63. package/src/node/mcp-server.sse.ts +2 -1
  64. package/lib/browser/mcp/tools/handlers/CreateNewFileWithText.d.ts +0 -15
  65. package/lib/browser/mcp/tools/handlers/CreateNewFileWithText.d.ts.map +0 -1
  66. package/lib/browser/mcp/tools/handlers/CreateNewFileWithText.js +0 -53
  67. package/lib/browser/mcp/tools/handlers/CreateNewFileWithText.js.map +0 -1
  68. package/src/browser/mcp/tools/handlers/CreateNewFileWithText.ts +0 -49
@@ -15,12 +15,13 @@ import {
15
15
  StorageProvider,
16
16
  debounce,
17
17
  } from '@opensumi/ide-core-common';
18
- import { IHistoryChatMessage } from '@opensumi/ide-core-common/lib/types/ai-native';
18
+ import { ChatFeatureRegistryToken, IHistoryChatMessage } from '@opensumi/ide-core-common/lib/types/ai-native';
19
19
 
20
20
  import { IChatAgentService, IChatFollowup, IChatRequestMessage, IChatResponseErrorDetails } from '../../common';
21
21
  import { MsgHistoryManager } from '../model/msg-history-manager';
22
22
 
23
23
  import { ChatModel, ChatRequestModel, ChatResponseModel, IChatProgressResponseContent } from './chat-model';
24
+ import { ChatFeatureRegistry } from './chat.feature.registry';
24
25
 
25
26
  interface ISessionModel {
26
27
  sessionId: string;
@@ -78,17 +79,23 @@ export class ChatManagerService extends Disposable {
78
79
  @Autowired(PreferenceService)
79
80
  private preferenceService: PreferenceService;
80
81
 
82
+ @Autowired(ChatFeatureRegistryToken)
83
+ private chatFeatureRegistry: ChatFeatureRegistry;
84
+
81
85
  private _chatStorage: IStorage;
82
86
 
83
87
  protected fromJSON(data: ISessionModel[]) {
84
88
  return data
85
89
  .filter((item) => item.history.messages.length > 0)
86
90
  .map((item) => {
87
- const model = new ChatModel({
88
- sessionId: item.sessionId,
89
- history: new MsgHistoryManager(item.history),
90
- modelId: item.modelId,
91
- });
91
+ const model = new ChatModel(
92
+ this.chatFeatureRegistry,
93
+ {
94
+ sessionId: item.sessionId,
95
+ history: new MsgHistoryManager(this.chatFeatureRegistry, item.history),
96
+ modelId: item.modelId,
97
+ },
98
+ );
92
99
  const requests = item.requests.map(
93
100
  (request) =>
94
101
  new ChatRequestModel(
@@ -131,7 +138,9 @@ export class ChatManagerService extends Disposable {
131
138
  }
132
139
 
133
140
  startSession() {
134
- const model = new ChatModel();
141
+ const model = new ChatModel(
142
+ this.chatFeatureRegistry,
143
+ );
135
144
  this.#sessionModels.set(model.sessionId, model);
136
145
  this.listenSession(model);
137
146
  return model;
@@ -28,7 +28,7 @@ import {
28
28
  import { MsgHistoryManager } from '../model/msg-history-manager';
29
29
  import { IChatSlashCommandItem } from '../types';
30
30
 
31
- import type { ImagePart, TextPart, ToolCallPart } from 'ai';
31
+ import { ChatFeatureRegistry } from './chat.feature.registry';
32
32
 
33
33
  export type IChatProgressResponseContent =
34
34
  | IChatMarkdownContent
@@ -296,10 +296,13 @@ export class ChatRequestModel implements IChatRequestModel {
296
296
  export class ChatModel extends Disposable implements IChatModel {
297
297
  private requestIdPool = 0;
298
298
 
299
- constructor(initParams?: { sessionId?: string; history?: MsgHistoryManager; modelId?: string }) {
299
+ constructor(
300
+ private chatFeatureRegistry: ChatFeatureRegistry,
301
+ initParams?: { sessionId?: string; history?: MsgHistoryManager; modelId?: string },
302
+ ) {
300
303
  super();
301
304
  this.#sessionId = initParams?.sessionId ?? uuid();
302
- this.history = initParams?.history ?? new MsgHistoryManager();
305
+ this.history = initParams?.history ?? new MsgHistoryManager(this.chatFeatureRegistry);
303
306
  this.#modelId = initParams?.modelId;
304
307
  }
305
308
 
@@ -336,90 +339,142 @@ export class ChatModel extends Disposable implements IChatModel {
336
339
  this.#modelId = modelId;
337
340
  }
338
341
 
339
- getMessageHistory(contextWindow?: number) {
342
+ private processMemorySummaries(): CoreMessage[] {
343
+ const memorySummaries = this.history.getMemorySummaries();
344
+ if (memorySummaries.length === 0) {
345
+ return [];
346
+ }
347
+
348
+ const processedSummaries = memorySummaries
349
+ .map((summary) => {
350
+ try {
351
+ const parsed = JSON.parse(summary.content);
352
+ return parsed.memory || parsed.content || summary.content;
353
+ } catch {
354
+ return summary.content;
355
+ }
356
+ })
357
+ .filter((content) => content && content !== 'no_memory_needed')
358
+ .filter((content, index, self) => self.indexOf(content) === index);
359
+
360
+ return processedSummaries.length > 0
361
+ ? [
362
+ {
363
+ role: 'system',
364
+ content: '以下是之前对话的总结:\n' + processedSummaries.join('\n\n'),
365
+ },
366
+ ]
367
+ : [];
368
+ }
369
+
370
+ private processToolCall(part: IChatToolContent, history: CoreMessage[]): void {
371
+ if (history[history.length - 1].role !== 'assistant') {
372
+ history.push({
373
+ role: 'assistant',
374
+ content: [],
375
+ });
376
+ }
377
+
378
+ const toolCallId = part.content.id;
379
+ const toolCallInfo = {
380
+ id: toolCallId,
381
+ name: part.content.function.name,
382
+ args: this.parseJsonSafely(part.content.function.arguments || '{}', 'tool call arguments'),
383
+ result: this.parseJsonSafely(part.content.result || '{}', 'tool result'),
384
+ };
385
+
386
+ this.history.addToolCall(toolCallInfo);
387
+ this.history.setMessageAdditional(part.content.id, { toolCallId });
388
+
389
+ const lastMessage = history[history.length - 1];
390
+ lastMessage.content = [
391
+ {
392
+ type: 'tool-call',
393
+ toolCallId: part.content.id,
394
+ toolName: part.content.function.name,
395
+ args: toolCallInfo.args,
396
+ },
397
+ ];
398
+
399
+ history.push({
400
+ role: 'tool',
401
+ content: [
402
+ {
403
+ type: 'tool-result',
404
+ toolCallId: part.content.id,
405
+ toolName: part.content.function.name,
406
+ result: toolCallInfo.result,
407
+ },
408
+ ],
409
+ });
410
+ }
411
+
412
+ private parseJsonSafely(jsonString: string, context: string): any {
413
+ try {
414
+ return JSON.parse(jsonString);
415
+ } catch (e) {
416
+ console.error(`[ChatModel] Failed to parse ${context}:`, e);
417
+ return {};
418
+ }
419
+ }
420
+
421
+ private processRecentMessages(): CoreMessage[] {
340
422
  const history: CoreMessage[] = [];
341
- for (const request of this.requests) {
342
- if (!request.response.isComplete) {
343
- continue;
344
- }
423
+ // MsgHistoryManager 获取消息,并过滤掉已总结的消息
424
+ const recentMessages = this.history
425
+ .getMessages()
426
+ .filter((msg) => !msg.isSummarized)
427
+ .slice(-this.history.memoryConfig.shortTermSize);
428
+
429
+ for (const message of recentMessages) {
345
430
  history.push({
346
431
  role: 'user',
347
- content: request.message.images?.length
348
- ? [
349
- { type: 'text', text: request.message.prompt },
350
- ...request.message.images.map((image) => ({ type: 'image', image: new URL(image) } as ImagePart)),
351
- ]
352
- : request.message.prompt,
432
+ content: message.content,
353
433
  });
354
- for (const part of request.response.responseParts) {
355
- // Remove reasoning_content from history
356
- // https://api-docs.deepseek.com/zh-cn/guides/reasoning_model#%E4%B8%8A%E4%B8%8B%E6%96%87%E6%8B%BC%E6%8E%A5
357
- if (part.kind === 'treeData' || part.kind === 'component' || part.kind === 'reasoning') {
358
- continue;
359
- }
360
- if (part.kind !== 'toolCall') {
361
- history.push({
362
- role: 'assistant',
363
- content: [
364
- {
365
- type: 'text',
366
- text: part.kind === 'markdownContent' ? part.content.value : part.content,
367
- },
368
- ],
369
- });
434
+
435
+ // 如果有响应内容,添加响应
436
+ const additional = this.history.getMessageAdditional(message.id);
437
+ if (additional?.response) {
438
+ const response = additional.response;
439
+ if (response.kind === 'toolCall') {
440
+ this.processToolCall(response, history);
370
441
  } else {
371
- // 直接开始toolCall场景
372
- if (history[history.length - 1].role !== 'assistant') {
373
- history.push({
374
- role: 'assistant',
375
- content: [],
376
- });
377
- }
378
- (history[history.length - 1].content as Array<TextPart | ToolCallPart>).push({
379
- type: 'tool-call',
380
- toolCallId: part.content.id,
381
- toolName: part.content.function.name,
382
- args: (() => {
383
- try {
384
- return JSON.parse(part.content.function.arguments || '{}');
385
- } catch (e) {
386
- console.error('Failed to parse tool call arguments:', e);
387
- return {};
388
- }
389
- })(),
390
- });
391
442
  history.push({
392
- role: 'tool',
393
- content: [
394
- {
395
- type: 'tool-result',
396
- toolCallId: part.content.id,
397
- toolName: part.content.function.name,
398
- result: (() => {
399
- try {
400
- return JSON.parse(part.content.result || '{}');
401
- } catch (e) {
402
- console.error('Failed to parse tool result:', e);
403
- return {};
404
- }
405
- })(),
406
- },
407
- ],
443
+ role: 'assistant',
444
+ content: response.kind === 'markdownContent' ? response.content.value : response.content,
408
445
  });
409
446
  }
410
447
  }
411
448
  }
412
- if (contextWindow) {
413
- while (this.#slicedMessageCount < history.length) {
414
- // 简单的使用 JSON.stringify 计算 token 数量
415
- const tokenCount = JSON.stringify(history.slice(this.#slicedMessageCount)).length / 3;
416
- if (tokenCount <= contextWindow) {
417
- break;
418
- }
419
- this.#slicedMessageCount++;
449
+
450
+ return history;
451
+ }
452
+
453
+ private limitTokens(history: CoreMessage[], contextWindow?: number): CoreMessage[] {
454
+ if (!contextWindow) {
455
+ return history;
456
+ }
457
+
458
+ let currentHistory = history;
459
+ let tokenCount = JSON.stringify(currentHistory).length / 3;
460
+
461
+ while (tokenCount > contextWindow && currentHistory.length > 1) {
462
+ if (currentHistory[0].role === 'system' && currentHistory.length > 2) {
463
+ currentHistory = [currentHistory[0], ...currentHistory.slice(2)];
464
+ } else {
465
+ currentHistory = currentHistory.slice(1);
420
466
  }
467
+ tokenCount = JSON.stringify(currentHistory).length / 3;
421
468
  }
422
- return history.slice(this.#slicedMessageCount);
469
+
470
+ return currentHistory;
471
+ }
472
+
473
+ getMessageHistory(contextWindow?: number): CoreMessage[] {
474
+ const memorySummaries = this.processMemorySummaries();
475
+ const recentMessages = this.processRecentMessages();
476
+ const history = [...memorySummaries, ...recentMessages];
477
+ return this.limitTokens(history, contextWindow);
423
478
  }
424
479
 
425
480
  addRequest(message: IChatRequestMessage): ChatRequestModel {
@@ -9,6 +9,7 @@ import {
9
9
  useInjectable,
10
10
  useUpdateOnEvent,
11
11
  } from '@opensumi/ide-core-browser';
12
+ import debounce from 'lodash/debounce';
12
13
  import { Popover, PopoverPosition } from '@opensumi/ide-core-browser/lib/components';
13
14
  import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
14
15
  import {
@@ -956,28 +957,59 @@ export function DefaultChatViewHeader({
956
957
  [aiChatService],
957
958
  );
958
959
 
960
+ // 防抖函数,避免频繁触发摘要生成
961
+ const debouncedGetSummary = React.useCallback(
962
+ debounce(
963
+ async (messages: { role: ChatMessageRole; content: string }[], currentTitle: string): Promise<string> => {
964
+ const summaryProvider = chatFeatureRegistry.getMessageSummaryProvider();
965
+ if (!summaryProvider || !aiChatService.sessionModel.sessionId) {
966
+ return currentTitle;
967
+ }
968
+
969
+ try {
970
+ const summary = await summaryProvider.getMessageSummary(messages);
971
+ return summary ? summary.slice(0, MAX_TITLE_LENGTH) : currentTitle;
972
+ } catch (error) {
973
+ console.error('[ChatView] Failed to get message summary:', error);
974
+ return currentTitle;
975
+ }
976
+ },
977
+ 1000,
978
+ { leading: false, trailing: true },
979
+ ),
980
+ [chatFeatureRegistry, aiChatService.sessionModel.sessionId],
981
+ );
982
+
983
+ // 使用 ref 来跟踪最新的请求
984
+ const latestSummaryRequestRef = React.useRef<number>(0);
985
+
959
986
  React.useEffect(() => {
960
- const getHistoryList = () => {
987
+ const getHistoryList = async () => {
961
988
  const currentMessages = aiChatService.sessionModel.history.getMessages();
962
- const latestUserMessage = currentMessages.findLast((m) => m.role === ChatMessageRole.User);
963
- const summaryProvider = chatFeatureRegistry.getMessageSummaryProvider();
989
+ const latestUserMessage = [...currentMessages].find((m) => m.role === ChatMessageRole.User);
964
990
  const currentTitle = latestUserMessage
965
991
  ? cleanAttachedTextWrapper(latestUserMessage.content).slice(0, MAX_TITLE_LENGTH)
966
992
  : '';
967
- const messages = aiChatService.sessionModel.history.getMessages().map((msg) => ({
993
+
994
+ // 设置初始标题
995
+ setCurrentTitle(currentTitle);
996
+
997
+ const messages = currentMessages.map((msg) => ({
968
998
  role: msg.role,
969
999
  content: msg.content,
970
1000
  }));
971
- if (messages.length > 2 && summaryProvider && aiChatService.sessionModel.sessionId) {
972
- summaryProvider.getMessageSummary(messages).then((summary) => {
973
- if (summary) {
974
- setCurrentTitle(summary.slice(0, MAX_TITLE_LENGTH));
975
- } else {
976
- setCurrentTitle(currentTitle);
977
- }
978
- });
979
- } else {
980
- setCurrentTitle(currentTitle);
1001
+
1002
+ // 只有当消息数量超过阈值时才生成摘要
1003
+ if (messages.length > 2) {
1004
+ const requestId = Date.now();
1005
+ latestSummaryRequestRef.current = requestId;
1006
+
1007
+ const summary = await debouncedGetSummary(messages, currentTitle);
1008
+
1009
+ // 检查是否是最新请求
1010
+ if (requestId === latestSummaryRequestRef.current && summary) {
1011
+ setCurrentTitle(summary);
1012
+ }
981
1013
  }
982
1014
 
983
1015
  setHistoryList(
@@ -44,12 +44,7 @@ export class MCPServerRegistry implements IMCPServerRegistry {
44
44
  }
45
45
 
46
46
  registerMCPTool(tool: MCPToolDefinition): void {
47
- const existingIndex = this.tools.findIndex((t) => t.name === tool.name);
48
- if (existingIndex !== -1) {
49
- this.tools[existingIndex] = tool;
50
- } else {
51
- this.tools.push(tool);
52
- }
47
+ this.tools.push(tool);
53
48
  }
54
49
 
55
50
  registerToolComponent(
@@ -1,27 +1,30 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  import { Autowired } from '@opensumi/di';
4
- import { Domain } from '@opensumi/ide-core-common';
4
+ import { Domain, URI, path } from '@opensumi/ide-core-common';
5
+ import { IFileServiceClient } from '@opensumi/ide-file-service';
6
+ import { IWorkspaceService } from '@opensumi/ide-workspace';
5
7
 
6
8
  import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition } from '../../types';
9
+ import { BaseApplyService } from '../base-apply.service';
7
10
 
8
11
  import { EditFileToolComponent } from './components/EditFile';
9
- import { CreateNewFileWithTextHandler } from './handlers/CreateNewFileWithText';
10
-
11
- const inputSchema = z
12
- .object({
13
- target_file: z.string().describe('The relative path where the file should be created'),
14
- code_edit: z.string().describe('The content to write into the new file'),
15
- })
16
- .transform((data) => ({
17
- targetFile: data.target_file,
18
- codeEdit: data.code_edit,
19
- }));
12
+
13
+ const inputSchema = z.object({
14
+ target_file: z.string().describe('The relative path where the file should be created'),
15
+ code_edit: z.string().describe('The content to write into the new file'),
16
+ });
20
17
 
21
18
  @Domain(MCPServerContribution)
22
19
  export class CreateNewFileWithTextTool implements MCPServerContribution {
23
- @Autowired(CreateNewFileWithTextHandler)
24
- private readonly createNewFileWithTextHandler: CreateNewFileWithTextHandler;
20
+ @Autowired(IWorkspaceService)
21
+ private readonly workspaceService: IWorkspaceService;
22
+
23
+ @Autowired(IFileServiceClient)
24
+ private readonly fileService: IFileServiceClient;
25
+
26
+ @Autowired(BaseApplyService)
27
+ private applyService: BaseApplyService;
25
28
 
26
29
  registerMCPServer(registry: IMCPServerRegistry): void {
27
30
  registry.registerMCPTool(this.getToolDefinition());
@@ -47,10 +50,36 @@ export class CreateNewFileWithTextTool implements MCPServerContribution {
47
50
 
48
51
  private async handler(args: z.infer<typeof inputSchema> & { toolCallId: string }, logger: MCPLogger) {
49
52
  try {
50
- await this.createNewFileWithTextHandler.handler(args, args.toolCallId);
51
- logger.appendLine(`Successfully created file at: ${args.targetFile}`);
53
+ // 获取工作区根目录
54
+ const workspaceRoots = this.workspaceService.tryGetRoots();
55
+ if (!workspaceRoots || workspaceRoots.length === 0) {
56
+ logger.appendLine('Error: Cannot determine project directory');
57
+ return {
58
+ content: [{ type: 'text', text: "can't find project dir" }],
59
+ isError: true,
60
+ };
61
+ }
62
+
63
+ // 构建完整的文件路径
64
+ const rootUri = URI.parse(workspaceRoots[0].uri);
65
+ const fullPath = path.join(rootUri.codeUri.fsPath, args.target_file);
66
+ const fileUri = URI.file(fullPath);
67
+
68
+ // 创建父目录
69
+ const parentDir = path.dirname(fullPath);
70
+ const parentUri = URI.file(parentDir);
71
+ await this.fileService.createFolder(parentUri.toString());
72
+
73
+ // 创建文件
74
+ await this.fileService.createFile(fileUri.toString());
75
+
76
+ // 使用 applyService 写入文件内容
77
+ const codeBlock = await this.applyService.registerCodeBlock(args.target_file, args.code_edit, args.toolCallId);
78
+ await this.applyService.apply(codeBlock);
79
+
80
+ logger.appendLine(`Successfully created file at: ${args.target_file}`);
52
81
  return {
53
- content: [{ type: 'text', text: 'create file with text success' }],
82
+ content: [{ type: 'text', text: 'ok' }],
54
83
  };
55
84
  } catch (error) {
56
85
  logger.appendLine(`Error during file creation: ${error}`);
@@ -80,14 +80,11 @@ export class FileSearchTool implements MCPServerContribution {
80
80
  });
81
81
 
82
82
  const messages = this.chatInternalService.sessionModel.history.getMessages();
83
- const messageId = messages[messages.length - 1]?.id;
84
- if (messageId) {
85
- this.chatInternalService.sessionModel.history.setMessageAdditional(messageId, {
86
- [args.toolCallId]: {
87
- files,
88
- },
89
- });
90
- }
83
+ this.chatInternalService.sessionModel.history.setMessageAdditional(messages[messages.length - 1].id, {
84
+ [args.toolCallId]: {
85
+ files,
86
+ },
87
+ });
91
88
 
92
89
  logger.appendLine(`Found ${files.length} files matching "${args.query}"`);
93
90
 
@@ -12,27 +12,19 @@ import { IMCPServerRegistry, MCPLogger, MCPServerContribution, MCPToolDefinition
12
12
 
13
13
  import { GrepSearchToolComponent } from './components/ExpandableFileList';
14
14
 
15
- const inputSchema = z
16
- .object({
17
- query: z.string().describe('The regex pattern to search for'),
18
- case_sensitive: z.boolean().optional().describe('Whether the search should be case sensitive'),
19
- include_pattern: z
20
- .string()
21
- .optional()
22
- .describe('Glob pattern for files to include (e.g. "*.ts" for TypeScript files)'),
23
- exclude_pattern: z.string().optional().describe('Glob pattern for files to exclude'),
24
- explanation: z
25
- .string()
26
- .optional()
27
- .describe('One sentence explanation as to why this tool is being used, and how it contributes to the goal.'),
28
- })
29
- .transform((data) => ({
30
- query: data.query,
31
- caseSensitive: data.case_sensitive,
32
- includePattern: data.include_pattern,
33
- excludePattern: data.exclude_pattern,
34
- explanation: data.explanation,
35
- }));
15
+ const inputSchema = z.object({
16
+ query: z.string().describe('The regex pattern to search for'),
17
+ case_sensitive: z.boolean().optional().describe('Whether the search should be case sensitive'),
18
+ include_pattern: z
19
+ .string()
20
+ .optional()
21
+ .describe('Glob pattern for files to include (e.g. "*.ts" for TypeScript files)'),
22
+ exclude_pattern: z.string().optional().describe('Glob pattern for files to exclude'),
23
+ explanation: z
24
+ .string()
25
+ .optional()
26
+ .describe('One sentence explanation as to why this tool is being used, and how it contributes to the goal.'),
27
+ });
36
28
 
37
29
  const MAX_RESULTS = 50;
38
30
 
@@ -80,9 +72,9 @@ export class GrepSearchTool implements MCPServerContribution {
80
72
  await this.searchService.doSearch(
81
73
  searchPattern,
82
74
  {
83
- isMatchCase: !!args.caseSensitive,
84
- include: args.includePattern?.split(','),
85
- exclude: args.excludePattern?.split(','),
75
+ isMatchCase: !!args.case_sensitive,
76
+ include: args.include_pattern?.split(','),
77
+ exclude: args.exclude_pattern?.split(','),
86
78
  maxResults: MAX_RESULTS,
87
79
  isUseRegexp: true,
88
80
  isToggleOpen: false,
@@ -119,14 +111,11 @@ export class GrepSearchTool implements MCPServerContribution {
119
111
  }
120
112
  deferred.resolve(results.join('\n\n'));
121
113
  const messages = this.chatInternalService.sessionModel.history.getMessages();
122
- const messageId = messages[messages.length - 1]?.id;
123
- if (messageId) {
124
- this.chatInternalService.sessionModel.history.setMessageAdditional(messageId, {
125
- [args.toolCallId]: {
126
- files,
127
- },
128
- });
129
- }
114
+ this.chatInternalService.sessionModel.history.setMessageAdditional(messages[messages.length - 1].id, {
115
+ [args.toolCallId]: {
116
+ files,
117
+ },
118
+ });
130
119
  });
131
120
  const text = await deferred.promise;
132
121
  return {
@@ -1,5 +1,5 @@
1
1
  import { Autowired, Injectable } from '@opensumi/di';
2
- import { AppConfig, URI, path } from '@opensumi/ide-core-browser';
2
+ import { AppConfig, URI } from '@opensumi/ide-core-browser';
3
3
  import { IFileServiceClient } from '@opensumi/ide-file-service';
4
4
 
5
5
  /**
@@ -67,7 +67,7 @@ export class ListDirHandler {
67
67
  }
68
68
 
69
69
  // 解析相对路径
70
- const absolutePath = path.join(this.appConfig.workspaceDir, relativeWorkspacePath);
70
+ const absolutePath = `${this.appConfig.workspaceDir}/${relativeWorkspacePath}`;
71
71
  const fileStat = await this.fileSystemService.getFileStat(absolutePath, true);
72
72
  // 验证路径有效性
73
73
  if (!fileStat || !fileStat.isDirectory) {
@@ -1,7 +1,7 @@
1
1
  import { Autowired, Injectable } from '@opensumi/di';
2
2
  import { FileSearchQuickCommandHandler } from '@opensumi/ide-addons/lib/browser/file-search.contribution';
3
3
  import { AppConfig } from '@opensumi/ide-core-browser';
4
- import { CancellationToken, URI, path } from '@opensumi/ide-core-common';
4
+ import { CancellationToken, URI } from '@opensumi/ide-core-common';
5
5
  import { IEditorDocumentModelRef, IEditorDocumentModelService } from '@opensumi/ide-editor/lib/browser';
6
6
  import { IFileServiceClient } from '@opensumi/ide-file-service';
7
7
 
@@ -107,7 +107,7 @@ export class FileHandler {
107
107
  throw new Error('No read file parameters provided. Need to give at least the path.');
108
108
  }
109
109
 
110
- const uri = new URI(path.join(this.appConfig.workspaceDir, fileParams.relativeWorkspacePath));
110
+ const uri = new URI(`${this.appConfig.workspaceDir}/${fileParams.relativeWorkspacePath}`);
111
111
  if (!uri) {
112
112
  const similarFiles = await this.findSimilarFiles(fileParams.relativeWorkspacePath, 3);
113
113
  throw this.createFileNotFoundError(fileParams.relativeWorkspacePath, similarFiles);
@@ -13,25 +13,18 @@ const color = {
13
13
  reset: '\x1b[0m',
14
14
  };
15
15
 
16
- export const inputSchema = z
17
- .object({
18
- command: z.string().describe('The terminal command to execute'),
19
- is_background: z.boolean().describe('Whether the command should be run in the background'),
20
- explanation: z
21
- .string()
22
- .describe('One sentence explanation as to why this command needs to be run and how it contributes to the goal.'),
23
- require_user_approval: z
24
- .boolean()
25
- .describe(
26
- "Whether the user must approve the command before it is executed. Only set this to false if the command is safe and if it matches the user's requirements for commands that should be executed automatically.",
27
- ),
28
- })
29
- .transform((data) => ({
30
- command: data.command,
31
- isBackground: data.is_background,
32
- explanation: data.explanation,
33
- requireUserApproval: data.require_user_approval,
34
- }));
16
+ export const inputSchema = z.object({
17
+ command: z.string().describe('The terminal command to execute'),
18
+ is_background: z.boolean().describe('Whether the command should be run in the background'),
19
+ explanation: z
20
+ .string()
21
+ .describe('One sentence explanation as to why this command needs to be run and how it contributes to the goal.'),
22
+ require_user_approval: z
23
+ .boolean()
24
+ .describe(
25
+ "Whether the user must approve the command before it is executed. Only set this to false if the command is safe and if it matches the user's requirements for commands that should be executed automatically.",
26
+ ),
27
+ });
35
28
 
36
29
  @Injectable()
37
30
  export class RunCommandHandler {
@@ -73,7 +66,7 @@ export class RunCommandHandler {
73
66
 
74
67
  async handler(args: z.infer<typeof inputSchema> & { toolCallId: string }, logger: MCPLogger) {
75
68
  logger.appendLine(`Executing command: ${args.command}`);
76
- if (this.isAlwaysApproval(args.requireUserApproval)) {
69
+ if (this.isAlwaysApproval(args.require_user_approval)) {
77
70
  const def = new Deferred<boolean>();
78
71
  this.approvalDeferredMap.set(args.toolCallId, def);
79
72
  const approval = await def.promise;
@@ -100,7 +93,7 @@ export class RunCommandHandler {
100
93
  const result: { type: string; text: string }[] = [];
101
94
  const def = new Deferred<{ isError?: boolean; content: { type: string; text: string }[] }>();
102
95
 
103
- if (args.isBackground) {
96
+ if (args.is_background) {
104
97
  def.resolve({
105
98
  isError: false,
106
99
  content: [{ type: 'text', text: `Successful run command ${args.command} in background.` }],