diff --git a/src/vs/sessions/contrib/sessions/browser/sessionHoverContent.ts b/src/vs/sessions/contrib/sessions/browser/sessionHoverContent.ts index cbb1fc3590ee1d..b2dca5076daf5a 100644 --- a/src/vs/sessions/contrib/sessions/browser/sessionHoverContent.ts +++ b/src/vs/sessions/contrib/sessions/browser/sessionHoverContent.ts @@ -49,6 +49,7 @@ export function getSessionSummaryHoverData( const mainWorkspace = mainChat?.workspace.get() ?? sessionWorkspace; const topPullRequestRefs = getPullRequestRefs([isMultiFolder ? mainWorkspace : sessionWorkspace]); return { + ...getProviderDetails(session, sessionsProvidersService), title: session.title.get() || getUntitledSessionTitle(session.isQuickChat?.get() ?? false), ...(includeUpdatedAt ? { updatedAt: session.updatedAt.get() } : {}), location: getLocation( @@ -60,7 +61,6 @@ export function getSessionSummaryHoverData( pullRequests: toHoverPullRequests(topPullRequestRefs.values(), openerService), createdBy, externalSession: getExternalSession(session, preferencesService), - providerLabel: getProviderLabel(session, sessionsProvidersService), ...(sessionWorkspace && isMultiFolder ? { sessionSummary: { workspaces: getWorkspaceSummaries(sessionWorkspace, session.worktreePending?.get() ?? false, labelService), @@ -87,6 +87,7 @@ export function getChatSummaryHoverData( includeUpdatedAt = false, ): ISessionSummaryHoverData { return { + ...getProviderDetails(session, sessionsProvidersService), title: chat.title.get().trim() || localize('untitledChat', "Untitled Chat"), ...(includeUpdatedAt ? { updatedAt: chat.updatedAt.get() } : {}), location: getLocation( @@ -98,7 +99,6 @@ export function getChatSummaryHoverData( pullRequests: getPullRequests(chat.workspace.get(), openerService), createdBy, externalSession: getExternalSession(session, preferencesService), - providerLabel: getProviderLabel(session, sessionsProvidersService), }; } @@ -253,11 +253,17 @@ function getExternalSession(session: ISession, preferencesService: IPreferencesS }; } -/** The kind of agent serving the session, e.g. "Claude". */ -function getProviderLabel(session: ISession, sessionsProvidersService: ISessionsProvidersService): string | undefined { +function getProviderDetails( + session: ISession, + sessionsProvidersService: ISessionsProvidersService, +): Pick { const provider = sessionsProvidersService.getProvider(session.providerId); if (!provider) { - return undefined; + return {}; } - return provider.sessionTypes.find(type => type.id === session.sessionType)?.label ?? provider.label; + const providerLabel = provider.sessionTypes.find(type => type.id === session.sessionType)?.label ?? provider.label; + return { + providerLabel, + ...(session.remoteConnectionStatus && providerLabel !== provider.label ? { remoteName: provider.label } : {}), + }; } diff --git a/src/vs/sessions/contrib/sessions/test/browser/sessionsList.test.ts b/src/vs/sessions/contrib/sessions/test/browser/sessionsList.test.ts index a392b755c39537..12a4477d741179 100644 --- a/src/vs/sessions/contrib/sessions/test/browser/sessionsList.test.ts +++ b/src/vs/sessions/contrib/sessions/test/browser/sessionsList.test.ts @@ -43,6 +43,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../../pla import { IAutomationRun } from '../../../../../workbench/contrib/chat/common/automations/automation.js'; import { IAutomationService } from '../../../../../workbench/contrib/chat/common/automations/automationService.js'; import { ChatAutomationsEnabledContext } from '../../../../../workbench/contrib/chat/common/automations/automationsEnabled.js'; +import { SessionSummaryHoverWidget } from '../../../../../workbench/contrib/chat/browser/agentSessions/sessionSummaryHover.js'; import { AICustomizationManagementEditorInput } from '../../../../../workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagementEditorInput.js'; import { IEditorService } from '../../../../../workbench/services/editor/common/editorService.js'; import { IPreferencesService, IOpenSettingsOptions } from '../../../../../workbench/services/preferences/common/preferences.js'; @@ -57,7 +58,7 @@ import type { ICustomViewDescriptor } from '../../../../services/customView/brow import { ISessionsListModelService, SessionsListModelService } from '../../../../services/sessions/browser/sessionsListModelService.js'; import { ISessionGroup, ISessionGroupsChangeEvent, ISessionGroupsService } from '../../../../services/sessions/browser/sessionGroupsService.js'; import { ISessionsService } from '../../../../services/sessions/browser/sessionsService.js'; -import { BRANCH_CHANGES_CHANGESET_ID, ChatInteractivity, ChatOriginKind, IChat, ISession, ISessionChangeset, ISessionChangesSummary, ISessionFileChange, ISessionFolder, SessionStatus } from '../../../../services/sessions/common/session.js'; +import { BRANCH_CHANGES_CHANGESET_ID, ChatInteractivity, ChatOriginKind, IChat, ISession, ISessionChangeset, ISessionChangesSummary, ISessionFileChange, ISessionFolder, ISessionType, SessionStatus } from '../../../../services/sessions/common/session.js'; import { IActiveSession, ISessionsManagementService } from '../../../../services/sessions/common/sessionsManagement.js'; import { ISessionsProvider } from '../../../../services/sessions/common/sessionsProvider.js'; import { ISessionsProvidersService } from '../../../../services/sessions/browser/sessionsProvidersService.js'; @@ -1940,6 +1941,53 @@ suite('Sessions - SessionsList', () => { }); }); + test('remote session hover shows the remote name after the session type', () => { + const localSession = createSession('Fix remote reconnection', { workspaceLabel: 'Workspace' }); + const remoteSession: ISession = { + ...localSession, + remoteConnectionStatus: constObservable({ kind: 'connected' }), + }; + const provider = upcastPartial({ + label: 'Remote Mac', + sessionTypes: [upcastPartial({ id: 'test', label: 'Copilot CLI' })], + }); + const createProvidersService = (provider: ISessionsProvider) => new class extends mock() { + override getProvider(): T | undefined { + return provider as T; + } + }; + const providersService = createProvidersService(provider); + const createHoverData = (session: ISession) => getSessionSummaryHoverData( + session, + providersService, + upcastPartial({}), + upcastPartial({}), + upcastPartial({}), + ); + const remoteData = createHoverData(remoteSession); + const titleLine = new SessionSummaryHoverWidget(remoteData).domNode.querySelector('.session-summary-hover-title')?.textContent; + const disconnectedData = getSessionSummaryHoverData( + remoteSession, + createProvidersService({ ...provider, sessionTypes: [] }), + upcastPartial({}), + upcastPartial({}), + upcastPartial({}), + ); + const disconnectedTitleLine = new SessionSummaryHoverWidget(disconnectedData).domNode.querySelector('.session-summary-hover-title')?.textContent; + + assert.deepStrictEqual({ + titleLine, + remoteName: remoteData.remoteName, + disconnectedTitleLine, + localRemoteName: createHoverData(localSession).remoteName, + }, { + titleLine: 'Fix remote reconnection · Copilot CLI · Remote Mac', + remoteName: 'Remote Mac', + disconnectedTitleLine: 'Fix remote reconnection · Remote Mac', + localRemoteName: undefined, + }); + }); + test('external session hover leads to the setting that governs external sessions', () => { const queries: (string | undefined)[] = []; const hoverFor = (isExternal: boolean) => getSessionSummaryHoverData( diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/sessionSummaryHover.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/sessionSummaryHover.ts index 5eacffe4fcd359..73112069e10cb6 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/sessionSummaryHover.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/sessionSummaryHover.ts @@ -86,6 +86,8 @@ export interface ISessionSummaryHoverData { * title as "Fix the redirect loop · Claude". */ readonly providerLabel?: string; + /** The remote host serving the session, shown after {@link providerLabel} when present. */ + readonly remoteName?: string; /** Session that created this session, when available. */ readonly createdBy?: { readonly title: string; @@ -149,6 +151,10 @@ export class SessionSummaryHoverWidget { appendSeparator(this._title); dom.append(this._title, dom.$('span.session-summary-hover-provider', undefined, data.providerLabel)); } + if (data.remoteName) { + appendSeparator(this._title); + dom.append(this._title, dom.$('span.session-summary-hover-provider', undefined, data.remoteName)); + } if (data.updatedAt) { appendSeparator(this._title); dom.append(this._title, dom.$('span.session-summary-hover-provider', undefined, fromNow(data.updatedAt, true)));