agent sessions - expand filter to support read state too (#281792)

This commit is contained in:
Benjamin Pasero
2025-12-07 17:52:00 +01:00
committed by Benjamin Pasero
parent 2a387cb657
commit cf3249c448
3 changed files with 94 additions and 30 deletions

View File

@@ -30,6 +30,15 @@ export interface IMarshalledChatSessionContext {
readonly session: IChatSessionItem;
}
export function isMarshalledChatSessionContext(thing: unknown): thing is IMarshalledChatSessionContext {
if (typeof thing === 'object' && thing !== null) {
const candidate = thing as IMarshalledChatSessionContext;
return candidate.$mid === MarshalledId.ChatSessionContext && typeof candidate.session === 'object' && candidate.session !== null;
}
return false;
}
export class RenameChatSessionAction extends Action2 {
static readonly id = 'workbench.action.chat.renameSession';

View File

@@ -20,7 +20,7 @@ import { AgentSessionsView } from './agentSessionsView.js';
import { URI } from '../../../../../base/common/uri.js';
import { IChatModelReference, IChatService } from '../../common/chatService.js';
import { ChatContextKeys } from '../../common/chatContextKeys.js';
import { IMarshalledChatSessionContext } from '../actions/chatSessionActions.js';
import { IMarshalledChatSessionContext, isMarshalledChatSessionContext } from '../actions/chatSessionActions.js';
import { IChatEditorOptions } from '../chatEditor.js';
import { ChatViewId, IChatWidgetService } from '../chat.js';
import { ACTIVE_GROUP, AUX_WINDOW_GROUP, PreferredGroup, SIDE_GROUP } from '../../../../services/editor/common/editorService.js';
@@ -28,47 +28,78 @@ import { IViewDescriptorService, ViewContainerLocation } from '../../../../commo
import { getPartByLocation } from '../../../../services/views/browser/viewsService.js';
import { IWorkbenchLayoutService } from '../../../../services/layout/browser/layoutService.js';
import { IAgentSessionsService } from './agentSessionsService.js';
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
abstract class BaseAgentSessionAction extends Action2 {
run(accessor: ServicesAccessor, context: IAgentSession | IMarshalledChatSessionContext): void {
const agentSessionsService = accessor.get(IAgentSessionsService);
let session: IAgentSession | undefined;
if (isMarshalledChatSessionContext(context)) {
session = agentSessionsService.getSession(context.session.resource);
} else {
session = context;
}
if (session) {
this.runWithSession(session);
}
}
abstract runWithSession(session: IAgentSession): void;
}
//#region Session Title Actions
export class ArchiveAgentSessionAction extends Action2 {
export class ArchiveAgentSessionAction extends BaseAgentSessionAction {
constructor() {
super({
id: 'agentSession.archive',
title: localize2('archive', "Archive"),
icon: Codicon.archive,
menu: {
menu: [{
id: MenuId.AgentSessionItemToolbar,
group: 'navigation',
order: 1,
when: ChatContextKeys.isArchivedAgentSession.negate(),
}
}, {
id: MenuId.AgentSessionsContext,
group: 'edit',
order: 2,
when: ChatContextKeys.isArchivedAgentSession.negate()
}]
});
}
run(accessor: ServicesAccessor, session: IAgentSession): void {
runWithSession(session: IAgentSession): void {
session.setArchived(true);
}
}
export class UnarchiveAgentSessionAction extends Action2 {
export class UnarchiveAgentSessionAction extends BaseAgentSessionAction {
constructor() {
super({
id: 'agentSession.unarchive',
title: localize2('unarchive', "Unarchive"),
icon: Codicon.unarchive,
menu: {
menu: [{
id: MenuId.AgentSessionItemToolbar,
group: 'navigation',
order: 1,
when: ChatContextKeys.isArchivedAgentSession,
}
}, {
id: MenuId.AgentSessionsContext,
group: 'edit',
order: 2,
when: ChatContextKeys.isArchivedAgentSession,
}]
});
}
run(accessor: ServicesAccessor, session: IAgentSession): void {
runWithSession(session: IAgentSession): void {
session.setArchived(false);
}
}
@@ -279,7 +310,7 @@ export class OpenAgentSessionInNewWindowAction extends BaseOpenAgentSessionActio
}
}
export class MarkAgentSessionUnreadAction extends Action2 {
export class MarkAgentSessionUnreadAction extends BaseAgentSessionAction {
constructor() {
super({
@@ -289,23 +320,20 @@ export class MarkAgentSessionUnreadAction extends Action2 {
id: MenuId.AgentSessionsContext,
group: 'edit',
order: 1,
when: ChatContextKeys.isReadAgentSession,
when: ContextKeyExpr.and(
ChatContextKeys.isReadAgentSession,
ChatContextKeys.isArchivedAgentSession.negate() // no read state for archived sessions
),
}
});
}
run(accessor: ServicesAccessor, context?: IMarshalledChatSessionContext): void {
const agentSessionsService = accessor.get(IAgentSessionsService);
if (!context) {
return;
}
agentSessionsService.getSession(context.session.resource)?.setRead(false);
runWithSession(session: IAgentSession): void {
session.setRead(false);
}
}
export class MarkAgentSessionReadAction extends Action2 {
export class MarkAgentSessionReadAction extends BaseAgentSessionAction {
constructor() {
super({
@@ -315,19 +343,16 @@ export class MarkAgentSessionReadAction extends Action2 {
id: MenuId.AgentSessionsContext,
group: 'edit',
order: 1,
when: ChatContextKeys.isReadAgentSession.negate(),
when: ContextKeyExpr.and(
ChatContextKeys.isReadAgentSession.negate(),
ChatContextKeys.isArchivedAgentSession.negate() // no read state for archived sessions
),
}
});
}
run(accessor: ServicesAccessor, context?: IMarshalledChatSessionContext): void {
const agentSessionsService = accessor.get(IAgentSessionsService);
if (!context) {
return;
}
agentSessionsService.getSession(context.session.resource)?.setRead(true);
runWithSession(session: IAgentSession): void {
session.setRead(true);
}
}

View File

@@ -28,13 +28,16 @@ export interface IAgentSessionsFilterOptions extends Partial<IAgentSessionsFilte
interface IAgentSessionsViewExcludes {
readonly providers: readonly string[];
readonly states: readonly ChatSessionStatus[];
readonly archived: boolean;
readonly read: boolean;
}
const DEFAULT_EXCLUDES: IAgentSessionsViewExcludes = Object.freeze({
providers: [] as const,
states: [] as const,
archived: true as const,
read: false as const,
});
export class AgentSessionsFilter extends Disposable implements Required<IAgentSessionsFilter> {
@@ -95,6 +98,7 @@ export class AgentSessionsFilter extends Disposable implements Required<IAgentSe
providers: [...DEFAULT_EXCLUDES.providers],
states: [...DEFAULT_EXCLUDES.states],
archived: DEFAULT_EXCLUDES.archived,
read: DEFAULT_EXCLUDES.read,
};
}
@@ -110,6 +114,7 @@ export class AgentSessionsFilter extends Disposable implements Required<IAgentSe
this.registerProviderActions(this.actionDisposables);
this.registerStateActions(this.actionDisposables);
this.registerArchivedActions(this.actionDisposables);
this.registerReadActions(this.actionDisposables);
this.registerResetAction(this.actionDisposables);
}
@@ -200,7 +205,7 @@ export class AgentSessionsFilter extends Disposable implements Required<IAgentSe
title: localize('agentSessions.filter.archived', 'Archived'),
menu: {
id: that.options.filterMenuId,
group: '2_states',
group: '3_props',
order: 1000,
},
toggled: that.excludes.archived ? ContextKeyExpr.false() : ContextKeyExpr.true(),
@@ -212,6 +217,27 @@ export class AgentSessionsFilter extends Disposable implements Required<IAgentSe
}));
}
private registerReadActions(disposables: DisposableStore): void {
const that = this;
disposables.add(registerAction2(class extends Action2 {
constructor() {
super({
id: `agentSessions.filter.toggleExcludeRead.${that.options.filterMenuId.id.toLowerCase()}`,
title: localize('agentSessions.filter.read', 'Read'),
menu: {
id: that.options.filterMenuId,
group: '3_props',
order: 0,
},
toggled: that.excludes.read ? ContextKeyExpr.false() : ContextKeyExpr.true(),
});
}
run(): void {
that.storeExcludes({ ...that.excludes, read: !that.excludes.read });
}
}));
}
private registerResetAction(disposables: DisposableStore): void {
const that = this;
disposables.add(registerAction2(class extends Action2 {
@@ -248,6 +274,10 @@ export class AgentSessionsFilter extends Disposable implements Required<IAgentSe
return true;
}
if (this.excludes.read && (session.isArchived() || session.isRead())) {
return true;
}
if (this.excludes.providers.includes(session.providerType)) {
return true;
}