@theia/ai-ide 1.62.0-next.3 → 1.62.1

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 (92) hide show
  1. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts +3 -3
  2. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
  3. package/lib/browser/ai-configuration/agent-configuration-widget.js +24 -18
  4. package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
  5. package/lib/browser/ai-configuration/ai-configuration-view-contribution.js +1 -1
  6. package/lib/browser/ai-configuration/ai-configuration-view-contribution.js.map +1 -1
  7. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +2 -0
  8. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -1
  9. package/lib/browser/ai-configuration/ai-configuration-widget.js +6 -0
  10. package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -1
  11. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts +138 -0
  12. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts.map +1 -0
  13. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js +492 -0
  14. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js.map +1 -0
  15. package/lib/browser/ai-configuration/template-settings-renderer.d.ts +4 -5
  16. package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -1
  17. package/lib/browser/ai-configuration/template-settings-renderer.js +15 -31
  18. package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
  19. package/lib/browser/architect-agent.d.ts +1 -1
  20. package/lib/browser/architect-agent.d.ts.map +1 -1
  21. package/lib/browser/architect-agent.js +2 -2
  22. package/lib/browser/architect-agent.js.map +1 -1
  23. package/lib/browser/coder-agent.d.ts +2 -2
  24. package/lib/browser/coder-agent.d.ts.map +1 -1
  25. package/lib/browser/coder-agent.js +7 -3
  26. package/lib/browser/coder-agent.js.map +1 -1
  27. package/lib/browser/file-changeset-functions.d.ts +14 -1
  28. package/lib/browser/file-changeset-functions.d.ts.map +1 -1
  29. package/lib/browser/file-changeset-functions.js +153 -14
  30. package/lib/browser/file-changeset-functions.js.map +1 -1
  31. package/lib/browser/frontend-module.d.ts.map +1 -1
  32. package/lib/browser/frontend-module.js +11 -1
  33. package/lib/browser/frontend-module.js.map +1 -1
  34. package/lib/browser/template-preference-contribution.d.ts +2 -2
  35. package/lib/browser/template-preference-contribution.d.ts.map +1 -1
  36. package/lib/browser/template-preference-contribution.js +2 -2
  37. package/lib/browser/template-preference-contribution.js.map +1 -1
  38. package/lib/browser/workspace-functions.d.ts +1 -1
  39. package/lib/browser/workspace-functions.js +13 -13
  40. package/lib/common/architect-prompt-template.d.ts +2 -3
  41. package/lib/common/architect-prompt-template.d.ts.map +1 -1
  42. package/lib/common/architect-prompt-template.js +49 -11
  43. package/lib/common/architect-prompt-template.js.map +1 -1
  44. package/lib/common/coder-replace-prompt-template.d.ts +6 -3
  45. package/lib/common/coder-replace-prompt-template.d.ts.map +1 -1
  46. package/lib/common/coder-replace-prompt-template.js +148 -22
  47. package/lib/common/coder-replace-prompt-template.js.map +1 -1
  48. package/lib/common/command-chat-agents.d.ts +1 -1
  49. package/lib/common/command-chat-agents.d.ts.map +1 -1
  50. package/lib/common/command-chat-agents.js +4 -4
  51. package/lib/common/command-chat-agents.js.map +1 -1
  52. package/lib/common/command-prompt-template.d.ts +2 -2
  53. package/lib/common/command-prompt-template.d.ts.map +1 -1
  54. package/lib/common/command-prompt-template.js +18 -15
  55. package/lib/common/command-prompt-template.js.map +1 -1
  56. package/lib/common/orchestrator-chat-agent.d.ts +2 -3
  57. package/lib/common/orchestrator-chat-agent.d.ts.map +1 -1
  58. package/lib/common/orchestrator-chat-agent.js +11 -26
  59. package/lib/common/orchestrator-chat-agent.js.map +1 -1
  60. package/lib/common/orchestrator-prompt-template.d.ts +2 -2
  61. package/lib/common/orchestrator-prompt-template.d.ts.map +1 -1
  62. package/lib/common/orchestrator-prompt-template.js +4 -1
  63. package/lib/common/orchestrator-prompt-template.js.map +1 -1
  64. package/lib/common/universal-chat-agent.d.ts +5 -1
  65. package/lib/common/universal-chat-agent.d.ts.map +1 -1
  66. package/lib/common/universal-chat-agent.js +2 -2
  67. package/lib/common/universal-chat-agent.js.map +1 -1
  68. package/lib/common/universal-prompt-template.d.ts +3 -3
  69. package/lib/common/universal-prompt-template.d.ts.map +1 -1
  70. package/lib/common/universal-prompt-template.js +1 -2
  71. package/lib/common/universal-prompt-template.js.map +1 -1
  72. package/package.json +17 -17
  73. package/src/browser/ai-configuration/agent-configuration-widget.tsx +31 -24
  74. package/src/browser/ai-configuration/ai-configuration-view-contribution.ts +1 -1
  75. package/src/browser/ai-configuration/ai-configuration-widget.tsx +6 -0
  76. package/src/browser/ai-configuration/prompt-fragments-configuration-widget.tsx +710 -0
  77. package/src/browser/ai-configuration/template-settings-renderer.tsx +18 -38
  78. package/src/browser/architect-agent.ts +3 -3
  79. package/src/browser/coder-agent.ts +10 -5
  80. package/src/browser/file-changeset-functions.ts +152 -14
  81. package/src/browser/frontend-module.ts +14 -2
  82. package/src/browser/style/index.css +320 -0
  83. package/src/browser/template-preference-contribution.ts +4 -4
  84. package/src/browser/workspace-functions.ts +3 -3
  85. package/src/common/architect-prompt-template.ts +54 -14
  86. package/src/common/coder-replace-prompt-template.ts +150 -24
  87. package/src/common/command-chat-agents.ts +4 -4
  88. package/src/common/command-prompt-template.ts +21 -18
  89. package/src/common/orchestrator-chat-agent.ts +12 -28
  90. package/src/common/orchestrator-prompt-template.ts +7 -4
  91. package/src/common/universal-chat-agent.ts +2 -2
  92. package/src/common/universal-prompt-template.ts +4 -5
@@ -13,84 +13,64 @@
13
13
  //
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
- import { AISettingsService, PromptCustomizationService, PromptService, PromptTemplate } from '@theia/ai-core/lib/common';
16
+ import { AISettingsService, PromptService, PromptVariantSet } from '@theia/ai-core/lib/common';
17
17
  import * as React from '@theia/core/shared/react';
18
18
  import { nls } from '@theia/core/lib/common/nls';
19
19
 
20
- const DEFAULT_VARIANT = 'default';
21
-
22
- export interface TemplateRendererProps {
20
+ export interface PromptVariantRendererProps {
23
21
  agentId: string;
24
- template: PromptTemplate;
25
- promptCustomizationService: PromptCustomizationService;
22
+ promptVariantSet: PromptVariantSet;
26
23
  promptService: PromptService;
27
24
  aiSettingsService: AISettingsService;
28
25
  }
29
26
 
30
- export const TemplateRenderer: React.FC<TemplateRendererProps> = ({
27
+ export const PromptVariantRenderer: React.FC<PromptVariantRendererProps> = ({
31
28
  agentId,
32
- template,
33
- promptCustomizationService,
29
+ promptVariantSet,
34
30
  promptService,
35
31
  aiSettingsService,
36
32
  }) => {
37
- const variantIds = [DEFAULT_VARIANT, ...promptService.getVariantIds(template.id)];
38
- const [selectedVariant, setSelectedVariant] = React.useState<string>(DEFAULT_VARIANT);
33
+ const variantIds = promptService.getVariantIds(promptVariantSet.id);
34
+ const defaultVariantId = promptService.getDefaultVariantId(promptVariantSet.id);
35
+ const [selectedVariant, setSelectedVariant] = React.useState<string>(defaultVariantId!);
39
36
 
40
37
  React.useEffect(() => {
41
38
  (async () => {
42
- const agentSettings = await aiSettingsService.getAgentSettings(agentId);
43
39
  const currentVariant =
44
- agentSettings?.selectedVariants?.[template.id] || DEFAULT_VARIANT;
45
- setSelectedVariant(currentVariant);
40
+ await promptService.getSelectedVariantId(promptVariantSet.id);
41
+ setSelectedVariant(currentVariant!);
46
42
  })();
47
- }, [template.id, aiSettingsService, agentId]);
43
+ }, [promptVariantSet.id, aiSettingsService, agentId]);
48
44
 
49
45
  const isInvalidVariant = !variantIds.includes(selectedVariant);
50
46
 
51
47
  const handleVariantChange = async (event: React.ChangeEvent<HTMLSelectElement>) => {
52
48
  const newVariant = event.target.value;
53
49
  setSelectedVariant(newVariant);
54
-
55
- const agentSettings = await aiSettingsService.getAgentSettings(agentId);
56
- const selectedVariants = agentSettings?.selectedVariants || {};
57
-
58
- const updatedVariants = { ...selectedVariants };
59
- if (newVariant === DEFAULT_VARIANT) {
60
- delete updatedVariants[template.id];
61
- } else {
62
- updatedVariants[template.id] = newVariant;
63
- }
64
-
65
- await aiSettingsService.updateAgentSettings(agentId, {
66
- selectedVariants: updatedVariants,
67
- });
50
+ promptService.updateSelectedVariantId(agentId, promptVariantSet.id, newVariant);
68
51
  };
69
52
 
70
53
  const openTemplate = () => {
71
- const templateId = selectedVariant === DEFAULT_VARIANT ? template.id : selectedVariant;
72
- const selectedTemplate = promptService.getRawPrompt(templateId);
73
- promptCustomizationService.editTemplate(templateId, selectedTemplate?.template || '');
54
+ promptService.editBuiltInCustomization(selectedVariant);
74
55
  };
75
56
 
76
57
  const resetTemplate = () => {
77
- const templateId = selectedVariant === DEFAULT_VARIANT ? template.id : selectedVariant;
78
- promptCustomizationService.resetTemplate(templateId);
58
+ promptService.resetToBuiltIn(selectedVariant);
79
59
  };
80
60
 
81
61
  return (
82
62
  <div className="template-renderer">
83
63
  <div className="settings-section-title template-header">
84
- <strong>{template.id}</strong>
64
+ <strong>{promptVariantSet.id}</strong>
85
65
  </div>
86
66
  <div className="template-controls">
87
67
  {(variantIds.length > 1 || isInvalidVariant) && (
88
68
  <>
89
- <label htmlFor={`variant-selector-${template.id}`} className="template-select-label">
69
+ <label htmlFor={`variant-selector-${promptVariantSet.id}`} className="template-select-label">
90
70
  {nls.localize('theia/ai/core/templateSettings/selectVariant', 'Select Variant:')}
91
71
  </label>
92
72
  <select
93
- id={`variant-selector-${template.id}`}
73
+ id={`variant-selector-${promptVariantSet.id}`}
94
74
  className={`theia-select template-variant-selector ${isInvalidVariant ? 'error' : ''}`}
95
75
  value={isInvalidVariant ? 'invalid' : selectedVariant}
96
76
  onChange={handleVariantChange}
@@ -102,7 +82,7 @@ export const TemplateRenderer: React.FC<TemplateRendererProps> = ({
102
82
  )}
103
83
  {variantIds.map(variantId => (
104
84
  <option key={variantId} value={variantId}>
105
- {variantId}
85
+ {variantId === defaultVariantId ? variantId + ' (default)' : variantId}
106
86
  </option>
107
87
  ))}
108
88
  </select>
@@ -16,7 +16,7 @@
16
16
  import { AbstractStreamParsingChatAgent, ChatRequestModel, ChatService, ChatSession, MutableChatModel, MutableChatRequestModel } from '@theia/ai-chat/lib/common';
17
17
  import { LanguageModelRequirement } from '@theia/ai-core';
18
18
  import { inject, injectable } from '@theia/core/shared/inversify';
19
- import { architectPromptTemplate, architectTaskSummaryPromptTemplate } from '../common/architect-prompt-template';
19
+ import { architectVariants } from '../common/architect-prompt-template';
20
20
  import { FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILE_LIST_FUNCTION_ID } from '../common/workspace-functions';
21
21
  import { nls } from '@theia/core';
22
22
  import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
@@ -38,9 +38,9 @@ export class ArchitectAgent extends AbstractStreamParsingChatAgent {
38
38
  'An AI assistant integrated into Theia IDE, designed to assist software developers. This agent can access the users workspace, it can get a list of all available files \
39
39
  and folders and retrieve their content. It cannot modify files. It can therefore answer questions about the current project, project files and source code in the \
40
40
  workspace, such as how to build the project, where to put source code, where to find specific code or configurations, etc.');
41
- override promptTemplates = [architectPromptTemplate, architectTaskSummaryPromptTemplate];
41
+ override prompts = [architectVariants];
42
42
  override functions = [GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID];
43
- protected override systemPromptId: string | undefined = architectPromptTemplate.id;
43
+ protected override systemPromptId: string | undefined = architectVariants.id;
44
44
 
45
45
  override async invoke(request: MutableChatRequestModel): Promise<void> {
46
46
  await super.invoke(request);
@@ -16,9 +16,9 @@
16
16
  import { AbstractStreamParsingChatAgent, ChatRequestModel, ChatService, ChatSession, MutableChatModel, MutableChatRequestModel } from '@theia/ai-chat/lib/common';
17
17
  import { inject, injectable } from '@theia/core/shared/inversify';
18
18
  import { FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILE_LIST_FUNCTION_ID, GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID } from '../common/workspace-functions';
19
- import { CODER_REPLACE_PROMPT_TEMPLATE_ID, getCoderReplacePromptTemplate, getCoderReplacePromptTemplateNext } from '../common/coder-replace-prompt-template';
19
+ import { CODER_SYSTEM_PROMPT_ID, getCoderAgentModePromptTemplate, getCoderReplacePromptTemplate, getCoderReplacePromptTemplateNext } from '../common/coder-replace-prompt-template';
20
20
  import { WriteChangeToFileProvider } from './file-changeset-functions';
21
- import { LanguageModelRequirement } from '@theia/ai-core';
21
+ import { LanguageModelRequirement, PromptVariantSet } from '@theia/ai-core';
22
22
  import { nls } from '@theia/core';
23
23
  import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
24
24
  import { AI_CHAT_NEW_CHAT_WINDOW_COMMAND, ChatCommands } from '@theia/ai-chat-ui/lib/browser/chat-view-commands';
@@ -36,11 +36,15 @@ export class CoderAgent extends AbstractStreamParsingChatAgent {
36
36
 
37
37
  override description = nls.localize('theia/ai/workspace/coderAgent/description',
38
38
  'An AI assistant integrated into Theia IDE, designed to assist software developers. This agent can access the users workspace, it can get a list of all available files \
39
- and folders and retrieve their content. Futhermore, it can suggest modifications of files to the user. It can therefore assist the user with coding tasks or other \
39
+ and folders and retrieve their content. Furthermore, it can suggest modifications of files to the user. It can therefore assist the user with coding tasks or other \
40
40
  tasks involving file changes.');
41
- override promptTemplates = [getCoderReplacePromptTemplate(true), getCoderReplacePromptTemplate(false), getCoderReplacePromptTemplateNext()];
41
+ override prompts: PromptVariantSet[] = [{
42
+ id: CODER_SYSTEM_PROMPT_ID,
43
+ defaultVariant: getCoderReplacePromptTemplate(true),
44
+ variants: [getCoderReplacePromptTemplate(false), getCoderReplacePromptTemplateNext(), getCoderAgentModePromptTemplate()]
45
+ }];
42
46
  override functions = [GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID, GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID, WriteChangeToFileProvider.ID];
43
- protected override systemPromptId: string | undefined = CODER_REPLACE_PROMPT_TEMPLATE_ID;
47
+ protected override systemPromptId: string | undefined = CODER_SYSTEM_PROMPT_ID;
44
48
  override async invoke(request: MutableChatRequestModel): Promise<void> {
45
49
  await super.invoke(request);
46
50
  this.suggest(request);
@@ -63,4 +67,5 @@ export class CoderAgent extends AbstractStreamParsingChatAgent {
63
67
  + ` or [start a new chat with a summary of this one](command:${ChatCommands.AI_CHAT_NEW_WITH_TASK_CONTEXT.id}).`)]);
64
68
  }
65
69
  }
70
+
66
71
  }
@@ -16,10 +16,11 @@
16
16
  import { injectable, inject } from '@theia/core/shared/inversify';
17
17
  import { ToolProvider, ToolRequest, ToolRequestParameters, ToolRequestParametersProperties } from '@theia/ai-core';
18
18
  import { WorkspaceFunctionScope } from './workspace-functions';
19
- import { ChangeSetFileElementFactory } from '@theia/ai-chat/lib/browser/change-set-file-element';
20
- import { ChangeSetImpl, MutableChatRequestModel } from '@theia/ai-chat';
19
+ import { ChangeSetFileElement, ChangeSetFileElementFactory } from '@theia/ai-chat/lib/browser/change-set-file-element';
20
+ import { ChangeSet, ChangeSetImpl, MutableChatRequestModel } from '@theia/ai-chat';
21
21
  import { FileService } from '@theia/filesystem/lib/browser/file-service';
22
22
  import { ContentReplacer, Replacement } from '@theia/core/lib/common/content-replacer';
23
+ import { URI } from '@theia/core/lib/common/uri';
23
24
 
24
25
  @injectable()
25
26
  export class WriteChangeToFileProvider implements ToolProvider {
@@ -106,7 +107,7 @@ export class ReplaceContentInFileFunctionHelper {
106
107
  this.replacer = new ContentReplacer();
107
108
  }
108
109
 
109
- getToolMetadata(supportMutipleReplace: boolean = false): { description: string, parameters: ToolRequestParameters } {
110
+ getToolMetadata(supportMultipleReplace: boolean = false): { description: string, parameters: ToolRequestParameters } {
110
111
  const replacementProperties: ToolRequestParametersProperties = {
111
112
  oldContent: {
112
113
  type: 'string',
@@ -118,7 +119,7 @@ export class ReplaceContentInFileFunctionHelper {
118
119
  }
119
120
  };
120
121
 
121
- if (supportMutipleReplace) {
122
+ if (supportMultipleReplace) {
122
123
  replacementProperties.multiple = {
123
124
  type: 'boolean',
124
125
  description: 'Set to true if multiple occurrences of the oldContent are expected to be replaced.'
@@ -139,12 +140,16 @@ export class ReplaceContentInFileFunctionHelper {
139
140
  required: ['oldContent', 'newContent']
140
141
  },
141
142
  description: 'An array of replacement objects, each containing oldContent and newContent strings.'
143
+ },
144
+ reset: {
145
+ type: 'boolean',
146
+ description: 'Set to true to clear any existing pending changes for this file and start fresh. Default is false, which merges with existing changes.'
142
147
  }
143
148
  },
144
149
  required: ['path', 'replacements']
145
150
  } as ToolRequestParameters;
146
151
 
147
- const replacementSentence = supportMutipleReplace
152
+ const replacementSentence = supportMultipleReplace
148
153
  ? 'By default, a single occurrence of each old content in the tuples is expected to be replaced. If the optional \'multiple\' flag is set to true, all occurrences will\
149
154
  be replaced. In either case, if the number of occurrences in the file does not match the expectation the function will return an error. \
150
155
  In that case try a different approach.'
@@ -152,10 +157,10 @@ export class ReplaceContentInFileFunctionHelper {
152
157
  the function will return an error. In that case try a different approach.';
153
158
 
154
159
  const replacementDescription = `Propose to replace sections of content in an existing file by providing a list of tuples with old content to be matched and replaced.
155
- ${replacementSentence}. For deletions, use an empty new content in the tuple.\
156
- Make sure you use the same line endings and whitespace as in the original file content. The proposed changes will be applied when the user accepts.\
157
- If called again for the same file, it will override previous change proposals for this file. So you must ultimatly call this function only once per file with a tuple\
158
- containing all proposed changes for this file`;
160
+ ${replacementSentence}. For deletions, use an empty new content in the tuple.
161
+ Make sure you use the same line endings and whitespace as in the original file content. The proposed changes will be applied when the user accepts.
162
+ Multiple calls for the same file will merge replacements unless the reset parameter is set to true. Use the reset parameter to clear previous changes and start
163
+ fresh if needed.`;
159
164
 
160
165
  return {
161
166
  description: replacementDescription,
@@ -166,17 +171,33 @@ export class ReplaceContentInFileFunctionHelper {
166
171
 
167
172
  async createChangesetFromToolCall(toolCallString: string, ctx: MutableChatRequestModel): Promise<string> {
168
173
  try {
169
- const { path, replacements } = JSON.parse(toolCallString) as { path: string, replacements: Replacement[] };
174
+ const { path, replacements, reset } = JSON.parse(toolCallString) as { path: string, replacements: Replacement[], reset?: boolean };
170
175
  const fileUri = await this.workspaceFunctionScope.resolveRelativePath(path);
171
- const fileContent = (await this.fileService.read(fileUri)).value.toString();
172
176
 
173
- const { updatedContent, errors } = this.replacer.applyReplacements(fileContent, replacements);
177
+ // Get the starting content - either original file or existing proposed state
178
+ let startingContent: string;
179
+ if (reset || !ctx.session.changeSet) {
180
+ // Start from original file content
181
+ startingContent = (await this.fileService.read(fileUri)).value.toString();
182
+ } else {
183
+ // Start from existing proposed state if available
184
+ const existingElement = this.findExistingChangeElement(ctx.session.changeSet, fileUri);
185
+ if (existingElement) {
186
+ startingContent = existingElement.targetState || (await this.fileService.read(fileUri)).value.toString();
187
+ } else {
188
+ startingContent = (await this.fileService.read(fileUri)).value.toString();
189
+ }
190
+ }
191
+
192
+ const { updatedContent, errors } = this.replacer.applyReplacements(startingContent, replacements);
174
193
 
175
194
  if (errors.length > 0) {
176
195
  return `Errors encountered: ${errors.join('; ')}`;
177
196
  }
178
197
 
179
- if (updatedContent !== fileContent) {
198
+ // Only create/update changeset if content actually changed
199
+ const originalContent = (await this.fileService.read(fileUri)).value.toString();
200
+ if (updatedContent !== originalContent) {
180
201
  let changeSet = ctx.session.changeSet;
181
202
  if (!changeSet) {
182
203
  changeSet = new ChangeSetImpl('Changes proposed by Coder');
@@ -194,12 +215,70 @@ export class ReplaceContentInFileFunctionHelper {
194
215
  })
195
216
  );
196
217
  }
197
- return `Proposed replacements in file ${path}. The user will review and potentially apply the changes.`;
218
+
219
+ const action = reset ? 'reset and applied' : 'applied';
220
+ return `Proposed replacements ${action} to file ${path}. The user will review and potentially apply the changes.`;
198
221
  } catch (error) {
199
222
  console.debug('Error processing replacements:', error.message);
200
223
  return JSON.stringify({ error: error.message });
201
224
  }
202
225
  }
226
+
227
+ private findExistingChangeElement(changeSet: ChangeSet, fileUri: URI): ChangeSetFileElement | undefined {
228
+ return changeSet.getElementByURI(fileUri) as ChangeSetFileElement;
229
+ }
230
+
231
+ async clearFileChanges(path: string, ctx: MutableChatRequestModel): Promise<string> {
232
+ try {
233
+ const fileUri = await this.workspaceFunctionScope.resolveRelativePath(path);
234
+
235
+ if (!ctx.session.changeSet) {
236
+ return `No pending changes found for file ${path}.`;
237
+ }
238
+
239
+ const elements = ctx.session.changeSet.getElements();
240
+ const indicesToRemove: number[] = [];
241
+ elements.forEach((element, index) => {
242
+ if (element.uri && element.uri.toString() === fileUri.toString()) {
243
+ indicesToRemove.push(index);
244
+ }
245
+ });
246
+
247
+ if (indicesToRemove.length > 0) {
248
+ ctx.session.changeSet.removeElements(...indicesToRemove);
249
+ return `Cleared ${indicesToRemove.length} pending change(s) for file ${path}.`;
250
+ } else {
251
+ return `No pending changes found for file ${path}.`;
252
+ }
253
+ } catch (error) {
254
+ console.debug('Error clearing file changes:', error.message);
255
+ return JSON.stringify({ error: error.message });
256
+ }
257
+ }
258
+
259
+ async getProposedFileState(path: string, ctx: MutableChatRequestModel): Promise<string> {
260
+ try {
261
+ const fileUri = await this.workspaceFunctionScope.resolveRelativePath(path);
262
+
263
+ if (!ctx.session.changeSet) {
264
+ // No changeset exists, return original file content
265
+ const originalContent = (await this.fileService.read(fileUri)).value.toString();
266
+ return `File ${path} has no pending changes. Original content:\n\n${originalContent}`;
267
+ }
268
+
269
+ const existingElement = this.findExistingChangeElement(ctx.session.changeSet, fileUri);
270
+ if (existingElement && existingElement.targetState) {
271
+ return `File ${path} has pending changes. Proposed content:\n\n${existingElement.targetState}`;
272
+ } else {
273
+ // No pending changes for this file
274
+ const originalContent = (await this.fileService.read(fileUri)).value.toString();
275
+ return `File ${path} has no pending changes. Original content:\n\n${originalContent}`;
276
+ }
277
+ } catch (error) {
278
+ console.debug('Error getting proposed file state:', error.message);
279
+ return JSON.stringify({ error: error.message });
280
+ }
281
+ }
203
282
  }
204
283
 
205
284
  @injectable()
@@ -239,3 +318,62 @@ export class ReplaceContentInFileProvider implements ToolProvider {
239
318
  };
240
319
  }
241
320
  }
321
+
322
+ @injectable()
323
+ export class ClearFileChangesProvider implements ToolProvider {
324
+ static ID = 'changeSet_clearFileChanges';
325
+ @inject(ReplaceContentInFileFunctionHelper)
326
+ protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
327
+
328
+ getTool(): ToolRequest {
329
+ return {
330
+ id: ClearFileChangesProvider.ID,
331
+ name: ClearFileChangesProvider.ID,
332
+ description: 'Clears all pending changes for a specific file, allowing you to start fresh with new modifications.',
333
+ parameters: {
334
+ type: 'object',
335
+ properties: {
336
+ path: {
337
+ type: 'string',
338
+ description: 'The path of the file to clear pending changes for.'
339
+ }
340
+ },
341
+ required: ['path']
342
+ },
343
+ handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> => {
344
+ const { path } = JSON.parse(args);
345
+ return this.replaceContentInFileFunctionHelper.clearFileChanges(path, ctx);
346
+ }
347
+ };
348
+ }
349
+ }
350
+
351
+ @injectable()
352
+ export class GetProposedFileStateProvider implements ToolProvider {
353
+ static ID = 'changeSet_getProposedFileState';
354
+ @inject(ReplaceContentInFileFunctionHelper)
355
+ protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
356
+
357
+ getTool(): ToolRequest {
358
+ return {
359
+ id: GetProposedFileStateProvider.ID,
360
+ name: GetProposedFileStateProvider.ID,
361
+ description: 'Returns the current proposed state of a file, including all pending changes that have been proposed ' +
362
+ 'but not yet applied. This allows you to inspect the current state before making additional changes.',
363
+ parameters: {
364
+ type: 'object',
365
+ properties: {
366
+ path: {
367
+ type: 'string',
368
+ description: 'The path of the file to get the proposed state for.'
369
+ }
370
+ },
371
+ required: ['path']
372
+ },
373
+ handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> => {
374
+ const { path } = JSON.parse(args);
375
+ return this.replaceContentInFileFunctionHelper.getProposedFileState(path, ctx);
376
+ }
377
+ };
378
+ }
379
+ }
@@ -20,12 +20,14 @@ import { Agent, AIVariableContribution, bindToolProvider } from '@theia/ai-core/
20
20
  import { ArchitectAgent } from './architect-agent';
21
21
  import { CoderAgent } from './coder-agent';
22
22
  import { SummarizeSessionCommandContribution } from './summarize-session-command-contribution';
23
- import { FileContentFunction, FileDiagonsticProvider, GetWorkspaceDirectoryStructure, GetWorkspaceFileList, WorkspaceFunctionScope } from './workspace-functions';
23
+ import { FileContentFunction, FileDiagnosticProvider, GetWorkspaceDirectoryStructure, GetWorkspaceFileList, WorkspaceFunctionScope } from './workspace-functions';
24
24
  import { WorkspaceSearchProvider } from './workspace-search-provider';
25
25
  import { FrontendApplicationContribution, PreferenceContribution, WidgetFactory, bindViewContribution } from '@theia/core/lib/browser';
26
26
  import { TaskListProvider, TaskRunnerProvider } from './workspace-task-provider';
27
27
  import { WorkspacePreferencesSchema } from './workspace-preferences';
28
28
  import {
29
+ ClearFileChangesProvider,
30
+ GetProposedFileStateProvider,
29
31
  ReplaceContentInFileFunctionHelper,
30
32
  ReplaceContentInFileProvider,
31
33
  SimpleReplaceContentInFileProvider,
@@ -52,6 +54,7 @@ import { TaskContextSummaryVariableContribution } from './task-background-summar
52
54
  import { TaskContextFileStorageService } from './task-context-file-storage-service';
53
55
  import { TaskContextStorageService } from '@theia/ai-chat/lib/browser/task-context-service';
54
56
  import { CommandContribution } from '@theia/core';
57
+ import { AIPromptFragmentsConfigurationWidget } from './ai-configuration/prompt-fragments-configuration-widget';
55
58
 
56
59
  export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
57
60
  bind(PreferenceContribution).toConstantValue({ schema: WorkspacePreferencesSchema });
@@ -84,7 +87,7 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
84
87
  bindToolProvider(GetWorkspaceFileList, bind);
85
88
  bindToolProvider(FileContentFunction, bind);
86
89
  bindToolProvider(GetWorkspaceDirectoryStructure, bind);
87
- bindToolProvider(FileDiagonsticProvider, bind);
90
+ bindToolProvider(FileDiagnosticProvider, bind);
88
91
  bind(WorkspaceFunctionScope).toSelf().inSingletonScope();
89
92
  bindToolProvider(WorkspaceSearchProvider, bind);
90
93
 
@@ -124,6 +127,8 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
124
127
  .inSingletonScope();
125
128
 
126
129
  bindToolProvider(SimpleReplaceContentInFileProvider, bind);
130
+ bindToolProvider(ClearFileChangesProvider, bind);
131
+ bindToolProvider(GetProposedFileStateProvider, bind);
127
132
  bindToolProvider(AddFileToChatContext, bind);
128
133
  bind(AIVariableContribution).to(ContextFilesVariableContribution).inSingletonScope();
129
134
  bind(PreferenceContribution).toConstantValue({ schema: AiConfigurationPreferences });
@@ -152,4 +157,11 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
152
157
  rebind(TaskContextStorageService).toService(TaskContextFileStorageService);
153
158
 
154
159
  bind(CommandContribution).to(SummarizeSessionCommandContribution);
160
+ bind(AIPromptFragmentsConfigurationWidget).toSelf();
161
+ bind(WidgetFactory)
162
+ .toDynamicValue(ctx => ({
163
+ id: AIPromptFragmentsConfigurationWidget.ID,
164
+ createWidget: () => ctx.container.get(AIPromptFragmentsConfigurationWidget)
165
+ }))
166
+ .inSingletonScope();
155
167
  });