Merge pull request #289060 from microsoft/benibenj/head-dove

Codex agent in agent type picker
This commit is contained in:
Benjamin Christopher Simmonds
2026-01-20 11:34:31 +01:00
committed by GitHub
4 changed files with 80 additions and 16 deletions
@@ -15,7 +15,8 @@ export enum AgentSessionProviders {
Local = localChatSessionType,
Background = 'copilotcli',
Cloud = 'copilot-cloud-agent',
ClaudeCode = 'claude-code',
Claude = 'claude-code',
Codex = 'openai-codex',
}
export function getAgentSessionProvider(sessionResource: URI | string): AgentSessionProviders | undefined {
@@ -24,7 +25,8 @@ export function getAgentSessionProvider(sessionResource: URI | string): AgentSes
case AgentSessionProviders.Local:
case AgentSessionProviders.Background:
case AgentSessionProviders.Cloud:
case AgentSessionProviders.ClaudeCode:
case AgentSessionProviders.Claude:
case AgentSessionProviders.Codex:
return type;
default:
return undefined;
@@ -39,8 +41,10 @@ export function getAgentSessionProviderName(provider: AgentSessionProviders): st
return localize('chat.session.providerLabel.background', "Background");
case AgentSessionProviders.Cloud:
return localize('chat.session.providerLabel.cloud', "Cloud");
case AgentSessionProviders.ClaudeCode:
return localize('chat.session.providerLabel.claude', "Claude");
case AgentSessionProviders.Claude:
return 'Claude';
case AgentSessionProviders.Codex:
return 'Codex';
}
}
@@ -52,7 +56,8 @@ export function getAgentSessionProviderIcon(provider: AgentSessionProviders): Th
return Codicon.worktree;
case AgentSessionProviders.Cloud:
return Codicon.cloud;
case AgentSessionProviders.ClaudeCode:
case AgentSessionProviders.Codex:
case AgentSessionProviders.Claude:
return Codicon.code;
}
}
@@ -63,7 +68,20 @@ export function isFirstPartyAgentSessionProvider(provider: AgentSessionProviders
case AgentSessionProviders.Background:
case AgentSessionProviders.Cloud:
return true;
case AgentSessionProviders.ClaudeCode:
case AgentSessionProviders.Claude:
case AgentSessionProviders.Codex:
return false;
}
}
export function getAgentCanContinueIn(provider: AgentSessionProviders): boolean {
switch (provider) {
case AgentSessionProviders.Local:
case AgentSessionProviders.Background:
case AgentSessionProviders.Cloud:
return true;
case AgentSessionProviders.Claude:
case AgentSessionProviders.Codex:
return false;
}
}
@@ -49,6 +49,7 @@ import { BugIndicatingError } from '../../../../../base/common/errors.js';
import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js';
import { LocalChatSessionUri } from '../../common/model/chatUri.js';
import { assertNever } from '../../../../../base/common/assert.js';
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
const extensionPoint = ExtensionsRegistry.registerExtensionPoint<IChatSessionsExtensionPoint[]>({
extensionPoint: 'chatSessions',
@@ -516,12 +517,18 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
const disposables = new DisposableStore();
// Mirror all create submenu actions into the global Chat New menu
for (const action of menuActions) {
for (let i = 0; i < menuActions.length; i++) {
const action = menuActions[i];
if (action instanceof MenuItemAction) {
disposables.add(MenuRegistry.appendMenuItem(MenuId.ChatNewMenu, {
command: action.item,
group: '4_externally_contributed',
}));
// TODO: This is an odd way to do this, but the best we can do currently
if (i === 0 && !contribution.canDelegate) {
disposables.add(registerNewSessionExternalAction(contribution.type, contribution.displayName, action.item.id));
} else {
disposables.add(MenuRegistry.appendMenuItem(MenuId.ChatNewMenu, {
command: action.item,
group: '4_externally_contributed',
}));
}
}
}
return {
@@ -1125,6 +1132,24 @@ function registerNewSessionInPlaceAction(type: string, displayName: string): IDi
});
}
function registerNewSessionExternalAction(type: string, displayName: string, commandId: string): IDisposable {
return registerAction2(class NewChatSessionExternalAction extends Action2 {
constructor() {
super({
id: `workbench.action.chat.openNewChatSessionExternal.${type}`,
title: localize2('interactiveSession.openNewChatSessionExternal', "New {0}", displayName),
category: CHAT_CATEGORY,
f1: false,
precondition: ChatContextKeys.enabled,
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const commandService = accessor.get(ICommandService);
await commandService.executeCommand(commandId);
}
});
}
enum ChatSessionPosition {
Editor = 'editor',
Sidebar = 'sidebar'
@@ -9,7 +9,7 @@ import { URI } from '../../../../../../base/common/uri.js';
import { localize } from '../../../../../../nls.js';
import { IActionWidgetDropdownAction } from '../../../../../../platform/actionWidget/browser/actionWidgetDropdown.js';
import { ACTION_ID_NEW_CHAT } from '../../actions/chatActions.js';
import { AgentSessionProviders, getAgentSessionProvider } from '../../agentSessions/agentSessions.js';
import { AgentSessionProviders, getAgentCanContinueIn, getAgentSessionProvider, isFirstPartyAgentSessionProvider } from '../../agentSessions/agentSessions.js';
import { ISessionTypeItem, SessionTypePickerActionItem } from './sessionTargetPickerActionItem.js';
/**
@@ -49,8 +49,19 @@ export class DelegationSessionPickerActionItem extends SessionTypePickerActionIt
return this._getSelectedSessionType() !== type; // Always allow switching back to active session
}
protected override _isVisible(type: AgentSessionProviders): boolean {
if (this.delegate.getActiveSessionProvider() === type) {
return true; // Always show active session type
}
return getAgentCanContinueIn(type);
}
protected override _getSessionCategory(sessionTypeItem: ISessionTypeItem) {
return { label: localize('continueIn', "Continue In"), order: 1, showHeader: true };
if (isFirstPartyAgentSessionProvider(sessionTypeItem.type)) {
return { label: localize('continueIn', "Continue In"), order: 1, showHeader: true };
}
return { label: localize('continueInThirdParty', "Continue In (Third Party)"), order: 2, showHeader: false };
}
protected override _getSessionDescription(sessionTypeItem: ISessionTypeItem): string | undefined {
@@ -58,6 +58,10 @@ export class SessionTypePickerActionItem extends ChatInputPickerActionViewItem {
const actions: IActionWidgetDropdownAction[] = [...this._getAdditionalActions()];
for (const sessionTypeItem of this._sessionTypeItems) {
if (!this._isVisible(sessionTypeItem.type)) {
continue;
}
actions.push({
...action,
id: sessionTypeItem.commandId,
@@ -134,14 +138,14 @@ export class SessionTypePickerActionItem extends ChatInputPickerActionViewItem {
}
private _updateAgentSessionItems(): void {
const localSessionItem = {
const localSessionItem: ISessionTypeItem = {
type: AgentSessionProviders.Local,
label: getAgentSessionProviderName(AgentSessionProviders.Local),
description: localize('chat.sessionTarget.local.description', "Local chat session"),
commandId: `workbench.action.chat.openNewChatSessionInPlace.${AgentSessionProviders.Local}`,
};
const agentSessionItems = [localSessionItem];
const agentSessionItems: ISessionTypeItem[] = [localSessionItem];
const contributions = this.chatSessionsService.getAllChatSessionContributions();
for (const contribution of contributions) {
@@ -154,12 +158,18 @@ export class SessionTypePickerActionItem extends ChatInputPickerActionViewItem {
type: agentSessionType,
label: getAgentSessionProviderName(agentSessionType),
description: contribution.description,
commandId: `workbench.action.chat.openNewChatSessionInPlace.${contribution.type}`,
commandId: contribution.canDelegate ?
`workbench.action.chat.openNewChatSessionInPlace.${contribution.type}` :
`workbench.action.chat.openNewChatSessionExternal.${contribution.type}`,
});
}
this._sessionTypeItems = agentSessionItems;
}
protected _isVisible(type: AgentSessionProviders): boolean {
return true;
}
protected _isSessionTypeEnabled(type: AgentSessionProviders): boolean {
return true;
}