@opensumi/ide-ai-native 3.9.1-next-1748593694.0 → 3.9.1-next-1748939097.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 (118) hide show
  1. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  2. package/lib/browser/chat/chat-model.js +3 -1
  3. package/lib/browser/chat/chat-model.js.map +1 -1
  4. package/lib/browser/chat/chat-proxy.service.d.ts +0 -2
  5. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  6. package/lib/browser/chat/chat-proxy.service.js +50 -57
  7. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  8. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  9. package/lib/browser/chat/chat.view.js +11 -7
  10. package/lib/browser/chat/chat.view.js.map +1 -1
  11. package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
  12. package/lib/browser/components/ChatMentionInput.js +123 -27
  13. package/lib/browser/components/ChatMentionInput.js.map +1 -1
  14. package/lib/browser/components/ChatToolRender.module.less +1 -0
  15. package/lib/browser/components/components.module.less +7 -0
  16. package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
  17. package/lib/browser/components/mention-input/mention-input.js +155 -13
  18. package/lib/browser/components/mention-input/mention-input.js.map +1 -1
  19. package/lib/browser/components/mention-input/mention-input.module.less +165 -0
  20. package/lib/browser/components/mention-input/types.d.ts +2 -1
  21. package/lib/browser/components/mention-input/types.d.ts.map +1 -1
  22. package/lib/browser/components/mention-input/types.js +1 -0
  23. package/lib/browser/components/mention-input/types.js.map +1 -1
  24. package/lib/browser/context/llm-context.service.d.ts +18 -2
  25. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  26. package/lib/browser/context/llm-context.service.js +168 -20
  27. package/lib/browser/context/llm-context.service.js.map +1 -1
  28. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.d.ts.map +1 -1
  29. package/lib/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.js.map +1 -1
  30. package/lib/browser/contrib/intelligent-completions/diff-computer.js +1 -1
  31. package/lib/browser/contrib/intelligent-completions/diff-computer.js.map +1 -1
  32. package/lib/browser/contrib/terminal/terminal.feature.registry.js.map +1 -1
  33. package/lib/browser/index.d.ts.map +1 -1
  34. package/lib/browser/index.js +7 -0
  35. package/lib/browser/index.js.map +1 -1
  36. package/lib/browser/mcp/base-apply.service.d.ts +2 -2
  37. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  38. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  39. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  40. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
  41. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  42. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  43. package/lib/browser/preferences/schema.d.ts.map +1 -1
  44. package/lib/browser/preferences/schema.js +5 -0
  45. package/lib/browser/preferences/schema.js.map +1 -1
  46. package/lib/browser/rules/rules.contribution.d.ts +29 -0
  47. package/lib/browser/rules/rules.contribution.d.ts.map +1 -0
  48. package/lib/browser/rules/rules.contribution.js +94 -0
  49. package/lib/browser/rules/rules.contribution.js.map +1 -0
  50. package/lib/browser/rules/rules.module.less +174 -0
  51. package/lib/browser/rules/rules.service.d.ts +25 -0
  52. package/lib/browser/rules/rules.service.d.ts.map +1 -0
  53. package/lib/browser/rules/rules.service.js +180 -0
  54. package/lib/browser/rules/rules.service.js.map +1 -0
  55. package/lib/browser/rules/rules.view.d.ts +3 -0
  56. package/lib/browser/rules/rules.view.d.ts.map +1 -0
  57. package/lib/browser/rules/rules.view.js +76 -0
  58. package/lib/browser/rules/rules.view.js.map +1 -0
  59. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  60. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  61. package/lib/common/MDC_PARSER_README.md +171 -0
  62. package/lib/common/index.d.ts +2 -0
  63. package/lib/common/index.d.ts.map +1 -1
  64. package/lib/common/index.js +2 -0
  65. package/lib/common/index.js.map +1 -1
  66. package/lib/common/llm-context.d.ts +19 -0
  67. package/lib/common/llm-context.d.ts.map +1 -1
  68. package/lib/common/llm-context.js.map +1 -1
  69. package/lib/common/mdc-parser.d.ts +60 -0
  70. package/lib/common/mdc-parser.d.ts.map +1 -0
  71. package/lib/common/mdc-parser.js +250 -0
  72. package/lib/common/mdc-parser.js.map +1 -0
  73. package/lib/common/prompts/context-prompt-provider.d.ts +0 -2
  74. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  75. package/lib/common/prompts/context-prompt-provider.js +35 -29
  76. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  77. package/lib/common/prompts/system-prompt.d.ts +2 -0
  78. package/lib/common/prompts/system-prompt.d.ts.map +1 -0
  79. package/lib/common/prompts/system-prompt.js +5 -0
  80. package/lib/common/prompts/system-prompt.js.map +1 -0
  81. package/lib/common/types.d.ts +7 -0
  82. package/lib/common/types.d.ts.map +1 -1
  83. package/lib/node/base-language-model.d.ts.map +1 -1
  84. package/lib/node/base-language-model.js.map +1 -1
  85. package/package.json +24 -24
  86. package/src/browser/chat/chat-model.ts +3 -1
  87. package/src/browser/chat/chat-proxy.service.ts +68 -81
  88. package/src/browser/chat/chat.view.tsx +19 -7
  89. package/src/browser/components/ChatMentionInput.tsx +143 -31
  90. package/src/browser/components/ChatToolRender.module.less +1 -0
  91. package/src/browser/components/components.module.less +7 -0
  92. package/src/browser/components/mention-input/mention-input.module.less +165 -0
  93. package/src/browser/components/mention-input/mention-input.tsx +244 -29
  94. package/src/browser/components/mention-input/types.ts +1 -0
  95. package/src/browser/context/llm-context.service.ts +185 -21
  96. package/src/browser/contrib/intelligent-completions/decoration/additions-deletions.decoration.ts +1 -1
  97. package/src/browser/contrib/intelligent-completions/diff-computer.ts +1 -1
  98. package/src/browser/contrib/terminal/terminal.feature.registry.ts +1 -1
  99. package/src/browser/index.ts +8 -0
  100. package/src/browser/mcp/base-apply.service.ts +0 -1
  101. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -1
  102. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -1
  103. package/src/browser/mcp/tools/handlers/ListDir.ts +1 -1
  104. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -1
  105. package/src/browser/preferences/schema.ts +5 -0
  106. package/src/browser/rules/rules.contribution.ts +105 -0
  107. package/src/browser/rules/rules.module.less +174 -0
  108. package/src/browser/rules/rules.service.ts +189 -0
  109. package/src/browser/rules/rules.view.tsx +127 -0
  110. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +0 -1
  111. package/src/common/MDC_PARSER_README.md +171 -0
  112. package/src/common/index.ts +3 -0
  113. package/src/common/llm-context.ts +23 -0
  114. package/src/common/mdc-parser.ts +298 -0
  115. package/src/common/prompts/context-prompt-provider.ts +55 -40
  116. package/src/common/prompts/system-prompt.ts +2 -0
  117. package/src/common/types.ts +8 -0
  118. package/src/node/base-language-model.ts +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensumi/ide-ai-native",
3
- "version": "3.9.1-next-1748593694.0",
3
+ "version": "3.9.1-next-1748939097.0",
4
4
  "files": [
5
5
  "lib",
6
6
  "src"
@@ -24,27 +24,27 @@
24
24
  "@ai-sdk/openai": "^1.1.9",
25
25
  "@ai-sdk/openai-compatible": "^0.1.11",
26
26
  "@modelcontextprotocol/sdk": "^1.3.1",
27
- "@opensumi/ide-addons": "3.9.1-next-1748593694.0",
28
- "@opensumi/ide-components": "3.9.1-next-1748593694.0",
29
- "@opensumi/ide-connection": "3.9.1-next-1748593694.0",
30
- "@opensumi/ide-core-common": "3.9.1-next-1748593694.0",
31
- "@opensumi/ide-core-node": "3.9.1-next-1748593694.0",
32
- "@opensumi/ide-debug": "3.9.1-next-1748593694.0",
33
- "@opensumi/ide-design": "3.9.1-next-1748593694.0",
34
- "@opensumi/ide-editor": "3.9.1-next-1748593694.0",
35
- "@opensumi/ide-file-search": "3.9.1-next-1748593694.0",
36
- "@opensumi/ide-file-service": "3.9.1-next-1748593694.0",
37
- "@opensumi/ide-main-layout": "3.9.1-next-1748593694.0",
38
- "@opensumi/ide-markers": "3.9.1-next-1748593694.0",
39
- "@opensumi/ide-monaco": "3.9.1-next-1748593694.0",
40
- "@opensumi/ide-outline": "3.9.1-next-1748593694.0",
41
- "@opensumi/ide-overlay": "3.9.1-next-1748593694.0",
42
- "@opensumi/ide-preferences": "3.9.1-next-1748593694.0",
43
- "@opensumi/ide-search": "3.9.1-next-1748593694.0",
44
- "@opensumi/ide-terminal-next": "3.9.1-next-1748593694.0",
45
- "@opensumi/ide-theme": "3.9.1-next-1748593694.0",
46
- "@opensumi/ide-utils": "3.9.1-next-1748593694.0",
47
- "@opensumi/ide-workspace": "3.9.1-next-1748593694.0",
27
+ "@opensumi/ide-addons": "3.9.1-next-1748939097.0",
28
+ "@opensumi/ide-components": "3.9.1-next-1748939097.0",
29
+ "@opensumi/ide-connection": "3.9.1-next-1748939097.0",
30
+ "@opensumi/ide-core-common": "3.9.1-next-1748939097.0",
31
+ "@opensumi/ide-core-node": "3.9.1-next-1748939097.0",
32
+ "@opensumi/ide-debug": "3.9.1-next-1748939097.0",
33
+ "@opensumi/ide-design": "3.9.1-next-1748939097.0",
34
+ "@opensumi/ide-editor": "3.9.1-next-1748939097.0",
35
+ "@opensumi/ide-file-search": "3.9.1-next-1748939097.0",
36
+ "@opensumi/ide-file-service": "3.9.1-next-1748939097.0",
37
+ "@opensumi/ide-main-layout": "3.9.1-next-1748939097.0",
38
+ "@opensumi/ide-markers": "3.9.1-next-1748939097.0",
39
+ "@opensumi/ide-monaco": "3.9.1-next-1748939097.0",
40
+ "@opensumi/ide-outline": "3.9.1-next-1748939097.0",
41
+ "@opensumi/ide-overlay": "3.9.1-next-1748939097.0",
42
+ "@opensumi/ide-preferences": "3.9.1-next-1748939097.0",
43
+ "@opensumi/ide-search": "3.9.1-next-1748939097.0",
44
+ "@opensumi/ide-terminal-next": "3.9.1-next-1748939097.0",
45
+ "@opensumi/ide-theme": "3.9.1-next-1748939097.0",
46
+ "@opensumi/ide-utils": "3.9.1-next-1748939097.0",
47
+ "@opensumi/ide-workspace": "3.9.1-next-1748939097.0",
48
48
  "@xterm/xterm": "5.5.0",
49
49
  "ai": "^4.1.45",
50
50
  "ansi-regex": "^2.0.0",
@@ -61,7 +61,7 @@
61
61
  "zod-to-json-schema": "^3.24.1"
62
62
  },
63
63
  "devDependencies": {
64
- "@opensumi/ide-core-browser": "3.9.1-next-1748593694.0"
64
+ "@opensumi/ide-core-browser": "3.9.1-next-1748939097.0"
65
65
  },
66
- "gitHead": "c927acc015244866bcd4d2360b4f608e422df548"
66
+ "gitHead": "54024133ab1833b52a0fbfc07683a46906d19d31"
67
67
  }
@@ -352,7 +352,9 @@ export class ChatModel extends Disposable implements IChatModel {
352
352
  : request.message.prompt,
353
353
  });
354
354
  for (const part of request.response.responseParts) {
355
- if (part.kind === 'treeData' || part.kind === 'component') {
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') {
356
358
  continue;
357
359
  }
358
360
  if (part.kind !== 'toolCall') {
@@ -1,18 +1,16 @@
1
1
  import { Autowired, Injectable } from '@opensumi/di';
2
- import { AppConfig, PreferenceService } from '@opensumi/ide-core-browser';
2
+ import { PreferenceService } from '@opensumi/ide-core-browser';
3
3
  import {
4
4
  AIBackSerivcePath,
5
5
  CancellationToken,
6
6
  ChatAgentViewServiceToken,
7
7
  ChatFeatureRegistryToken,
8
- ChatServiceToken,
9
8
  Deferred,
10
9
  Disposable,
11
10
  IAIBackService,
12
11
  IAIReporter,
13
12
  IApplicationService,
14
13
  IChatProgress,
15
- getOperatingSystemName,
16
14
  } from '@opensumi/ide-core-common';
17
15
  import { AINativeSettingSectionsId } from '@opensumi/ide-core-common/lib/settings/ai-native';
18
16
  import { MonacoCommandRegistry } from '@opensumi/ide-editor/lib/browser/monaco-contrib/command/command.service';
@@ -27,10 +25,10 @@ import {
27
25
  IChatAgentService,
28
26
  IChatAgentWelcomeMessage,
29
27
  } from '../../common';
28
+ import { DEFAULT_SYSTEM_PROMPT } from '../../common/prompts/system-prompt';
30
29
  import { ChatToolRender } from '../components/ChatToolRender';
31
30
  import { IChatAgentViewService } from '../types';
32
31
 
33
- import { ChatService } from './chat.api.service';
34
32
  import { ChatFeatureRegistry } from './chat.feature.registry';
35
33
 
36
34
  /**
@@ -53,9 +51,6 @@ export class ChatProxyService extends Disposable {
53
51
  @Autowired(MonacoCommandRegistry)
54
52
  private readonly monacoCommandRegistry: MonacoCommandRegistry;
55
53
 
56
- @Autowired(ChatServiceToken)
57
- private aiChatService: ChatService;
58
-
59
54
  @Autowired(IAIReporter)
60
55
  private readonly aiReporter: IAIReporter;
61
56
 
@@ -71,9 +66,6 @@ export class ChatProxyService extends Disposable {
71
66
  @Autowired(IMessageService)
72
67
  private readonly messageService: IMessageService;
73
68
 
74
- @Autowired(AppConfig)
75
- private readonly appConfig: AppConfig;
76
-
77
69
  private chatDeferred: Deferred<void> = new Deferred<void>();
78
70
 
79
71
  public getRequestOptions() {
@@ -112,79 +104,74 @@ export class ChatProxyService extends Disposable {
112
104
  initialProps: {},
113
105
  });
114
106
 
115
- this.addDispose(
116
- this.chatAgentService.registerAgent({
117
- id: ChatProxyService.AGENT_ID,
118
- metadata: {
119
- systemPrompt:
120
- this.preferenceService.get<string>(
107
+ this.applicationService.getBackendOS().then(() => {
108
+ this.addDispose(
109
+ this.chatAgentService.registerAgent({
110
+ id: ChatProxyService.AGENT_ID,
111
+ metadata: {
112
+ systemPrompt: this.preferenceService.get<string>(
121
113
  AINativeSettingSectionsId.SystemPrompt,
122
- 'You are a powerful AI coding assistant working in OpenSumi, a top IDE framework. You collaborate with a USER to solve coding tasks, which may involve creating, modifying, or debugging code, or answering questions. When the USER sends a message, relevant context (e.g., open files, cursor position, edit history, linter errors) may be attached. Use this information as needed.\n\n<tool_calling>\nYou have access to tools to assist with tasks. Follow these rules:\n1. Always adhere to the tool call schema and provide all required parameters.\n2. Only use tools explicitly provided; ignore unavailable ones.\n3. Avoid mentioning tool names to the USER (e.g., say "I will edit your file" instead of "I need to use the edit_file tool").\n4. Only call tools when necessary; respond directly if the task is general or you already know the answer.\n5. Explain to the USER why you’re using a tool before calling it.\n</tool_calling>\n\n<making_code_changes>\nWhen modifying code:\n1. Use code edit tools instead of outputting code unless explicitly requested.\n2. Limit tool calls to one per turn.\n3. Ensure generated code is immediately executable by including necessary imports, dependencies, and endpoints.\n4. For new projects, create a dependency management file (e.g., requirements.txt) and a README.\n5. For web apps, design a modern, user-friendly UI.\n6. Avoid generating non-textual or excessively long code.\n7. Read file contents before editing, unless appending a small change or creating a new file.\n8. Fix introduced linter errors if possible, but stop after 3 attempts and ask the USER for guidance.\n9. Reapply reasonable code edits if they weren’t followed initially.\n</making_code_changes>\n\nUse the appropriate tools to fulfill the USER’s request, ensuring all required parameters are provided or inferred from context.',
123
- ) +
124
- `\n\n<user_info>\nThe user's OS is ${getOperatingSystemName()}. The absolute path of the user's workspace is ${
125
- this.appConfig.workspaceDir
126
- }.\n</user_info>`,
127
- },
128
- invoke: async (
129
- request: IChatAgentRequest,
130
- progress: (part: IChatProgress) => void,
131
- history: CoreMessage[],
132
- token: CancellationToken,
133
- ): Promise<IChatAgentResult> => {
134
- this.chatDeferred = new Deferred<void>();
135
-
136
- const { message, command } = request;
137
- let prompt: string = message;
138
-
139
- if (command) {
140
- const commandHandler = this.chatFeatureRegistry.getSlashCommandHandler(command);
141
- if (commandHandler && commandHandler.providerPrompt) {
142
- const editor = this.monacoCommandRegistry.getActiveCodeEditor();
143
- const slashCommandPrompt = await commandHandler.providerPrompt(message, editor);
144
- prompt = slashCommandPrompt;
114
+ DEFAULT_SYSTEM_PROMPT,
115
+ ),
116
+ },
117
+ invoke: async (
118
+ request: IChatAgentRequest,
119
+ progress: (part: IChatProgress) => void,
120
+ history: CoreMessage[],
121
+ token: CancellationToken,
122
+ ): Promise<IChatAgentResult> => {
123
+ this.chatDeferred = new Deferred<void>();
124
+ const { message, command } = request;
125
+ let prompt: string = message;
126
+ if (command) {
127
+ const commandHandler = this.chatFeatureRegistry.getSlashCommandHandler(command);
128
+ if (commandHandler && commandHandler.providerPrompt) {
129
+ const editor = this.monacoCommandRegistry.getActiveCodeEditor();
130
+ const slashCommandPrompt = await commandHandler.providerPrompt(message, editor);
131
+ prompt = slashCommandPrompt;
132
+ }
145
133
  }
146
- }
147
-
148
- const stream = await this.aiBackService.requestStream(
149
- prompt,
150
- {
151
- requestId: request.requestId,
152
- sessionId: request.sessionId,
153
- history,
154
- images: request.images,
155
- ...this.getRequestOptions(),
156
- },
157
- token,
158
- );
159
-
160
- listenReadable<IChatProgress>(stream, {
161
- onData: (data) => {
162
- progress(data);
163
- },
164
- onEnd: () => {
165
- this.chatDeferred.resolve();
166
- },
167
- onError: (error) => {
168
- this.messageService.error(error.message);
169
- this.aiReporter.end(request.sessionId + '_' + request.requestId, {
170
- message: error.message,
171
- success: false,
172
- command,
173
- });
174
- },
175
- });
176
-
177
- await this.chatDeferred.promise;
178
- return {};
179
- },
180
- provideSlashCommands: async (token: CancellationToken): Promise<IChatAgentCommand[]> =>
181
- this.chatFeatureRegistry
182
- .getAllSlashCommand()
183
- .map((s) => ({ ...s, name: s.name, description: s.description || '' })),
184
- provideChatWelcomeMessage: async (token: CancellationToken): Promise<IChatAgentWelcomeMessage | undefined> =>
185
- undefined,
186
- }),
187
- );
134
+
135
+ const stream = await this.aiBackService.requestStream(
136
+ prompt,
137
+ {
138
+ requestId: request.requestId,
139
+ sessionId: request.sessionId,
140
+ history,
141
+ images: request.images,
142
+ ...this.getRequestOptions(),
143
+ },
144
+ token,
145
+ );
146
+
147
+ listenReadable<IChatProgress>(stream, {
148
+ onData: (data) => {
149
+ progress(data);
150
+ },
151
+ onEnd: () => {
152
+ this.chatDeferred.resolve();
153
+ },
154
+ onError: (error) => {
155
+ this.messageService.error(error.message);
156
+ this.aiReporter.end(request.sessionId + '_' + request.requestId, {
157
+ message: error.message,
158
+ success: false,
159
+ command,
160
+ });
161
+ },
162
+ });
163
+
164
+ await this.chatDeferred.promise;
165
+ return {};
166
+ },
167
+ provideSlashCommands: async (): Promise<IChatAgentCommand[]> =>
168
+ this.chatFeatureRegistry
169
+ .getAllSlashCommand()
170
+ .map((s) => ({ ...s, name: s.name, description: s.description || '' })),
171
+ provideChatWelcomeMessage: async (): Promise<IChatAgentWelcomeMessage | undefined> => undefined,
172
+ }),
173
+ );
174
+ });
188
175
 
189
176
  queueMicrotask(() => {
190
177
  this.chatAgentService.updateAgent(ChatProxyService.AGENT_ID, {});
@@ -617,7 +617,12 @@ export const AIChatView = () => {
617
617
  const { message, images, agentId, command, reportExtra } = value;
618
618
  const { actionType, actionSource } = reportExtra || {};
619
619
 
620
- const request = aiChatService.createRequest(message, agentId!, images, command);
620
+ const request = aiChatService.createRequest(
621
+ message.replaceAll(LLM_CONTEXT_KEY_REGEX, ''),
622
+ agentId!,
623
+ images,
624
+ command,
625
+ );
621
626
  if (!request) {
622
627
  return;
623
628
  }
@@ -641,7 +646,7 @@ export const AIChatView = () => {
641
646
  600 * 1000,
642
647
  );
643
648
  msgHistoryManager.addUserMessage({
644
- content: message.replaceAll(LLM_CONTEXT_KEY_REGEX, ''),
649
+ content: message,
645
650
  images: images || [],
646
651
  agentId: agentId!,
647
652
  agentCommand: command!,
@@ -695,14 +700,9 @@ export const AIChatView = () => {
695
700
  let processedContent = message;
696
701
  const filePattern = /\{\{@file:(.*?)\}\}/g;
697
702
  const fileMatches = message.match(filePattern);
698
- let isCleanContext = false;
699
703
  if (fileMatches) {
700
704
  for (const match of fileMatches) {
701
705
  const filePath = match.replace(/\{\{@file:(.*?)\}\}/, '$1');
702
- if (filePath && !isCleanContext) {
703
- isCleanContext = true;
704
- llmContextService.cleanFileContext();
705
- }
706
706
  const fileUri = new URI(filePath);
707
707
  const relativePath = (await workspaceService.asRelativePath(fileUri))?.path || fileUri.displayName;
708
708
  processedContent = processedContent.replace(match, `\`${LLM_CONTEXT_KEY.AttachedFile}${relativePath}\``);
@@ -738,6 +738,18 @@ export const AIChatView = () => {
738
738
  );
739
739
  }
740
740
  }
741
+ const rulePattern = /\{\{@rule:(.*?)\}\}/g;
742
+ const ruleMatches = processedContent.match(rulePattern);
743
+ if (ruleMatches) {
744
+ for (const match of ruleMatches) {
745
+ const ruleName = match.replace(/\{\{@rule:(.*?)\}\}/, '$1');
746
+ const ruleUri = new URI(ruleName);
747
+ processedContent = processedContent.replace(
748
+ match,
749
+ `\`${LLM_CONTEXT_KEY.AttachedFile}${ruleUri.displayName}\``,
750
+ );
751
+ }
752
+ }
741
753
  return handleAgentReply({ message: processedContent, images, agentId, command, reportExtra });
742
754
  },
743
755
  [handleAgentReply],
@@ -10,7 +10,13 @@ import {
10
10
  useInjectable,
11
11
  } from '@opensumi/ide-core-browser';
12
12
  import { Icon, getIcon } from '@opensumi/ide-core-browser/lib/components';
13
- import { AINativeSettingSectionsId, ChatFeatureRegistryToken, URI, localize } from '@opensumi/ide-core-common';
13
+ import {
14
+ AINativeSettingSectionsId,
15
+ ChatFeatureRegistryToken,
16
+ RulesServiceToken,
17
+ URI,
18
+ localize,
19
+ } from '@opensumi/ide-core-common';
14
20
  import { CommandService } from '@opensumi/ide-core-common/lib/command';
15
21
  import { defaultFilesWatcherExcludes } from '@opensumi/ide-core-common/lib/preferences/file-watch';
16
22
  import { WorkbenchEditorService } from '@opensumi/ide-editor';
@@ -25,6 +31,8 @@ import { LLMContextService } from '../../common/llm-context';
25
31
  import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
26
32
  import { ChatInternalService } from '../chat/chat.internal.service';
27
33
  import { MCPConfigCommands } from '../mcp/config/mcp-config.commands';
34
+ import { RulesCommands } from '../rules/rules.contribution';
35
+ import { RulesService } from '../rules/rules.service';
28
36
 
29
37
  import styles from './components.module.less';
30
38
  import { MentionInput } from './mention-input/mention-input';
@@ -77,10 +85,15 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
77
85
  const outlineTreeService = useInjectable<OutlineTreeService>(OutlineTreeService);
78
86
  const prevOutlineItems = useRef<MentionItem[]>([]);
79
87
  const preferenceService = useInjectable<PreferenceService>(PreferenceService);
88
+ const rulesService = useInjectable<RulesService>(RulesServiceToken);
80
89
  const handleShowMCPConfig = React.useCallback(() => {
81
90
  commandService.executeCommand(MCPConfigCommands.OPEN_MCP_CONFIG.id);
82
91
  }, [commandService]);
83
92
 
93
+ const handleShowRules = React.useCallback(() => {
94
+ commandService.executeCommand(RulesCommands.OPEN_RULES_FILE.id);
95
+ }, [commandService]);
96
+
84
97
  useEffect(() => {
85
98
  if (props.value !== value) {
86
99
  setValue(props.value || '');
@@ -107,6 +120,55 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
107
120
  [outlineTreeService],
108
121
  );
109
122
 
123
+ // 拆分目录路径为多个层级的辅助函数
124
+ const expandFolderPaths = async (folderPaths: string[], workspaceRootPath: string): Promise<MentionItem[]> => {
125
+ const expandedPaths = new Set<string>();
126
+ const workspaceUri = new URI(workspaceRootPath);
127
+
128
+ // 将所有路径展开为多层级
129
+ for (const folderPath of folderPaths) {
130
+ const uri = new URI(folderPath);
131
+ const relativePath = await workspaceService.asRelativePath(uri);
132
+
133
+ if (relativePath?.path) {
134
+ const pathSegments = relativePath.path.split('/').filter(Boolean);
135
+
136
+ // 为每个层级创建路径
137
+ for (let i = 0; i < pathSegments.length; i++) {
138
+ const segmentPath = pathSegments.slice(0, i + 1).join('/');
139
+ const fullPath = workspaceUri.resolve(segmentPath).codeUri.fsPath;
140
+
141
+ // 避免添加工作区本身或其上级目录
142
+ if (fullPath !== workspaceRootPath && !workspaceRootPath.startsWith(fullPath)) {
143
+ expandedPaths.add(fullPath);
144
+ }
145
+ }
146
+ } else {
147
+ // 如果无法获取相对路径,直接添加(但仍要过滤工作区路径)
148
+ if (folderPath !== workspaceRootPath && !workspaceRootPath.startsWith(folderPath)) {
149
+ expandedPaths.add(folderPath);
150
+ }
151
+ }
152
+ }
153
+
154
+ // 转换为 MentionItem 格式
155
+ return Promise.all(
156
+ Array.from(expandedPaths).map(async (folderPath) => {
157
+ const uri = new URI(folderPath);
158
+ const relativePath = await workspaceService.asRelativePath(uri);
159
+ return {
160
+ id: uri.codeUri.fsPath,
161
+ type: MentionType.FOLDER,
162
+ text: uri.displayName,
163
+ value: uri.codeUri.fsPath,
164
+ description: relativePath?.root ? relativePath.path : '',
165
+ contextId: uri.codeUri.fsPath,
166
+ icon: getIcon('folder'),
167
+ };
168
+ }),
169
+ );
170
+ };
171
+
110
172
  // 默认菜单项
111
173
  const defaultMenuItems: MentionItem[] = [
112
174
  {
@@ -214,21 +276,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
214
276
  .filter((folder) => folder !== workspaceService.workspace?.uri.toString() && folder !== '/'),
215
277
  ),
216
278
  );
217
- folders = await Promise.all(
218
- recentFolder.map(async (folder) => {
219
- const uri = new URI(folder);
220
- const relativePath = await workspaceService.asRelativePath(uri);
221
- return {
222
- id: uri.codeUri.fsPath,
223
- type: MentionType.FOLDER,
224
- text: uri.displayName,
225
- value: uri.codeUri.fsPath,
226
- description: relativePath?.root ? relativePath.path : '',
227
- contextId: uri.codeUri.fsPath,
228
- icon: getIcon('folder'),
229
- };
230
- }),
231
- );
279
+ folders = await expandFolderPaths(recentFolder, workspaceService.workspace?.uri.toString() || '');
232
280
  } else {
233
281
  const rootUris = (await workspaceService.roots).map((root) => new URI(root.uri).codeUri.fsPath.toString());
234
282
  const files = await searchService.find(searchText, {
@@ -246,22 +294,11 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
246
294
  .filter((folder) => folder !== workspaceService.workspace?.uri.toString()),
247
295
  ),
248
296
  );
249
- return Promise.all(
250
- folders.map(async (folder) => {
251
- const uri = new URI(folder);
252
- return {
253
- id: uri.codeUri.fsPath,
254
- type: MentionType.FOLDER,
255
- text: uri.displayName,
256
- value: uri.codeUri.fsPath,
257
- description: (await workspaceService.asRelativePath(uri.parent))?.path || '',
258
- contextId: uri.codeUri.fsPath,
259
- icon: getIcon('folder'),
260
- };
261
- }),
262
- );
297
+ return await expandFolderPaths(folders, workspaceService.workspace?.uri.toString() || '');
263
298
  }
264
- return folders.filter((folder) => folder.id !== new URI(workspaceService.workspace?.uri).codeUri.fsPath);
299
+ return folders
300
+ .filter(Boolean)
301
+ .filter((folder) => folder.id !== new URI(workspaceService.workspace?.uri).codeUri.fsPath);
265
302
  },
266
303
  },
267
304
  {
@@ -312,6 +349,74 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
312
349
  }
313
350
  },
314
351
  },
352
+ {
353
+ id: MentionType.RULE,
354
+ type: MentionType.RULE,
355
+ text: 'Rule',
356
+ icon: getIcon('rules'),
357
+ getHighestLevelItems: () => [],
358
+ getItems: async (searchText: string) => {
359
+ const rules = await rulesService.projectRules;
360
+ const mappedRules = rules.map((rule) => {
361
+ const uri = new URI(rule.path);
362
+ return {
363
+ id: uri.codeUri.fsPath,
364
+ type: MentionType.RULE,
365
+ text: uri.displayName,
366
+ value: uri.codeUri.fsPath,
367
+ contextId: uri.codeUri.fsPath,
368
+ description: rule.description,
369
+ icon: getIcon('rules'),
370
+ };
371
+ });
372
+
373
+ if (!searchText) {
374
+ return mappedRules.slice(0, 10);
375
+ }
376
+
377
+ const lowerSearchText = searchText.toLocaleLowerCase();
378
+ return mappedRules
379
+ .filter((rule) => rule.text.toLocaleLowerCase().includes(lowerSearchText))
380
+ .sort((a, b) => {
381
+ const aTextLower = a.text.toLocaleLowerCase();
382
+ const bTextLower = b.text.toLocaleLowerCase();
383
+ const aDescLower = a.description?.toLocaleLowerCase() || '';
384
+ const bDescLower = b.description?.toLocaleLowerCase() || '';
385
+
386
+ // 优先级:文件名包含搜索文本 > 描述包含搜索文本
387
+ const aTextMatch = aTextLower.includes(lowerSearchText);
388
+ const bTextMatch = bTextLower.includes(lowerSearchText);
389
+ const aDescMatch = aDescLower.includes(lowerSearchText);
390
+ const bDescMatch = bDescLower.includes(lowerSearchText);
391
+
392
+ if (aTextMatch && bTextMatch) {
393
+ // 如果都匹配文件名,按文件名字母序排序
394
+ return aTextLower.localeCompare(bTextLower);
395
+ }
396
+ if (aTextMatch && !bTextMatch) {
397
+ return -1;
398
+ }
399
+ if (!aTextMatch && bTextMatch) {
400
+ return 1;
401
+ }
402
+
403
+ // 如果文件名都不匹配,比较描述
404
+ if (aDescMatch && bDescMatch) {
405
+ return aTextLower.localeCompare(bTextLower);
406
+ }
407
+ if (aDescMatch && !bDescMatch) {
408
+ return -1;
409
+ }
410
+ if (!aDescMatch && bDescMatch) {
411
+ return 1;
412
+ }
413
+
414
+ // 如果都不匹配,按文件名字母序排序
415
+ return aTextLower.localeCompare(bTextLower);
416
+ })
417
+ .slice(0, 10);
418
+ },
419
+ },
315
420
  ];
316
421
 
317
422
  const defaultMentionInputFooterOptions: FooterConfig = useMemo(
@@ -330,6 +435,13 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
330
435
  onClick: handleShowMCPConfig,
331
436
  position: FooterButtonPosition.LEFT,
332
437
  },
438
+ {
439
+ id: 'rules',
440
+ icon: 'rules',
441
+ title: 'Rules',
442
+ onClick: handleShowRules,
443
+ position: FooterButtonPosition.LEFT,
444
+ },
333
445
  {
334
446
  id: 'upload-image',
335
447
  icon: 'image',
@@ -52,6 +52,7 @@
52
52
  flex: 1;
53
53
  text-overflow: ellipsis;
54
54
  direction: rtl;
55
+ unicode-bidi: plaintext;
55
56
  overflow: hidden;
56
57
  white-space: nowrap;
57
58
  display: inline-block;
@@ -620,6 +620,13 @@
620
620
  vertical-align: middle;
621
621
  font-size: 12px;
622
622
  word-break: break-all;
623
+ max-width: 100px;
624
+ text-overflow: ellipsis;
625
+ direction: rtl;
626
+ unicode-bidi: plaintext;
627
+ overflow: hidden;
628
+ white-space: nowrap;
629
+ display: inline-block;
623
630
  }
624
631
 
625
632
  .thumbnail_container {