From a33c1a10301300a0f6abbc7b63069be9bf05fe49 Mon Sep 17 00:00:00 2001 From: kieferrm <4674940+kieferrm@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:27:05 -0700 Subject: [PATCH 1/8] change terminilogy --- .../contrib/chat/browser/chatSessions.contribution.ts | 8 ++++---- .../chat/browser/chatSessions/view/chatSessionsView.ts | 4 ++-- .../chat/browser/chatSessions/view/sessionsViewPane.ts | 2 +- src/vs/workbench/contrib/chat/browser/chatStatus.ts | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts index e15549a35ae..7ea23ab4316 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts @@ -207,7 +207,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ let displayName: string | undefined; if (chatSessionType === 'local') { - displayName = 'Local Chat Sessions'; + displayName = 'Local Chat Agent'; } else { displayName = this._contributions.get(chatSessionType)?.displayName; } @@ -275,7 +275,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ return MenuRegistry.appendMenuItem(MenuId.ViewTitle, { command: { id: `${NEW_CHAT_SESSION_ACTION_ID}.${contribution.type}`, - title: localize('interactiveSession.openNewSessionEditor', "New {0} Chat Editor", contribution.displayName), + title: localize('interactiveSession.openNewSessionEditor', "New {0}", contribution.displayName), icon: Codicon.plus, source: { id: contribution.extensionDescription.identifier.value, @@ -295,7 +295,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ constructor() { super({ id: `workbench.action.chat.openNewSessionEditor.${contribution.type}`, - title: localize2('interactiveSession.openNewSessionEditor', "New {0} Chat Editor", contribution.displayName), + title: localize2('interactiveSession.openNewSessionEditor', "New {0}", contribution.displayName), category: CHAT_CATEGORY, icon: Codicon.plus, f1: true, // Show in command palette @@ -314,7 +314,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ override: ChatEditorInput.EditorID, pinned: true, title: { - fallback: localize('chatEditorContributionName', "{0} chat", contribution.displayName), + fallback: localize('chatEditorContributionName', "{0}", contribution.displayName), } }; const untitledId = `untitled-${generateUuid()}`; diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions/view/chatSessionsView.ts b/src/vs/workbench/contrib/chat/browser/chatSessions/view/chatSessionsView.ts index e5f3ce688b4..925bc2270cd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions/view/chatSessionsView.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions/view/chatSessionsView.ts @@ -254,7 +254,7 @@ class ChatSessionsViewPaneContainer extends ViewPaneContainer { // Register views in priority order: local, history, then alphabetically sorted others const orderedProviders = [ - ...(localProvider ? [{ provider: localProvider, displayName: 'Local Chat Sessions', baseOrder: 0 }] : []), + ...(localProvider ? [{ provider: localProvider, displayName: 'Local Chat Agent', baseOrder: 0 }] : []), ...(historyProvider ? [{ provider: historyProvider, displayName: 'History', baseOrder: 1, when: undefined }] : []), ...providersWithDisplayNames.map((item, index) => ({ ...item, @@ -286,7 +286,7 @@ class ChatSessionsViewPaneContainer extends ViewPaneContainer { if (provider.chatSessionType === 'local') { const viewsRegistry = Registry.as(Extensions.ViewsRegistry); this._register(viewsRegistry.registerViewWelcomeContent(viewDescriptor.id, { - content: nls.localize('chatSessions.noResults', "No local chat sessions\n[Start a Chat](command:{0})", ACTION_ID_OPEN_CHAT), + content: nls.localize('chatSessions.noResults', "No local chat agent sessions\n[Start an Agent Session](command:{0})", ACTION_ID_OPEN_CHAT), })); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions/view/sessionsViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatSessions/view/sessionsViewPane.ts index 790c3c959c9..c7e0ac14a02 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions/view/sessionsViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions/view/sessionsViewPane.ts @@ -336,7 +336,7 @@ export class SessionsViewPane extends ViewPane { if (elements.length === 1) { return elements[0].label; } - return nls.localize('chatSessions.dragLabel', "{0} chat sessions", elements.length); + return nls.localize('chatSessions.dragLabel', "{0} agent sessions", elements.length); }, drop: () => { }, onDragOver: () => false, diff --git a/src/vs/workbench/contrib/chat/browser/chatStatus.ts b/src/vs/workbench/contrib/chat/browser/chatStatus.ts index a6225c451e0..75a0e100fb7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatStatus.ts +++ b/src/vs/workbench/contrib/chat/browser/chatStatus.ts @@ -215,9 +215,9 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu else if (chatSessionsInProgressCount > 0) { text = '$(copilot-in-progress)'; if (chatSessionsInProgressCount > 1) { - ariaLabel = localize('chatSessionsInProgressStatus', "{0} chat sessions in progress", chatSessionsInProgressCount); + ariaLabel = localize('chatSessionsInProgressStatus', "{0} agent sessions in progress", chatSessionsInProgressCount); } else { - ariaLabel = localize('chatSessionInProgressStatus', "1 chat session in progress"); + ariaLabel = localize('chatSessionInProgressStatus', "1 agent session in progress"); } } From dfa4b5b3c27b6dbdad5b0dc4fc333a90a3c104d9 Mon Sep 17 00:00:00 2001 From: kieferrm <4674940+kieferrm@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:59:34 -0700 Subject: [PATCH 2/8] Add icon support for chat session editor tabs --- .../contrib/chat/browser/chatEditorInput.ts | 90 ++++++++++++++++++- .../chat/browser/chatSessions.contribution.ts | 25 ++++++ .../chat/common/chatSessionsService.ts | 2 + 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts index bfeb8de21b7..b725de037dc 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts @@ -19,7 +19,9 @@ import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js' import { EditorInputCapabilities, IEditorIdentifier, IEditorSerializer, IUntypedEditorInput } from '../../../common/editor.js'; import { EditorInput, IEditorCloseHandler } from '../../../common/editor/editorInput.js'; import { IChatEditingSession, ModifiedFileEntryState } from '../common/chatEditingService.js'; +import { IChatSessionsService } from '../common/chatSessionsService.js'; import { IChatModel } from '../common/chatModel.js'; +import { ChatSessionUri } from '../common/chatUri.js'; import { IChatService } from '../common/chatService.js'; import { ChatAgentLocation, ChatEditorTitleMaxLength } from '../common/constants.js'; import { IClearEditingSessionConfirmationOptions } from './actions/chatActions.js'; @@ -39,6 +41,7 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler public sessionId: string | undefined; private hasCustomTitle: boolean = false; + private cachedIcon: ThemeIcon | URI | undefined; private model: IChatModel | undefined; @@ -61,6 +64,7 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler readonly options: IChatEditorOptions, @IChatService private readonly chatService: IChatService, @IDialogService private readonly dialogService: IDialogService, + @IChatSessionsService private readonly chatSessionsService: IChatSessionsService, ) { super(); @@ -182,10 +186,75 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler return defaultName + inputCountSuffix; } - override getIcon(): ThemeIcon { + override getIcon(): ThemeIcon | undefined { + // Return cached icon if available + if (this.cachedIcon) { + return ThemeIcon.isThemeIcon(this.cachedIcon) ? this.cachedIcon : undefined; + } + + // Try to resolve icon and cache it + const resolvedIcon = this.resolveIcon(); + if (resolvedIcon) { + this.cachedIcon = resolvedIcon; + return ThemeIcon.isThemeIcon(resolvedIcon) ? resolvedIcon : undefined; + } + + // Fall back to default icon return ChatEditorIcon; } + private resolveIcon(): ThemeIcon | URI | undefined { + // 1. Try to get session-specific icon from IChatSessionItem.iconPath (supports both ThemeIcon and URI) + if (this.sessionId) { + const sessionType = this.getSessionType(); + if (sessionType !== 'local') { + // For non-local sessions, try to find the session item + // Note: This is a best-effort synchronous lookup. Session items may not be loaded yet. + const providers = this.chatSessionsService.getAllChatSessionItemProviders(); + const provider = providers.find(p => p.chatSessionType === sessionType); + if (provider) { + // We can't await here, so we'll check for icon updates during resolve() + // For now, fall through to session type icon + } + } + } + + // 2. Fall back to session type icon from extension point + const sessionType = this.getSessionType(); + if (sessionType !== 'local') { + const typeIcon = this.chatSessionsService.getIconForSessionType(sessionType); + if (typeIcon) { + return typeIcon; + } + } + + // 3. No custom icon found + return undefined; + } + + private getSessionType(): string { + if (!this.resource) { + return 'local'; + } + + const { scheme, query } = this.resource; + + if (scheme === Schemas.vscodeChatSession) { + const parsed = ChatSessionUri.parse(this.resource); + if (parsed) { + return parsed.chatSessionType; + } + } + + const sessionTypeFromQuery = new URLSearchParams(query).get('chatSessionType'); + if (sessionTypeFromQuery) { + return sessionTypeFromQuery; + } + + // Default to 'local' for vscode-chat-editor scheme or when type cannot be determined + return 'local'; + } + override async resolve(): Promise { const searchParams = new URLSearchParams(this.resource.query); const chatSessionType = searchParams.get('chatSessionType'); @@ -215,12 +284,31 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler ChatEditorInput.countsInUseMap.delete(this.inputName); } } + // Invalidate icon cache when label changes + this.cachedIcon = undefined; this._onDidChangeLabel.fire(); })); + // Check if icon has changed after model resolution + const newIcon = this.resolveIcon(); + if (newIcon && (!this.cachedIcon || !this.iconsEqual(this.cachedIcon, newIcon))) { + this.cachedIcon = newIcon; + this._onDidChangeLabel.fire(); + } + return this._register(new ChatEditorModel(this.model)); } + private iconsEqual(a: ThemeIcon | URI, b: ThemeIcon | URI): boolean { + if (ThemeIcon.isThemeIcon(a) && ThemeIcon.isThemeIcon(b)) { + return a.id === b.id; + } + if (a instanceof URI && b instanceof URI) { + return a.toString() === b.toString(); + } + return false; + } + override dispose(): void { super.dispose(); if (this.sessionId) { diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts index 7ea23ab4316..3b93af99507 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts @@ -7,6 +7,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { localize, localize2 } from '../../../../nls.js'; @@ -58,6 +59,10 @@ const extensionPoint = ExtensionsRegistry.registerExtensionPoint = new Map(); private readonly _sessionTypeModels: Map = new Map(); + private readonly _sessionTypeIcons: Map = new Map(); constructor( @ILogService private readonly _logService: ILogService, @@ -182,6 +188,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ displayName: contribution.displayName, description: contribution.description, when: contribution.when, + icon: contribution.icon, capabilities: contribution.capabilities, extensionDescription: ext.description, commands: contribution.commands @@ -249,11 +256,22 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ } this._contributions.set(contribution.type, contribution); + + // Store icon mapping if provided + if (contribution.icon) { + // Parse icon string - support both "$(iconId)" and "iconId" formats + const iconId = contribution.icon.startsWith('$(') && contribution.icon.endsWith(')') + ? contribution.icon.slice(2, -1) + : contribution.icon; + this._sessionTypeIcons.set(contribution.type, ThemeIcon.fromId(iconId)); + } + this._evaluateAvailability(); return { dispose: () => { this._contributions.delete(contribution.type); + this._sessionTypeIcons.delete(contribution.type); const store = this._disposableStores.get(contribution.type); if (store) { store.dispose(); @@ -684,6 +702,13 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ this.setSessionOption(chatSessionType, sessionId, u.optionId, u.value); } } + + /** + * Get the icon for a specific session type + */ + public getIconForSessionType(chatSessionType: string): ThemeIcon | undefined { + return this._sessionTypeIcons.get(chatSessionType); + } } registerSingleton(IChatSessionsService, ChatSessionsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/chat/common/chatSessionsService.ts b/src/vs/workbench/contrib/chat/common/chatSessionsService.ts index 0b22578f247..0055a4afc4d 100644 --- a/src/vs/workbench/contrib/chat/common/chatSessionsService.ts +++ b/src/vs/workbench/contrib/chat/common/chatSessionsService.ts @@ -36,6 +36,7 @@ export interface IChatSessionsExtensionPoint { readonly description: string; readonly extensionDescription: IRelaxedExtensionDescription; readonly when?: string; + readonly icon?: string; readonly capabilities?: { supportsFileAttachments?: boolean; supportsToolAttachments?: boolean; @@ -121,6 +122,7 @@ export interface IChatSessionsService { getAllChatSessionContributions(): IChatSessionsExtensionPoint[]; canResolveItemProvider(chatSessionType: string): Promise; getAllChatSessionItemProviders(): IChatSessionItemProvider[]; + getIconForSessionType(chatSessionType: string): ThemeIcon | undefined; provideNewChatSessionItem(chatSessionType: string, options: { request: IChatAgentRequest; metadata?: any; From f7fc5ff92e7df95c110abd67be643817b5e93295 Mon Sep 17 00:00:00 2001 From: kieferrm <4674940+kieferrm@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:59:58 -0700 Subject: [PATCH 3/8] Add icon support for chat session editor tabs --- src/vs/workbench/contrib/chat/browser/chatEditorInput.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts index b725de037dc..85917e73b9a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts @@ -19,10 +19,10 @@ import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js' import { EditorInputCapabilities, IEditorIdentifier, IEditorSerializer, IUntypedEditorInput } from '../../../common/editor.js'; import { EditorInput, IEditorCloseHandler } from '../../../common/editor/editorInput.js'; import { IChatEditingSession, ModifiedFileEntryState } from '../common/chatEditingService.js'; -import { IChatSessionsService } from '../common/chatSessionsService.js'; import { IChatModel } from '../common/chatModel.js'; -import { ChatSessionUri } from '../common/chatUri.js'; import { IChatService } from '../common/chatService.js'; +import { IChatSessionsService } from '../common/chatSessionsService.js'; +import { ChatSessionUri } from '../common/chatUri.js'; import { ChatAgentLocation, ChatEditorTitleMaxLength } from '../common/constants.js'; import { IClearEditingSessionConfirmationOptions } from './actions/chatActions.js'; import type { IChatEditorOptions } from './chatEditor.js'; From 65b2dcd24af55ca7a9e3672582390e7d20bd7c4b Mon Sep 17 00:00:00 2001 From: kieferrm <4674940+kieferrm@users.noreply.github.com> Date: Sat, 18 Oct 2025 14:21:04 -0700 Subject: [PATCH 4/8] Add support for provider specific messaging in the editor --- .../chat/browser/chatSessions.contribution.ts | 53 +++++++++++++++++++ .../contrib/chat/browser/chatWidget.ts | 20 +++++-- .../chat/common/chatSessionsService.ts | 6 +++ 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts index 3b93af99507..97dbc6edb9d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts @@ -63,6 +63,18 @@ const extensionPoint = ExtensionsRegistry.registerExtensionPoint = new Map(); private readonly _sessionTypeModels: Map = new Map(); private readonly _sessionTypeIcons: Map = new Map(); + private readonly _sessionTypeWelcomeTitles: Map = new Map(); + private readonly _sessionTypeWelcomeMessages: Map = new Map(); + private readonly _sessionTypeInputPlaceholders: Map = new Map(); constructor( @ILogService private readonly _logService: ILogService, @@ -189,6 +204,9 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ description: contribution.description, when: contribution.when, icon: contribution.icon, + welcomeTitle: contribution.welcomeTitle, + welcomeMessage: contribution.welcomeMessage, + inputPlaceholder: contribution.inputPlaceholder, capabilities: contribution.capabilities, extensionDescription: ext.description, commands: contribution.commands @@ -266,12 +284,26 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ this._sessionTypeIcons.set(contribution.type, ThemeIcon.fromId(iconId)); } + // Store welcome title, message, 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.inputPlaceholder) { + this._sessionTypeInputPlaceholders.set(contribution.type, contribution.inputPlaceholder); + } + this._evaluateAvailability(); return { dispose: () => { this._contributions.delete(contribution.type); this._sessionTypeIcons.delete(contribution.type); + this._sessionTypeWelcomeTitles.delete(contribution.type); + this._sessionTypeWelcomeMessages.delete(contribution.type); + this._sessionTypeInputPlaceholders.delete(contribution.type); const store = this._disposableStores.get(contribution.type); if (store) { store.dispose(); @@ -709,6 +741,27 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ public getIconForSessionType(chatSessionType: string): ThemeIcon | undefined { return this._sessionTypeIcons.get(chatSessionType); } + + /** + * 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); + } } registerSingleton(IChatSessionsService, ChatSessionsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 39691612fb1..31a80f60918 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -66,6 +66,7 @@ import { IChatModeService } from '../common/chatModes.js'; import { chatAgentLeader, ChatRequestAgentPart, ChatRequestDynamicVariablePart, ChatRequestSlashPromptPart, ChatRequestToolPart, ChatRequestToolSetPart, chatSubcommandLeader, formatChatQuestion, IParsedChatRequest } from '../common/chatParserTypes.js'; import { ChatRequestParser } from '../common/chatRequestParser.js'; import { IChatLocationData, IChatSendRequestOptions, IChatService } from '../common/chatService.js'; +import { IChatSessionsService } from '../common/chatSessionsService.js'; import { IChatSlashCommandService } from '../common/chatSlashCommands.js'; import { ChatRequestVariableSet, IChatRequestVariableEntry, isPromptFileVariableEntry, isPromptTextVariableEntry, PromptFileVariableKind, toPromptFileVariableEntry } from '../common/chatVariableEntries.js'; import { ChatViewModel, IChatRequestViewModel, IChatResponseViewModel, isRequestVM, isResponseVM } from '../common/chatViewModel.js'; @@ -482,6 +483,7 @@ export class ChatWidget extends Disposable implements IChatWidget { @IChatEntitlementService private readonly chatEntitlementService: IChatEntitlementService, @ICommandService private readonly commandService: ICommandService, @IHoverService private readonly hoverService: IHoverService, + @IChatSessionsService private readonly chatSessionsService: IChatSessionsService, ) { super(); this._lockedToCodingAgentContextKey = ChatContextKeys.lockedToCodingAgent.bindTo(this.contextKeyService); @@ -1412,10 +1414,15 @@ export class ChatWidget extends Disposable implements IChatWidget { additionalMessage = localize('expChatAdditionalMessage', "AI responses may be inaccurate."); } + // Check for provider-specific customizations + const providerIcon = this._lockedAgentId ? this.chatSessionsService.getIconForSessionType(this._lockedAgentId) : undefined; + const providerTitle = this._lockedAgentId ? this.chatSessionsService.getWelcomeTitleForSessionType(this._lockedAgentId) : undefined; + const providerMessage = this._lockedAgentId ? this.chatSessionsService.getWelcomeMessageForSessionType(this._lockedAgentId) : undefined; + const welcomeContent: IChatViewWelcomeContent = { - title: localize('expChatTitle', 'Build with agent mode'), - message: new MarkdownString(localize('expchatMessage', "Let's get started")), - icon: Codicon.chatSparkle, + title: providerTitle ?? localize('expChatTitle', 'Build with agent mode'), + message: providerMessage ? new MarkdownString(providerMessage) : new MarkdownString(localize('expchatMessage', "Let's get started")), + icon: providerIcon ?? Codicon.chatSparkle, inputPart: this.inputPart.element, additionalMessage, isNew: true, @@ -2268,8 +2275,11 @@ export class ChatWidget extends Disposable implements IChatWidget { this.container.setAttribute('data-session-id', model.sessionId); this.viewModel = this.instantiationService.createInstance(ChatViewModel, model, this._codeBlockModelCollection); - if (this._lockedToCodingAgent) { - const placeholder = localize('chat.input.placeholder.lockedToAgent', "Chat with {0}", this._lockedToCodingAgent); + if (this._lockedAgentId) { + let placeholder = this._lockedAgentId ? this.chatSessionsService.getInputPlaceholderForSessionType(this._lockedAgentId) : undefined; + if (!placeholder) { + placeholder = localize('chat.input.placeholder.lockedToAgent', "Chat with {0}", this._lockedAgentId); + } this.viewModel.setInputPlaceholder(placeholder); this.inputEditor.updateOptions({ placeholder }); } else if (this.viewModel.inputPlaceholder) { diff --git a/src/vs/workbench/contrib/chat/common/chatSessionsService.ts b/src/vs/workbench/contrib/chat/common/chatSessionsService.ts index 0055a4afc4d..4be7387d2b6 100644 --- a/src/vs/workbench/contrib/chat/common/chatSessionsService.ts +++ b/src/vs/workbench/contrib/chat/common/chatSessionsService.ts @@ -37,6 +37,9 @@ export interface IChatSessionsExtensionPoint { readonly extensionDescription: IRelaxedExtensionDescription; readonly when?: string; readonly icon?: string; + readonly welcomeTitle?: string; + readonly welcomeMessage?: string; + readonly inputPlaceholder?: string; readonly capabilities?: { supportsFileAttachments?: boolean; supportsToolAttachments?: boolean; @@ -123,6 +126,9 @@ export interface IChatSessionsService { canResolveItemProvider(chatSessionType: string): Promise; getAllChatSessionItemProviders(): IChatSessionItemProvider[]; getIconForSessionType(chatSessionType: string): ThemeIcon | undefined; + getWelcomeTitleForSessionType(chatSessionType: string): string | undefined; + getWelcomeMessageForSessionType(chatSessionType: string): string | undefined; + getInputPlaceholderForSessionType(chatSessionType: string): string | undefined; provideNewChatSessionItem(chatSessionType: string, options: { request: IChatAgentRequest; metadata?: any; From 8a68ac06c549721fef21bd56c37f65e926340d18 Mon Sep 17 00:00:00 2001 From: kieferrm <4674940+kieferrm@users.noreply.github.com> Date: Sat, 18 Oct 2025 14:37:34 -0700 Subject: [PATCH 5/8] don't show suggested prompts for agents --- src/vs/workbench/contrib/chat/browser/chatWidget.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 31a80f60918..fd0169b2051 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -1418,6 +1418,7 @@ export class ChatWidget extends Disposable implements IChatWidget { const providerIcon = this._lockedAgentId ? this.chatSessionsService.getIconForSessionType(this._lockedAgentId) : undefined; const providerTitle = this._lockedAgentId ? this.chatSessionsService.getWelcomeTitleForSessionType(this._lockedAgentId) : undefined; const providerMessage = this._lockedAgentId ? this.chatSessionsService.getWelcomeMessageForSessionType(this._lockedAgentId) : undefined; + const suggestedPrompts = this._lockedAgentId ? undefined : this.getNewSuggestedPrompts(); const welcomeContent: IChatViewWelcomeContent = { title: providerTitle ?? localize('expChatTitle', 'Build with agent mode'), @@ -1426,7 +1427,7 @@ export class ChatWidget extends Disposable implements IChatWidget { inputPart: this.inputPart.element, additionalMessage, isNew: true, - suggestedPrompts: this.getNewSuggestedPrompts(), + suggestedPrompts }; return welcomeContent; } From f7d918f6030862374146af00a703ffdbea6aa81a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Sun, 19 Oct 2025 12:25:11 -0700 Subject: [PATCH 6/8] Update todos --- .../contrib/chat/browser/chatEditorInput.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts index 85917e73b9a..c62c7736dba 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts @@ -204,22 +204,7 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler } private resolveIcon(): ThemeIcon | URI | undefined { - // 1. Try to get session-specific icon from IChatSessionItem.iconPath (supports both ThemeIcon and URI) - if (this.sessionId) { - const sessionType = this.getSessionType(); - if (sessionType !== 'local') { - // For non-local sessions, try to find the session item - // Note: This is a best-effort synchronous lookup. Session items may not be loaded yet. - const providers = this.chatSessionsService.getAllChatSessionItemProviders(); - const provider = providers.find(p => p.chatSessionType === sessionType); - if (provider) { - // We can't await here, so we'll check for icon updates during resolve() - // For now, fall through to session type icon - } - } - } - - // 2. Fall back to session type icon from extension point + // 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 !== 'local') { const typeIcon = this.chatSessionsService.getIconForSessionType(sessionType); @@ -228,7 +213,6 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler } } - // 3. No custom icon found return undefined; } From 48c93224ca007619fa9698e8180ac68cdaa2a189 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Sun, 19 Oct 2025 12:56:19 -0700 Subject: [PATCH 7/8] :lipstick: load theme icon --- .../chat/browser/chatSessions.contribution.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts index a944aab5a26..983494d16da 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.contribution.ts @@ -277,12 +277,17 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ this._contributions.set(contribution.type, contribution); // Store icon mapping if provided + let icon: ThemeIcon | undefined; + if (contribution.icon) { // Parse icon string - support both "$(iconId)" and "iconId" formats - const iconId = contribution.icon.startsWith('$(') && contribution.icon.endsWith(')') - ? contribution.icon.slice(2, -1) - : contribution.icon; - this._sessionTypeIcons.set(contribution.type, ThemeIcon.fromId(iconId)); + icon = contribution.icon.startsWith('$(') && contribution.icon.endsWith(')') + ? ThemeIcon.fromString(contribution.icon) + : ThemeIcon.fromId(contribution.icon); + } + + if (icon) { + this._sessionTypeIcons.set(contribution.type, icon); } // Store welcome title, message, and input placeholder if provided From a676d9651ceecb9a70df6ad25801de48fa0953f1 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Sun, 19 Oct 2025 12:56:48 -0700 Subject: [PATCH 8/8] support welcome title/description in empty editor (when users login) --- .../contrib/chat/browser/chatWidget.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index fd0169b2051..6487ffae34d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -1358,15 +1358,22 @@ export class ChatWidget extends Disposable implements IChatWidget { if (this.isLockedToCodingAgent) { - // TODO(jospicer): Let extensions contribute this welcome message/docs - const message = this._codingAgentPrefix === '@copilot ' - ? new MarkdownString(localize('copilotCodingAgentMessage', "This chat session will be forwarded to the {0} [coding agent]({1}) where work is completed in the background. ", this._codingAgentPrefix, 'https://aka.ms/coding-agent-docs') + this.chatDisclaimer, { isTrusted: true }) - : new MarkdownString(localize('genericCodingAgentMessage', "This chat session will be forwarded to the {0} coding agent where work is completed in the background. ", this._codingAgentPrefix) + this.chatDisclaimer); + // Check for provider-specific customizations from chat sessions service + const providerIcon = this._lockedAgentId ? this.chatSessionsService.getIconForSessionType(this._lockedAgentId) : undefined; + const providerTitle = this._lockedAgentId ? this.chatSessionsService.getWelcomeTitleForSessionType(this._lockedAgentId) : undefined; + const providerMessage = this._lockedAgentId ? this.chatSessionsService.getWelcomeMessageForSessionType(this._lockedAgentId) : undefined; + + // Fallback to default messages if provider doesn't specify + const message = providerMessage + ? new MarkdownString(providerMessage) + : (this._codingAgentPrefix === '@copilot ' + ? new MarkdownString(localize('copilotCodingAgentMessage', "This chat session will be forwarded to the {0} [coding agent]({1}) where work is completed in the background. ", this._codingAgentPrefix, 'https://aka.ms/coding-agent-docs') + this.chatDisclaimer, { isTrusted: true }) + : new MarkdownString(localize('genericCodingAgentMessage', "This chat session will be forwarded to the {0} coding agent where work is completed in the background. ", this._codingAgentPrefix) + this.chatDisclaimer)); return { - title: localize('codingAgentTitle', "Delegate to {0}", this._codingAgentPrefix), + title: providerTitle ?? localize('codingAgentTitle', "Delegate to {0}", this._codingAgentPrefix), message, - icon: Codicon.sendToRemoteAgent, + icon: providerIcon ?? Codicon.sendToRemoteAgent, additionalMessage, }; }