Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/vs/sessions/contrib/chat/browser/newChatViewPane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,16 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
this._newSession.value = session;

// Wire pickers to the new session
this._folderPicker.setNewSession(session);
this._repoPicker.setNewSession(session);
this._isolationModePicker.setNewSession(session);
this._branchPicker.setNewSession(session);
const target = this._targetPicker.selectedTarget;
if (target === AgentSessionProviders.Background) {
this._folderPicker.setNewSession(session);
this._isolationModePicker.setNewSession(session);
this._branchPicker.setNewSession(session);
}

if (target === AgentSessionProviders.Cloud) {
this._repoPicker.setNewSession(session);
}

// Set the current model on the session (for local sessions)
const currentModel = this._currentLanguageModel.get();
Expand Down
3 changes: 2 additions & 1 deletion src/vs/sessions/contrib/chat/browser/newSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ export class RemoteNewSession extends Disposable implements INewSession {
this._repoUri = uri;
this._onDidChange.fire('repoUri');
this._onDidChange.fire('disabled');
this.setOption('repository', uri.fsPath);
const id = uri.path.substring(1);
this.setOption('repositories', { id, name: id });
}

setIsolationMode(_mode: IsolationMode): void {
Expand Down
11 changes: 9 additions & 2 deletions src/vs/sessions/contrib/chat/browser/repoPicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo
import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js';
import { ICommandService } from '../../../../platform/commands/common/commands.js';
import { INewSession } from './newSession.js';
import { URI } from '../../../../base/common/uri.js';
import { GITHUB_REMOTE_FILE_SCHEME } from '../../fileTreeView/browser/githubFileSystemProvider.js';

const OPEN_REPO_COMMAND = 'github.copilot.chat.cloudSessions.openRepository';
const STORAGE_KEY_LAST_REPO = 'agentSessions.lastPickedRepo';
Expand Down Expand Up @@ -82,7 +84,7 @@ export class RepoPicker extends Disposable {
this._newSession = session;
this._browseGeneration++;
if (session && this._selectedRepo) {
session.setOption('repositories', this._selectedRepo);
this._setRepo(this._selectedRepo);
}
}

Expand Down Expand Up @@ -178,7 +180,7 @@ export class RepoPicker extends Disposable {
this._addToRecentlyPicked(item);
this.storageService.store(STORAGE_KEY_LAST_REPO, JSON.stringify(item), StorageScope.PROFILE, StorageTarget.MACHINE);
this._updateTriggerLabel();
this._newSession?.setOption('repositories', item);
this._setRepo(item);
this._onDidSelectRepo.fire(item.id);
}

Expand Down Expand Up @@ -267,4 +269,9 @@ export class RepoPicker extends Disposable {
labelSpan.textContent = label;
dom.append(this._triggerElement, renderIcon(Codicon.chevronDown));
}

private _setRepo(repo: IRepoItem): void {
this._newSession?.setRepoUri(URI.parse(`${GITHUB_REMOTE_FILE_SCHEME}://github/${repo.id}`));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class WorkspaceFolderManagementContribution extends Disposable implements
}

private getActiveSessionFolderData(session: IActiveSessionItem | undefined): IWorkspaceFolderCreationData | undefined {
if (session?.providerType !== AgentSessionProviders.Background) {
if (!session) {
return undefined;
}

Expand All @@ -70,7 +70,14 @@ export class WorkspaceFolderManagementContribution extends Disposable implements
}

if (session.repository) {
return { uri: session.repository };
if (session.providerType === AgentSessionProviders.Background) {
return { uri: session.repository };
}
// if (session.providerType === AgentSessionProviders.Cloud) {
// return {
// uri: session.repository
// };
// }
}

return undefined;
Expand Down
16 changes: 16 additions & 0 deletions src/vs/workbench/api/browser/mainThreadChatAgents2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,22 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
if (newItem) {
chatSessionResource = newItem.resource;
isUntitled = false;

// Update the model's contributed session with the resolved resource
// so subsequent requests don't re-invoke newChatSessionItemHandler
// and getChatSessionFromInternalUri returns the real resource.
chatSession?.setContributedChatSession({
chatSessionType: contributedSession.chatSessionType,
chatSessionResource,
isUntitled: false,
initialSessionOptions: contributedSession.initialSessionOptions,
});

// Register alias so session-option lookups work with the new resource
this._chatSessionService.registerSessionResourceAlias(
contributedSession.chatSessionResource,
chatSessionResource
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,9 +477,10 @@ export class OpenModePickerAction extends Action2 {
ContextKeyExpr.or(
ChatContextKeys.lockedToCodingAgent.negate(),
ChatContextKeys.chatSessionHasCustomAgentTarget),
// Hide in welcome view when session type is not local
// Show in welcome view for local sessions or sessions with custom agent target
ContextKeyExpr.or(
ChatContextKeys.inAgentSessionsWelcome.negate(),
ChatContextKeys.chatSessionHasCustomAgentTarget,
ChatContextKeys.agentSessionType.isEqualTo(AgentSessionProviders.Local))),
group: 'navigation',
},
Expand Down
3 changes: 1 addition & 2 deletions src/vs/workbench/contrib/chat/browser/agentPluginsView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,12 @@ class UninstallPluginAction extends Action {

constructor(
private readonly plugin: IAgentPlugin,
@IPluginInstallService private readonly pluginInstallService: IPluginInstallService,
) {
super(UninstallPluginAction.ID, localize('uninstall', "Uninstall"));
}

override async run(): Promise<void> {
this.pluginInstallService.uninstallPlugin(this.plugin.uri);
this.plugin.remove();
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/contrib/chat/browser/chat.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ import './widget/input/editor/chatInputEditorContrib.js';
import './widget/input/editor/chatInputEditorHover.js';
import { LanguageModelToolsConfirmationService } from './tools/languageModelToolsConfirmationService.js';
import { LanguageModelToolsService, globalAutoApproveDescription } from './tools/languageModelToolsService.js';
import { AgentPluginService, ConfiguredAgentPluginDiscovery } from '../common/plugins/agentPluginServiceImpl.js';
import { AgentPluginService, ConfiguredAgentPluginDiscovery, MarketplaceAgentPluginDiscovery } from '../common/plugins/agentPluginServiceImpl.js';
import { IAgentPluginRepositoryService } from '../common/plugins/agentPluginRepositoryService.js';
import { IPluginInstallService } from '../common/plugins/pluginInstallService.js';
import { IPluginMarketplaceService, PluginMarketplaceService } from '../common/plugins/pluginMarketplaceService.js';
Expand Down Expand Up @@ -1701,6 +1701,7 @@ registerAction2(ConfigureToolSets);
registerEditorFeature(ChatPasteProvidersFeature);

agentPluginDiscoveryRegistry.register(new SyncDescriptor(ConfiguredAgentPluginDiscovery));
agentPluginDiscoveryRegistry.register(new SyncDescriptor(MarketplaceAgentPluginDiscovery));

registerSingleton(IChatTransferService, ChatTransferService, InstantiationType.Delayed);
registerSingleton(IChatService, ChatService, InstantiationType.Delayed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
private readonly _sessionTypeInputPlaceholders: Map<string, string> = new Map();

private readonly _sessions = new ResourceMap<ContributedChatSessionData>();
private readonly _resourceAliases = new ResourceMap<URI>(); // real resource -> untitled resource

private readonly _hasCanDelegateProvidersKey: IContextKey<boolean>;

Expand Down Expand Up @@ -1078,20 +1079,33 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
}

public hasAnySessionOptions(sessionResource: URI): boolean {
const session = this._sessions.get(sessionResource);
const session = this._sessions.get(this._resolveResource(sessionResource));
return !!session && !!session.options && Object.keys(session.options).length > 0;
}

public getSessionOption(sessionResource: URI, optionId: string): string | IChatSessionProviderOptionItem | undefined {
const session = this._sessions.get(sessionResource);
const session = this._sessions.get(this._resolveResource(sessionResource));
return session?.getOption(optionId);
}

public setSessionOption(sessionResource: URI, optionId: string, value: string | IChatSessionProviderOptionItem): boolean {
const session = this._sessions.get(sessionResource);
const session = this._sessions.get(this._resolveResource(sessionResource));
return !!session?.setOption(optionId, value);
}

/**
* Resolve a resource through the alias map. If the resource is a real
* resource that has been aliased to an untitled resource, return the
* untitled resource (the canonical key in {@link _sessions}).
*/
private _resolveResource(resource: URI): URI {
return this._resourceAliases.get(resource) ?? resource;
}

public registerSessionResourceAlias(untitledResource: URI, realResource: URI): void {
this._resourceAliases.set(realResource, untitledResource);
}

/**
* Store option groups for a session type
*/
Expand Down Expand Up @@ -1134,7 +1148,7 @@ export class ChatSessionsService extends Disposable implements IChatSessionsServ
for (const u of updates) {
this.setSessionOption(sessionResource, u.optionId, u.value);
}
this._onDidChangeSessionOptions.fire(sessionResource);
this._onDidChangeSessionOptions.fire(this._resolveResource(sessionResource));
this._logService.trace(`[ChatSessionsService] notifySessionOptionsChange: finished for ${sessionResource}`);
}

Expand Down
44 changes: 3 additions & 41 deletions src/vs/workbench/contrib/chat/browser/pluginInstallService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@

import { URI } from '../../../../base/common/uri.js';
import { localize } from '../../../../nls.js';
import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { IFileService } from '../../../../platform/files/common/files.js';
import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';
import { ChatConfiguration } from '../common/constants.js';
import { IAgentPluginRepositoryService } from '../common/plugins/agentPluginRepositoryService.js';
import { IPluginInstallService } from '../common/plugins/pluginInstallService.js';
import { IMarketplacePlugin } from '../common/plugins/pluginMarketplaceService.js';
import { IMarketplacePlugin, IPluginMarketplaceService } from '../common/plugins/pluginMarketplaceService.js';

export class PluginInstallService implements IPluginInstallService {
declare readonly _serviceBrand: undefined;

constructor(
@IAgentPluginRepositoryService private readonly _pluginRepositoryService: IAgentPluginRepositoryService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IPluginMarketplaceService private readonly _pluginMarketplaceService: IPluginMarketplaceService,
@IFileService private readonly _fileService: IFileService,
@INotificationService private readonly _notificationService: INotificationService,
) { }
Expand Down Expand Up @@ -54,7 +52,7 @@ export class PluginInstallService implements IPluginInstallService {
return;
}

this._addPluginPath(pluginDir.fsPath);
this._pluginMarketplaceService.addInstalledPlugin(pluginDir, plugin);
}

async updatePlugin(plugin: IMarketplacePlugin): Promise<void> {
Expand All @@ -65,43 +63,7 @@ export class PluginInstallService implements IPluginInstallService {
});
}

async uninstallPlugin(pluginUri: URI): Promise<void> {
await this._removePluginPath(pluginUri.fsPath);
}

getPluginInstallUri(plugin: IMarketplacePlugin): URI {
return this._pluginRepositoryService.getPluginInstallUri(plugin);
}

/**
* Adds the given file-system path to `chat.plugins.paths` in user-local config.
*/
private _addPluginPath(fsPath: string): void {
const current = this._configurationService.getValue<Record<string, boolean>>(ChatConfiguration.PluginPaths) ?? {};
if (Object.prototype.hasOwnProperty.call(current, fsPath)) {
return;
}
this._configurationService.updateValue(
ChatConfiguration.PluginPaths,
{ ...current, [fsPath]: true },
ConfigurationTarget.USER_LOCAL,
);
}

/**
* Removes the given file-system path from `chat.plugins.paths` in user-local config.
*/
private _removePluginPath(fsPath: string) {
const current = this._configurationService.getValue<Record<string, boolean>>(ChatConfiguration.PluginPaths) ?? {};
if (!Object.prototype.hasOwnProperty.call(current, fsPath)) {
return;
}
const updated = { ...current };
delete updated[fsPath];
return this._configurationService.updateValue(
ChatConfiguration.PluginPaths,
updated,
ConfigurationTarget.USER_LOCAL,
);
}
}
Loading
Loading