mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-04 01:44:44 -06:00
agent sessions - introduce experiments and adopt for projection & status (#288696)
This commit is contained in:
parent
471da7c9b8
commit
a2ad5acb04
2
.github/CODENOTIFY
vendored
2
.github/CODENOTIFY
vendored
@ -7,7 +7,6 @@ src/vs/base/common/path.ts @bpasero
|
||||
src/vs/base/common/stream.ts @bpasero
|
||||
src/vs/base/common/uri.ts @jrieken
|
||||
src/vs/base/browser/domSanitize.ts @mjbvz
|
||||
src/vs/base/browser/** @bpasero
|
||||
src/vs/base/node/pfs.ts @bpasero
|
||||
src/vs/base/node/unc.ts @bpasero
|
||||
src/vs/base/parts/contextmenu/** @bpasero
|
||||
@ -110,7 +109,6 @@ src/vs/workbench/contrib/chat/browser/media/chatViewTitleControl.css @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatManagement/chatUsageWidget.ts @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatManagement/media/chatUsageWidget.css @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/agentSessions/** @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatSessions/** @bpasero
|
||||
src/vs/workbench/contrib/localization/** @TylerLeonhardt
|
||||
src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @TylerLeonhardt
|
||||
src/vs/workbench/contrib/scm/** @lszomoru
|
||||
|
||||
@ -292,7 +292,7 @@ export class MenuId {
|
||||
static readonly AgentSessionsToolbar = new MenuId('AgentSessionsToolbar');
|
||||
static readonly AgentSessionItemToolbar = new MenuId('AgentSessionItemToolbar');
|
||||
static readonly AgentSessionSectionToolbar = new MenuId('AgentSessionSectionToolbar');
|
||||
static readonly AgentsControlMenu = new MenuId('AgentsControlMenu');
|
||||
static readonly AgentsTitleBarControlMenu = new MenuId('AgentsTitleBarControlMenu');
|
||||
static readonly ChatViewSessionTitleNavigationToolbar = new MenuId('ChatViewSessionTitleNavigationToolbar');
|
||||
static readonly ChatViewSessionTitleToolbar = new MenuId('ChatViewSessionTitleToolbar');
|
||||
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from '../../../../base/common/lifecycle.js';
|
||||
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
|
||||
/**
|
||||
* Interface for a command center control that can be registered with the titlebar.
|
||||
*/
|
||||
export interface ICommandCenterControl extends IDisposable {
|
||||
readonly element: HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* A registration for a custom command center control.
|
||||
*/
|
||||
export interface ICommandCenterControlRegistration {
|
||||
/**
|
||||
* The context key that must be truthy for this control to be shown.
|
||||
* When this context key is true, this control replaces the default command center.
|
||||
*/
|
||||
readonly contextKey: string;
|
||||
|
||||
/**
|
||||
* Priority for when multiple controls match. Higher priority wins.
|
||||
*/
|
||||
readonly priority: number;
|
||||
|
||||
/**
|
||||
* Factory function to create the control.
|
||||
*/
|
||||
create(instantiationService: IInstantiationService): ICommandCenterControl;
|
||||
}
|
||||
|
||||
class CommandCenterControlRegistryImpl {
|
||||
private readonly registrations: ICommandCenterControlRegistration[] = [];
|
||||
|
||||
/**
|
||||
* Register a custom command center control.
|
||||
*/
|
||||
register(registration: ICommandCenterControlRegistration): IDisposable {
|
||||
this.registrations.push(registration);
|
||||
// Sort by priority descending
|
||||
this.registrations.sort((a, b) => b.priority - a.priority);
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
const index = this.registrations.indexOf(registration);
|
||||
if (index >= 0) {
|
||||
this.registrations.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered command center controls.
|
||||
*/
|
||||
getRegistrations(): readonly ICommandCenterControlRegistration[] {
|
||||
return this.registrations;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry for custom command center controls.
|
||||
* Contrib modules can register controls here, and the titlebar will use them
|
||||
* when their context key conditions are met.
|
||||
*/
|
||||
export const CommandCenterControlRegistry = new CommandCenterControlRegistryImpl();
|
||||
@ -30,7 +30,6 @@ import { IContextKey, IContextKeyService } from '../../../../platform/contextkey
|
||||
import { IHostService } from '../../../services/host/browser/host.js';
|
||||
import { WindowTitle } from './windowTitle.js';
|
||||
import { CommandCenterControl } from './commandCenterControl.js';
|
||||
import { CommandCenterControlRegistry } from './commandCenterControlRegistry.js';
|
||||
import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';
|
||||
import { WorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js';
|
||||
import { ACCOUNTS_ACTIVITY_ID, GLOBAL_ACTIVITY_ID } from '../../../common/activity.js';
|
||||
@ -329,14 +328,6 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||
this._register(this.hostService.onDidChangeActiveWindow(windowId => windowId === targetWindowId ? this.onFocus() : this.onBlur()));
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e)));
|
||||
this._register(this.editorGroupsContainer.onDidChangeEditorPartOptions(e => this.onEditorPartConfigurationChange(e)));
|
||||
|
||||
// Re-create title when any registered command center control's context key changes
|
||||
this._register(this.contextKeyService.onDidChangeContext(e => {
|
||||
const registeredContextKeys = new Set(CommandCenterControlRegistry.getRegistrations().map(r => r.contextKey));
|
||||
if (registeredContextKeys.size > 0 && e.affectsSome(registeredContextKeys)) {
|
||||
this.createTitle();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onBlur(): void {
|
||||
@ -585,24 +576,9 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||
|
||||
// Menu Title
|
||||
else {
|
||||
// Check if any registered command center control should be shown
|
||||
let customControlShown = false;
|
||||
for (const registration of CommandCenterControlRegistry.getRegistrations()) {
|
||||
if (this.contextKeyService.getContextKeyValue<boolean>(registration.contextKey)) {
|
||||
const control = registration.create(this.instantiationService);
|
||||
reset(this.title, control.element);
|
||||
this.titleDisposables.add(control);
|
||||
customControlShown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!customControlShown) {
|
||||
// Normal mode - show regular command center
|
||||
const commandCenter = this.instantiationService.createInstance(CommandCenterControl, this.windowTitle, this.hoverDelegate);
|
||||
reset(this.title, commandCenter.element);
|
||||
this.titleDisposables.add(commandCenter);
|
||||
}
|
||||
const commandCenter = this.instantiationService.createInstance(CommandCenterControl, this.windowTitle, this.hoverDelegate);
|
||||
reset(this.title, commandCenter.element);
|
||||
this.titleDisposables.add(commandCenter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -948,10 +948,6 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, {
|
||||
ChatContextKeys.Setup.disabled.negate()
|
||||
),
|
||||
ContextKeyExpr.has('config.chat.commandCenter.enabled'),
|
||||
ContextKeyExpr.or(
|
||||
ContextKeyExpr.has(`config.${ChatConfiguration.AgentStatusEnabled}`).negate(), // Show when agent status is disabled
|
||||
ChatContextKeys.agentStatusHasNotifications.negate() // Or when agent status has no notifications
|
||||
)
|
||||
),
|
||||
order: 10003 // to the right of agent controls
|
||||
});
|
||||
|
||||
@ -30,7 +30,6 @@ import { ChatViewPane } from '../widgetHosts/viewPane/chatViewPane.js';
|
||||
import { ACTION_ID_NEW_CHAT, ACTION_ID_NEW_EDIT_SESSION, CHAT_CATEGORY, handleCurrentEditingSession } from './chatActions.js';
|
||||
import { clearChatEditor } from './chatClear.js';
|
||||
import { AgentSessionsViewerOrientation } from '../agentSessions/agentSessions.js';
|
||||
import { IAgentSessionProjectionService } from '../agentSessions/agentSessionProjectionService.js';
|
||||
|
||||
export interface INewEditSessionActionContext {
|
||||
|
||||
@ -121,13 +120,6 @@ export function registerNewChatActions() {
|
||||
|
||||
async run(accessor: ServicesAccessor, ...args: unknown[]) {
|
||||
const accessibilityService = accessor.get(IAccessibilityService);
|
||||
const projectionService = accessor.get(IAgentSessionProjectionService);
|
||||
|
||||
// Exit projection mode if active (back button behavior)
|
||||
if (projectionService.isActive) {
|
||||
await projectionService.exitProjection();
|
||||
return;
|
||||
}
|
||||
const viewsService = accessor.get(IViewsService);
|
||||
|
||||
const executeCommandContext = args[0] as INewEditSessionActionContext | undefined;
|
||||
|
||||
@ -3,10 +3,9 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import './experiments/agentSessionsExperiments.contribution.js';
|
||||
import { Codicon } from '../../../../../base/common/codicons.js';
|
||||
import { Disposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { localize, localize2 } from '../../../../../nls.js';
|
||||
import { mainWindow } from '../../../../../base/browser/window.js';
|
||||
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { registerSingleton, InstantiationType } from '../../../../../platform/instantiation/common/extensions.js';
|
||||
import { Registry } from '../../../../../platform/registry/common/platform.js';
|
||||
@ -15,20 +14,11 @@ import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';
|
||||
import { AgentSessionsViewerOrientation, AgentSessionsViewerPosition } from './agentSessions.js';
|
||||
import { IAgentSessionsService, AgentSessionsService } from './agentSessionsService.js';
|
||||
import { LocalAgentsSessionsProvider } from './localAgentSessionsProvider.js';
|
||||
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../common/contributions.js';
|
||||
import { ISubmenuItem, MenuId, MenuRegistry, registerAction2, SubmenuItemAction } from '../../../../../platform/actions/common/actions.js';
|
||||
import { ArchiveAgentSessionAction, ArchiveAgentSessionSectionAction, UnarchiveAgentSessionSectionAction, UnarchiveAgentSessionAction, OpenAgentSessionInEditorGroupAction, OpenAgentSessionInNewEditorGroupAction, OpenAgentSessionInNewWindowAction, ShowAgentSessionsSidebar, HideAgentSessionsSidebar, ToggleAgentSessionsSidebar, RefreshAgentSessionsViewerAction, FindAgentSessionInViewerAction, MarkAgentSessionUnreadAction, MarkAgentSessionReadAction, MarkAgentSessionSectionReadAction, FocusAgentSessionsAction, SetAgentSessionsOrientationStackedAction, SetAgentSessionsOrientationSideBySideAction, ShowAllAgentSessionsAction, ShowRecentAgentSessionsAction, HideAgentSessionsAction, PickAgentSessionAction, ArchiveAllAgentSessionsAction, RenameAgentSessionAction, DeleteAgentSessionAction, DeleteAllLocalSessionsAction } from './agentSessionsActions.js';
|
||||
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../../common/contributions.js';
|
||||
import { ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js';
|
||||
import { ArchiveAgentSessionAction, ArchiveAgentSessionSectionAction, UnarchiveAgentSessionAction, OpenAgentSessionInEditorGroupAction, OpenAgentSessionInNewEditorGroupAction, OpenAgentSessionInNewWindowAction, ShowAgentSessionsSidebar, HideAgentSessionsSidebar, ToggleAgentSessionsSidebar, RefreshAgentSessionsViewerAction, FindAgentSessionInViewerAction, MarkAgentSessionUnreadAction, MarkAgentSessionReadAction, FocusAgentSessionsAction, SetAgentSessionsOrientationStackedAction, SetAgentSessionsOrientationSideBySideAction, PickAgentSessionAction, ArchiveAllAgentSessionsAction, RenameAgentSessionAction, DeleteAgentSessionAction, DeleteAllLocalSessionsAction, HideAgentSessionsAction, MarkAgentSessionSectionReadAction, ShowAllAgentSessionsAction, ShowRecentAgentSessionsAction, UnarchiveAgentSessionSectionAction } from './agentSessionsActions.js';
|
||||
import { AgentSessionsQuickAccessProvider, AGENT_SESSIONS_QUICK_ACCESS_PREFIX } from './agentSessionsQuickAccess.js';
|
||||
import { IAgentSessionProjectionService, AgentSessionProjectionService } from './agentSessionProjectionService.js';
|
||||
import { EnterAgentSessionProjectionAction, ExitAgentSessionProjectionAction, ToggleAgentStatusAction, ToggleAgentSessionProjectionAction } from './agentSessionProjectionActions.js';
|
||||
import { IAgentStatusService, AgentStatusService } from './agentStatusService.js';
|
||||
import { AgentStatusWidget } from './agentStatusWidget.js';
|
||||
import { IActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js';
|
||||
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
|
||||
import { ChatConfiguration } from '../../common/constants.js';
|
||||
import { AuxiliaryBarMaximizedContext } from '../../../../common/contextkeys.js';
|
||||
import { LayoutSettings } from '../../../../services/layout/browser/layoutService.js';
|
||||
|
||||
//#region Actions and Menus
|
||||
|
||||
@ -59,12 +49,6 @@ registerAction2(HideAgentSessionsAction);
|
||||
registerAction2(SetAgentSessionsOrientationStackedAction);
|
||||
registerAction2(SetAgentSessionsOrientationSideBySideAction);
|
||||
|
||||
// Agent Session Projection
|
||||
registerAction2(EnterAgentSessionProjectionAction);
|
||||
registerAction2(ExitAgentSessionProjectionAction);
|
||||
registerAction2(ToggleAgentStatusAction);
|
||||
registerAction2(ToggleAgentSessionProjectionAction);
|
||||
|
||||
// --- Agent Sessions Toolbar
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.AgentSessionsToolbar, {
|
||||
@ -193,73 +177,7 @@ Registry.as<IQuickAccessRegistry>(QuickAccessExtensions.Quickaccess).registerQui
|
||||
//#region Workbench Contributions
|
||||
|
||||
registerWorkbenchContribution2(LocalAgentsSessionsProvider.ID, LocalAgentsSessionsProvider, WorkbenchPhase.AfterRestored);
|
||||
|
||||
registerSingleton(IAgentSessionsService, AgentSessionsService, InstantiationType.Delayed);
|
||||
registerSingleton(IAgentStatusService, AgentStatusService, InstantiationType.Delayed);
|
||||
registerSingleton(IAgentSessionProjectionService, AgentSessionProjectionService, InstantiationType.Delayed);
|
||||
|
||||
// Register Agent Status as a menu item in the command center (alongside the search box, not replacing it)
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandCenter, {
|
||||
submenu: MenuId.AgentsControlMenu,
|
||||
title: localize('agentsControl', "Agents"),
|
||||
icon: Codicon.chatSparkle,
|
||||
when: ContextKeyExpr.has(`config.${ChatConfiguration.AgentStatusEnabled}`),
|
||||
order: 10002 // to the right of the chat button
|
||||
});
|
||||
|
||||
// Register a placeholder action to the submenu so it appears (required for submenus)
|
||||
MenuRegistry.appendMenuItem(MenuId.AgentsControlMenu, {
|
||||
command: {
|
||||
id: 'workbench.action.chat.toggle',
|
||||
title: localize('openChat', "Open Chat"),
|
||||
},
|
||||
when: ContextKeyExpr.has(`config.${ChatConfiguration.AgentStatusEnabled}`),
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides custom rendering for the agent status in the command center.
|
||||
* Uses IActionViewItemService to render a custom AgentStatusWidget
|
||||
* for the AgentsControlMenu submenu.
|
||||
* Also adds a CSS class to the workbench when agent status is enabled.
|
||||
*/
|
||||
class AgentStatusRendering extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
static readonly ID = 'workbench.contrib.agentStatus.rendering';
|
||||
|
||||
constructor(
|
||||
@IActionViewItemService actionViewItemService: IActionViewItemService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(actionViewItemService.register(MenuId.CommandCenter, MenuId.AgentsControlMenu, (action, options) => {
|
||||
if (!(action instanceof SubmenuItemAction)) {
|
||||
return undefined;
|
||||
}
|
||||
return instantiationService.createInstance(AgentStatusWidget, action, options);
|
||||
}, undefined));
|
||||
|
||||
// Add/remove CSS class on workbench based on setting
|
||||
// Also force enable command center when agent status is enabled
|
||||
const updateClass = () => {
|
||||
const enabled = configurationService.getValue<boolean>(ChatConfiguration.AgentStatusEnabled) === true;
|
||||
mainWindow.document.body.classList.toggle('agent-status-enabled', enabled);
|
||||
|
||||
// Force enable command center when agent status is enabled
|
||||
if (enabled && configurationService.getValue<boolean>(LayoutSettings.COMMAND_CENTER) !== true) {
|
||||
configurationService.updateValue(LayoutSettings.COMMAND_CENTER, true);
|
||||
}
|
||||
};
|
||||
updateClass();
|
||||
this._register(configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(ChatConfiguration.AgentStatusEnabled)) {
|
||||
updateClass();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Register the workbench contribution that provides custom rendering for the agent status
|
||||
registerWorkbenchContribution2(AgentStatusRendering.ID, AgentStatusRendering, WorkbenchPhase.AfterRestored);
|
||||
|
||||
//#endregion
|
||||
|
||||
@ -68,10 +68,12 @@ export enum AgentSessionsViewerPosition {
|
||||
}
|
||||
|
||||
export interface IAgentSessionsControl {
|
||||
|
||||
readonly element: HTMLElement | undefined;
|
||||
|
||||
refresh(): void;
|
||||
openFind(): void;
|
||||
reveal(sessionResource: URI): void;
|
||||
setGridMarginOffset(offset: number): void;
|
||||
}
|
||||
|
||||
export const agentSessionReadIndicatorForeground = registerColor(
|
||||
|
||||
@ -33,19 +33,17 @@ import { openSession } from './agentSessionsOpener.js';
|
||||
import { IEditorService } from '../../../../services/editor/common/editorService.js';
|
||||
import { ChatEditorInput } from '../widgetHosts/editor/chatEditorInput.js';
|
||||
import { IMouseEvent } from '../../../../../base/browser/mouseEvent.js';
|
||||
import { IChatWidget } from '../chat.js';
|
||||
|
||||
export interface IAgentSessionsControlOptions extends IAgentSessionsSorterOptions {
|
||||
readonly overrideStyles: IStyleOverride<IListStyles>;
|
||||
readonly filter: IAgentSessionsFilter;
|
||||
readonly source: AgentSessionsControlSource;
|
||||
readonly source: string;
|
||||
|
||||
getHoverPosition(): HoverPosition;
|
||||
trackActiveEditorSession(): boolean;
|
||||
}
|
||||
|
||||
export const enum AgentSessionsControlSource {
|
||||
ChatViewPane = 'chatViewPane',
|
||||
WelcomeView = 'welcomeView'
|
||||
notifySessionOpened?(resource: URI, widget: IChatWidget): void;
|
||||
}
|
||||
|
||||
type AgentSessionOpenedClassification = {
|
||||
@ -57,12 +55,14 @@ type AgentSessionOpenedClassification = {
|
||||
|
||||
type AgentSessionOpenedEvent = {
|
||||
providerType: string;
|
||||
source: AgentSessionsControlSource;
|
||||
source: string;
|
||||
};
|
||||
|
||||
export class AgentSessionsControl extends Disposable implements IAgentSessionsControl {
|
||||
|
||||
private sessionsContainer: HTMLElement | undefined;
|
||||
get element(): HTMLElement | undefined { return this.sessionsContainer; }
|
||||
|
||||
private sessionsList: WorkbenchCompressibleAsyncDataTree<IAgentSessionsModel, AgentSessionListItem, FuzzyScore> | undefined;
|
||||
|
||||
private visible: boolean = true;
|
||||
@ -213,7 +213,10 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo
|
||||
source: this.options.source
|
||||
});
|
||||
|
||||
await this.instantiationService.invokeFunction(openSession, element, { ...e, expanded: this.options.source === AgentSessionsControlSource.WelcomeView });
|
||||
const widget = await this.instantiationService.invokeFunction(openSession, element, e);
|
||||
if (widget) {
|
||||
this.options.notifySessionOpened?.(element.resource, widget);
|
||||
}
|
||||
}
|
||||
|
||||
private async showContextMenu({ element, anchor, browserEvent }: ITreeContextMenuEvent<AgentSessionListItem>): Promise<void> {
|
||||
@ -358,10 +361,4 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo
|
||||
this.sessionsList.setFocus([session]);
|
||||
this.sessionsList.setSelection([session]);
|
||||
}
|
||||
|
||||
setGridMarginOffset(offset: number): void {
|
||||
if (this.sessionsContainer) {
|
||||
this.sessionsContainer.style.marginBottom = `-${offset}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,39 +3,65 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { IAgentSession, isLocalAgentSessionItem } from './agentSessionsModel.js';
|
||||
import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js';
|
||||
import { IChatEditorOptions } from '../widgetHosts/editor/chatEditor.js';
|
||||
import { ChatViewPaneTarget, IChatWidgetService } from '../chat.js';
|
||||
import { ChatViewPaneTarget, IChatWidget, IChatWidgetService } from '../chat.js';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from '../../../../services/editor/common/editorService.js';
|
||||
import { IEditorOptions } from '../../../../../platform/editor/common/editor.js';
|
||||
import { IChatSessionsService } from '../../common/chatSessionsService.js';
|
||||
import { Schemas } from '../../../../../base/common/network.js';
|
||||
import { IAgentSessionProjectionService } from './agentSessionProjectionService.js';
|
||||
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
|
||||
import { ChatConfiguration } from '../../common/constants.js';
|
||||
|
||||
export async function openSession(accessor: ServicesAccessor, session: IAgentSession, openOptions?: { sideBySide?: boolean; editorOptions?: IEditorOptions; expanded?: boolean }): Promise<void> {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const projectionService = accessor.get(IAgentSessionProjectionService);
|
||||
//#region Session Opener Registry
|
||||
|
||||
session.setRead(true); // mark as read when opened
|
||||
export interface ISessionOpenerParticipant {
|
||||
handleOpenSession(accessor: ServicesAccessor, session: IAgentSession, openOptions?: ISessionOpenOptions): Promise<boolean>;
|
||||
}
|
||||
|
||||
const agentSessionProjectionEnabled = configurationService.getValue<boolean>(ChatConfiguration.AgentSessionProjectionEnabled) === true;
|
||||
if (agentSessionProjectionEnabled) {
|
||||
// Enter Agent Session Projection mode for the session
|
||||
await projectionService.enterProjection(session);
|
||||
} else {
|
||||
// Fall back to opening in chat widget when Agent Session Projection is disabled
|
||||
await openSessionInChatWidget(accessor, session, openOptions);
|
||||
export interface ISessionOpenOptions {
|
||||
readonly sideBySide?: boolean;
|
||||
readonly editorOptions?: IEditorOptions;
|
||||
}
|
||||
|
||||
class SessionOpenerRegistry {
|
||||
|
||||
private readonly participants = new Set<ISessionOpenerParticipant>();
|
||||
|
||||
registerParticipant(participant: ISessionOpenerParticipant): IDisposable {
|
||||
this.participants.add(participant);
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
this.participants.delete(participant);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getParticipants(): readonly ISessionOpenerParticipant[] {
|
||||
return Array.from(this.participants);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a session in the traditional chat widget (side panel or editor).
|
||||
* Use this when you explicitly want to open in the chat widget rather than agent session projection mode.
|
||||
*/
|
||||
export async function openSessionInChatWidget(accessor: ServicesAccessor, session: IAgentSession, openOptions?: { sideBySide?: boolean; editorOptions?: IEditorOptions; expanded?: boolean }): Promise<void> {
|
||||
export const sessionOpenerRegistry = new SessionOpenerRegistry();
|
||||
|
||||
//#endregion
|
||||
|
||||
export async function openSession(accessor: ServicesAccessor, session: IAgentSession, openOptions?: ISessionOpenOptions): Promise<IChatWidget | undefined> {
|
||||
|
||||
// First, give registered participants a chance to handle the session
|
||||
for (const participant of sessionOpenerRegistry.getParticipants()) {
|
||||
const handled = await participant.handleOpenSession(accessor, session, openOptions);
|
||||
if (handled) {
|
||||
return undefined; // Participant handled the session, skip default opening
|
||||
}
|
||||
}
|
||||
|
||||
// Default session opening logic
|
||||
return openSessionDefault(accessor, session, openOptions);
|
||||
}
|
||||
|
||||
async function openSessionDefault(accessor: ServicesAccessor, session: IAgentSession, openOptions?: ISessionOpenOptions): Promise<IChatWidget | undefined> {
|
||||
const chatSessionsService = accessor.get(IChatSessionsService);
|
||||
const chatWidgetService = accessor.get(IChatWidgetService);
|
||||
|
||||
@ -52,7 +78,6 @@ export async function openSessionInChatWidget(accessor: ServicesAccessor, sessio
|
||||
...sessionOptions,
|
||||
...openOptions?.editorOptions,
|
||||
revealIfOpened: true, // always try to reveal if already opened
|
||||
expanded: openOptions?.expanded
|
||||
};
|
||||
|
||||
await chatSessionsService.activateChatSessionItemProvider(session.providerType); // ensure provider is activated before trying to open
|
||||
@ -70,5 +95,5 @@ export async function openSessionInChatWidget(accessor: ServicesAccessor, sessio
|
||||
options = { ...options, revealIfOpened: true };
|
||||
}
|
||||
|
||||
await chatWidgetService.openSession(session.resource, target, options);
|
||||
return chatWidgetService.openSession(session.resource, target, options);
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from '../../../../../../nls.js';
|
||||
import { RawContextKey } from '../../../../../../platform/contextkey/common/contextkey.js';
|
||||
|
||||
export const inAgentSessionProjection = new RawContextKey<boolean>('chatInAgentSessionProjection', false, { type: 'boolean', description: localize('chatInAgentSessionProjection', "True when the workbench is in agent session projection mode for reviewing an agent session.") });
|
||||
@ -3,20 +3,21 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize, localize2 } from '../../../../../nls.js';
|
||||
import { Action2 } from '../../../../../platform/actions/common/actions.js';
|
||||
import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js';
|
||||
import { KeyCode } from '../../../../../base/common/keyCodes.js';
|
||||
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';
|
||||
import { ChatConfiguration } from '../../common/constants.js';
|
||||
import { localize, localize2 } from '../../../../../../nls.js';
|
||||
import { Action2 } from '../../../../../../platform/actions/common/actions.js';
|
||||
import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { KeybindingWeight } from '../../../../../../platform/keybinding/common/keybindingsRegistry.js';
|
||||
import { KeyCode } from '../../../../../../base/common/keyCodes.js';
|
||||
import { ContextKeyExpr } from '../../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { ChatContextKeys } from '../../../common/actions/chatContextKeys.js';
|
||||
import { IAgentSessionProjectionService } from './agentSessionProjectionService.js';
|
||||
import { IAgentSession, isMarshalledAgentSessionContext, IMarshalledAgentSessionContext } from './agentSessionsModel.js';
|
||||
import { IAgentSessionsService } from './agentSessionsService.js';
|
||||
import { CHAT_CATEGORY } from '../actions/chatActions.js';
|
||||
import { ToggleTitleBarConfigAction } from '../../../../browser/parts/titlebar/titlebarActions.js';
|
||||
import { IsCompactTitleBarContext } from '../../../../common/contextkeys.js';
|
||||
import { IAgentSession, isMarshalledAgentSessionContext, IMarshalledAgentSessionContext } from '../agentSessionsModel.js';
|
||||
import { IAgentSessionsService } from '../agentSessionsService.js';
|
||||
import { CHAT_CATEGORY } from '../../actions/chatActions.js';
|
||||
import { ToggleTitleBarConfigAction } from '../../../../../browser/parts/titlebar/titlebarActions.js';
|
||||
import { IsCompactTitleBarContext } from '../../../../../common/contextkeys.js';
|
||||
import { inAgentSessionProjection } from './agentSessionProjection.js';
|
||||
import { ChatConfiguration } from '../../../common/constants.js';
|
||||
|
||||
//#region Enter Agent Session Projection
|
||||
|
||||
@ -32,7 +33,7 @@ export class EnterAgentSessionProjectionAction extends Action2 {
|
||||
precondition: ContextKeyExpr.and(
|
||||
ChatContextKeys.enabled,
|
||||
ContextKeyExpr.has(`config.${ChatConfiguration.AgentSessionProjectionEnabled}`),
|
||||
ChatContextKeys.inAgentSessionProjection.negate()
|
||||
inAgentSessionProjection.negate()
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -71,12 +72,12 @@ export class ExitAgentSessionProjectionAction extends Action2 {
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(
|
||||
ChatContextKeys.enabled,
|
||||
ChatContextKeys.inAgentSessionProjection
|
||||
inAgentSessionProjection
|
||||
),
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.Escape,
|
||||
when: ChatContextKeys.inAgentSessionProjection,
|
||||
when: inAgentSessionProjection,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -3,28 +3,29 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import './media/agentSessionProjection.css';
|
||||
|
||||
import { Emitter, Event } from '../../../../../base/common/event.js';
|
||||
import { Disposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { localize } from '../../../../../nls.js';
|
||||
import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
|
||||
import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { ILogService } from '../../../../../platform/log/common/log.js';
|
||||
import { IEditorGroupsService, IEditorWorkingSet } from '../../../../services/editor/common/editorGroupsService.js';
|
||||
import { IEditorService } from '../../../../services/editor/common/editorService.js';
|
||||
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
|
||||
import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';
|
||||
import { IAgentSession } from './agentSessionsModel.js';
|
||||
import { ChatViewPaneTarget, IChatWidgetService } from '../chat.js';
|
||||
import { AgentSessionProviders } from './agentSessions.js';
|
||||
import { IChatSessionsService } from '../../common/chatSessionsService.js';
|
||||
import { ChatConfiguration } from '../../common/constants.js';
|
||||
import { IWorkbenchLayoutService } from '../../../../services/layout/browser/layoutService.js';
|
||||
import { ACTION_ID_NEW_CHAT } from '../actions/chatActions.js';
|
||||
import { IChatEditingService, ModifiedFileEntryState } from '../../common/editing/chatEditingService.js';
|
||||
import { IAgentStatusService } from './agentStatusService.js';
|
||||
import './media/agentsessionprojection.css';
|
||||
import { Emitter, Event } from '../../../../../../base/common/event.js';
|
||||
import { Disposable } from '../../../../../../base/common/lifecycle.js';
|
||||
import { localize } from '../../../../../../nls.js';
|
||||
import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js';
|
||||
import { createDecorator } from '../../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { ILogService } from '../../../../../../platform/log/common/log.js';
|
||||
import { IEditorGroupsService, IEditorWorkingSet } from '../../../../../services/editor/common/editorGroupsService.js';
|
||||
import { IEditorService } from '../../../../../services/editor/common/editorService.js';
|
||||
import { ICommandService } from '../../../../../../platform/commands/common/commands.js';
|
||||
import { IAgentSession } from '../agentSessionsModel.js';
|
||||
import { ChatViewPaneTarget, IChatWidgetService } from '../../chat.js';
|
||||
import { AgentSessionProviders } from '../agentSessions.js';
|
||||
import { IChatSessionsService } from '../../../common/chatSessionsService.js';
|
||||
import { IWorkbenchLayoutService } from '../../../../../services/layout/browser/layoutService.js';
|
||||
import { ACTION_ID_NEW_CHAT } from '../../actions/chatActions.js';
|
||||
import { IChatEditingService, ModifiedFileEntryState } from '../../../common/editing/chatEditingService.js';
|
||||
import { IAgentTitleBarStatusService } from './agentTitleBarStatusService.js';
|
||||
import { ISessionOpenerParticipant, ISessionOpenOptions, sessionOpenerRegistry } from '../agentSessionsOpener.js';
|
||||
import { ServicesAccessor } from '../../../../../../editor/browser/editorExtensions.js';
|
||||
import { inAgentSessionProjection } from './agentSessionProjection.js';
|
||||
import { ChatConfiguration } from '../../../common/constants.js';
|
||||
|
||||
//#region Configuration
|
||||
|
||||
@ -113,14 +114,34 @@ export class AgentSessionProjectionService extends Disposable implements IAgentS
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@IChatEditingService private readonly chatEditingService: IChatEditingService,
|
||||
@IAgentStatusService private readonly agentStatusService: IAgentStatusService,
|
||||
@IAgentTitleBarStatusService private readonly agentTitleBarStatusService: IAgentTitleBarStatusService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._inProjectionModeContextKey = ChatContextKeys.inAgentSessionProjection.bindTo(contextKeyService);
|
||||
this._inProjectionModeContextKey = inAgentSessionProjection.bindTo(contextKeyService);
|
||||
|
||||
// Listen for editor close events to exit projection mode when all editors are closed
|
||||
this._register(this.editorService.onDidCloseEditor(() => this._checkForEmptyEditors()));
|
||||
|
||||
// Register as a session opener participant to enter projection mode when sessions are opened
|
||||
this._register(sessionOpenerRegistry.registerParticipant(this._createSessionOpenerParticipant()));
|
||||
}
|
||||
|
||||
private _createSessionOpenerParticipant(): ISessionOpenerParticipant {
|
||||
return {
|
||||
handleOpenSession: async (_accessor: ServicesAccessor, session: IAgentSession, _openOptions?: ISessionOpenOptions): Promise<boolean> => {
|
||||
// Only handle if projection mode is enabled
|
||||
if (!this._isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enter projection mode for the session
|
||||
await this.enterProjection(session);
|
||||
|
||||
// Return true to indicate we handled the session (projection mode opens the chat itself)
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private _isEnabled(): boolean {
|
||||
@ -260,7 +281,7 @@ export class AgentSessionProjectionService extends Disposable implements IAgentS
|
||||
this.layoutService.mainContainer.classList.add('agent-session-projection-active');
|
||||
|
||||
// Update the agent status to show session mode
|
||||
this.agentStatusService.enterSessionMode(session.resource.toString(), session.label);
|
||||
this.agentTitleBarStatusService.enterSessionMode(session.resource.toString(), session.label);
|
||||
|
||||
if (!wasActive) {
|
||||
this._onDidChangeProjectionMode.fire(true);
|
||||
@ -316,7 +337,7 @@ export class AgentSessionProjectionService extends Disposable implements IAgentS
|
||||
this.layoutService.mainContainer.classList.remove('agent-session-projection-active');
|
||||
|
||||
// Update the agent status to exit session mode
|
||||
this.agentStatusService.exitSessionMode();
|
||||
this.agentTitleBarStatusService.exitSessionMode();
|
||||
|
||||
this._onDidChangeProjectionMode.fire(false);
|
||||
this._onDidChangeActiveSession.fire(undefined);
|
||||
@ -0,0 +1,48 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { registerSingleton, InstantiationType } from '../../../../../../platform/instantiation/common/extensions.js';
|
||||
import { MenuId, MenuRegistry, registerAction2 } from '../../../../../../platform/actions/common/actions.js';
|
||||
import { IAgentSessionProjectionService, AgentSessionProjectionService } from './agentSessionProjectionService.js';
|
||||
import { EnterAgentSessionProjectionAction, ExitAgentSessionProjectionAction, ToggleAgentStatusAction, ToggleAgentSessionProjectionAction } from './agentSessionProjectionActions.js';
|
||||
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../../../common/contributions.js';
|
||||
import { AgentTitleBarStatusRendering } from './agentTitleBarStatusWidget.js';
|
||||
import { AgentTitleBarStatusService, IAgentTitleBarStatusService } from './agentTitleBarStatusService.js';
|
||||
import { Codicon } from '../../../../../../base/common/codicons.js';
|
||||
import { localize } from '../../../../../../nls.js';
|
||||
import { ContextKeyExpr } from '../../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { ChatConfiguration } from '../../../common/constants.js';
|
||||
|
||||
// #region Agent Session Projection & Status
|
||||
|
||||
registerAction2(EnterAgentSessionProjectionAction);
|
||||
registerAction2(ExitAgentSessionProjectionAction);
|
||||
registerAction2(ToggleAgentStatusAction);
|
||||
registerAction2(ToggleAgentSessionProjectionAction);
|
||||
|
||||
registerSingleton(IAgentSessionProjectionService, AgentSessionProjectionService, InstantiationType.Delayed);
|
||||
registerSingleton(IAgentTitleBarStatusService, AgentTitleBarStatusService, InstantiationType.Delayed);
|
||||
|
||||
registerWorkbenchContribution2(AgentTitleBarStatusRendering.ID, AgentTitleBarStatusRendering, WorkbenchPhase.AfterRestored);
|
||||
|
||||
// Register Agent Status as a menu item in the command center (alongside the search box, not replacing it)
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandCenter, {
|
||||
submenu: MenuId.AgentsTitleBarControlMenu,
|
||||
title: localize('agentsControl', "Agents"),
|
||||
icon: Codicon.chatSparkle,
|
||||
when: ContextKeyExpr.has(`config.${ChatConfiguration.AgentStatusEnabled}`),
|
||||
order: 10002 // to the right of the chat button
|
||||
});
|
||||
|
||||
// Register a placeholder action to the submenu so it appears (required for submenus)
|
||||
MenuRegistry.appendMenuItem(MenuId.AgentsTitleBarControlMenu, {
|
||||
command: {
|
||||
id: 'workbench.action.chat.toggle',
|
||||
title: localize('openChat', "Open Chat"),
|
||||
},
|
||||
when: ContextKeyExpr.has(`config.${ChatConfiguration.AgentStatusEnabled}`),
|
||||
});
|
||||
|
||||
//#endregion
|
||||
@ -3,9 +3,9 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from '../../../../../base/common/event.js';
|
||||
import { Disposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { Emitter, Event } from '../../../../../../base/common/event.js';
|
||||
import { Disposable } from '../../../../../../base/common/lifecycle.js';
|
||||
import { createDecorator } from '../../../../../../platform/instantiation/common/instantiation.js';
|
||||
|
||||
//#region Agent Status Mode
|
||||
|
||||
@ -25,7 +25,7 @@ export interface IAgentStatusSessionInfo {
|
||||
|
||||
//#region Agent Status Service Interface
|
||||
|
||||
export interface IAgentStatusService {
|
||||
export interface IAgentTitleBarStatusService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
/**
|
||||
@ -66,13 +66,13 @@ export interface IAgentStatusService {
|
||||
updateSessionTitle(title: string): void;
|
||||
}
|
||||
|
||||
export const IAgentStatusService = createDecorator<IAgentStatusService>('agentStatusService');
|
||||
export const IAgentTitleBarStatusService = createDecorator<IAgentTitleBarStatusService>('agentTitleBarStatusService');
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Agent Status Service Implementation
|
||||
|
||||
export class AgentStatusService extends Disposable implements IAgentStatusService {
|
||||
export class AgentTitleBarStatusService extends Disposable implements IAgentTitleBarStatusService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
@ -3,39 +3,44 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import './media/agentStatusWidget.css';
|
||||
|
||||
import { $, addDisposableListener, EventType, reset } from '../../../../../base/browser/dom.js';
|
||||
import { renderIcon } from '../../../../../base/browser/ui/iconLabel/iconLabels.js';
|
||||
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
|
||||
import { Codicon } from '../../../../../base/common/codicons.js';
|
||||
import { localize } from '../../../../../nls.js';
|
||||
import { IHoverService } from '../../../../../platform/hover/browser/hover.js';
|
||||
import { getDefaultHoverDelegate } from '../../../../../base/browser/ui/hover/hoverDelegateFactory.js';
|
||||
import { AgentStatusMode, IAgentStatusService } from './agentStatusService.js';
|
||||
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
|
||||
import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';
|
||||
import './media/agenttitlebarstatuswidget.css';
|
||||
import { $, addDisposableListener, EventType, reset } from '../../../../../../base/browser/dom.js';
|
||||
import { renderIcon } from '../../../../../../base/browser/ui/iconLabel/iconLabels.js';
|
||||
import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js';
|
||||
import { Codicon } from '../../../../../../base/common/codicons.js';
|
||||
import { localize } from '../../../../../../nls.js';
|
||||
import { IHoverService } from '../../../../../../platform/hover/browser/hover.js';
|
||||
import { getDefaultHoverDelegate } from '../../../../../../base/browser/ui/hover/hoverDelegateFactory.js';
|
||||
import { AgentStatusMode, IAgentTitleBarStatusService } from './agentTitleBarStatusService.js';
|
||||
import { ICommandService } from '../../../../../../platform/commands/common/commands.js';
|
||||
import { IKeybindingService } from '../../../../../../platform/keybinding/common/keybinding.js';
|
||||
import { ExitAgentSessionProjectionAction } from './agentSessionProjectionActions.js';
|
||||
import { IAgentSessionsService } from './agentSessionsService.js';
|
||||
import { AgentSessionStatus, IAgentSession, isSessionInProgressStatus } from './agentSessionsModel.js';
|
||||
import { BaseActionViewItem, IBaseActionViewItemOptions } from '../../../../../base/browser/ui/actionbar/actionViewItems.js';
|
||||
import { IAction, SubmenuAction } from '../../../../../base/common/actions.js';
|
||||
import { ILabelService } from '../../../../../platform/label/common/label.js';
|
||||
import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';
|
||||
import { IBrowserWorkbenchEnvironmentService } from '../../../../services/environment/browser/environmentService.js';
|
||||
import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js';
|
||||
import { IEditorService } from '../../../../services/editor/common/editorService.js';
|
||||
import { Verbosity } from '../../../../common/editor.js';
|
||||
import { Schemas } from '../../../../../base/common/network.js';
|
||||
import { renderAsPlaintext } from '../../../../../base/browser/markdownRenderer.js';
|
||||
import { openSession } from './agentSessionsOpener.js';
|
||||
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IMenuService, MenuId } from '../../../../../platform/actions/common/actions.js';
|
||||
import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { HiddenItemStrategy, WorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js';
|
||||
import { createActionViewItem } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
|
||||
import { FocusAgentSessionsAction } from './agentSessionsActions.js';
|
||||
import { IAgentSessionsService } from '../agentSessionsService.js';
|
||||
import { AgentSessionStatus, IAgentSession, isSessionInProgressStatus } from '../agentSessionsModel.js';
|
||||
import { BaseActionViewItem, IBaseActionViewItemOptions } from '../../../../../../base/browser/ui/actionbar/actionViewItems.js';
|
||||
import { IAction, SubmenuAction } from '../../../../../../base/common/actions.js';
|
||||
import { ILabelService } from '../../../../../../platform/label/common/label.js';
|
||||
import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js';
|
||||
import { IBrowserWorkbenchEnvironmentService } from '../../../../../services/environment/browser/environmentService.js';
|
||||
import { IEditorGroupsService } from '../../../../../services/editor/common/editorGroupsService.js';
|
||||
import { IEditorService } from '../../../../../services/editor/common/editorService.js';
|
||||
import { Verbosity } from '../../../../../common/editor.js';
|
||||
import { Schemas } from '../../../../../../base/common/network.js';
|
||||
import { renderAsPlaintext } from '../../../../../../base/browser/markdownRenderer.js';
|
||||
import { openSession } from '../agentSessionsOpener.js';
|
||||
import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IMenuService, MenuId, SubmenuItemAction } from '../../../../../../platform/actions/common/actions.js';
|
||||
import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { HiddenItemStrategy, WorkbenchToolBar } from '../../../../../../platform/actions/browser/toolbar.js';
|
||||
import { createActionViewItem } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../../../platform/storage/common/storage.js';
|
||||
import { FocusAgentSessionsAction } from '../agentSessionsActions.js';
|
||||
import { IWorkbenchContribution } from '../../../../../common/contributions.js';
|
||||
import { IActionViewItemService } from '../../../../../../platform/actions/browser/actionViewItemService.js';
|
||||
import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js';
|
||||
import { mainWindow } from '../../../../../../base/browser/window.js';
|
||||
import { LayoutSettings } from '../../../../../services/layout/browser/layoutService.js';
|
||||
import { ChatConfiguration } from '../../../common/constants.js';
|
||||
|
||||
// Action triggered when clicking the main pill - change this to modify the primary action
|
||||
const ACTION_ID = 'workbench.action.quickchat.toggle';
|
||||
@ -53,7 +58,7 @@ const TITLE_DIRTY = '\u25cf ';
|
||||
*
|
||||
* The command center search box and navigation controls remain visible alongside this control.
|
||||
*/
|
||||
export class AgentStatusWidget extends BaseActionViewItem {
|
||||
export class AgentTitleBarStatusWidget extends BaseActionViewItem {
|
||||
|
||||
private static readonly _quickOpenCommandId = 'workbench.action.quickOpenWithModes';
|
||||
|
||||
@ -73,7 +78,7 @@ export class AgentStatusWidget extends BaseActionViewItem {
|
||||
action: IAction,
|
||||
options: IBaseActionViewItemOptions | undefined,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IAgentStatusService private readonly agentStatusService: IAgentStatusService,
|
||||
@IAgentTitleBarStatusService private readonly agentTitleBarStatusService: IAgentTitleBarStatusService,
|
||||
@IHoverService private readonly hoverService: IHoverService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@ -93,11 +98,11 @@ export class AgentStatusWidget extends BaseActionViewItem {
|
||||
this._commandCenterMenu = this._register(this.menuService.createMenu(MenuId.CommandCenterCenter, this.contextKeyService));
|
||||
|
||||
// Re-render when control mode or session info changes
|
||||
this._register(this.agentStatusService.onDidChangeMode(() => {
|
||||
this._register(this.agentTitleBarStatusService.onDidChangeMode(() => {
|
||||
this._render();
|
||||
}));
|
||||
|
||||
this._register(this.agentStatusService.onDidChangeSessionInfo(() => {
|
||||
this._register(this.agentTitleBarStatusService.onDidChangeSessionInfo(() => {
|
||||
this._render();
|
||||
}));
|
||||
|
||||
@ -140,8 +145,8 @@ export class AgentStatusWidget extends BaseActionViewItem {
|
||||
}
|
||||
|
||||
// Compute current render state to avoid unnecessary DOM rebuilds
|
||||
const mode = this.agentStatusService.mode;
|
||||
const sessionInfo = this.agentStatusService.sessionInfo;
|
||||
const mode = this.agentTitleBarStatusService.mode;
|
||||
const sessionInfo = this.agentTitleBarStatusService.sessionInfo;
|
||||
const { activeSessions, unreadSessions, attentionNeededSessions } = this._getSessionStats();
|
||||
|
||||
// Get attention session info for state computation
|
||||
@ -184,7 +189,7 @@ export class AgentStatusWidget extends BaseActionViewItem {
|
||||
// Clear previous disposables for dynamic content
|
||||
this._dynamicDisposables.clear();
|
||||
|
||||
if (this.agentStatusService.mode === AgentStatusMode.Session) {
|
||||
if (this.agentTitleBarStatusService.mode === AgentStatusMode.Session) {
|
||||
// Agent Session Projection mode - show session title + close button
|
||||
this._renderSessionMode(this._dynamicDisposables);
|
||||
} else {
|
||||
@ -352,7 +357,7 @@ export class AgentStatusWidget extends BaseActionViewItem {
|
||||
|
||||
// Session title (center)
|
||||
const titleLabel = $('span.agent-status-title');
|
||||
const sessionInfo = this.agentStatusService.sessionInfo;
|
||||
const sessionInfo = this.agentTitleBarStatusService.sessionInfo;
|
||||
titleLabel.textContent = sessionInfo?.title ?? localize('agentSessionProjection', "Agent Session Projection");
|
||||
pill.appendChild(titleLabel);
|
||||
|
||||
@ -362,7 +367,7 @@ export class AgentStatusWidget extends BaseActionViewItem {
|
||||
// Setup pill hover
|
||||
const hoverDelegate = getDefaultHoverDelegate('mouse');
|
||||
disposables.add(this.hoverService.setupManagedHover(hoverDelegate, pill, () => {
|
||||
const sessionInfo = this.agentStatusService.sessionInfo;
|
||||
const sessionInfo = this.agentTitleBarStatusService.sessionInfo;
|
||||
return sessionInfo ? localize('agentSessionProjectionTooltip', "Agent Session Projection: {0}", sessionInfo.title) : localize('agentSessionProjection', "Agent Session Projection");
|
||||
}));
|
||||
|
||||
@ -389,7 +394,7 @@ export class AgentStatusWidget extends BaseActionViewItem {
|
||||
for (const [, actions] of this._commandCenterMenu.getActions({ shouldForwardArgs: true })) {
|
||||
for (const action of actions) {
|
||||
// Filter out the quick open action - we provide our own search UI
|
||||
if (action.id === AgentStatusWidget._quickOpenCommandId) {
|
||||
if (action.id === AgentTitleBarStatusWidget._quickOpenCommandId) {
|
||||
continue;
|
||||
}
|
||||
// For submenus (like debug toolbar), add the submenu actions
|
||||
@ -824,3 +829,47 @@ export class AgentStatusWidget extends BaseActionViewItem {
|
||||
|
||||
// #endregion
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides custom rendering for the agent status in the command center.
|
||||
* Uses IActionViewItemService to render a custom AgentStatusWidget
|
||||
* for the AgentsControlMenu submenu.
|
||||
* Also adds a CSS class to the workbench when agent status is enabled.
|
||||
*/
|
||||
export class AgentTitleBarStatusRendering extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
static readonly ID = 'workbench.contrib.agentStatus.rendering';
|
||||
|
||||
constructor(
|
||||
@IActionViewItemService actionViewItemService: IActionViewItemService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(actionViewItemService.register(MenuId.CommandCenter, MenuId.AgentsTitleBarControlMenu, (action, options) => {
|
||||
if (!(action instanceof SubmenuItemAction)) {
|
||||
return undefined;
|
||||
}
|
||||
return instantiationService.createInstance(AgentTitleBarStatusWidget, action, options);
|
||||
}, undefined));
|
||||
|
||||
// Add/remove CSS class on workbench based on setting
|
||||
// Also force enable command center when agent status is enabled
|
||||
const updateClass = () => {
|
||||
const enabled = configurationService.getValue<boolean>(ChatConfiguration.AgentStatusEnabled) === true;
|
||||
mainWindow.document.body.classList.toggle('agent-status-enabled', enabled);
|
||||
|
||||
// Force enable command center when agent status is enabled
|
||||
if (enabled && configurationService.getValue<boolean>(LayoutSettings.COMMAND_CENTER) !== true) {
|
||||
configurationService.updateValue(LayoutSettings.COMMAND_CENTER, true);
|
||||
}
|
||||
};
|
||||
updateClass();
|
||||
this._register(configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(ChatConfiguration.AgentStatusEnabled)) {
|
||||
updateClass();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -3,10 +3,6 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* ========================================
|
||||
Agent Session Projection Mode - Tab and Editor styling
|
||||
======================================== */
|
||||
|
||||
/* Style all tabs with the same background as the agent status */
|
||||
.monaco-workbench.agent-session-projection-active .part.editor > .content .editor-group-container > .title .tabs-container > .tab {
|
||||
background-color: color-mix(in srgb, var(--vscode-progressBar-background) 15%, transparent) !important;
|
||||
@ -3,10 +3,6 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* ========================================
|
||||
Agent Status Widget - Titlebar control
|
||||
======================================== */
|
||||
|
||||
/* Hide command center search box when agent status enabled */
|
||||
.agent-status-enabled .command-center .action-item.command-center-center {
|
||||
display: none !important;
|
||||
@ -19,7 +19,6 @@ import { ChatViewId, ChatViewPaneTarget, IChatWidget, IChatWidgetService, IQuick
|
||||
import { ChatEditor, IChatEditorOptions } from '../widgetHosts/editor/chatEditor.js';
|
||||
import { ChatEditorInput } from '../widgetHosts/editor/chatEditorInput.js';
|
||||
import { ChatViewPane } from '../widgetHosts/viewPane/chatViewPane.js';
|
||||
import { IWorkbenchLayoutService } from '../../../../services/layout/browser/layoutService.js';
|
||||
|
||||
export class ChatWidgetService extends Disposable implements IChatWidgetService {
|
||||
|
||||
@ -41,7 +40,6 @@ export class ChatWidgetService extends Disposable implements IChatWidgetService
|
||||
@ILayoutService private readonly layoutService: ILayoutService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IChatService private readonly chatService: IChatService,
|
||||
@IWorkbenchLayoutService private readonly workbenchLayoutService: IWorkbenchLayoutService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -118,9 +116,6 @@ export class ChatWidgetService extends Disposable implements IChatWidgetService
|
||||
if (!options?.preserveFocus) {
|
||||
chatView.focusInput();
|
||||
}
|
||||
if (options?.expanded) {
|
||||
this.workbenchLayoutService.setAuxiliaryBarMaximized(true);
|
||||
}
|
||||
}
|
||||
return chatView?.widget;
|
||||
}
|
||||
|
||||
@ -43,7 +43,6 @@ export interface IChatEditorOptions extends IEditorOptions {
|
||||
preferred?: string;
|
||||
fallback?: string;
|
||||
};
|
||||
expanded?: boolean;
|
||||
}
|
||||
|
||||
export class ChatEditor extends EditorPane {
|
||||
|
||||
@ -47,7 +47,7 @@ import { IChatModelReference, IChatService } from '../../../common/chatService/c
|
||||
import { IChatSessionsService, localChatSessionType } from '../../../common/chatSessionsService.js';
|
||||
import { LocalChatSessionUri, getChatSessionType } from '../../../common/model/chatUri.js';
|
||||
import { ChatAgentLocation, ChatConfiguration, ChatModeKind } from '../../../common/constants.js';
|
||||
import { AgentSessionsControl, AgentSessionsControlSource } from '../../agentSessions/agentSessionsControl.js';
|
||||
import { AgentSessionsControl } from '../../agentSessions/agentSessionsControl.js';
|
||||
import { AgentSessionsListDelegate } from '../../agentSessions/agentSessionsViewer.js';
|
||||
import { ACTION_ID_NEW_CHAT } from '../../actions/chatActions.js';
|
||||
import { ChatWidget } from '../../widget/chatWidget.js';
|
||||
@ -394,7 +394,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate {
|
||||
// Sessions Control
|
||||
this.sessionsControlContainer = append(sessionsContainer, $('.agent-sessions-control-container'));
|
||||
const sessionsControl = this.sessionsControl = this._register(this.instantiationService.createInstance(AgentSessionsControl, this.sessionsControlContainer, {
|
||||
source: AgentSessionsControlSource.ChatViewPane,
|
||||
source: 'chatViewPane',
|
||||
filter: sessionsFilter,
|
||||
overrideStyles: this.getLocationBasedColors().listOverrideStyles,
|
||||
getHoverPosition: () => {
|
||||
|
||||
@ -108,10 +108,6 @@ export namespace ChatContextKeys {
|
||||
export const hasAgentSessionChanges = new RawContextKey<boolean>('agentSessionHasChanges', false, { type: 'boolean', description: localize('agentSessionHasChanges', "True when the current agent session item has changes.") });
|
||||
|
||||
export const isKatexMathElement = new RawContextKey<boolean>('chatIsKatexMathElement', false, { type: 'boolean', description: localize('chatIsKatexMathElement', "True when focusing a KaTeX math element.") });
|
||||
|
||||
export const inAgentSessionProjection = new RawContextKey<boolean>('chatInAgentSessionProjection', false, { type: 'boolean', description: localize('chatInAgentSessionProjection', "True when the workbench is in agent session projection mode for reviewing an agent session.") });
|
||||
|
||||
export const agentStatusHasNotifications = new RawContextKey<boolean>('agentStatusHasNotifications', false, { type: 'boolean', description: localize('agentStatusHasNotifications', "True when the agent status widget has unread or in-progress sessions.") });
|
||||
}
|
||||
|
||||
export namespace ChatContextKeyExprs {
|
||||
|
||||
@ -40,7 +40,7 @@ import { AgentSessionsWelcomeEditorOptions, AgentSessionsWelcomeInput } from './
|
||||
import { IChatService } from '../../chat/common/chatService/chatService.js';
|
||||
import { IChatModel } from '../../chat/common/model/chatModel.js';
|
||||
import { ISessionTypePickerDelegate } from '../../chat/browser/chat.js';
|
||||
import { AgentSessionsControl, AgentSessionsControlSource, IAgentSessionsControlOptions } from '../../chat/browser/agentSessions/agentSessionsControl.js';
|
||||
import { AgentSessionsControl, IAgentSessionsControlOptions } from '../../chat/browser/agentSessions/agentSessionsControl.js';
|
||||
import { IAgentSessionsFilter } from '../../chat/browser/agentSessions/agentSessionsViewer.js';
|
||||
import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js';
|
||||
import { IResolvedWalkthrough, IWalkthroughsService } from '../../welcomeGettingStarted/browser/gettingStartedService.js';
|
||||
@ -280,7 +280,8 @@ export class AgentSessionsWelcomePage extends EditorPane {
|
||||
filter,
|
||||
getHoverPosition: () => HoverPosition.BELOW,
|
||||
trackActiveEditorSession: () => false,
|
||||
source: AgentSessionsControlSource.WelcomeView,
|
||||
source: 'welcomeView',
|
||||
notifySessionOpened: () => this.layoutService.setAuxiliaryBarMaximized(true) // TODO@osortega what if the session did not open in the 2nd sidebar?
|
||||
};
|
||||
|
||||
this.sessionsControl = this.sessionsControlDisposables.add(this.instantiationService.createInstance(
|
||||
@ -433,7 +434,7 @@ export class AgentSessionsWelcomePage extends EditorPane {
|
||||
// Set margin offset for 2-column layout: actual height - visual height
|
||||
// Visual height = ceil(n/2) * 52, so offset = floor(n/2) * 52
|
||||
const marginOffset = Math.floor(visibleSessions / 2) * 52;
|
||||
this.sessionsControl.setGridMarginOffset(marginOffset);
|
||||
this.sessionsControl.element!.style.marginBottom = `-${marginOffset}px`;
|
||||
}
|
||||
|
||||
override focus(): void {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user