@theia/ai-ide 1.63.0-next.0 → 1.63.0-next.52

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 (102) hide show
  1. package/lib/browser/ai-configuration/ai-configuration-view-contribution.js +1 -1
  2. package/lib/browser/ai-configuration/ai-configuration-view-contribution.js.map +1 -1
  3. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +2 -1
  4. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -1
  5. package/lib/browser/ai-configuration/ai-configuration-widget.js +6 -1
  6. package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -1
  7. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts +0 -1
  8. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts.map +1 -1
  9. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js +0 -1
  10. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js.map +1 -1
  11. package/lib/browser/ai-configuration/token-usage-configuration-widget.d.ts +1 -0
  12. package/lib/browser/ai-configuration/token-usage-configuration-widget.d.ts.map +1 -1
  13. package/lib/browser/ai-configuration/token-usage-configuration-widget.js +25 -3
  14. package/lib/browser/ai-configuration/token-usage-configuration-widget.js.map +1 -1
  15. package/lib/browser/ai-configuration/tools-configuration-widget.d.ts +28 -0
  16. package/lib/browser/ai-configuration/tools-configuration-widget.d.ts.map +1 -0
  17. package/lib/browser/ai-configuration/tools-configuration-widget.js +148 -0
  18. package/lib/browser/ai-configuration/tools-configuration-widget.js.map +1 -0
  19. package/lib/browser/app-tester-chat-agent.d.ts +36 -0
  20. package/lib/browser/app-tester-chat-agent.d.ts.map +1 -0
  21. package/lib/browser/app-tester-chat-agent.js +172 -0
  22. package/lib/browser/app-tester-chat-agent.js.map +1 -0
  23. package/lib/browser/architect-agent.d.ts.map +1 -1
  24. package/lib/browser/architect-agent.js +4 -1
  25. package/lib/browser/architect-agent.js.map +1 -1
  26. package/lib/browser/coder-agent.d.ts.map +1 -1
  27. package/lib/browser/coder-agent.js +3 -3
  28. package/lib/browser/coder-agent.js.map +1 -1
  29. package/lib/browser/file-changeset-functions.d.ts +25 -6
  30. package/lib/browser/file-changeset-functions.d.ts.map +1 -1
  31. package/lib/browser/file-changeset-functions.js +248 -106
  32. package/lib/browser/file-changeset-functions.js.map +1 -1
  33. package/lib/browser/frontend-module.d.ts +1 -0
  34. package/lib/browser/frontend-module.d.ts.map +1 -1
  35. package/lib/browser/frontend-module.js +21 -5
  36. package/lib/browser/frontend-module.js.map +1 -1
  37. package/lib/browser/summarize-session-command-contribution.d.ts +6 -1
  38. package/lib/browser/summarize-session-command-contribution.d.ts.map +1 -1
  39. package/lib/browser/summarize-session-command-contribution.js +53 -4
  40. package/lib/browser/summarize-session-command-contribution.js.map +1 -1
  41. package/lib/browser/task-context-file-storage-service.d.ts +4 -2
  42. package/lib/browser/task-context-file-storage-service.d.ts.map +1 -1
  43. package/lib/browser/task-context-file-storage-service.js +19 -9
  44. package/lib/browser/task-context-file-storage-service.js.map +1 -1
  45. package/lib/browser/workspace-functions.d.ts.map +1 -1
  46. package/lib/browser/workspace-functions.js +6 -10
  47. package/lib/browser/workspace-functions.js.map +1 -1
  48. package/lib/browser/workspace-preferences.d.ts +1 -0
  49. package/lib/browser/workspace-preferences.d.ts.map +1 -1
  50. package/lib/browser/workspace-preferences.js +9 -1
  51. package/lib/browser/workspace-preferences.js.map +1 -1
  52. package/lib/browser/workspace-search-provider.d.ts +7 -1
  53. package/lib/browser/workspace-search-provider.d.ts.map +1 -1
  54. package/lib/browser/workspace-search-provider.js +73 -11
  55. package/lib/browser/workspace-search-provider.js.map +1 -1
  56. package/lib/browser/workspace-search-provider.spec.d.ts +2 -0
  57. package/lib/browser/workspace-search-provider.spec.d.ts.map +1 -0
  58. package/lib/browser/workspace-search-provider.spec.js +227 -0
  59. package/lib/browser/workspace-search-provider.spec.js.map +1 -0
  60. package/lib/common/architect-prompt-template.d.ts +1 -0
  61. package/lib/common/architect-prompt-template.d.ts.map +1 -1
  62. package/lib/common/architect-prompt-template.js +166 -5
  63. package/lib/common/architect-prompt-template.js.map +1 -1
  64. package/lib/common/coder-replace-prompt-template.d.ts +4 -5
  65. package/lib/common/coder-replace-prompt-template.d.ts.map +1 -1
  66. package/lib/common/coder-replace-prompt-template.js +95 -67
  67. package/lib/common/coder-replace-prompt-template.js.map +1 -1
  68. package/lib/common/file-changeset-function-ids.d.ts +7 -0
  69. package/lib/common/file-changeset-function-ids.d.ts.map +1 -0
  70. package/lib/common/file-changeset-function-ids.js +25 -0
  71. package/lib/common/file-changeset-function-ids.js.map +1 -0
  72. package/lib/common/summarize-session-commands.d.ts +1 -0
  73. package/lib/common/summarize-session-commands.d.ts.map +1 -1
  74. package/lib/common/summarize-session-commands.js +5 -1
  75. package/lib/common/summarize-session-commands.js.map +1 -1
  76. package/lib/common/workspace-search-provider-util.d.ts +17 -0
  77. package/lib/common/workspace-search-provider-util.d.ts.map +1 -0
  78. package/lib/common/workspace-search-provider-util.js +51 -0
  79. package/lib/common/workspace-search-provider-util.js.map +1 -0
  80. package/package.json +19 -18
  81. package/src/browser/ai-configuration/ai-configuration-view-contribution.ts +1 -1
  82. package/src/browser/ai-configuration/ai-configuration-widget.tsx +6 -1
  83. package/src/browser/ai-configuration/prompt-fragments-configuration-widget.tsx +0 -1
  84. package/src/browser/ai-configuration/token-usage-configuration-widget.tsx +63 -4
  85. package/src/browser/ai-configuration/tools-configuration-widget.tsx +178 -0
  86. package/src/browser/app-tester-chat-agent.ts +178 -0
  87. package/src/browser/architect-agent.ts +5 -2
  88. package/src/browser/coder-agent.ts +5 -5
  89. package/src/browser/file-changeset-functions.ts +236 -89
  90. package/src/browser/frontend-module.ts +33 -10
  91. package/src/browser/style/index.css +84 -11
  92. package/src/browser/summarize-session-command-contribution.ts +58 -6
  93. package/src/browser/task-context-file-storage-service.ts +20 -10
  94. package/src/browser/workspace-functions.ts +7 -11
  95. package/src/browser/workspace-preferences.ts +9 -0
  96. package/src/browser/workspace-search-provider.spec.ts +255 -0
  97. package/src/browser/workspace-search-provider.ts +78 -11
  98. package/src/common/architect-prompt-template.ts +165 -5
  99. package/src/common/coder-replace-prompt-template.ts +101 -65
  100. package/src/common/file-changeset-function-ids.ts +22 -0
  101. package/src/common/summarize-session-commands.ts +5 -0
  102. package/src/common/workspace-search-provider-util.ts +50 -0
@@ -16,15 +16,24 @@
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 { ChangeSetFileElement, ChangeSetFileElementFactory } from '@theia/ai-chat/lib/browser/change-set-file-element';
20
- import { ChangeSet, ChangeSetImpl, MutableChatRequestModel } from '@theia/ai-chat';
19
+ import { ChangeSetElementArgs, ChangeSetFileElement, ChangeSetFileElementFactory } from '@theia/ai-chat/lib/browser/change-set-file-element';
20
+ import { ChangeSet, 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
23
  import { URI } from '@theia/core/lib/common/uri';
24
24
 
25
+ import {
26
+ SUGGEST_FILE_CONTENT_ID,
27
+ WRITE_FILE_CONTENT_ID,
28
+ SUGGEST_FILE_REPLACEMENTS_ID,
29
+ WRITE_FILE_REPLACEMENTS_ID,
30
+ CLEAR_FILE_CHANGES_ID,
31
+ GET_PROPOSED_CHANGES_ID
32
+ } from '../common/file-changeset-function-ids';
33
+
25
34
  @injectable()
26
- export class WriteChangeToFileProvider implements ToolProvider {
27
- static ID = 'changeSet_writeChangeToFile';
35
+ export class SuggestFileContent implements ToolProvider {
36
+ static ID = SUGGEST_FILE_CONTENT_ID;
28
37
 
29
38
  @inject(WorkspaceFunctionScope)
30
39
  protected readonly workspaceFunctionScope: WorkspaceFunctionScope;
@@ -37,8 +46,8 @@ export class WriteChangeToFileProvider implements ToolProvider {
37
46
 
38
47
  getTool(): ToolRequest {
39
48
  return {
40
- id: WriteChangeToFileProvider.ID,
41
- name: WriteChangeToFileProvider.ID,
49
+ id: SuggestFileContent.ID,
50
+ name: SuggestFileContent.ID,
42
51
  description: `Proposes writing content to a file. If the file exists, it will be overwritten with the provided content.\n
43
52
  If the file does not exist, it will be created. This tool will automatically create any directories needed to write the file.\n
44
53
  If the new content is empty, the file will be deleted. To move a file, delete it and re-create it at the new location.\n
@@ -61,35 +70,105 @@ export class WriteChangeToFileProvider implements ToolProvider {
61
70
  handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> => {
62
71
  const { path, content } = JSON.parse(args);
63
72
  const chatSessionId = ctx.session.id;
64
- let changeSet = ctx.session.changeSet;
65
- if (!changeSet) {
66
- changeSet = new ChangeSetImpl('Changes proposed by Coder');
67
- ctx.session.setChangeSet(changeSet);
68
- }
69
73
  const uri = await this.workspaceFunctionScope.resolveRelativePath(path);
70
- let type = 'modify';
74
+ let type: ChangeSetElementArgs['type'] = 'modify';
71
75
  if (content === '') {
72
76
  type = 'delete';
73
77
  }
74
78
  if (!await this.fileService.exists(uri)) {
75
79
  type = 'add';
76
80
  }
77
- changeSet.addElements(
81
+ ctx.session.changeSet.addElements(
78
82
  this.fileChangeFactory({
79
83
  uri: uri,
80
- type: type as 'modify' | 'add' | 'delete',
84
+ type,
81
85
  state: 'pending',
82
86
  targetState: content,
83
- changeSet,
87
+ requestId: ctx.id,
84
88
  chatSessionId
85
89
  })
86
90
  );
91
+ ctx.session.changeSet.setTitle('Changes proposed by Coder');
87
92
  return `Proposed writing to file ${path}. The user will review and potentially apply the changes`;
88
93
  }
89
94
  };
90
95
  }
91
96
  }
92
97
 
98
+ @injectable()
99
+ export class WriteFileContent implements ToolProvider {
100
+ static ID = WRITE_FILE_CONTENT_ID;
101
+
102
+ @inject(WorkspaceFunctionScope)
103
+ protected readonly workspaceFunctionScope: WorkspaceFunctionScope;
104
+
105
+ @inject(FileService)
106
+ fileService: FileService;
107
+
108
+ @inject(ChangeSetFileElementFactory)
109
+ protected readonly fileChangeFactory: ChangeSetFileElementFactory;
110
+
111
+ getTool(): ToolRequest {
112
+ return {
113
+ id: WriteFileContent.ID,
114
+ name: WriteFileContent.ID,
115
+ description: `Immediately writes content to a file. If the file exists, it will be overwritten with the provided content.\n
116
+ If the file does not exist, it will be created. This tool will automatically create any directories needed to write the file.\n
117
+ If the new content is empty, the file will be deleted. To move a file, delete it and re-create it at the new location.\n
118
+ Unlike suggestFileContent, this function applies the changes immediately without user confirmation.`,
119
+ parameters: {
120
+ type: 'object',
121
+ properties: {
122
+ path: {
123
+ type: 'string',
124
+ description: 'The path of the file to write to.'
125
+ },
126
+ content: {
127
+ type: 'string',
128
+ description: `The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions.\n
129
+ You MUST include ALL parts of the file, even if they haven\'t been modified.`
130
+ }
131
+ },
132
+ required: ['path', 'content']
133
+ },
134
+ handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> => {
135
+ const { path, content } = JSON.parse(args);
136
+ const chatSessionId = ctx.session.id;
137
+ const uri = await this.workspaceFunctionScope.resolveRelativePath(path);
138
+ let type = 'modify';
139
+ if (content === '') {
140
+ type = 'delete';
141
+ }
142
+ if (!await this.fileService.exists(uri)) {
143
+ type = 'add';
144
+ }
145
+
146
+ // Create the file change element
147
+ const fileElement = this.fileChangeFactory({
148
+ uri: uri,
149
+ type: type as 'modify' | 'add' | 'delete',
150
+ state: 'pending',
151
+ targetState: content,
152
+ requestId: ctx.id,
153
+ chatSessionId
154
+ });
155
+
156
+ ctx.session.changeSet.setTitle('Changes applied by Coder');
157
+ // Add the element to the change set
158
+ ctx.session.changeSet.addElements(fileElement);
159
+
160
+ try {
161
+ // Immediately apply the change
162
+ await fileElement.apply();
163
+ return `Successfully wrote content to file ${path}.`;
164
+ } catch (error) {
165
+ return `Failed to write content to file ${path}: ${error.message}`;
166
+ }
167
+ }
168
+ };
169
+ }
170
+ }
171
+
93
172
  @injectable()
94
173
  export class ReplaceContentInFileFunctionHelper {
95
174
  @inject(WorkspaceFunctionScope)
@@ -107,7 +186,7 @@ export class ReplaceContentInFileFunctionHelper {
107
186
  this.replacer = new ContentReplacer();
108
187
  }
109
188
 
110
- getToolMetadata(supportMultipleReplace: boolean = false): { description: string, parameters: ToolRequestParameters } {
189
+ getToolMetadata(supportMultipleReplace: boolean = false, immediateApplication: boolean = false): { description: string, parameters: ToolRequestParameters } {
111
190
  const replacementProperties: ToolRequestParametersProperties = {
112
191
  oldContent: {
113
192
  type: 'string',
@@ -156,10 +235,14 @@ export class ReplaceContentInFileFunctionHelper {
156
235
  : 'A single occurrence of each old content in the tuples is expected to be replaced. If the number of occurrences in the file does not match the expectation,\
157
236
  the function will return an error. In that case try a different approach.';
158
237
 
238
+ const applicationText = immediateApplication
239
+ ? 'The changes will be applied immediately without user confirmation.'
240
+ : 'The proposed changes will be applied when the user accepts.';
241
+
159
242
  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.
160
243
  ${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
244
+ Make sure you use the same line endings and whitespace as in the original file content. ${applicationText}
245
+ 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
246
  fresh if needed.`;
164
247
 
165
248
  return {
@@ -171,82 +254,108 @@ export class ReplaceContentInFileFunctionHelper {
171
254
 
172
255
  async createChangesetFromToolCall(toolCallString: string, ctx: MutableChatRequestModel): Promise<string> {
173
256
  try {
174
- const { path, replacements, reset } = JSON.parse(toolCallString) as { path: string, replacements: Replacement[], reset?: boolean };
175
- const fileUri = await this.workspaceFunctionScope.resolveRelativePath(path);
257
+ const result = await this.processReplacementsCommon(toolCallString, ctx, 'Changes proposed by Coder');
176
258
 
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();
259
+ if (result.errors.length > 0) {
260
+ return `Errors encountered: ${result.errors.join('; ')}`;
261
+ }
262
+
263
+ if (result.fileElement) {
264
+ const action = result.reset ? 'reset and applied' : 'applied';
265
+ return `Proposed replacements ${action} to file ${result.path}. The user will review and potentially apply the changes.`;
182
266
  } 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
- }
267
+ return `No changes needed for file ${result.path}. Content already matches the requested state.`;
190
268
  }
269
+ } catch (error) {
270
+ console.debug('Error processing replacements:', error.message);
271
+ return JSON.stringify({ error: error.message });
272
+ }
273
+ }
191
274
 
192
- const { updatedContent, errors } = this.replacer.applyReplacements(startingContent, replacements);
275
+ async writeChangesetFromToolCall(toolCallString: string, ctx: MutableChatRequestModel): Promise<string> {
276
+ try {
277
+ const result = await this.processReplacementsCommon(toolCallString, ctx, 'Changes applied by Coder');
193
278
 
194
- if (errors.length > 0) {
195
- return `Errors encountered: ${errors.join('; ')}`;
279
+ if (result.errors.length > 0) {
280
+ return `Errors encountered: ${result.errors.join('; ')}`;
196
281
  }
197
282
 
198
- // Only create/update changeset if content actually changed
199
- const originalContent = (await this.fileService.read(fileUri)).value.toString();
200
- if (updatedContent !== originalContent) {
201
- let changeSet = ctx.session.changeSet;
202
- if (!changeSet) {
203
- changeSet = new ChangeSetImpl('Changes proposed by Coder');
204
- ctx.session.setChangeSet(changeSet);
205
- }
283
+ if (result.fileElement) {
284
+ // Immediately apply the change
285
+ await result.fileElement.apply();
206
286
 
207
- changeSet.addElements(
208
- this.fileChangeFactory({
209
- uri: fileUri,
210
- type: 'modify',
211
- state: 'pending',
212
- targetState: updatedContent,
213
- changeSet,
214
- chatSessionId: ctx.session.id
215
- })
216
- );
287
+ const action = result.reset ? 'reset and' : '';
288
+ return `Successfully ${action} applied replacements to file ${result.path}.`;
289
+ } else {
290
+ return `No changes needed for file ${result.path}. Content already matches the requested state.`;
217
291
  }
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.`;
221
292
  } catch (error) {
222
293
  console.debug('Error processing replacements:', error.message);
223
294
  return JSON.stringify({ error: error.message });
224
295
  }
225
296
  }
226
297
 
298
+ private async processReplacementsCommon(
299
+ toolCallString: string,
300
+ ctx: MutableChatRequestModel,
301
+ changeSetTitle: string
302
+ ): Promise<{ fileElement: ChangeSetFileElement | undefined, path: string, reset: boolean, errors: string[] }> {
303
+ const { path, replacements, reset } = JSON.parse(toolCallString) as { path: string, replacements: Replacement[], reset?: boolean };
304
+ const fileUri = await this.workspaceFunctionScope.resolveRelativePath(path);
305
+
306
+ // Get the starting content - either original file or existing proposed state
307
+ let startingContent: string;
308
+ if (reset || !ctx.session.changeSet) {
309
+ // Start from original file content
310
+ startingContent = (await this.fileService.read(fileUri)).value.toString();
311
+ } else {
312
+ // Start from existing proposed state if available
313
+ const existingElement = this.findExistingChangeElement(ctx.session.changeSet, fileUri);
314
+ if (existingElement) {
315
+ startingContent = existingElement.targetState || (await this.fileService.read(fileUri)).value.toString();
316
+ } else {
317
+ startingContent = (await this.fileService.read(fileUri)).value.toString();
318
+ }
319
+ }
320
+
321
+ const { updatedContent, errors } = this.replacer.applyReplacements(startingContent, replacements);
322
+
323
+ if (errors.length > 0) {
324
+ return { fileElement: undefined, path, reset: reset || false, errors };
325
+ }
326
+
327
+ // Only create/update changeset if content actually changed
328
+ const originalContent = (await this.fileService.read(fileUri)).value.toString();
329
+ if (updatedContent !== originalContent) {
330
+ ctx.session.changeSet.setTitle(changeSetTitle);
331
+
332
+ const fileElement = this.fileChangeFactory({
333
+ uri: fileUri,
334
+ type: 'modify',
335
+ state: 'pending',
336
+ targetState: updatedContent,
337
+ requestId: ctx.id,
338
+ chatSessionId: ctx.session.id
339
+ });
340
+
341
+ ctx.session.changeSet.addElements(fileElement);
342
+
343
+ return { fileElement, path, reset: reset || false, errors: [] };
344
+ } else {
345
+ return { fileElement: undefined, path, reset: reset || false, errors: [] };
346
+ }
347
+ }
348
+
227
349
  private findExistingChangeElement(changeSet: ChangeSet, fileUri: URI): ChangeSetFileElement | undefined {
228
- return changeSet.getElementByURI(fileUri) as ChangeSetFileElement;
350
+ const element = changeSet.getElementByURI(fileUri);
351
+ if (element instanceof ChangeSetFileElement) { return element; }
229
352
  }
230
353
 
231
354
  async clearFileChanges(path: string, ctx: MutableChatRequestModel): Promise<string> {
232
355
  try {
233
356
  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}.`;
357
+ if (ctx.session.changeSet.removeElements(fileUri)) {
358
+ return `Cleared pending change(s) for file ${path}.`;
250
359
  } else {
251
360
  return `No pending changes found for file ${path}.`;
252
361
  }
@@ -282,16 +391,16 @@ export class ReplaceContentInFileFunctionHelper {
282
391
  }
283
392
 
284
393
  @injectable()
285
- export class SimpleReplaceContentInFileProvider implements ToolProvider {
286
- static ID = 'changeSet_replaceContentInFilev1';
394
+ export class SimpleSuggestFileReplacements implements ToolProvider {
395
+ static ID = 'simpleSuggestFileReplacements';
287
396
  @inject(ReplaceContentInFileFunctionHelper)
288
397
  protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
289
398
 
290
399
  getTool(): ToolRequest {
291
400
  const metadata = this.replaceContentInFileFunctionHelper.getToolMetadata();
292
401
  return {
293
- id: SimpleReplaceContentInFileProvider.ID,
294
- name: SimpleReplaceContentInFileProvider.ID,
402
+ id: SimpleSuggestFileReplacements.ID,
403
+ name: SimpleSuggestFileReplacements.ID,
295
404
  description: metadata.description,
296
405
  parameters: metadata.parameters,
297
406
  handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> =>
@@ -301,16 +410,35 @@ export class SimpleReplaceContentInFileProvider implements ToolProvider {
301
410
  }
302
411
 
303
412
  @injectable()
304
- export class ReplaceContentInFileProvider implements ToolProvider {
305
- static ID = 'changeSet_replaceContentInFile';
413
+ export class SimpleWriteFileReplacements implements ToolProvider {
414
+ static ID = 'simpleWriteFileReplacements';
415
+ @inject(ReplaceContentInFileFunctionHelper)
416
+ protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
417
+
418
+ getTool(): ToolRequest {
419
+ const metadata = this.replaceContentInFileFunctionHelper.getToolMetadata(false, true);
420
+ return {
421
+ id: SimpleWriteFileReplacements.ID,
422
+ name: SimpleWriteFileReplacements.ID,
423
+ description: metadata.description,
424
+ parameters: metadata.parameters,
425
+ handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> =>
426
+ this.replaceContentInFileFunctionHelper.writeChangesetFromToolCall(args, ctx)
427
+ };
428
+ }
429
+ }
430
+
431
+ @injectable()
432
+ export class SuggestFileReplacements implements ToolProvider {
433
+ static ID = SUGGEST_FILE_REPLACEMENTS_ID;
306
434
  @inject(ReplaceContentInFileFunctionHelper)
307
435
  protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
308
436
 
309
437
  getTool(): ToolRequest {
310
438
  const metadata = this.replaceContentInFileFunctionHelper.getToolMetadata(true);
311
439
  return {
312
- id: ReplaceContentInFileProvider.ID,
313
- name: ReplaceContentInFileProvider.ID,
440
+ id: SuggestFileReplacements.ID,
441
+ name: SuggestFileReplacements.ID,
314
442
  description: metadata.description,
315
443
  parameters: metadata.parameters,
316
444
  handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> =>
@@ -320,15 +448,34 @@ export class ReplaceContentInFileProvider implements ToolProvider {
320
448
  }
321
449
 
322
450
  @injectable()
323
- export class ClearFileChangesProvider implements ToolProvider {
324
- static ID = 'changeSet_clearFileChanges';
451
+ export class WriteFileReplacements implements ToolProvider {
452
+ static ID = WRITE_FILE_REPLACEMENTS_ID;
453
+ @inject(ReplaceContentInFileFunctionHelper)
454
+ protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
455
+
456
+ getTool(): ToolRequest {
457
+ const metadata = this.replaceContentInFileFunctionHelper.getToolMetadata(true, true);
458
+ return {
459
+ id: WriteFileReplacements.ID,
460
+ name: WriteFileReplacements.ID,
461
+ description: metadata.description,
462
+ parameters: metadata.parameters,
463
+ handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> =>
464
+ this.replaceContentInFileFunctionHelper.writeChangesetFromToolCall(args, ctx)
465
+ };
466
+ }
467
+ }
468
+
469
+ @injectable()
470
+ export class ClearFileChanges implements ToolProvider {
471
+ static ID = CLEAR_FILE_CHANGES_ID;
325
472
  @inject(ReplaceContentInFileFunctionHelper)
326
473
  protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
327
474
 
328
475
  getTool(): ToolRequest {
329
476
  return {
330
- id: ClearFileChangesProvider.ID,
331
- name: ClearFileChangesProvider.ID,
477
+ id: ClearFileChanges.ID,
478
+ name: ClearFileChanges.ID,
332
479
  description: 'Clears all pending changes for a specific file, allowing you to start fresh with new modifications.',
333
480
  parameters: {
334
481
  type: 'object',
@@ -349,15 +496,15 @@ export class ClearFileChangesProvider implements ToolProvider {
349
496
  }
350
497
 
351
498
  @injectable()
352
- export class GetProposedFileStateProvider implements ToolProvider {
353
- static ID = 'changeSet_getProposedFileState';
499
+ export class GetProposedFileState implements ToolProvider {
500
+ static ID = GET_PROPOSED_CHANGES_ID;
354
501
  @inject(ReplaceContentInFileFunctionHelper)
355
502
  protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
356
503
 
357
504
  getTool(): ToolRequest {
358
505
  return {
359
- id: GetProposedFileStateProvider.ID,
360
- name: GetProposedFileStateProvider.ID,
506
+ id: GET_PROPOSED_CHANGES_ID,
507
+ name: GET_PROPOSED_CHANGES_ID,
361
508
  description: 'Returns the current proposed state of a file, including all pending changes that have been proposed ' +
362
509
  'but not yet applied. This allows you to inspect the current state before making additional changes.',
363
510
  parameters: {
@@ -14,6 +14,8 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
+ import '../../src/browser/style/index.css';
18
+
17
19
  import { ContainerModule } from '@theia/core/shared/inversify';
18
20
  import { ChatAgent, DefaultChatAgentId, FallbackChatAgentId } from '@theia/ai-chat/lib/common';
19
21
  import { Agent, AIVariableContribution, bindToolProvider } from '@theia/ai-core/lib/common';
@@ -26,15 +28,19 @@ import { FrontendApplicationContribution, PreferenceContribution, WidgetFactory,
26
28
  import { TaskListProvider, TaskRunnerProvider } from './workspace-task-provider';
27
29
  import { WorkspacePreferencesSchema } from './workspace-preferences';
28
30
  import {
29
- ClearFileChangesProvider,
30
- GetProposedFileStateProvider,
31
+ ClearFileChanges,
32
+ GetProposedFileState,
31
33
  ReplaceContentInFileFunctionHelper,
32
- ReplaceContentInFileProvider,
33
- SimpleReplaceContentInFileProvider,
34
- WriteChangeToFileProvider
34
+ SuggestFileReplacements,
35
+ SimpleSuggestFileReplacements,
36
+ SuggestFileContent,
37
+ WriteFileContent,
38
+ WriteFileReplacements,
39
+ SimpleWriteFileReplacements
35
40
  } from './file-changeset-functions';
36
41
  import { OrchestratorChatAgent, OrchestratorChatAgentId } from '../common/orchestrator-chat-agent';
37
42
  import { UniversalChatAgent, UniversalChatAgentId } from '../common/universal-chat-agent';
43
+ import { AppTesterChatAgent } from './app-tester-chat-agent';
38
44
  import { CommandChatAgent } from '../common/command-chat-agents';
39
45
  import { ListChatContext, ResolveChatContext, AddFileToChatContext } from './context-functions';
40
46
  import { AIAgentConfigurationWidget } from './ai-configuration/agent-configuration-widget';
@@ -43,6 +49,7 @@ import { AIAgentConfigurationViewContribution } from './ai-configuration/ai-conf
43
49
  import { AIConfigurationContainerWidget } from './ai-configuration/ai-configuration-widget';
44
50
  import { AIVariableConfigurationWidget } from './ai-configuration/variable-configuration-widget';
45
51
  import { ContextFilesVariableContribution } from '../common/context-files-variable';
52
+ import { AIToolsConfigurationWidget } from './ai-configuration/tools-configuration-widget';
46
53
  import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
47
54
  import { AiConfigurationPreferences } from './ai-configuration/ai-configuration-preferences';
48
55
  import { TemplatePreferenceContribution } from './template-preference-contribution';
@@ -75,6 +82,10 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
75
82
  bind(Agent).toService(UniversalChatAgent);
76
83
  bind(ChatAgent).toService(UniversalChatAgent);
77
84
 
85
+ bind(AppTesterChatAgent).toSelf().inSingletonScope();
86
+ bind(Agent).toService(AppTesterChatAgent);
87
+ bind(ChatAgent).toService(AppTesterChatAgent);
88
+
78
89
  bind(CommandChatAgent).toSelf().inSingletonScope();
79
90
  bind(Agent).toService(CommandChatAgent);
80
91
  bind(ChatAgent).toService(CommandChatAgent);
@@ -91,11 +102,13 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
91
102
  bind(WorkspaceFunctionScope).toSelf().inSingletonScope();
92
103
  bindToolProvider(WorkspaceSearchProvider, bind);
93
104
 
94
- bindToolProvider(WriteChangeToFileProvider, bind);
105
+ bindToolProvider(SuggestFileContent, bind);
106
+ bindToolProvider(WriteFileContent, bind);
95
107
  bindToolProvider(TaskListProvider, bind);
96
108
  bindToolProvider(TaskRunnerProvider, bind);
97
109
  bind(ReplaceContentInFileFunctionHelper).toSelf().inSingletonScope();
98
- bindToolProvider(ReplaceContentInFileProvider, bind);
110
+ bindToolProvider(SuggestFileReplacements, bind);
111
+ bindToolProvider(WriteFileReplacements, bind);
99
112
  bindToolProvider(ListChatContext, bind);
100
113
  bindToolProvider(ResolveChatContext, bind);
101
114
  bind(AIConfigurationSelectionService).toSelf().inSingletonScope();
@@ -126,10 +139,20 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
126
139
  }))
127
140
  .inSingletonScope();
128
141
 
129
- bindToolProvider(SimpleReplaceContentInFileProvider, bind);
130
- bindToolProvider(ClearFileChangesProvider, bind);
131
- bindToolProvider(GetProposedFileStateProvider, bind);
142
+ bindToolProvider(SimpleSuggestFileReplacements, bind);
143
+ bindToolProvider(SimpleWriteFileReplacements, bind);
144
+ bindToolProvider(ClearFileChanges, bind);
145
+ bindToolProvider(GetProposedFileState, bind);
132
146
  bindToolProvider(AddFileToChatContext, bind);
147
+
148
+ bind(AIToolsConfigurationWidget).toSelf();
149
+ bind(WidgetFactory)
150
+ .toDynamicValue(ctx => ({
151
+ id: AIToolsConfigurationWidget.ID,
152
+ createWidget: () => ctx.container.get(AIToolsConfigurationWidget)
153
+ }))
154
+ .inSingletonScope();
155
+
133
156
  bind(AIVariableContribution).to(ContextFilesVariableContribution).inSingletonScope();
134
157
  bind(PreferenceContribution).toConstantValue({ schema: AiConfigurationPreferences });
135
158