Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { ChatModel } from '../../common/model/chatModel.js';
import { ChatRequestParser } from '../../common/requestParser/chatRequestParser.js';
import { getDynamicVariablesForWidget, getSelectedToolAndToolSetsForWidget } from '../attachments/chatVariables.js';
import { ChatSendResult, IChatService } from '../../common/chatService/chatService.js';
import { IChatSessionsExtensionPoint, IChatSessionsService } from '../../common/chatSessionsService.js';
import { ResolvedChatSessionsExtensionPoint, IChatSessionsService } from '../../common/chatSessionsService.js';
import { ChatAgentLocation } from '../../common/constants.js';
import { PROMPT_LANGUAGE_ID } from '../../common/promptSyntax/promptTypes.js';
import { AgentSessionProviders, getAgentSessionProvider, getAgentSessionProviderIcon, getAgentSessionProviderName } from '../agentSessions/agentSessions.js';
Expand Down Expand Up @@ -215,7 +215,7 @@ export class ChatContinueInSessionActionItem extends ActionWidgetDropdownActionV
};
}

private static toAction(provider: AgentSessionProviders, contrib: IChatSessionsExtensionPoint, instantiationService: IInstantiationService, location: ActionLocation): IActionWidgetDropdownAction {
private static toAction(provider: AgentSessionProviders, contrib: ResolvedChatSessionsExtensionPoint, instantiationService: IInstantiationService, location: ActionLocation): IActionWidgetDropdownAction {
return {
id: contrib.type,
enabled: true,
Expand Down Expand Up @@ -271,7 +271,7 @@ const NEW_CHAT_SESSION_ACTION_ID = 'workbench.action.chat.openNewSessionEditor';
export class CreateRemoteAgentJobAction {
constructor() { }

private openUntitledEditor(commandService: ICommandService, continuationTarget: IChatSessionsExtensionPoint) {
private openUntitledEditor(commandService: ICommandService, continuationTarget: ResolvedChatSessionsExtensionPoint) {
commandService.executeCommand(`${NEW_CHAT_SESSION_ACTION_ID}.${continuationTarget.type}`);
}

Expand Down Expand Up @@ -367,7 +367,7 @@ export class CreateRemoteAgentJobAction {
return undefined;
}

async run(accessor: ServicesAccessor, continuationTarget: IChatSessionsExtensionPoint, _widget?: IChatWidget) {
async run(accessor: ServicesAccessor, continuationTarget: ResolvedChatSessionsExtensionPoint, _widget?: IChatWidget) {
const contextKeyService = accessor.get(IContextKeyService);
const commandService = accessor.get(ICommandService);
const widgetService = accessor.get(IChatWidgetService);
Expand Down Expand Up @@ -512,7 +512,7 @@ export class CreateRemoteAgentJobAction {
class CreateRemoteAgentJobFromEditorAction {
constructor() { }

async run(accessor: ServicesAccessor, continuationTarget: IChatSessionsExtensionPoint) {
async run(accessor: ServicesAccessor, continuationTarget: ResolvedChatSessionsExtensionPoint) {

try {
const editorService = accessor.get(IEditorService);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { IWorkspaceTrustManagementService } from '../../../../../platform/worksp
import { IChatEntitlementService } from '../../../../services/chat/common/chatEntitlementService.js';
import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js';
import { Extensions, IOutputChannelRegistry, IOutputService } from '../../../../services/output/common/output.js';
import { ChatSessionStatus as AgentSessionStatus, IChatSessionFileChange, IChatSessionFileChange2, IChatSessionItem, IChatSessionsExtensionPoint, IChatSessionsService } from '../../common/chatSessionsService.js';
import { ChatSessionStatus as AgentSessionStatus, IChatSessionFileChange, IChatSessionFileChange2, IChatSessionItem, IChatSessionsService, ResolvedChatSessionsExtensionPoint } from '../../common/chatSessionsService.js';
import { IChatWidgetService } from '../chat.js';
import { AgentSessionProviders, getAgentSessionProvider, getAgentSessionProviderIcon, getAgentSessionProviderName, isBuiltInAgentSessionProvider } from './agentSessions.js';

Expand Down Expand Up @@ -486,7 +486,7 @@ export class AgentSessionsModel extends Disposable implements IAgentSessionsMode
* Update the sessions by fetching from the service. This does not trigger an explicit refresh
*/
private async updateItems(providerFilter: readonly string[] | undefined, token: CancellationToken): Promise<void> {
const mapSessionContributionToType = new Map<string, IChatSessionsExtensionPoint>();
const mapSessionContributionToType = new Map<string, ResolvedChatSessionsExtensionPoint>();
for (const contribution of this.chatSessionsService.getAllChatSessionContributions()) {
mapSessionContributionToType.set(contribution.type, contribution);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { ExtensionsRegistry } from '../../../../services/extensions/common/exten
import { ChatEditorInput } from '../widgetHosts/editor/chatEditorInput.js';
import { IChatAgentAttachmentCapabilities, IChatAgentData, IChatAgentRequest, IChatAgentService } from '../../common/participants/chatAgents.js';
import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';
import { IChatSession, IChatSessionContentProvider, IChatSessionItem, IChatSessionItemController, IChatSessionOptionsWillNotifyExtensionEvent, IChatSessionProviderOptionGroup, IChatSessionProviderOptionItem, IChatSessionsExtensionPoint, IChatSessionsService, isSessionInProgressStatus } from '../../common/chatSessionsService.js';
import { IChatSession, IChatSessionContentProvider, IChatSessionItem, IChatSessionItemController, IChatSessionOptionsWillNotifyExtensionEvent, IChatSessionProviderOptionGroup, IChatSessionProviderOptionItem, IChatSessionsExtensionPoint, IChatSessionsService, isSessionInProgressStatus, ResolvedChatSessionsExtensionPoint } from '../../common/chatSessionsService.js';
import { ChatAgentLocation, ChatModeKind } from '../../common/constants.js';
import { CHAT_CATEGORY } from '../actions/chatActions.js';
import { IChatEditorOptions } from '../widgetHosts/editor/chatEditor.js';
Expand Down Expand Up @@ -297,11 +297,6 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
private readonly inProgressMap: Map<string, number> = new Map();
private readonly _sessionTypeOptions: Map<string, IChatSessionProviderOptionGroup[]> = new Map();
private readonly _sessionTypeNewSessionOptions: Map<string, Record<string, string | IChatSessionProviderOptionItem>> = new Map();
private readonly _sessionTypeIcons: Map<string, ThemeIcon | { light: URI; dark: URI }> = new Map();
private readonly _sessionTypeWelcomeTitles: Map<string, string> = new Map();
private readonly _sessionTypeWelcomeMessages: Map<string, string> = new Map();
private readonly _sessionTypeWelcomeTips: Map<string, string> = new Map();
private readonly _sessionTypeInputPlaceholders: Map<string, string> = new Map();

private readonly _sessions = new ResourceMap<ContributedChatSessionData>();
private readonly _resourceAliases = new ResourceMap<URI>(); // real resource -> untitled resource
Expand Down Expand Up @@ -409,7 +404,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
this._logService.info(`[ChatSessionsService] registerContribution called for type='${contribution.type}', canDelegate=${contribution.canDelegate}, when='${contribution.when}', extension='${ext.identifier.value}'`);
if (this._contributions.has(contribution.type)) {
this._logService.info(`[ChatSessionsService] registerContribution: type='${contribution.type}' already registered, skipping`);
return { dispose: () => { } };
return Disposable.None;
}

// Track context keys from the when condition
Expand All @@ -434,41 +429,6 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
}
}

// Store icon mapping if provided
let icon: ThemeIcon | { dark: URI; light: URI } | undefined;

if (contribution.icon) {
// Parse icon string - support ThemeIcon format or file path from extension
if (typeof contribution.icon === 'string') {
icon = contribution.icon.startsWith('$(') && contribution.icon.endsWith(')')
? ThemeIcon.fromString(contribution.icon)
: ThemeIcon.fromId(contribution.icon);
} else {
icon = {
dark: resources.joinPath(ext.extensionLocation, contribution.icon.dark),
light: resources.joinPath(ext.extensionLocation, contribution.icon.light)
};
}
}

if (icon) {
this._sessionTypeIcons.set(contribution.type, icon);
}

// Store welcome title, message, tips, and input placeholder if provided
if (contribution.welcomeTitle) {
this._sessionTypeWelcomeTitles.set(contribution.type, contribution.welcomeTitle);
}
if (contribution.welcomeMessage) {
this._sessionTypeWelcomeMessages.set(contribution.type, contribution.welcomeMessage);
}
if (contribution.welcomeTips) {
this._sessionTypeWelcomeTips.set(contribution.type, contribution.welcomeTips);
}
if (contribution.inputPlaceholder) {
this._sessionTypeInputPlaceholders.set(contribution.type, contribution.inputPlaceholder);
}

this._evaluateAvailability();

return {
Expand All @@ -482,11 +442,6 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
}
}
}
this._sessionTypeIcons.delete(contribution.type);
this._sessionTypeWelcomeTitles.delete(contribution.type);
this._sessionTypeWelcomeMessages.delete(contribution.type);
this._sessionTypeWelcomeTips.delete(contribution.type);
this._sessionTypeInputPlaceholders.delete(contribution.type);
this._contributionDisposables.deleteAndDispose(contribution.type);
this._updateHasCanDelegateProvidersContextKey();
}
Expand Down Expand Up @@ -703,19 +658,19 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
}

private _registerAgent(contribution: IChatSessionsExtensionPoint, ext: IRelaxedExtensionDescription): IDisposable {
const { type: id, name, displayName, description } = contribution;
const storedIcon = this._sessionTypeIcons.get(id);
const storedIcon = this.getContributionIcon(ext, contribution);
const icons = ThemeIcon.isThemeIcon(storedIcon)
? { themeIcon: storedIcon, icon: undefined, iconDark: undefined }
: storedIcon
? { icon: storedIcon.light, iconDark: storedIcon.dark }
: { themeIcon: Codicon.sendToRemoteAgent };

const id = contribution.type;
const agentData: IChatAgentData = {
id,
name,
fullName: displayName,
description: description,
name: contribution.name,
fullName: contribution.displayName,
description: contribution.description,
isDefault: false,
isCore: false,
isDynamic: true,
Expand All @@ -737,9 +692,10 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
return this._chatAgentService.registerAgent(id, agentData);
}

getAllChatSessionContributions(): IChatSessionsExtensionPoint[] {
return Array.from(this._contributions.values(), x => x.contribution)
.filter(contribution => this._isContributionAvailable(contribution));
getAllChatSessionContributions(): ResolvedChatSessionsExtensionPoint[] {
return Array.from(this._contributions.values())
.filter(entry => this._isContributionAvailable(entry.contribution))
.map(entry => this.resolveChatSessionContribution(entry.extension, entry.contribution));
}

private _updateHasCanDelegateProvidersContextKey(): void {
Expand All @@ -749,15 +705,56 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
this._hasCanDelegateProvidersKey.set(canDelegateEnabled);
}

getChatSessionContribution(chatSessionType: string): IChatSessionsExtensionPoint | undefined {
const contribution = this._contributions.get(chatSessionType)?.contribution;
if (!contribution) {
getChatSessionContribution(chatSessionType: string): ResolvedChatSessionsExtensionPoint | undefined {
const entry = this._contributions.get(chatSessionType);
if (!entry) {
return undefined;
}

if (!this._isContributionAvailable(entry.contribution)) {
return undefined;
}

return this.resolveChatSessionContribution(entry.extension, entry.contribution);
}

private resolveChatSessionContribution(ext: IRelaxedExtensionDescription, contribution: IChatSessionsExtensionPoint) {
return {
...contribution,
icon: this.resolveIconForCurrentColorTheme(this.getContributionIcon(ext, contribution)),
};
}

private getContributionIcon(ext: IRelaxedExtensionDescription, contribution: IChatSessionsExtensionPoint): ThemeIcon | { light: URI; dark: URI } | undefined {
if (!contribution.icon) {
return undefined;
}
if (typeof contribution.icon === 'string') {
return contribution.icon.startsWith('$(') && contribution.icon.endsWith(')')
? ThemeIcon.fromString(contribution.icon)
: ThemeIcon.fromId(contribution.icon);
}
return {
dark: resources.joinPath(ext.extensionLocation, contribution.icon.dark),
light: resources.joinPath(ext.extensionLocation, contribution.icon.light)
};
}

private resolveIconForCurrentColorTheme(rawIcon: ThemeIcon | { light: URI; dark: URI } | undefined) {
if (!rawIcon) {
return undefined;
}

return this._isContributionAvailable(contribution) ? contribution : undefined;
if (ThemeIcon.isThemeIcon(rawIcon)) {
return rawIcon;
} else if (isDark(this._themeService.getColorTheme().type)) {
return rawIcon.dark;
} else {
return rawIcon.light;
}
}


async activateChatSessionItemProvider(chatViewType: string): Promise<void> {
await this.doActivateChatSessionItemController(chatViewType);
}
Expand Down Expand Up @@ -1119,44 +1116,6 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
this._logService.trace(`[ChatSessionsService] notifySessionOptionsChange: finished for ${sessionResource}`);
}

/**
* Get the icon for a specific session type
*/
public getIconForSessionType(chatSessionType: string): ThemeIcon | URI | undefined {
const sessionTypeIcon = this._sessionTypeIcons.get(chatSessionType);

if (ThemeIcon.isThemeIcon(sessionTypeIcon)) {
return sessionTypeIcon;
}

if (isDark(this._themeService.getColorTheme().type)) {
return sessionTypeIcon?.dark;
} else {
return sessionTypeIcon?.light;
}
}

/**
* Get the welcome title for a specific session type
*/
public getWelcomeTitleForSessionType(chatSessionType: string): string | undefined {
return this._sessionTypeWelcomeTitles.get(chatSessionType);
}

/**
* Get the welcome message for a specific session type
*/
public getWelcomeMessageForSessionType(chatSessionType: string): string | undefined {
return this._sessionTypeWelcomeMessages.get(chatSessionType);
}

/**
* Get the input placeholder for a specific session type
*/
public getInputPlaceholderForSessionType(chatSessionType: string): string | undefined {
return this._sessionTypeInputPlaceholders.get(chatSessionType);
}

/**
* Get the capabilities for a specific session type
*/
Expand Down Expand Up @@ -1186,6 +1145,8 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ

registerSingleton(IChatSessionsService, ChatSessionsService, InstantiationType.Delayed);



function registerNewSessionInPlaceAction(type: string, displayName: string): IDisposable {
return registerAction2(class NewChatSessionInPlaceAction extends Action2 {
constructor() {
Expand Down Expand Up @@ -1341,3 +1302,4 @@ export function getResourceForNewChatSession(options: NewChatSessionOpenOptions)
function isAgentSessionProviderType(type: string): boolean {
return Object.values(AgentSessionProviders).includes(type as AgentSessionProviders);
}

9 changes: 5 additions & 4 deletions src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1113,9 +1113,10 @@ export class ChatWidget extends Disposable implements IChatWidget {
private getWelcomeViewContent(additionalMessage: string | IMarkdownString | undefined): IChatViewWelcomeContent {
if (this.isLockedToCodingAgent) {
// Check for provider-specific customizations from chat sessions service
const providerIcon = this._lockedAgent ? this.chatSessionsService.getIconForSessionType(this._lockedAgent.id) : undefined;
const providerTitle = this._lockedAgent ? this.chatSessionsService.getWelcomeTitleForSessionType(this._lockedAgent.id) : undefined;
const providerMessage = this._lockedAgent ? this.chatSessionsService.getWelcomeMessageForSessionType(this._lockedAgent.id) : undefined;
const contribution = this._lockedAgent ? this.chatSessionsService.getChatSessionContribution(this._lockedAgent.id) : undefined;
const providerIcon = contribution?.icon;
const providerTitle = contribution?.welcomeTitle;
const providerMessage = contribution?.welcomeMessage;

// Fallback to default messages if provider doesn't specify
const message = providerMessage
Expand Down Expand Up @@ -1950,7 +1951,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
this.listWidget.setViewModel(this.viewModel);

if (this._lockedAgent) {
let placeholder = this.chatSessionsService.getInputPlaceholderForSessionType(this._lockedAgent.id);
let placeholder = this.chatSessionsService.getChatSessionContribution(this._lockedAgent.id)?.inputPlaceholder;
if (!placeholder) {
placeholder = localize('chat.input.placeholder.lockedToAgent', "Chat with {0}", this._lockedAgent.id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,7 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler
// TODO@osortega,@rebornix double check: Chat Session Item icon is reserved for chat session list and deprecated for chat session status. thus here we use session type icon. We may want to show status for the Editor Title.
const sessionType = this.getSessionType();
if (sessionType !== localChatSessionType) {
const typeIcon = this.chatSessionsService.getIconForSessionType(sessionType);
if (typeIcon) {
return typeIcon;
}
return this.chatSessionsService.getChatSessionContribution(sessionType)?.icon;
}

return undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate {

const contribution = this.chatSessionsService.getChatSessionContribution(sessionType);
if (contribution) {
this._widget.lockToCodingAgent(contribution.name, contribution.displayName, contribution.type);
this._widget.lockToCodingAgent(contribution.name, contribution.displayName, sessionType);
} else {
this._widget.unlockFromCodingAgent();
}
Expand Down
Loading
Loading