@theia/ai-ide 1.64.0-next.35 → 1.65.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.js +1 -1
- package/lib/browser/app-tester-chat-agent.js.map +1 -1
- 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 +1 -1
- 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,65 @@
|
|
|
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 { inject, injectable } from '@theia/core/shared/inversify';
|
|
17
|
+
import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser';
|
|
18
|
+
import { Emitter, MaybePromise, Event, } from '@theia/core';
|
|
19
|
+
import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service';
|
|
20
|
+
import { AIActivationService, ENABLE_AI_CONTEXT_KEY } from '@theia/ai-core/lib/browser/ai-activation-service';
|
|
21
|
+
import { PREFERENCE_NAME_ENABLE_AI } from './ai-ide-preferences';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Implements AI Activation Service based on preferences.
|
|
25
|
+
*/
|
|
26
|
+
@injectable()
|
|
27
|
+
export class AIIdeActivationServiceImpl implements AIActivationService, FrontendApplicationContribution {
|
|
28
|
+
@inject(ContextKeyService)
|
|
29
|
+
protected readonly contextKeyService: ContextKeyService;
|
|
30
|
+
|
|
31
|
+
@inject(PreferenceService)
|
|
32
|
+
protected preferenceService: PreferenceService;
|
|
33
|
+
|
|
34
|
+
protected isAiEnabledKey: ContextKey<boolean>;
|
|
35
|
+
|
|
36
|
+
protected onDidChangeAIEnabled = new Emitter<boolean>();
|
|
37
|
+
get onDidChangeActiveStatus(): Event<boolean> {
|
|
38
|
+
return this.onDidChangeAIEnabled.event;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get isActive(): boolean {
|
|
42
|
+
return this.isAiEnabledKey.get() ?? false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected updateEnableValue(value: boolean): void {
|
|
46
|
+
if (value !== this.isAiEnabledKey.get()) {
|
|
47
|
+
this.isAiEnabledKey.set(value);
|
|
48
|
+
this.onDidChangeAIEnabled.fire(value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
initialize(): MaybePromise<void> {
|
|
53
|
+
this.isAiEnabledKey = this.contextKeyService.createKey(ENABLE_AI_CONTEXT_KEY, false);
|
|
54
|
+
// make sure we don't miss once preferences are ready
|
|
55
|
+
this.preferenceService.ready.then(() => {
|
|
56
|
+
const enableValue = this.preferenceService.get<boolean>(PREFERENCE_NAME_ENABLE_AI, false);
|
|
57
|
+
this.updateEnableValue(enableValue);
|
|
58
|
+
});
|
|
59
|
+
this.preferenceService.onPreferenceChanged(e => {
|
|
60
|
+
if (e.preferenceName === PREFERENCE_NAME_ENABLE_AI) {
|
|
61
|
+
this.updateEnableValue(e.newValue);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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 { AI_CORE_PREFERENCES_TITLE, ENABLE_AI_CONTEXT_KEY } from '@theia/ai-core/lib/browser';
|
|
18
|
+
import { nls } from '@theia/core';
|
|
19
|
+
import { PreferenceSchema } from '@theia/core/lib/browser';
|
|
20
|
+
|
|
21
|
+
// We reuse the context key for the preference name
|
|
22
|
+
export const PREFERENCE_NAME_ENABLE_AI = ENABLE_AI_CONTEXT_KEY;
|
|
23
|
+
|
|
24
|
+
export const aiIdePreferenceSchema: PreferenceSchema = {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
[PREFERENCE_NAME_ENABLE_AI]: {
|
|
28
|
+
title: AI_CORE_PREFERENCES_TITLE,
|
|
29
|
+
markdownDescription: nls.localize('theia/ai/ide/enableAI/mdDescription',
|
|
30
|
+
'❗ This setting allows you to access the latest AI capabilities (Beta version).\
|
|
31
|
+
\n\
|
|
32
|
+
Please note that these features are in a beta phase, which means they may \
|
|
33
|
+
undergo changes and will be further improved. It is important to be aware that these features may generate\
|
|
34
|
+
continuous requests to the language models (LLMs) you provide access to. This might incur costs that you\
|
|
35
|
+
need to monitor closely. By enabling this option, you acknowledge these risks.\
|
|
36
|
+
\n\
|
|
37
|
+
**Please note! The settings below in this section will only take effect\n\
|
|
38
|
+
once the main feature setting is enabled. After enabling the feature, you need to configure at least one\
|
|
39
|
+
LLM provider below. Also see [the documentation](https://theia-ide.org/docs/user_ai/)**.'),
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
default: false,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -40,7 +40,7 @@ export class AppTesterChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
40
40
|
name = AppTesterChatAgentId;
|
|
41
41
|
languageModelRequirements: LanguageModelRequirement[] = [{
|
|
42
42
|
purpose: 'chat',
|
|
43
|
-
identifier: '
|
|
43
|
+
identifier: 'default/code',
|
|
44
44
|
}];
|
|
45
45
|
protected defaultLanguageModelPurpose: string = 'chat';
|
|
46
46
|
override description = nls.localize('theia/ai/chat/app-tester/description', 'This agent tests your application user interface to verify user-specified test scenarios through the Playwright MCP server. '
|
|
@@ -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
|
+
});
|