@theia/ai-ide 1.64.0-next.28 → 1.64.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 (152) hide show
  1. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts +5 -2
  2. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
  3. package/lib/browser/ai-configuration/agent-configuration-widget.js +15 -1
  4. package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
  5. package/lib/browser/ai-configuration/ai-configuration-service.d.ts +6 -1
  6. package/lib/browser/ai-configuration/ai-configuration-service.d.ts.map +1 -1
  7. package/lib/browser/ai-configuration/ai-configuration-service.js +10 -1
  8. package/lib/browser/ai-configuration/ai-configuration-service.js.map +1 -1
  9. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +2 -0
  10. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -1
  11. package/lib/browser/ai-configuration/ai-configuration-widget.js +7 -1
  12. package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -1
  13. package/lib/browser/ai-configuration/language-model-renderer.d.ts +4 -2
  14. package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -1
  15. package/lib/browser/ai-configuration/language-model-renderer.js +49 -71
  16. package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -1
  17. package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts +41 -0
  18. package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts.map +1 -0
  19. package/lib/browser/ai-configuration/model-aliases-configuration-widget.js +225 -0
  20. package/lib/browser/ai-configuration/model-aliases-configuration-widget.js.map +1 -0
  21. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts +7 -3
  22. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts.map +1 -1
  23. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js +35 -13
  24. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js.map +1 -1
  25. package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -1
  26. package/lib/browser/ai-configuration/template-settings-renderer.js +11 -6
  27. package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
  28. package/lib/browser/ai-ide-activation-service.d.ts +18 -0
  29. package/lib/browser/ai-ide-activation-service.d.ts.map +1 -0
  30. package/lib/browser/ai-ide-activation-service.js +72 -0
  31. package/lib/browser/ai-ide-activation-service.js.map +1 -0
  32. package/lib/browser/ai-ide-preferences.d.ts +4 -0
  33. package/lib/browser/ai-ide-preferences.d.ts.map +1 -0
  34. package/lib/browser/ai-ide-preferences.js +43 -0
  35. package/lib/browser/ai-ide-preferences.js.map +1 -0
  36. package/lib/browser/app-tester-chat-agent.d.ts +3 -6
  37. package/lib/browser/app-tester-chat-agent.d.ts.map +1 -1
  38. package/lib/browser/app-tester-chat-agent.js +6 -71
  39. package/lib/browser/app-tester-chat-agent.js.map +1 -1
  40. package/lib/browser/app-tester-prompt-template.d.ts +6 -0
  41. package/lib/browser/app-tester-prompt-template.d.ts.map +1 -0
  42. package/lib/browser/app-tester-prompt-template.js +79 -0
  43. package/lib/browser/app-tester-prompt-template.js.map +1 -0
  44. package/lib/browser/architect-agent.js +1 -1
  45. package/lib/browser/architect-agent.js.map +1 -1
  46. package/lib/browser/coder-agent.js +1 -1
  47. package/lib/browser/coder-agent.js.map +1 -1
  48. package/lib/browser/context-functions.d.ts.map +1 -1
  49. package/lib/browser/context-functions.js +12 -0
  50. package/lib/browser/context-functions.js.map +1 -1
  51. package/lib/browser/context-functions.spec.d.ts +2 -0
  52. package/lib/browser/context-functions.spec.d.ts.map +1 -0
  53. package/lib/browser/context-functions.spec.js +93 -0
  54. package/lib/browser/context-functions.spec.js.map +1 -0
  55. package/lib/browser/file-changeset-function.spec.d.ts +2 -0
  56. package/lib/browser/file-changeset-function.spec.d.ts.map +1 -0
  57. package/lib/browser/file-changeset-function.spec.js +45 -0
  58. package/lib/browser/file-changeset-function.spec.js.map +1 -0
  59. package/lib/browser/file-changeset-functions.d.ts +13 -3
  60. package/lib/browser/file-changeset-functions.d.ts.map +1 -1
  61. package/lib/browser/file-changeset-functions.js +100 -29
  62. package/lib/browser/file-changeset-functions.js.map +1 -1
  63. package/lib/browser/file-changeset-functions.spec.d.ts +2 -0
  64. package/lib/browser/file-changeset-functions.spec.d.ts.map +1 -0
  65. package/lib/browser/file-changeset-functions.spec.js +161 -0
  66. package/lib/browser/file-changeset-functions.spec.js.map +1 -0
  67. package/lib/browser/frontend-module.d.ts.map +1 -1
  68. package/lib/browser/frontend-module.js +20 -0
  69. package/lib/browser/frontend-module.js.map +1 -1
  70. package/lib/browser/ide-chat-welcome-message-provider.js +2 -2
  71. package/lib/browser/ide-chat-welcome-message-provider.js.map +1 -1
  72. package/lib/browser/test/tool-provider-cancellation-test-util.spec.d.ts +2 -0
  73. package/lib/browser/test/tool-provider-cancellation-test-util.spec.d.ts.map +1 -0
  74. package/lib/browser/test/tool-provider-cancellation-test-util.spec.js +52 -0
  75. package/lib/browser/test/tool-provider-cancellation-test-util.spec.js.map +1 -0
  76. package/lib/browser/workspace-functions.d.ts +3 -3
  77. package/lib/browser/workspace-functions.d.ts.map +1 -1
  78. package/lib/browser/workspace-functions.js +79 -28
  79. package/lib/browser/workspace-functions.js.map +1 -1
  80. package/lib/browser/workspace-functions.spec.d.ts +2 -0
  81. package/lib/browser/workspace-functions.spec.d.ts.map +1 -0
  82. package/lib/browser/workspace-functions.spec.js +161 -0
  83. package/lib/browser/workspace-functions.spec.js.map +1 -0
  84. package/lib/browser/workspace-launch-provider.d.ts +24 -0
  85. package/lib/browser/workspace-launch-provider.d.ts.map +1 -0
  86. package/lib/browser/workspace-launch-provider.js +216 -0
  87. package/lib/browser/workspace-launch-provider.js.map +1 -0
  88. package/lib/browser/workspace-launch-provider.spec.d.ts +2 -0
  89. package/lib/browser/workspace-launch-provider.spec.d.ts.map +1 -0
  90. package/lib/browser/workspace-launch-provider.spec.js +245 -0
  91. package/lib/browser/workspace-launch-provider.spec.js.map +1 -0
  92. package/lib/browser/workspace-search-provider.d.ts.map +1 -1
  93. package/lib/browser/workspace-search-provider.js +9 -0
  94. package/lib/browser/workspace-search-provider.js.map +1 -1
  95. package/lib/browser/workspace-search-provider.spec.js +59 -203
  96. package/lib/browser/workspace-search-provider.spec.js.map +1 -1
  97. package/lib/browser/workspace-task-provider.d.ts.map +1 -1
  98. package/lib/browser/workspace-task-provider.js +8 -1
  99. package/lib/browser/workspace-task-provider.js.map +1 -1
  100. package/lib/browser/workspace-task-provider.spec.d.ts +2 -0
  101. package/lib/browser/workspace-task-provider.spec.d.ts.map +1 -0
  102. package/lib/browser/workspace-task-provider.spec.js +109 -0
  103. package/lib/browser/workspace-task-provider.spec.js.map +1 -0
  104. package/lib/common/architect-prompt-template.d.ts.map +1 -1
  105. package/lib/common/architect-prompt-template.js +11 -0
  106. package/lib/common/architect-prompt-template.js.map +1 -1
  107. package/lib/common/command-chat-agents.js +1 -1
  108. package/lib/common/command-chat-agents.js.map +1 -1
  109. package/lib/common/orchestrator-chat-agent.js +1 -1
  110. package/lib/common/orchestrator-chat-agent.js.map +1 -1
  111. package/lib/common/universal-chat-agent.js +1 -1
  112. package/lib/common/universal-chat-agent.js.map +1 -1
  113. package/lib/common/workspace-functions.d.ts +3 -0
  114. package/lib/common/workspace-functions.d.ts.map +1 -1
  115. package/lib/common/workspace-functions.js +4 -1
  116. package/lib/common/workspace-functions.js.map +1 -1
  117. package/package.json +18 -17
  118. package/src/browser/ai-configuration/agent-configuration-widget.tsx +18 -2
  119. package/src/browser/ai-configuration/ai-configuration-service.ts +14 -1
  120. package/src/browser/ai-configuration/ai-configuration-widget.tsx +7 -1
  121. package/src/browser/ai-configuration/language-model-renderer.tsx +87 -59
  122. package/src/browser/ai-configuration/model-aliases-configuration-widget.tsx +279 -0
  123. package/src/browser/ai-configuration/prompt-fragments-configuration-widget.tsx +43 -13
  124. package/src/browser/ai-configuration/template-settings-renderer.tsx +11 -7
  125. package/src/browser/ai-ide-activation-service.ts +65 -0
  126. package/src/browser/ai-ide-preferences.ts +44 -0
  127. package/src/browser/app-tester-chat-agent.ts +5 -73
  128. package/src/browser/app-tester-prompt-template.ts +81 -0
  129. package/src/browser/architect-agent.ts +1 -1
  130. package/src/browser/coder-agent.ts +1 -1
  131. package/src/browser/context-functions.spec.ts +102 -0
  132. package/src/browser/context-functions.ts +11 -0
  133. package/src/browser/file-changeset-function.spec.ts +52 -0
  134. package/src/browser/file-changeset-functions.spec.ts +212 -0
  135. package/src/browser/file-changeset-functions.ts +102 -25
  136. package/src/browser/frontend-module.ts +29 -1
  137. package/src/browser/ide-chat-welcome-message-provider.tsx +4 -4
  138. package/src/browser/style/index.css +111 -6
  139. package/src/browser/test/tool-provider-cancellation-test-util.spec.ts +60 -0
  140. package/src/browser/workspace-functions.spec.ts +199 -0
  141. package/src/browser/workspace-functions.ts +105 -32
  142. package/src/browser/workspace-launch-provider.spec.ts +320 -0
  143. package/src/browser/workspace-launch-provider.ts +231 -0
  144. package/src/browser/workspace-search-provider.spec.ts +79 -229
  145. package/src/browser/workspace-search-provider.ts +10 -1
  146. package/src/browser/workspace-task-provider.spec.ts +125 -0
  147. package/src/browser/workspace-task-provider.ts +7 -2
  148. package/src/common/architect-prompt-template.ts +11 -0
  149. package/src/common/command-chat-agents.ts +1 -1
  150. package/src/common/orchestrator-chat-agent.ts +1 -1
  151. package/src/common/universal-chat-agent.ts +1 -1
  152. package/src/common/workspace-functions.ts +3 -0
@@ -0,0 +1,81 @@
1
+ /* eslint-disable @typescript-eslint/tslint/config */
2
+ // *****************************************************************************
3
+ // Copyright (C) 2025 EclipseSource GmbH and others.
4
+ //
5
+ // This file is licensed under the MIT License.
6
+ // See LICENSE-MIT.txt in the project root for license information.
7
+ // https://opensource.org/license/mit.
8
+ //
9
+ // SPDX-License-Identifier: MIT
10
+ // *****************************************************************************
11
+
12
+ import { BasePromptFragment } from '@theia/ai-core/lib/common';
13
+ import { CHAT_CONTEXT_DETAILS_VARIABLE_ID } from '@theia/ai-chat';
14
+ import { QUERY_DOM_FUNCTION_ID, LAUNCH_BROWSER_FUNCTION_ID, CLOSE_BROWSER_FUNCTION_ID, IS_BROWSER_RUNNING_FUNCTION_ID } from '../common/app-tester-chat-functions';
15
+ import { MCPServerDescription } from '@theia/ai-mcp/lib/common/mcp-server-manager';
16
+
17
+ export const REQUIRED_MCP_SERVERS: MCPServerDescription[] = [
18
+ {
19
+ name: 'playwright',
20
+ command: 'npx',
21
+ args: ['-y', '@playwright/mcp@latest',
22
+ '--cdp-endpoint',
23
+ 'http://localhost:9222/'],
24
+ autostart: false,
25
+ env: {},
26
+ },
27
+ {
28
+ name: 'playwright-visual',
29
+ command: 'npx',
30
+ args: ['-y', '@playwright/mcp@latest', '--vision',
31
+ '--cdp-endpoint',
32
+ 'http://localhost:9222/'],
33
+ autostart: false,
34
+ env: {},
35
+ }
36
+ ];
37
+
38
+ export const appTesterTemplate: BasePromptFragment = {
39
+ id: 'app-tester-system-default',
40
+ template: `{{!-- This prompt is licensed under the MIT License (https://opensource.org/license/mit).
41
+ Made improvements or adaptations to this prompt template? We'd love for you to share it with the community! Contribute back here:
42
+ https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
43
+
44
+ You are AppTester, an AI assistant integrated into Theia IDE specifically designed to help developers test running applications using Playwright.
45
+ Your role is to inspect the application for user-specified test scenarios through the Playwright MCP server.
46
+
47
+ ## Your Workflow
48
+ 1. Help the user build and launch their application
49
+ 2. Use Playwright browser automation to validate test scenarios
50
+ 3. Report results and provide actionable feedback
51
+ 4. Help fix issues when needed
52
+
53
+ ## Available Playwright Testing Tools
54
+ You have access to these powerful automation tools:
55
+ ${REQUIRED_MCP_SERVERS.map(server => `{{prompt:mcp_${server.name}_tools}}`)}
56
+
57
+ - **~{${LAUNCH_BROWSER_FUNCTION_ID}}**: Launch the browser. This is required before performing any browser interactions. Always launch a new browser when starting a test session.
58
+ - **~{${IS_BROWSER_RUNNING_FUNCTION_ID}}**: Check if the browser is running. If a tool fails by saying that the connection failed, you can verify the connection by using this tool.
59
+ - **~{${CLOSE_BROWSER_FUNCTION_ID}}**: Close the browser.
60
+ - **~{${QUERY_DOM_FUNCTION_ID}}**: Query the DOM for specific elements and their properties. Only use when explicitly requested by the user.
61
+ - **browser_snapshot**: Capture the current state of the page for verification or debugging purposes.
62
+
63
+ Prefer snapshots for investigating the page.
64
+
65
+ ## Workflow Approach
66
+ 1. **Understand Requirements**: Ask the user to clearly define what needs to be tested
67
+ 2. **Launch Browser**: Start a fresh browser instance for testing
68
+ 3. **Navigate and Test**: Execute the test scenario methodically
69
+ 4. **Document Results**: Provide detailed results with screenshots when helpful
70
+ 5. **Clean Up**: Always close the browser when testing is complete
71
+
72
+ ## Current Context
73
+ Some files and other pieces of data may have been added by the user to the context of the chat. If any have, the details can be found below.
74
+ {{${CHAT_CONTEXT_DETAILS_VARIABLE_ID}}}
75
+ `
76
+ };
77
+
78
+ export const appTesterTemplateVariant: BasePromptFragment = {
79
+ id: 'app-tester-system-empty',
80
+ template: '',
81
+ };
@@ -30,7 +30,7 @@ export class ArchitectAgent extends AbstractStreamParsingChatAgent {
30
30
  id = 'Architect';
31
31
  languageModelRequirements: LanguageModelRequirement[] = [{
32
32
  purpose: 'chat',
33
- identifier: 'openai/gpt-4o',
33
+ identifier: 'default/code',
34
34
  }];
35
35
  protected defaultLanguageModelPurpose: string = 'chat';
36
36
 
@@ -30,7 +30,7 @@ export class CoderAgent extends AbstractStreamParsingChatAgent {
30
30
  name = 'Coder';
31
31
  languageModelRequirements: LanguageModelRequirement[] = [{
32
32
  purpose: 'chat',
33
- identifier: 'openai/gpt-4o',
33
+ identifier: 'default/code',
34
34
  }];
35
35
  protected defaultLanguageModelPurpose: string = 'chat';
36
36
 
@@ -0,0 +1,102 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
17
+ let disableJSDOM = enableJSDOM();
18
+ import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
19
+ FrontendApplicationConfigProvider.set({});
20
+ import { expect } from 'chai';
21
+ import { ListChatContext, ResolveChatContext, AddFileToChatContext } from './context-functions';
22
+ import { CancellationTokenSource } from '@theia/core';
23
+ import { ChatContextManager, MutableChatModel, MutableChatRequestModel, MutableChatResponseModel } from '@theia/ai-chat';
24
+ import { fail } from 'assert';
25
+ import { ResolvedAIContextVariable } from '@theia/ai-core';
26
+ disableJSDOM();
27
+
28
+ describe('Context Functions Cancellation Tests', () => {
29
+ let cancellationTokenSource: CancellationTokenSource;
30
+ let mockCtx: Partial<MutableChatRequestModel>;
31
+
32
+ before(() => {
33
+ disableJSDOM = enableJSDOM();
34
+ });
35
+ after(() => {
36
+ // Disable JSDOM after all tests
37
+ disableJSDOM();
38
+ });
39
+
40
+ beforeEach(() => {
41
+ cancellationTokenSource = new CancellationTokenSource();
42
+ const context: Partial<ChatContextManager> = {
43
+ addVariables: () => { },
44
+ getVariables: () => mockCtx.context?.variables as ResolvedAIContextVariable[]
45
+ };
46
+ mockCtx = {
47
+ response: {
48
+ cancellationToken: cancellationTokenSource.token
49
+ } as MutableChatResponseModel,
50
+ context: {
51
+ variables: [{
52
+ variable: { id: 'file1', name: 'File' },
53
+ arg: '/path/to/file',
54
+ contextValue: 'file content'
55
+ } as ResolvedAIContextVariable]
56
+ },
57
+ session: {
58
+ context
59
+ } as MutableChatModel
60
+ };
61
+ });
62
+
63
+ afterEach(() => {
64
+ cancellationTokenSource.dispose();
65
+ });
66
+
67
+ it('ListChatContext should respect cancellation token', async () => {
68
+ const listChatContext = new ListChatContext();
69
+ cancellationTokenSource.cancel();
70
+
71
+ const result = await listChatContext.getTool().handler('', mockCtx);
72
+ if (typeof result !== 'string') {
73
+ fail(`Wrong tool call result type: ${result}`);
74
+ }
75
+ const jsonResponse = JSON.parse(result);
76
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
77
+ });
78
+
79
+ it('ResolveChatContext should respect cancellation token', async () => {
80
+ const resolveChatContext = new ResolveChatContext();
81
+ cancellationTokenSource.cancel();
82
+
83
+ const result = await resolveChatContext.getTool().handler('{"contextElementId":"file1/path/to/file"}', mockCtx);
84
+ if (typeof result !== 'string') {
85
+ fail(`Wrong tool call result type: ${result}`);
86
+ }
87
+ const jsonResponse = JSON.parse(result);
88
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
89
+ });
90
+
91
+ it('AddFileToChatContext should respect cancellation token', async () => {
92
+ const addFileToChatContext = new AddFileToChatContext();
93
+ cancellationTokenSource.cancel();
94
+
95
+ const result = await addFileToChatContext.getTool().handler('{"filesToAdd":["/new/path/to/file"]}', mockCtx);
96
+ if (typeof result !== 'string') {
97
+ fail(`Wrong tool call result type: ${result}`);
98
+ }
99
+ const jsonResponse = JSON.parse(result);
100
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
101
+ });
102
+ });
@@ -30,6 +30,9 @@ export class ListChatContext implements ToolProvider {
30
30
  name: ListChatContext.ID,
31
31
  description: 'Returns the list of context elements (such as files) specified by the user manually as part of the chat request.',
32
32
  handler: async (_: string, ctx: MutableChatRequestModel): Promise<string> => {
33
+ if (ctx?.response?.cancellationToken?.isCancellationRequested) {
34
+ return JSON.stringify({ error: 'Operation cancelled by user' });
35
+ }
33
36
  const result = ctx.context.variables.map(contextElement => ({
34
37
  id: contextElement.variable.id + contextElement.arg,
35
38
  type: contextElement.variable.name
@@ -64,6 +67,10 @@ export class ResolveChatContext implements ToolProvider {
64
67
  required: ['contextElementId']
65
68
  },
66
69
  handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> => {
70
+ if (ctx?.response?.cancellationToken?.isCancellationRequested) {
71
+ return JSON.stringify({ error: 'Operation cancelled by user' });
72
+ }
73
+
67
74
  const { contextElementId } = JSON.parse(args) as { contextElementId: string };
68
75
  const variable = ctx.context.variables.find(contextElement => contextElement.variable.id + contextElement.arg === contextElementId);
69
76
  if (variable) {
@@ -101,6 +108,10 @@ export class AddFileToChatContext implements ToolProvider {
101
108
  },
102
109
  description: 'Adds one or more files to the context of the current chat session, and returns the current list of files in the context.',
103
110
  handler: async (arg: string, ctx: MutableChatRequestModel): Promise<string> => {
111
+ if (ctx?.response?.cancellationToken?.isCancellationRequested) {
112
+ return JSON.stringify({ error: 'Operation cancelled by user' });
113
+ }
114
+
104
115
  const { filesToAdd } = JSON.parse(arg) as { filesToAdd: string[] };
105
116
 
106
117
  ctx.session.context.addVariables(...filesToAdd.map(file => ({ arg: file, variable: FILE_VARIABLE })));
@@ -0,0 +1,52 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 Lonti.com Pty Ltd.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
18
+ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
19
+ let disableJSDOM = enableJSDOM();
20
+ FrontendApplicationConfigProvider.set({});
21
+
22
+ import { MutableChatRequestModel } from '@theia/ai-chat';
23
+ import { Container } from '@theia/core/shared/inversify';
24
+ import { expect } from 'chai';
25
+ import { DefaultFileChangeSetTitleProvider } from './file-changeset-functions';
26
+
27
+ disableJSDOM();
28
+
29
+ describe('DefaultFileChangeSetTitleProvider', () => {
30
+ let provider: DefaultFileChangeSetTitleProvider;
31
+
32
+ before(() => {
33
+ const container = new Container();
34
+ container.bind(DefaultFileChangeSetTitleProvider).toSelf();
35
+
36
+ provider = container.get(DefaultFileChangeSetTitleProvider);
37
+ disableJSDOM = enableJSDOM();
38
+ });
39
+
40
+ after(() => {
41
+ disableJSDOM();
42
+ });
43
+
44
+ it('should provide the title', () => {
45
+ const ctx = {
46
+ agentId: 'test-agent',
47
+ } as MutableChatRequestModel;
48
+
49
+ const title = provider.getChangeSetTitle(ctx);
50
+ expect(title).to.equal('Changes proposed');
51
+ });
52
+ });
@@ -0,0 +1,212 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
18
+ let disableJSDOM = enableJSDOM();
19
+ import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
20
+ FrontendApplicationConfigProvider.set({});
21
+
22
+ import { expect } from 'chai';
23
+ import { CancellationTokenSource } from '@theia/core';
24
+ import {
25
+ SuggestFileContent,
26
+ WriteFileContent,
27
+ SuggestFileReplacements,
28
+ WriteFileReplacements,
29
+ ClearFileChanges,
30
+ GetProposedFileState,
31
+ ReplaceContentInFileFunctionHelper,
32
+ FileChangeSetTitleProvider,
33
+ DefaultFileChangeSetTitleProvider
34
+ } from './file-changeset-functions';
35
+ import { MutableChatRequestModel, MutableChatResponseModel, ChangeSet, ChangeSetElement, MutableChatModel } from '@theia/ai-chat';
36
+ import { Container } from '@theia/core/shared/inversify';
37
+ import { WorkspaceFunctionScope } from './workspace-functions';
38
+ import { FileService } from '@theia/filesystem/lib/browser/file-service';
39
+ import { ChangeSetFileElementFactory, ChangeSetFileElement } from '@theia/ai-chat/lib/browser/change-set-file-element';
40
+ import { URI } from '@theia/core/lib/common/uri';
41
+
42
+ disableJSDOM();
43
+
44
+ describe('File Changeset Functions Cancellation Tests', () => {
45
+ let cancellationTokenSource: CancellationTokenSource;
46
+ let mockCtx: Partial<MutableChatRequestModel>;
47
+ let container: Container;
48
+ before(() => {
49
+ disableJSDOM = enableJSDOM();
50
+ });
51
+ after(() => {
52
+ // Disable JSDOM after all tests
53
+ disableJSDOM();
54
+ });
55
+ beforeEach(() => {
56
+ cancellationTokenSource = new CancellationTokenSource();
57
+
58
+ // Create a mock change set that doesn't do anything
59
+ const mockChangeSet: Partial<ChangeSet> = {
60
+ addElements: (...elements: ChangeSetElement[]) => true,
61
+ setTitle: () => { },
62
+ removeElements: () => true,
63
+ getElementByURI: () => undefined
64
+ };
65
+
66
+ // Setup mock context
67
+ mockCtx = {
68
+ id: 'test-request-id',
69
+ response: {
70
+ cancellationToken: cancellationTokenSource.token
71
+ } as MutableChatResponseModel,
72
+ session: {
73
+ id: 'test-session-id',
74
+ changeSet: mockChangeSet as ChangeSet
75
+ } as MutableChatModel
76
+ };
77
+
78
+ // Create a new container for each test
79
+ container = new Container();
80
+
81
+ // Mock dependencies
82
+ const mockWorkspaceScope = {
83
+ resolveRelativePath: async () => new URI('file:///workspace/test.txt')
84
+ } as unknown as WorkspaceFunctionScope;
85
+
86
+ const mockFileService = {
87
+ exists: async () => true,
88
+ read: async () => ({ value: { toString: () => 'test content' } })
89
+ } as unknown as FileService;
90
+
91
+ const mockFileChangeFactory: ChangeSetFileElementFactory = () => ({
92
+ uri: new URI('file:///workspace/test.txt'),
93
+ type: 'modify',
94
+ state: 'pending',
95
+ targetState: 'new content',
96
+ apply: async () => { },
97
+ } as ChangeSetFileElement);
98
+
99
+ // Register mocks in the container
100
+ container.bind(WorkspaceFunctionScope).toConstantValue(mockWorkspaceScope);
101
+ container.bind(FileService).toConstantValue(mockFileService);
102
+ container.bind(ChangeSetFileElementFactory).toConstantValue(mockFileChangeFactory);
103
+ container.bind(FileChangeSetTitleProvider).to(DefaultFileChangeSetTitleProvider).inSingletonScope();
104
+ container.bind(ReplaceContentInFileFunctionHelper).toSelf();
105
+ container.bind(SuggestFileContent).toSelf();
106
+ container.bind(WriteFileContent).toSelf();
107
+ container.bind(SuggestFileReplacements).toSelf();
108
+ container.bind(WriteFileReplacements).toSelf();
109
+ container.bind(ClearFileChanges).toSelf();
110
+ container.bind(GetProposedFileState).toSelf();
111
+ });
112
+
113
+ afterEach(() => {
114
+ cancellationTokenSource.dispose();
115
+ });
116
+
117
+ it('SuggestFileContent should respect cancellation token', async () => {
118
+ const suggestFileContent = container.get(SuggestFileContent);
119
+ cancellationTokenSource.cancel();
120
+
121
+ const handler = suggestFileContent.getTool().handler;
122
+ const result = await handler(JSON.stringify({ path: 'test.txt', content: 'test content' }), mockCtx as MutableChatRequestModel);
123
+
124
+ const jsonResponse = typeof result === 'string' ? JSON.parse(result) : result;
125
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
126
+ });
127
+
128
+ it('WriteFileContent should respect cancellation token', async () => {
129
+ const writeFileContent = container.get(WriteFileContent);
130
+ cancellationTokenSource.cancel();
131
+
132
+ const handler = writeFileContent.getTool().handler;
133
+ const result = await handler(JSON.stringify({ path: 'test.txt', content: 'test content' }), mockCtx as MutableChatRequestModel);
134
+
135
+ const jsonResponse = typeof result === 'string' ? JSON.parse(result) : result;
136
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
137
+ });
138
+
139
+ it('SuggestFileReplacements should respect cancellation token', async () => {
140
+ const suggestFileReplacements = container.get(SuggestFileReplacements);
141
+ cancellationTokenSource.cancel();
142
+
143
+ const handler = suggestFileReplacements.getTool().handler;
144
+ const result = await handler(
145
+ JSON.stringify({
146
+ path: 'test.txt',
147
+ replacements: [{ oldContent: 'old', newContent: 'new' }]
148
+ }),
149
+ mockCtx as MutableChatRequestModel
150
+ );
151
+
152
+ const jsonResponse = typeof result === 'string' ? JSON.parse(result) : result;
153
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
154
+ });
155
+
156
+ it('WriteFileReplacements should respect cancellation token', async () => {
157
+ const writeFileReplacements = container.get(WriteFileReplacements);
158
+ cancellationTokenSource.cancel();
159
+
160
+ const handler = writeFileReplacements.getTool().handler;
161
+ const result = await handler(
162
+ JSON.stringify({
163
+ path: 'test.txt',
164
+ replacements: [{ oldContent: 'old', newContent: 'new' }]
165
+ }),
166
+ mockCtx as MutableChatRequestModel
167
+ );
168
+
169
+ const jsonResponse = typeof result === 'string' ? JSON.parse(result) : result;
170
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
171
+ });
172
+
173
+ it('ClearFileChanges should respect cancellation token', async () => {
174
+ const clearFileChanges = container.get(ClearFileChanges);
175
+ cancellationTokenSource.cancel();
176
+
177
+ const handler = clearFileChanges.getTool().handler;
178
+ const result = await handler(JSON.stringify({ path: 'test.txt' }), mockCtx as MutableChatRequestModel);
179
+
180
+ const jsonResponse = typeof result === 'string' ? JSON.parse(result) : result;
181
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
182
+ });
183
+
184
+ it('GetProposedFileState should respect cancellation token', async () => {
185
+ const getProposedFileState = container.get(GetProposedFileState);
186
+ cancellationTokenSource.cancel();
187
+
188
+ const handler = getProposedFileState.getTool().handler;
189
+ const result = await handler(JSON.stringify({ path: 'test.txt' }), mockCtx as MutableChatRequestModel);
190
+
191
+ const jsonResponse = typeof result === 'string' ? JSON.parse(result) : result;
192
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
193
+ });
194
+
195
+ it('ReplaceContentInFileFunctionHelper should handle cancellation in common processing', async () => {
196
+ const helper = container.get(ReplaceContentInFileFunctionHelper);
197
+ cancellationTokenSource.cancel();
198
+
199
+ // Test the underlying helper method through the public methods
200
+
201
+ const result = await helper.createChangesetFromToolCall(
202
+ JSON.stringify({
203
+ path: 'test.txt',
204
+ replacements: [{ oldContent: 'old', newContent: 'new' }]
205
+ }),
206
+ mockCtx as MutableChatRequestModel
207
+ );
208
+ const jsonResponse = typeof result === 'string' ? JSON.parse(result) : result;
209
+ expect(jsonResponse.error).to.equal('Operation cancelled by user');
210
+
211
+ });
212
+ });