Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/contrib/agentHost/browser/localAgentHostSessionsProvider.ts
13401 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Codicon } from '../../../../base/common/codicons.js';
7
import { MarkdownString } from '../../../../base/common/htmlContent.js';
8
import { Schemas } from '../../../../base/common/network.js';
9
import { autorun, IObservable } from '../../../../base/common/observable.js';
10
import { basename, dirname } from '../../../../base/common/resources.js';
11
import { ThemeIcon } from '../../../../base/common/themables.js';
12
import { URI } from '../../../../base/common/uri.js';
13
import { localize } from '../../../../nls.js';
14
import { IAgentConnection, IAgentHostService, type IAgentSessionMetadata } from '../../../../platform/agentHost/common/agentService.js';
15
import type { ISessionGitState } from '../../../../platform/agentHost/common/state/sessionState.js';
16
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
17
import { ILabelService } from '../../../../platform/label/common/label.js';
18
import { IChatWidgetService } from '../../../../workbench/contrib/chat/browser/chat.js';
19
import { IChatService } from '../../../../workbench/contrib/chat/common/chatService/chatService.js';
20
import { IChatSessionsService } from '../../../../workbench/contrib/chat/common/chatSessionsService.js';
21
import { ILanguageModelsService } from '../../../../workbench/contrib/chat/common/languageModels.js';
22
import { BaseAgentHostSessionsProvider } from './baseAgentHostSessionsProvider.js';
23
import { buildAgentHostSessionWorkspace, readBranchProtectionPatterns } from '../../../common/agentHostSessionWorkspace.js';
24
import { ISessionWorkspace, ISessionWorkspaceBrowseAction, SESSION_WORKSPACE_GROUP_LOCAL } from '../../../services/sessions/common/session.js';
25
import { toAgentHostUri } from '../../../../platform/agentHost/common/agentHostUri.js';
26
import { LOCAL_AGENT_HOST_PROVIDER_ID } from '../../../common/agentHostSessionsProvider.js';
27
28
const LOCAL_RESOURCE_SCHEME_PREFIX = 'agent-host-';
29
30
/**
31
* Local-window sessions provider backed by the in-process
32
* {@link IAgentHostService}. A thin subclass of
33
* {@link BaseAgentHostSessionsProvider} that supplies the local-only
34
* variation: a built-in connection that is always present, session-type
35
* synchronization from the local agent host's `rootState`, and a local
36
* file-picker browse action.
37
*/
38
export class LocalAgentHostSessionsProvider extends BaseAgentHostSessionsProvider {
39
40
readonly id = LOCAL_AGENT_HOST_PROVIDER_ID;
41
readonly label: string;
42
readonly icon: ThemeIcon = Codicon.vm;
43
readonly browseActions: readonly ISessionWorkspaceBrowseAction[];
44
readonly supportsLocalWorkspaces = true;
45
46
private readonly _localLabel = localize('localAgentHostSessionTypeLocation', "Local");
47
private readonly _localDescription = new MarkdownString(this._localLabel);
48
49
constructor(
50
@IAgentHostService private readonly _agentHostService: IAgentHostService,
51
@IChatSessionsService chatSessionsService: IChatSessionsService,
52
@IChatService chatService: IChatService,
53
@IChatWidgetService chatWidgetService: IChatWidgetService,
54
@ILanguageModelsService languageModelsService: ILanguageModelsService,
55
@ILabelService private readonly _labelService: ILabelService,
56
@IConfigurationService private readonly _configurationService: IConfigurationService,
57
) {
58
super(chatSessionsService, chatService, chatWidgetService, languageModelsService);
59
60
this.label = localize('localAgentHostLabel', "Local Agent Host");
61
62
this.browseActions = [];
63
64
this._attachConnectionListeners(this._agentHostService, this._store);
65
66
const rootStateValue = this._agentHostService.rootState.value;
67
if (rootStateValue && !(rootStateValue instanceof Error)) {
68
this._syncSessionTypesFromRootState(rootStateValue);
69
this._syncRootConfigFromRootState(rootStateValue);
70
}
71
this._register(this._agentHostService.rootState.onDidChange(rootState => {
72
this._syncSessionTypesFromRootState(rootState);
73
this._syncRootConfigFromRootState(rootState);
74
}));
75
76
// Eagerly populate the session cache once authentication has settled.
77
// Without this, the sidebar would only call `getSessions()` after some
78
// other event (e.g. a `notify/sessionAdded` after the user sends a
79
// message) forced a refresh. We wait for `authenticationPending` to
80
// settle because the underlying agent (e.g. CopilotAgent) throws
81
// `AHP_AUTH_REQUIRED` from `listSessions()` until its auth token is
82
// resolved. The `authenticationPending` observable is sticky (once
83
// it goes false it stays false), so this autorun fires
84
// `_refreshSessions()` at most once for the eager-load case.
85
this._register(autorun(reader => {
86
if (this._agentHostService.authenticationPending.read(reader)) {
87
return;
88
}
89
this._cacheInitialized = true;
90
this._refreshSessions();
91
}));
92
}
93
94
// -- BaseAgentHostSessionsProvider hooks ---------------------------------
95
96
protected get connection(): IAgentConnection { return this._agentHostService; }
97
98
protected get authenticationPending(): IObservable<boolean> { return this._agentHostService.authenticationPending; }
99
100
/**
101
* Local resource scheme: `agent-host-${provider}`. Must match the type
102
* string registered by AgentHostContribution. Distinct from the logical
103
* {@link ISession.sessionType}, which is the agent provider name itself
104
* (e.g. `copilotcli`) so the same agent shares one session type across
105
* local and remote hosts.
106
*/
107
protected resourceSchemeForProvider(provider: string): string {
108
return `${LOCAL_RESOURCE_SCHEME_PREFIX}${provider}`;
109
}
110
111
protected _adapterOptions() {
112
return {
113
description: this._localDescription,
114
buildWorkspace: (project: IAgentSessionMetadata['project'], workingDirectory: URI | undefined, gitState: ISessionGitState | undefined) => {
115
const uriForDescription = project?.uri ?? workingDirectory;
116
const description = uriForDescription ? this._labelService.getUriLabel(dirname(uriForDescription), { relative: false }) : undefined;
117
const branchProtectionPatterns = readBranchProtectionPatterns(this._configurationService, workingDirectory ?? project?.uri);
118
return LocalAgentHostSessionsProvider.buildWorkspace(project, workingDirectory, this._localLabel, gitState, description, branchProtectionPatterns);
119
},
120
};
121
}
122
123
protected _formatSessionTypeLabel(agentLabel: string): string {
124
// Use the unadorned agent label (e.g. "Copilot") rather than tagging it
125
// with `[Local]`. The session type id is shared with the extension-host
126
// Copilot CLI provider, so the filter menu / new-session picker entry
127
// covers both sets of sessions; the `[Local]` tag belongs on the
128
// per-session workspace label, not the type label.
129
return agentLabel;
130
}
131
132
protected override _diffUriMapper(): (uri: URI) => URI {
133
return uri => toAgentHostUri(uri, 'local');
134
}
135
136
// -- Workspaces ----------------------------------------------------------
137
138
static buildWorkspace(project: IAgentSessionMetadata['project'], workingDirectory: URI | undefined, providerLabel: string, gitState: ISessionGitState | undefined, description?: string, branchProtectionPatterns?: readonly string[]): ISessionWorkspace | undefined {
139
return buildAgentHostSessionWorkspace(project, workingDirectory, { providerLabel, fallbackIcon: Codicon.folder, requiresWorkspaceTrust: true, description, branchProtectionPatterns, group: SESSION_WORKSPACE_GROUP_LOCAL }, gitState);
140
}
141
142
resolveWorkspace(repositoryUri: URI): ISessionWorkspace | undefined {
143
if (repositoryUri.scheme !== Schemas.file) {
144
return undefined;
145
}
146
const folderName = basename(repositoryUri) || repositoryUri.path;
147
return {
148
label: `${folderName} [${this._localLabel}]`,
149
description: this._labelService.getUriLabel(dirname(repositoryUri), { relative: false }),
150
group: SESSION_WORKSPACE_GROUP_LOCAL,
151
icon: Codicon.folder,
152
repositories: [{ uri: repositoryUri, workingDirectory: undefined, detail: undefined, baseBranchName: undefined }],
153
requiresWorkspaceTrust: true,
154
};
155
}
156
}
157
158