@theia/ai-ide 1.76.0-next.7 → 1.76.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-allow-all-mode-chat-banner.js +1 -1
- package/lib/browser/ai-allow-all-mode-chat-banner.js.map +1 -1
- package/lib/browser/ai-aware-scm-commit-widget.d.ts +27 -0
- package/lib/browser/ai-aware-scm-commit-widget.d.ts.map +1 -0
- package/lib/browser/ai-aware-scm-commit-widget.js +142 -0
- package/lib/browser/ai-aware-scm-commit-widget.js.map +1 -0
- package/lib/browser/ai-configuration/ai-configuration-detail-widget.d.ts +10 -4
- package/lib/browser/ai-configuration/ai-configuration-detail-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-detail-widget.js +12 -7
- package/lib/browser/ai-configuration/ai-configuration-detail-widget.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-detail-widget.spec.js +14 -14
- package/lib/browser/ai-configuration/ai-configuration-detail-widget.spec.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-tree-widget.d.ts +6 -0
- package/lib/browser/ai-configuration/ai-configuration-tree-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-tree-widget.js +9 -0
- package/lib/browser/ai-configuration/ai-configuration-tree-widget.js.map +1 -1
- package/lib/browser/ai-configuration/categories/agent-capabilities-settings.d.ts.map +1 -1
- package/lib/browser/ai-configuration/categories/agent-capabilities-settings.js +2 -1
- package/lib/browser/ai-configuration/categories/agent-capabilities-settings.js.map +1 -1
- package/lib/browser/ai-configuration/categories/agent-chips.js +1 -1
- package/lib/browser/ai-configuration/categories/agent-chips.js.map +1 -1
- package/lib/browser/ai-configuration/categories/model-aliases-configuration-category.d.ts +8 -0
- package/lib/browser/ai-configuration/categories/model-aliases-configuration-category.d.ts.map +1 -1
- package/lib/browser/ai-configuration/categories/model-aliases-configuration-category.js +27 -8
- package/lib/browser/ai-configuration/categories/model-aliases-configuration-category.js.map +1 -1
- package/lib/browser/ai-configuration/categories/model-aliases-configuration-category.spec.js +38 -2
- package/lib/browser/ai-configuration/categories/model-aliases-configuration-category.spec.js.map +1 -1
- package/lib/browser/ai-configuration/categories/models-configuration-category.d.ts +13 -2
- package/lib/browser/ai-configuration/categories/models-configuration-category.d.ts.map +1 -1
- package/lib/browser/ai-configuration/categories/models-configuration-category.js +41 -2
- package/lib/browser/ai-configuration/categories/models-configuration-category.js.map +1 -1
- package/lib/browser/ai-configuration/categories/models-configuration-category.spec.js +32 -2
- package/lib/browser/ai-configuration/categories/models-configuration-category.spec.js.map +1 -1
- package/lib/browser/ai-configuration/categories/provider-model-discovery-section.d.ts +26 -0
- package/lib/browser/ai-configuration/categories/provider-model-discovery-section.d.ts.map +1 -0
- package/lib/browser/ai-configuration/categories/provider-model-discovery-section.js +212 -0
- package/lib/browser/ai-configuration/categories/provider-model-discovery-section.js.map +1 -0
- package/lib/browser/ai-configuration/categories/provider-model-discovery-section.spec.d.ts +2 -0
- package/lib/browser/ai-configuration/categories/provider-model-discovery-section.spec.d.ts.map +1 -0
- package/lib/browser/ai-configuration/categories/provider-model-discovery-section.spec.js +423 -0
- package/lib/browser/ai-configuration/categories/provider-model-discovery-section.spec.js.map +1 -0
- package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/language-model-renderer.js +10 -3
- package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -1
- package/lib/browser/ai-getting-started-commands.d.ts +10 -0
- package/lib/browser/ai-getting-started-commands.d.ts.map +1 -0
- package/lib/browser/ai-getting-started-commands.js +34 -0
- package/lib/browser/ai-getting-started-commands.js.map +1 -0
- package/lib/browser/ai-getting-started-contribution.d.ts +20 -0
- package/lib/browser/ai-getting-started-contribution.d.ts.map +1 -0
- package/lib/browser/ai-getting-started-contribution.js +70 -0
- package/lib/browser/ai-getting-started-contribution.js.map +1 -0
- package/lib/browser/ai-getting-started-walkthrough.d.ts +41 -0
- package/lib/browser/ai-getting-started-walkthrough.d.ts.map +1 -0
- package/lib/browser/ai-getting-started-walkthrough.js +225 -0
- package/lib/browser/ai-getting-started-walkthrough.js.map +1 -0
- package/lib/browser/ai-getting-started-walkthrough.spec.d.ts +2 -0
- package/lib/browser/ai-getting-started-walkthrough.spec.d.ts.map +1 -0
- package/lib/browser/ai-getting-started-walkthrough.spec.js +124 -0
- package/lib/browser/ai-getting-started-walkthrough.spec.js.map +1 -0
- package/lib/browser/ai-walkthrough-context-keys.d.ts +55 -0
- package/lib/browser/ai-walkthrough-context-keys.d.ts.map +1 -0
- package/lib/browser/ai-walkthrough-context-keys.js +145 -0
- package/lib/browser/ai-walkthrough-context-keys.js.map +1 -0
- package/lib/browser/ai-walkthrough-context-keys.spec.d.ts +2 -0
- package/lib/browser/ai-walkthrough-context-keys.spec.d.ts.map +1 -0
- package/lib/browser/ai-walkthrough-context-keys.spec.js +166 -0
- package/lib/browser/ai-walkthrough-context-keys.spec.js.map +1 -0
- package/lib/browser/chat-session-item.js +2 -2
- package/lib/browser/chat-session-item.js.map +1 -1
- package/lib/browser/commit-message-agent.d.ts +31 -0
- package/lib/browser/commit-message-agent.d.ts.map +1 -0
- package/lib/browser/commit-message-agent.js +99 -0
- package/lib/browser/commit-message-agent.js.map +1 -0
- package/lib/browser/commit-message-agent.spec.d.ts +2 -0
- package/lib/browser/commit-message-agent.spec.d.ts.map +1 -0
- package/lib/browser/commit-message-agent.spec.js +82 -0
- package/lib/browser/commit-message-agent.spec.js.map +1 -0
- package/lib/browser/commit-message-command-contribution.d.ts +15 -0
- package/lib/browser/commit-message-command-contribution.d.ts.map +1 -0
- package/lib/browser/commit-message-command-contribution.js +73 -0
- package/lib/browser/commit-message-command-contribution.js.map +1 -0
- package/lib/browser/commit-message-commands.d.ts +5 -0
- package/lib/browser/commit-message-commands.d.ts.map +1 -0
- package/lib/browser/commit-message-commands.js +28 -0
- package/lib/browser/commit-message-commands.js.map +1 -0
- package/lib/browser/commit-message-prompt-template.d.ts +11 -0
- package/lib/browser/commit-message-prompt-template.d.ts.map +1 -0
- package/lib/browser/commit-message-prompt-template.js +63 -0
- package/lib/browser/commit-message-prompt-template.js.map +1 -0
- package/lib/browser/commit-message-runner.d.ts +41 -0
- package/lib/browser/commit-message-runner.d.ts.map +1 -0
- package/lib/browser/commit-message-runner.js +143 -0
- package/lib/browser/commit-message-runner.js.map +1 -0
- package/lib/browser/commit-message-runner.spec.d.ts +2 -0
- package/lib/browser/commit-message-runner.spec.d.ts.map +1 -0
- package/lib/browser/commit-message-runner.spec.js +206 -0
- package/lib/browser/commit-message-runner.spec.js.map +1 -0
- package/lib/browser/frontend-module.d.ts.map +1 -1
- package/lib/browser/frontend-module.js +25 -0
- package/lib/browser/frontend-module.js.map +1 -1
- package/lib/browser/git-changes-tool.d.ts +73 -0
- package/lib/browser/git-changes-tool.d.ts.map +1 -0
- package/lib/browser/git-changes-tool.js +217 -0
- package/lib/browser/git-changes-tool.js.map +1 -0
- package/lib/browser/git-changes-tool.spec.d.ts +2 -0
- package/lib/browser/git-changes-tool.spec.d.ts.map +1 -0
- package/lib/browser/git-changes-tool.spec.js +167 -0
- package/lib/browser/git-changes-tool.spec.js.map +1 -0
- package/lib/browser/ide-chat-welcome-message-provider.d.ts +26 -28
- package/lib/browser/ide-chat-welcome-message-provider.d.ts.map +1 -1
- package/lib/browser/ide-chat-welcome-message-provider.js +61 -151
- package/lib/browser/ide-chat-welcome-message-provider.js.map +1 -1
- package/lib/browser/review/pr-review-capability-contribution.js +1 -1
- package/lib/browser/review/pr-review-capability-contribution.js.map +1 -1
- package/lib/browser/user-interaction-tool-renderer.d.ts +6 -0
- package/lib/browser/user-interaction-tool-renderer.d.ts.map +1 -1
- package/lib/browser/user-interaction-tool-renderer.js +36 -7
- package/lib/browser/user-interaction-tool-renderer.js.map +1 -1
- package/lib/browser/user-interaction-tool.d.ts +23 -1
- package/lib/browser/user-interaction-tool.d.ts.map +1 -1
- package/lib/browser/user-interaction-tool.js +56 -17
- package/lib/browser/user-interaction-tool.js.map +1 -1
- package/lib/browser/user-interaction-tool.spec.js +151 -2
- package/lib/browser/user-interaction-tool.spec.js.map +1 -1
- package/lib/common/command-prompt-template.js +1 -1
- package/lib/common/user-interaction-tool.d.ts +19 -1
- package/lib/common/user-interaction-tool.d.ts.map +1 -1
- package/lib/common/user-interaction-tool.js +100 -29
- package/lib/common/user-interaction-tool.js.map +1 -1
- package/lib/common/user-interaction-tool.spec.js +215 -174
- package/lib/common/user-interaction-tool.spec.js.map +1 -1
- package/package.json +26 -25
- package/src/browser/ai-allow-all-mode-chat-banner.tsx +2 -2
- package/src/browser/ai-aware-scm-commit-widget.tsx +153 -0
- package/src/browser/ai-configuration/ai-configuration-detail-widget.spec.ts +14 -14
- package/src/browser/ai-configuration/ai-configuration-detail-widget.tsx +12 -7
- package/src/browser/ai-configuration/ai-configuration-tree-widget.tsx +8 -0
- package/src/browser/ai-configuration/categories/agent-capabilities-settings.tsx +7 -8
- package/src/browser/ai-configuration/categories/agent-chips.tsx +1 -1
- package/src/browser/ai-configuration/categories/model-aliases-configuration-category.spec.ts +40 -2
- package/src/browser/ai-configuration/categories/model-aliases-configuration-category.tsx +31 -10
- package/src/browser/ai-configuration/categories/models-configuration-category.spec.ts +47 -2
- package/src/browser/ai-configuration/categories/models-configuration-category.tsx +52 -5
- package/src/browser/ai-configuration/categories/provider-model-discovery-section.spec.tsx +451 -0
- package/src/browser/ai-configuration/categories/provider-model-discovery-section.tsx +341 -0
- package/src/browser/ai-configuration/language-model-renderer.tsx +11 -3
- package/src/browser/ai-getting-started-commands.ts +33 -0
- package/src/browser/ai-getting-started-contribution.ts +64 -0
- package/src/browser/ai-getting-started-walkthrough.spec.ts +146 -0
- package/src/browser/ai-getting-started-walkthrough.ts +249 -0
- package/src/browser/ai-walkthrough-context-keys.spec.ts +209 -0
- package/src/browser/ai-walkthrough-context-keys.ts +147 -0
- package/src/browser/chat-session-item.tsx +2 -2
- package/src/browser/commit-message-agent.spec.ts +97 -0
- package/src/browser/commit-message-agent.ts +113 -0
- package/src/browser/commit-message-command-contribution.ts +67 -0
- package/src/browser/commit-message-commands.ts +25 -0
- package/src/browser/commit-message-prompt-template.ts +65 -0
- package/src/browser/commit-message-runner.spec.ts +266 -0
- package/src/browser/commit-message-runner.ts +163 -0
- package/src/browser/frontend-module.ts +27 -0
- package/src/browser/git-changes-tool.spec.ts +200 -0
- package/src/browser/git-changes-tool.ts +239 -0
- package/src/browser/ide-chat-welcome-message-provider.tsx +93 -230
- package/src/browser/review/pr-review-capability-contribution.ts +1 -1
- package/src/browser/style/commit-message-overlay.css +64 -0
- package/src/browser/style/index.css +4 -42
- package/src/browser/user-interaction-tool-renderer.tsx +38 -8
- package/src/browser/user-interaction-tool.spec.ts +201 -2
- package/src/browser/user-interaction-tool.ts +69 -18
- package/src/common/command-prompt-template.ts +1 -1
- package/src/common/user-interaction-tool.spec.ts +217 -161
- package/src/common/user-interaction-tool.ts +127 -32
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 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_CHAT_TOGGLE_COMMAND_ID } from '@theia/ai-chat-ui/lib/browser/ai-chat-ui-contribution';
|
|
18
|
+
import { ChatAgentRecommendationService } from '@theia/ai-chat/lib/common';
|
|
19
|
+
import { DEFAULT_CHAT_AGENT_PREF } from '@theia/ai-chat/lib/common/ai-chat-preferences';
|
|
20
|
+
import { ENABLE_AI_CONTEXT_KEY } from '@theia/ai-core/lib/browser/ai-activation-service';
|
|
21
|
+
import { AiConfigurationCategoryId } from '@theia/ai-core-ui/lib/browser/ai-configuration/ai-configuration-category';
|
|
22
|
+
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
|
23
|
+
import { PerspectiveServiceImpl } from '@theia/core/lib/browser/perspective-service';
|
|
24
|
+
import { nls } from '@theia/core/lib/common/nls';
|
|
25
|
+
import { WalkthroughProvider } from '@theia/getting-started/lib/common/walkthrough-provider';
|
|
26
|
+
import { WalkthroughDefinition, WalkthroughStepDefinition } from '@theia/getting-started/lib/common/walkthrough-types';
|
|
27
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
28
|
+
import { WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands';
|
|
29
|
+
import { AI_SET_DEFAULT_CHAT_AGENT } from './ai-getting-started-commands';
|
|
30
|
+
import { OPEN_AI_CONFIG_VIEW, OPEN_AI_CONFIG_VIEW_TOOLS } from './ai-configuration/ai-configuration-view-contribution';
|
|
31
|
+
import {
|
|
32
|
+
AI_CHAT_USED_CONTEXT_KEY,
|
|
33
|
+
AI_DEFAULT_AGENT_CONTEXT_KEY,
|
|
34
|
+
AI_MODEL_READY_CONTEXT_KEY,
|
|
35
|
+
AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY
|
|
36
|
+
} from './ai-walkthrough-context-keys';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Id of the walkthrough that guides a new user through setting up and using the AI features.
|
|
40
|
+
* Also the argument of the `walkthrough.open` command.
|
|
41
|
+
*/
|
|
42
|
+
export const AI_GETTING_STARTED_WALKTHROUGH_ID = 'ai-features.getting-started';
|
|
43
|
+
|
|
44
|
+
const AI_DOC_URL = 'https://theia-ide.org/docs/user_ai/';
|
|
45
|
+
const THEIA_AI_DOC_URL = 'https://theia-ide.org/docs/theia_ai/';
|
|
46
|
+
const FEEDBACK_URL = 'https://github.com/eclipse-theia/theia/issues/new/choose';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Contributes the "Get started with AI" walkthrough to the welcome page: the single, progress-tracked path
|
|
50
|
+
* from disabled AI features to a first answer in the chat.
|
|
51
|
+
*/
|
|
52
|
+
@injectable()
|
|
53
|
+
export class AiGettingStartedWalkthroughProvider implements WalkthroughProvider {
|
|
54
|
+
|
|
55
|
+
@inject(ChatAgentRecommendationService)
|
|
56
|
+
protected readonly recommendationService: ChatAgentRecommendationService;
|
|
57
|
+
|
|
58
|
+
protected walkthrough: WalkthroughDefinition | undefined;
|
|
59
|
+
|
|
60
|
+
getWalkthroughs(): WalkthroughDefinition[] {
|
|
61
|
+
// Built on demand rather than in the field initializer, so that the localization is in place.
|
|
62
|
+
this.walkthrough ??= this.createWalkthrough();
|
|
63
|
+
return [this.walkthrough];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected get applicationName(): string {
|
|
67
|
+
return FrontendApplicationConfigProvider.get().applicationName;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Deep-links into the AI Configuration view, which takes a category id or an `ai-features.*` preference id. */
|
|
71
|
+
protected aiConfiguration(target?: string): string {
|
|
72
|
+
return `command:${OPEN_AI_CONFIG_VIEW.id}${target ? `?${target}` : ''}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected createWalkthrough(): WalkthroughDefinition {
|
|
76
|
+
return {
|
|
77
|
+
id: AI_GETTING_STARTED_WALKTHROUGH_ID,
|
|
78
|
+
icon: 'sparkle',
|
|
79
|
+
title: nls.localize('theia/ai/ide/walkthrough/title', 'Get started with AI'),
|
|
80
|
+
description: nls.localize('theia/ai/ide/walkthrough/description', 'Set up the AI features of {0} and learn how to work with them.', this.applicationName),
|
|
81
|
+
steps: [
|
|
82
|
+
this.createAiSupportStep(),
|
|
83
|
+
this.createEnableStep(),
|
|
84
|
+
this.createModelStep(),
|
|
85
|
+
this.createAgentStep(),
|
|
86
|
+
this.createFirstQuestionStep(),
|
|
87
|
+
this.createToolConfirmationStep(),
|
|
88
|
+
this.createGoFurtherStep()
|
|
89
|
+
]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The opening slide, carrying what the welcome page used to announce in its AI banner: what the features
|
|
95
|
+
* are, that they are opt-in and may incur costs, and the invitation to the community behind them.
|
|
96
|
+
*/
|
|
97
|
+
protected createAiSupportStep(): WalkthroughStepDefinition {
|
|
98
|
+
return {
|
|
99
|
+
id: 'ai-support',
|
|
100
|
+
title: nls.localize('theia/ai/ide/walkthrough/aiSupport/title', 'AI support in {0}', this.applicationName),
|
|
101
|
+
description: nls.localize('theia/ai/ide/walkthrough/aiSupport/description', `
|
|
102
|
+
{0} comes with AI support: chat with specialized agents about your project, let them change code and run tasks, complete code as you type and get help in the terminal.
|
|
103
|
+
|
|
104
|
+
These features are disabled by default, so that you can opt in at your own discretion.
|
|
105
|
+
|
|
106
|
+
Once enabled, they may generate continuous requests to the language models you provide access to. This might incur costs that you need to monitor closely.
|
|
107
|
+
|
|
108
|
+
[Read the documentation]({1}).
|
|
109
|
+
|
|
110
|
+
We welcome your feedback, contributions and sponsorship! Visit the [GitHub project]({2}) to support the ongoing development. Thank you for being part of our community!
|
|
111
|
+
|
|
112
|
+
All of this is built on Theia AI, the framework for AI-powered tools and IDEs, published as a stable release. [Read the framework documentation]({3}).
|
|
113
|
+
`, this.applicationName, AI_DOC_URL, FEEDBACK_URL, THEIA_AI_DOC_URL).trim(),
|
|
114
|
+
// Reading it is done once the user acts on it, either by following a link or by turning the features on.
|
|
115
|
+
completionEvents: [`onContext:${ENABLE_AI_CONTEXT_KEY}`, `onLink:${AI_DOC_URL}`]
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected createEnableStep(): WalkthroughStepDefinition {
|
|
120
|
+
return {
|
|
121
|
+
id: 'enable-ai',
|
|
122
|
+
title: nls.localize('theia/ai/ide/walkthrough/enableAi/title', 'Turn on the AI features'),
|
|
123
|
+
description: nls.localize('theia/ai/ide/walkthrough/enableAi/description', `
|
|
124
|
+
Nothing is sent anywhere until you switch the AI features on, and you can switch them off again at any time.
|
|
125
|
+
|
|
126
|
+
[Open the AI Configuration]({0}) and enable them there.
|
|
127
|
+
`, this.aiConfiguration(ENABLE_AI_CONTEXT_KEY)).trim(),
|
|
128
|
+
completionEvents: [`onContext:${ENABLE_AI_CONTEXT_KEY}`]
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
protected createModelStep(): WalkthroughStepDefinition {
|
|
133
|
+
return {
|
|
134
|
+
id: 'connect-model',
|
|
135
|
+
title: nls.localize('theia/ai/ide/walkthrough/connectModel/title', 'Connect a language model'),
|
|
136
|
+
description: nls.localize('theia/ai/ide/walkthrough/connectModel/description', `
|
|
137
|
+
Add an API key for a hosted provider:
|
|
138
|
+
|
|
139
|
+
- [OpenAI]({0})
|
|
140
|
+
- [Anthropic]({1})
|
|
141
|
+
- [Google]({2})
|
|
142
|
+
|
|
143
|
+
Prefer to stay local or use another service? Ollama, llamafile and any OpenAI-compatible endpoint can be set up in the [Models]({3}) page.
|
|
144
|
+
|
|
145
|
+
Some agents bring their own model, for example Claude Code, and work without a provider.
|
|
146
|
+
`, this.aiConfiguration('ai-features.openAiOfficial.openAiApiKey'),
|
|
147
|
+
this.aiConfiguration('ai-features.anthropic.AnthropicApiKey'),
|
|
148
|
+
this.aiConfiguration('ai-features.google.apiKey'),
|
|
149
|
+
this.aiConfiguration(AiConfigurationCategoryId.MODELS)).trim(),
|
|
150
|
+
completionEvents: [`onContext:${AI_MODEL_READY_CONTEXT_KEY}`]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
protected createAgentStep(): WalkthroughStepDefinition {
|
|
155
|
+
const intro = nls.localize('theia/ai/ide/walkthrough/chooseAgent/intro', `
|
|
156
|
+
Every chat request goes to an agent, and one of them answers whenever you do not name one yourself.
|
|
157
|
+
|
|
158
|
+
**Set your default agent:**
|
|
159
|
+
`).trim();
|
|
160
|
+
const outro = nls.localize('theia/ai/ide/walkthrough/chooseAgent/outro', `
|
|
161
|
+
You can also [choose the default agent]({0}) in the settings, and address another one for a single message by starting it with *@AgentName*.
|
|
162
|
+
The [Agents]({1}) page lists them all.
|
|
163
|
+
`, this.aiConfiguration(DEFAULT_CHAT_AGENT_PREF), this.aiConfiguration(AiConfigurationCategoryId.AGENTS)).trim();
|
|
164
|
+
return {
|
|
165
|
+
id: 'choose-agent',
|
|
166
|
+
title: nls.localize('theia/ai/ide/walkthrough/chooseAgent/title', 'Choose your default agent'),
|
|
167
|
+
description: [intro, this.renderAgentChoices(), outro].filter(part => !!part).join('\n\n'),
|
|
168
|
+
completionEvents: [`onContext:${AI_DEFAULT_AGENT_CONTEXT_KEY}`]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* One action per recommended agent, each setting it as the default right away.
|
|
174
|
+
*
|
|
175
|
+
* Every link stands alone in its paragraph, which is what the welcome page renders as a button, so this
|
|
176
|
+
* reads like the buttons the chat welcome used to offer. Products customize the list through
|
|
177
|
+
* {@link ChatAgentRecommendationService}.
|
|
178
|
+
*/
|
|
179
|
+
protected renderAgentChoices(): string {
|
|
180
|
+
return this.recommendationService.getRecommendedAgents()
|
|
181
|
+
.map(agent => {
|
|
182
|
+
const command = `command:${AI_SET_DEFAULT_CHAT_AGENT.id}?${encodeURIComponent(JSON.stringify(agent.id))}`;
|
|
183
|
+
// Carried as the link title, so what the agent is good at stays available as a tooltip.
|
|
184
|
+
const title = agent.description ? ` "${agent.description.replace(/"/g, '')}"` : '';
|
|
185
|
+
return `[@${agent.label}](${command}${title})`;
|
|
186
|
+
})
|
|
187
|
+
.join('\n\n');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
protected createFirstQuestionStep(): WalkthroughStepDefinition {
|
|
191
|
+
return {
|
|
192
|
+
id: 'first-question',
|
|
193
|
+
title: nls.localize('theia/ai/ide/walkthrough/firstQuestion/title', 'Ask your first question'),
|
|
194
|
+
description: nls.localize('theia/ai/ide/walkthrough/firstQuestion/description', `
|
|
195
|
+
[Open the AI Chat]({0}) and ask something about your project.
|
|
196
|
+
|
|
197
|
+
Two things make the answers a lot better:
|
|
198
|
+
|
|
199
|
+
- *@AgentName* picks the agent for this message, for example *@Coder*.
|
|
200
|
+
- *#* attaches context, for example *#file* or *#selectedText*. The paperclip button next to the input does the same.
|
|
201
|
+
|
|
202
|
+
The chat remembers your sessions, so you can pick a conversation up later.
|
|
203
|
+
`, `command:${AI_CHAT_TOGGLE_COMMAND_ID}`).trim(),
|
|
204
|
+
completionEvents: [`onContext:${AI_CHAT_USED_CONTEXT_KEY}`]
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
protected createToolConfirmationStep(): WalkthroughStepDefinition {
|
|
209
|
+
return {
|
|
210
|
+
id: 'tool-confirmation',
|
|
211
|
+
title: nls.localize('theia/ai/ide/walkthrough/toolConfirmation/title', 'Stay in control of what agents do'),
|
|
212
|
+
description: nls.localize('theia/ai/ide/walkthrough/toolConfirmation/description', `
|
|
213
|
+
Agents act on your workspace through tools: they read files, run terminal commands or edit code.
|
|
214
|
+
|
|
215
|
+
Every tool call asks for your confirmation first. In the [Tools]({0}) page you can change that default or pre-approve the tools you trust.
|
|
216
|
+
|
|
217
|
+
AI features stay off entirely while a workspace is not trusted. [Manage workspace trust]({1}) to decide per project.
|
|
218
|
+
`, `command:${OPEN_AI_CONFIG_VIEW_TOOLS.id}`, `command:${WorkspaceCommands.MANAGE_WORKSPACE_TRUST.id}`).trim(),
|
|
219
|
+
completionEvents: [
|
|
220
|
+
`onContext:${AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY}`,
|
|
221
|
+
`onLink:command:${OPEN_AI_CONFIG_VIEW_TOOLS.id}`
|
|
222
|
+
]
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
protected createGoFurtherStep(): WalkthroughStepDefinition {
|
|
227
|
+
return {
|
|
228
|
+
id: 'go-further',
|
|
229
|
+
title: nls.localize('theia/ai/ide/walkthrough/goFurther/title', 'Go further'),
|
|
230
|
+
description: nls.localize('theia/ai/ide/walkthrough/goFurther/description', `
|
|
231
|
+
The chat is only the beginning. AI also completes code as you type and assists in the terminal with *Ctrl+I*.
|
|
232
|
+
|
|
233
|
+
- [Prompts and skills]({0}): read and adapt the instructions the agents work with.
|
|
234
|
+
- [MCP servers]({1}): add tools beyond the ones {3} ships with.
|
|
235
|
+
- [Token usage]({2}): see what your requests consume.
|
|
236
|
+
- [Switch Perspective]({5}) (experimental): the *AI First* layout moves the chat into the main area, with the explorer and source control on the right.
|
|
237
|
+
|
|
238
|
+
The AI History view, where available, shows what was actually sent to the model.
|
|
239
|
+
|
|
240
|
+
There is more in [the documentation]({4}) - and your feedback is always welcome.
|
|
241
|
+
`, this.aiConfiguration(AiConfigurationCategoryId.PROMPTS_AND_SKILLS),
|
|
242
|
+
this.aiConfiguration(AiConfigurationCategoryId.MCP_SERVERS),
|
|
243
|
+
this.aiConfiguration(AiConfigurationCategoryId.TOKEN_USAGE),
|
|
244
|
+
this.applicationName,
|
|
245
|
+
AI_DOC_URL,
|
|
246
|
+
`command:${PerspectiveServiceImpl.SWITCH_PERSPECTIVE_COMMAND.id}`).trim()
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 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
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
18
|
+
|
|
19
|
+
import { expect } from 'chai';
|
|
20
|
+
import { Emitter } from '@theia/core/lib/common/event';
|
|
21
|
+
import { DEFAULT_CHAT_AGENT_PREF } from '@theia/ai-chat/lib/common/ai-chat-preferences';
|
|
22
|
+
import { DEFAULT_TOOL_CONFIRMATION_PREFERENCE, TOOL_CONFIRMATION_PREFERENCE, ToolConfirmationMode } from '@theia/ai-chat/lib/common/chat-tool-preferences';
|
|
23
|
+
import {
|
|
24
|
+
AI_CHAT_USED_CONTEXT_KEY,
|
|
25
|
+
AI_DEFAULT_AGENT_CONTEXT_KEY,
|
|
26
|
+
AI_MODEL_READY_CONTEXT_KEY,
|
|
27
|
+
AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY,
|
|
28
|
+
AiWalkthroughContextKeys
|
|
29
|
+
} from './ai-walkthrough-context-keys';
|
|
30
|
+
|
|
31
|
+
describe('AiWalkthroughContextKeys', () => {
|
|
32
|
+
|
|
33
|
+
let contextKeys: AiWalkthroughContextKeys;
|
|
34
|
+
let keyValues: Record<string, boolean>;
|
|
35
|
+
let models: { status: { status: string } }[];
|
|
36
|
+
let sessions: { title?: string }[];
|
|
37
|
+
let persistedSessions: boolean;
|
|
38
|
+
let preferences: Record<string, unknown>;
|
|
39
|
+
let productDefaults: Record<string, unknown>;
|
|
40
|
+
let onLanguageModelChangeEmitter: Emitter<void>;
|
|
41
|
+
let onSessionEventEmitter: Emitter<void>;
|
|
42
|
+
let onPreferenceChangedEmitter: Emitter<{ preferenceName: string }>;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
keyValues = {};
|
|
46
|
+
models = [];
|
|
47
|
+
sessions = [];
|
|
48
|
+
persistedSessions = false;
|
|
49
|
+
preferences = {};
|
|
50
|
+
productDefaults = {};
|
|
51
|
+
onLanguageModelChangeEmitter = new Emitter();
|
|
52
|
+
onSessionEventEmitter = new Emitter();
|
|
53
|
+
onPreferenceChangedEmitter = new Emitter();
|
|
54
|
+
|
|
55
|
+
contextKeys = new AiWalkthroughContextKeys();
|
|
56
|
+
(contextKeys as any).contextKeyService = {
|
|
57
|
+
createKey: (key: string, defaultValue: boolean) => {
|
|
58
|
+
keyValues[key] = defaultValue;
|
|
59
|
+
return {
|
|
60
|
+
get: () => keyValues[key],
|
|
61
|
+
set: (value: boolean) => { keyValues[key] = value; }
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
(contextKeys as any).languageModelRegistry = {
|
|
66
|
+
onChange: onLanguageModelChangeEmitter.event,
|
|
67
|
+
getLanguageModels: () => Promise.resolve(models)
|
|
68
|
+
};
|
|
69
|
+
(contextKeys as any).chatService = {
|
|
70
|
+
onSessionEvent: onSessionEventEmitter.event,
|
|
71
|
+
getSessions: () => sessions,
|
|
72
|
+
hasPersistedSessions: () => Promise.resolve(persistedSessions)
|
|
73
|
+
};
|
|
74
|
+
(contextKeys as any).preferenceService = {
|
|
75
|
+
onPreferenceChanged: onPreferenceChangedEmitter.event,
|
|
76
|
+
ready: Promise.resolve(),
|
|
77
|
+
get: (key: string, defaultValue: unknown) => preferences[key] ?? defaultValue,
|
|
78
|
+
// Only the values the user set, which is what distinguishes a decision from a product default.
|
|
79
|
+
inspect: (key: string) => ({ preferenceName: key, defaultValue: productDefaults[key], globalValue: preferences[key] })
|
|
80
|
+
};
|
|
81
|
+
(contextKeys as any).init();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
contextKeys.onStop();
|
|
86
|
+
onLanguageModelChangeEmitter.dispose();
|
|
87
|
+
onSessionEventEmitter.dispose();
|
|
88
|
+
onPreferenceChangedEmitter.dispose();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe(AI_MODEL_READY_CONTEXT_KEY, () => {
|
|
92
|
+
it('should be false without a usable model', async () => {
|
|
93
|
+
models = [{ status: { status: 'unavailable' } }];
|
|
94
|
+
await (contextKeys as any).updateModelState();
|
|
95
|
+
|
|
96
|
+
expect(keyValues[AI_MODEL_READY_CONTEXT_KEY]).to.be.false;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should become true once a model is ready', async () => {
|
|
100
|
+
models = [{ status: { status: 'unavailable' } }, { status: { status: 'ready' } }];
|
|
101
|
+
await (contextKeys as any).updateModelState();
|
|
102
|
+
|
|
103
|
+
expect(keyValues[AI_MODEL_READY_CONTEXT_KEY]).to.be.true;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should follow the registry when a model becomes unusable again', async () => {
|
|
107
|
+
models = [{ status: { status: 'ready' } }];
|
|
108
|
+
await (contextKeys as any).updateModelState();
|
|
109
|
+
|
|
110
|
+
models = [{ status: { status: 'unavailable' } }];
|
|
111
|
+
await (contextKeys as any).updateModelState();
|
|
112
|
+
|
|
113
|
+
expect(keyValues[AI_MODEL_READY_CONTEXT_KEY]).to.be.false;
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe(AI_CHAT_USED_CONTEXT_KEY, () => {
|
|
118
|
+
it('should be false while no chat was held', async () => {
|
|
119
|
+
sessions = [{}];
|
|
120
|
+
await (contextKeys as any).updateChatState();
|
|
121
|
+
|
|
122
|
+
expect(keyValues[AI_CHAT_USED_CONTEXT_KEY]).to.be.false;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should become true for a session that has a title', async () => {
|
|
126
|
+
sessions = [{ title: 'How do I build this?' }];
|
|
127
|
+
await (contextKeys as any).updateChatState();
|
|
128
|
+
|
|
129
|
+
expect(keyValues[AI_CHAT_USED_CONTEXT_KEY]).to.be.true;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should become true for a persisted session of an earlier run', async () => {
|
|
133
|
+
persistedSessions = true;
|
|
134
|
+
await (contextKeys as any).updateChatState();
|
|
135
|
+
|
|
136
|
+
expect(keyValues[AI_CHAT_USED_CONTEXT_KEY]).to.be.true;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should stay true once the chats are gone', async () => {
|
|
140
|
+
sessions = [{ title: 'How do I build this?' }];
|
|
141
|
+
await (contextKeys as any).updateChatState();
|
|
142
|
+
|
|
143
|
+
sessions = [];
|
|
144
|
+
await (contextKeys as any).updateChatState();
|
|
145
|
+
|
|
146
|
+
expect(keyValues[AI_CHAT_USED_CONTEXT_KEY]).to.be.true;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should not fail when the persisted sessions cannot be read', async () => {
|
|
150
|
+
(contextKeys as any).chatService.hasPersistedSessions = () => Promise.reject(new Error('no storage'));
|
|
151
|
+
|
|
152
|
+
await (contextKeys as any).updateChatState();
|
|
153
|
+
|
|
154
|
+
expect(keyValues[AI_CHAT_USED_CONTEXT_KEY]).to.be.false;
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe(AI_DEFAULT_AGENT_CONTEXT_KEY, () => {
|
|
159
|
+
it('should be false while no default agent is configured', () => {
|
|
160
|
+
(contextKeys as any).updateDefaultAgentState();
|
|
161
|
+
|
|
162
|
+
expect(keyValues[AI_DEFAULT_AGENT_CONTEXT_KEY]).to.be.false;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should be true for a configured default agent', () => {
|
|
166
|
+
preferences[DEFAULT_CHAT_AGENT_PREF] = 'Coder';
|
|
167
|
+
(contextKeys as any).updateDefaultAgentState();
|
|
168
|
+
|
|
169
|
+
expect(keyValues[AI_DEFAULT_AGENT_CONTEXT_KEY]).to.be.true;
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe(AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY, () => {
|
|
174
|
+
it('should be false while every tool call is confirmed and no tool was pre-approved', () => {
|
|
175
|
+
(contextKeys as any).updateToolConfirmationState();
|
|
176
|
+
|
|
177
|
+
expect(keyValues[AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY]).to.be.false;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should be true once the default mode was changed', () => {
|
|
181
|
+
preferences[DEFAULT_TOOL_CONFIRMATION_PREFERENCE] = ToolConfirmationMode.ALWAYS_ALLOW;
|
|
182
|
+
(contextKeys as any).updateToolConfirmationState();
|
|
183
|
+
|
|
184
|
+
expect(keyValues[AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY]).to.be.true;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should be true once single tools were pre-approved', () => {
|
|
188
|
+
preferences[TOOL_CONFIRMATION_PREFERENCE] = { 'some-tool': ToolConfirmationMode.ALWAYS_ALLOW };
|
|
189
|
+
(contextKeys as any).updateToolConfirmationState();
|
|
190
|
+
|
|
191
|
+
expect(keyValues[AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY]).to.be.true;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should stay false for defaults the product ships rather than the user setting them', () => {
|
|
195
|
+
productDefaults[DEFAULT_TOOL_CONFIRMATION_PREFERENCE] = ToolConfirmationMode.ALWAYS_ALLOW;
|
|
196
|
+
productDefaults[TOOL_CONFIRMATION_PREFERENCE] = { 'some-tool': ToolConfirmationMode.ALWAYS_ALLOW };
|
|
197
|
+
(contextKeys as any).updateToolConfirmationState();
|
|
198
|
+
|
|
199
|
+
expect(keyValues[AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY]).to.be.false;
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should stay false after the per-tool settings were reset to an empty map', () => {
|
|
203
|
+
preferences[TOOL_CONFIRMATION_PREFERENCE] = {};
|
|
204
|
+
(contextKeys as any).updateToolConfirmationState();
|
|
205
|
+
|
|
206
|
+
expect(keyValues[AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY]).to.be.false;
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 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 { ChatService } from '@theia/ai-chat/lib/common';
|
|
18
|
+
import { DEFAULT_CHAT_AGENT_PREF } from '@theia/ai-chat/lib/common/ai-chat-preferences';
|
|
19
|
+
import { DEFAULT_TOOL_CONFIRMATION_PREFERENCE, TOOL_CONFIRMATION_PREFERENCE } from '@theia/ai-chat/lib/common/chat-tool-preferences';
|
|
20
|
+
import { FrontendLanguageModelRegistry } from '@theia/ai-core/lib/common';
|
|
21
|
+
import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
22
|
+
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
|
|
23
|
+
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
|
24
|
+
import { PreferenceService } from '@theia/core/lib/common/preferences';
|
|
25
|
+
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
26
|
+
|
|
27
|
+
/** True as soon as at least one language model is ready to be used. */
|
|
28
|
+
export const AI_MODEL_READY_CONTEXT_KEY = 'ai.hasReadyLanguageModel';
|
|
29
|
+
/** True as soon as the user has held at least one conversation with an agent. */
|
|
30
|
+
export const AI_CHAT_USED_CONTEXT_KEY = 'ai.chat.hasRequest';
|
|
31
|
+
/** True while a default chat agent is configured. */
|
|
32
|
+
export const AI_DEFAULT_AGENT_CONTEXT_KEY = 'ai.chat.hasDefaultAgent';
|
|
33
|
+
/** True once the user has decided how tool calls are confirmed, instead of staying on the default. */
|
|
34
|
+
export const AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY = 'ai.chat.toolConfirmationConfigured';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Publishes the state that the getting started walkthrough observes to tick off its steps.
|
|
38
|
+
*
|
|
39
|
+
* The keys describe what the user achieved rather than what they clicked. A step is therefore only completed
|
|
40
|
+
* once the setup actually works, and a user who configured everything before ever opening the walkthrough
|
|
41
|
+
* finds it completed rather than waiting for changes that already happened.
|
|
42
|
+
*/
|
|
43
|
+
@injectable()
|
|
44
|
+
export class AiWalkthroughContextKeys implements FrontendApplicationContribution {
|
|
45
|
+
|
|
46
|
+
@inject(ContextKeyService)
|
|
47
|
+
protected readonly contextKeyService: ContextKeyService;
|
|
48
|
+
|
|
49
|
+
@inject(FrontendLanguageModelRegistry)
|
|
50
|
+
protected readonly languageModelRegistry: FrontendLanguageModelRegistry;
|
|
51
|
+
|
|
52
|
+
@inject(ChatService)
|
|
53
|
+
protected readonly chatService: ChatService;
|
|
54
|
+
|
|
55
|
+
@inject(PreferenceService)
|
|
56
|
+
protected readonly preferenceService: PreferenceService;
|
|
57
|
+
|
|
58
|
+
protected readonly toDispose = new DisposableCollection();
|
|
59
|
+
|
|
60
|
+
protected hasReadyLanguageModel: ContextKey<boolean>;
|
|
61
|
+
protected hasChatRequest: ContextKey<boolean>;
|
|
62
|
+
protected hasDefaultAgent: ContextKey<boolean>;
|
|
63
|
+
protected toolConfirmationConfigured: ContextKey<boolean>;
|
|
64
|
+
|
|
65
|
+
@postConstruct()
|
|
66
|
+
protected init(): void {
|
|
67
|
+
this.hasReadyLanguageModel = this.contextKeyService.createKey(AI_MODEL_READY_CONTEXT_KEY, false);
|
|
68
|
+
this.hasChatRequest = this.contextKeyService.createKey(AI_CHAT_USED_CONTEXT_KEY, false);
|
|
69
|
+
this.hasDefaultAgent = this.contextKeyService.createKey(AI_DEFAULT_AGENT_CONTEXT_KEY, false);
|
|
70
|
+
this.toolConfirmationConfigured = this.contextKeyService.createKey(AI_TOOL_CONFIRMATION_CONFIGURED_CONTEXT_KEY, false);
|
|
71
|
+
|
|
72
|
+
this.toDispose.push(this.languageModelRegistry.onChange(() => this.updateModelState()));
|
|
73
|
+
this.toDispose.push(this.chatService.onSessionEvent(() => this.updateChatState()));
|
|
74
|
+
this.toDispose.push(this.preferenceService.onPreferenceChanged(event => {
|
|
75
|
+
if (event.preferenceName === DEFAULT_CHAT_AGENT_PREF) {
|
|
76
|
+
this.updateDefaultAgentState();
|
|
77
|
+
} else if (event.preferenceName === DEFAULT_TOOL_CONFIRMATION_PREFERENCE || event.preferenceName === TOOL_CONFIRMATION_PREFERENCE) {
|
|
78
|
+
this.updateToolConfirmationState();
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
this.updateModelState();
|
|
83
|
+
this.updateChatState();
|
|
84
|
+
this.preferenceService.ready.then(() => {
|
|
85
|
+
this.updateDefaultAgentState();
|
|
86
|
+
this.updateToolConfirmationState();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
protected async updateModelState(): Promise<void> {
|
|
91
|
+
const models = await this.languageModelRegistry.getLanguageModels();
|
|
92
|
+
this.hasReadyLanguageModel.set(models.some(model => model.status.status === 'ready'));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A session gets its title from its first exchange, so a titled or persisted session means the user has
|
|
97
|
+
* asked something. Once true, the key stays true: the achievement does not go away when a chat is deleted.
|
|
98
|
+
*/
|
|
99
|
+
protected async updateChatState(): Promise<void> {
|
|
100
|
+
if (this.hasChatRequest.get()) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (this.chatService.getSessions().some(session => !!session.title)) {
|
|
104
|
+
this.hasChatRequest.set(true);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
if (await this.chatService.hasPersistedSessions()) {
|
|
109
|
+
this.hasChatRequest.set(true);
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// The persisted sessions cannot be read; the key is updated again on the next session event.
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
protected updateDefaultAgentState(): void {
|
|
117
|
+
this.hasDefaultAgent.set(!!this.preferenceService.get<string>(DEFAULT_CHAT_AGENT_PREF, ''));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The user has taken a stance on tool confirmation as soon as they changed the default mode or
|
|
122
|
+
* pre-approved individual tools.
|
|
123
|
+
*/
|
|
124
|
+
protected updateToolConfirmationState(): void {
|
|
125
|
+
this.toolConfirmationConfigured.set(
|
|
126
|
+
this.isSetByUser(DEFAULT_TOOL_CONFIRMATION_PREFERENCE) || this.isSetByUser(TOOL_CONFIRMATION_PREFERENCE)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether a preference carries a value the user set, rather than the one the product ships.
|
|
132
|
+
*
|
|
133
|
+
* The effective value cannot answer this: a product is free to ship its own confirmation defaults, and
|
|
134
|
+
* reading those back as a decision would tick the step for a user who never saw it. An empty object does
|
|
135
|
+
* not count either, since that is what resetting the per-tool settings leaves behind.
|
|
136
|
+
*/
|
|
137
|
+
protected isSetByUser(preferenceName: string): boolean {
|
|
138
|
+
const inspection = this.preferenceService.inspect(preferenceName);
|
|
139
|
+
return [inspection?.globalValue, inspection?.workspaceValue, inspection?.workspaceFolderValue].some(
|
|
140
|
+
value => value !== undefined && !(typeof value === 'object' && Object.keys(value as object).length === 0)
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
onStop(): void {
|
|
145
|
+
this.toDispose.dispose();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -262,8 +262,8 @@ export function ChatSessionItem(props: ChatSessionItemComponentProps): React.Rea
|
|
|
262
262
|
tabIndex={0}
|
|
263
263
|
aria-expanded={isExpanded}
|
|
264
264
|
aria-label={isExpanded
|
|
265
|
-
? nls.
|
|
266
|
-
: nls.
|
|
265
|
+
? nls.localizeByDefault('Collapse {0}', title)
|
|
266
|
+
: nls.localizeByDefault('Expand {0}', title)}>
|
|
267
267
|
<span className={`codicon ${isExpanded ? 'codicon-chevron-down' : 'codicon-chevron-right'}`} />
|
|
268
268
|
</span>
|
|
269
269
|
) : (
|