@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.
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts +5 -2
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js +15 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-service.d.ts +6 -1
- package/lib/browser/ai-configuration/ai-configuration-service.d.ts.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-service.js +10 -1
- package/lib/browser/ai-configuration/ai-configuration-service.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +2 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.js +7 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/language-model-renderer.d.ts +4 -2
- package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/language-model-renderer.js +49 -71
- package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -1
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts +41 -0
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.js +225 -0
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts +7 -3
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js +35 -13
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.js +11 -6
- package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
- package/lib/browser/ai-ide-activation-service.d.ts +18 -0
- package/lib/browser/ai-ide-activation-service.d.ts.map +1 -0
- package/lib/browser/ai-ide-activation-service.js +72 -0
- package/lib/browser/ai-ide-activation-service.js.map +1 -0
- package/lib/browser/ai-ide-preferences.d.ts +4 -0
- package/lib/browser/ai-ide-preferences.d.ts.map +1 -0
- package/lib/browser/ai-ide-preferences.js +43 -0
- package/lib/browser/ai-ide-preferences.js.map +1 -0
- package/lib/browser/app-tester-chat-agent.d.ts +3 -6
- package/lib/browser/app-tester-chat-agent.d.ts.map +1 -1
- package/lib/browser/app-tester-chat-agent.js +6 -71
- package/lib/browser/app-tester-chat-agent.js.map +1 -1
- package/lib/browser/app-tester-prompt-template.d.ts +6 -0
- package/lib/browser/app-tester-prompt-template.d.ts.map +1 -0
- package/lib/browser/app-tester-prompt-template.js +79 -0
- package/lib/browser/app-tester-prompt-template.js.map +1 -0
- package/lib/browser/architect-agent.js +1 -1
- package/lib/browser/architect-agent.js.map +1 -1
- package/lib/browser/coder-agent.js +1 -1
- package/lib/browser/coder-agent.js.map +1 -1
- package/lib/browser/context-functions.d.ts.map +1 -1
- package/lib/browser/context-functions.js +12 -0
- package/lib/browser/context-functions.js.map +1 -1
- package/lib/browser/context-functions.spec.d.ts +2 -0
- package/lib/browser/context-functions.spec.d.ts.map +1 -0
- package/lib/browser/context-functions.spec.js +93 -0
- package/lib/browser/context-functions.spec.js.map +1 -0
- package/lib/browser/file-changeset-function.spec.d.ts +2 -0
- package/lib/browser/file-changeset-function.spec.d.ts.map +1 -0
- package/lib/browser/file-changeset-function.spec.js +45 -0
- package/lib/browser/file-changeset-function.spec.js.map +1 -0
- package/lib/browser/file-changeset-functions.d.ts +13 -3
- package/lib/browser/file-changeset-functions.d.ts.map +1 -1
- package/lib/browser/file-changeset-functions.js +100 -29
- package/lib/browser/file-changeset-functions.js.map +1 -1
- package/lib/browser/file-changeset-functions.spec.d.ts +2 -0
- package/lib/browser/file-changeset-functions.spec.d.ts.map +1 -0
- package/lib/browser/file-changeset-functions.spec.js +161 -0
- package/lib/browser/file-changeset-functions.spec.js.map +1 -0
- package/lib/browser/frontend-module.d.ts.map +1 -1
- package/lib/browser/frontend-module.js +20 -0
- package/lib/browser/frontend-module.js.map +1 -1
- package/lib/browser/ide-chat-welcome-message-provider.js +2 -2
- package/lib/browser/ide-chat-welcome-message-provider.js.map +1 -1
- package/lib/browser/test/tool-provider-cancellation-test-util.spec.d.ts +2 -0
- package/lib/browser/test/tool-provider-cancellation-test-util.spec.d.ts.map +1 -0
- package/lib/browser/test/tool-provider-cancellation-test-util.spec.js +52 -0
- package/lib/browser/test/tool-provider-cancellation-test-util.spec.js.map +1 -0
- package/lib/browser/workspace-functions.d.ts +3 -3
- package/lib/browser/workspace-functions.d.ts.map +1 -1
- package/lib/browser/workspace-functions.js +79 -28
- package/lib/browser/workspace-functions.js.map +1 -1
- package/lib/browser/workspace-functions.spec.d.ts +2 -0
- package/lib/browser/workspace-functions.spec.d.ts.map +1 -0
- package/lib/browser/workspace-functions.spec.js +161 -0
- package/lib/browser/workspace-functions.spec.js.map +1 -0
- package/lib/browser/workspace-launch-provider.d.ts +24 -0
- package/lib/browser/workspace-launch-provider.d.ts.map +1 -0
- package/lib/browser/workspace-launch-provider.js +216 -0
- package/lib/browser/workspace-launch-provider.js.map +1 -0
- package/lib/browser/workspace-launch-provider.spec.d.ts +2 -0
- package/lib/browser/workspace-launch-provider.spec.d.ts.map +1 -0
- package/lib/browser/workspace-launch-provider.spec.js +245 -0
- package/lib/browser/workspace-launch-provider.spec.js.map +1 -0
- package/lib/browser/workspace-search-provider.d.ts.map +1 -1
- package/lib/browser/workspace-search-provider.js +9 -0
- package/lib/browser/workspace-search-provider.js.map +1 -1
- package/lib/browser/workspace-search-provider.spec.js +59 -203
- package/lib/browser/workspace-search-provider.spec.js.map +1 -1
- package/lib/browser/workspace-task-provider.d.ts.map +1 -1
- package/lib/browser/workspace-task-provider.js +8 -1
- package/lib/browser/workspace-task-provider.js.map +1 -1
- package/lib/browser/workspace-task-provider.spec.d.ts +2 -0
- package/lib/browser/workspace-task-provider.spec.d.ts.map +1 -0
- package/lib/browser/workspace-task-provider.spec.js +109 -0
- package/lib/browser/workspace-task-provider.spec.js.map +1 -0
- package/lib/common/architect-prompt-template.d.ts.map +1 -1
- package/lib/common/architect-prompt-template.js +11 -0
- package/lib/common/architect-prompt-template.js.map +1 -1
- package/lib/common/command-chat-agents.js +1 -1
- package/lib/common/command-chat-agents.js.map +1 -1
- package/lib/common/orchestrator-chat-agent.js +1 -1
- package/lib/common/orchestrator-chat-agent.js.map +1 -1
- package/lib/common/universal-chat-agent.js +1 -1
- package/lib/common/universal-chat-agent.js.map +1 -1
- package/lib/common/workspace-functions.d.ts +3 -0
- package/lib/common/workspace-functions.d.ts.map +1 -1
- package/lib/common/workspace-functions.js +4 -1
- package/lib/common/workspace-functions.js.map +1 -1
- package/package.json +18 -17
- package/src/browser/ai-configuration/agent-configuration-widget.tsx +18 -2
- package/src/browser/ai-configuration/ai-configuration-service.ts +14 -1
- package/src/browser/ai-configuration/ai-configuration-widget.tsx +7 -1
- package/src/browser/ai-configuration/language-model-renderer.tsx +87 -59
- package/src/browser/ai-configuration/model-aliases-configuration-widget.tsx +279 -0
- package/src/browser/ai-configuration/prompt-fragments-configuration-widget.tsx +43 -13
- package/src/browser/ai-configuration/template-settings-renderer.tsx +11 -7
- package/src/browser/ai-ide-activation-service.ts +65 -0
- package/src/browser/ai-ide-preferences.ts +44 -0
- package/src/browser/app-tester-chat-agent.ts +5 -73
- package/src/browser/app-tester-prompt-template.ts +81 -0
- package/src/browser/architect-agent.ts +1 -1
- package/src/browser/coder-agent.ts +1 -1
- package/src/browser/context-functions.spec.ts +102 -0
- package/src/browser/context-functions.ts +11 -0
- package/src/browser/file-changeset-function.spec.ts +52 -0
- package/src/browser/file-changeset-functions.spec.ts +212 -0
- package/src/browser/file-changeset-functions.ts +102 -25
- package/src/browser/frontend-module.ts +29 -1
- package/src/browser/ide-chat-welcome-message-provider.tsx +4 -4
- package/src/browser/style/index.css +111 -6
- package/src/browser/test/tool-provider-cancellation-test-util.spec.ts +60 -0
- package/src/browser/workspace-functions.spec.ts +199 -0
- package/src/browser/workspace-functions.ts +105 -32
- package/src/browser/workspace-launch-provider.spec.ts +320 -0
- package/src/browser/workspace-launch-provider.ts +231 -0
- package/src/browser/workspace-search-provider.spec.ts +79 -229
- package/src/browser/workspace-search-provider.ts +10 -1
- package/src/browser/workspace-task-provider.spec.ts +125 -0
- package/src/browser/workspace-task-provider.ts +7 -2
- package/src/common/architect-prompt-template.ts +11 -0
- package/src/common/command-chat-agents.ts +1 -1
- package/src/common/orchestrator-chat-agent.ts +1 -1
- package/src/common/universal-chat-agent.ts +1 -1
- 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: '
|
|
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: '
|
|
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
|
+
});
|