Skip to content

Commit 1bf95e6

Browse files
authored
Merge branch 'main' into tyriar/update_cursor_still__suggest__2
2 parents 8d40850 + 34a283a commit 1bf95e6

File tree

13 files changed

+178
-23
lines changed

13 files changed

+178
-23
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "code-oss-dev",
33
"version": "1.89.0",
4-
"distro": "853f475a6e8e85ad811a2f8e49c3032bdb660558",
4+
"distro": "a6c3333a909e54cb5353b27766ec4388e5f50cd8",
55
"author": {
66
"name": "Microsoft Corporation"
77
},

src/vs/base/common/product.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ export interface IProductConfiguration {
190190
readonly aiGeneratedWorkspaceTrust?: IAiGeneratedWorkspaceTrust;
191191
readonly gitHubEntitlement?: IGitHubEntitlement;
192192
readonly chatWelcomeView?: IChatWelcomeView;
193+
readonly chatParticipantRegistry?: string;
193194
}
194195

195196
export interface ITunnelApplicationConfig {

src/vs/platform/files/common/watcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,14 @@ export interface IRecursiveWatcherWithSubscribe extends IRecursiveWatcher {
135135

136136
/**
137137
* Subscribe to file events for the given path. The callback is called
138-
* whenever a file event occurs for the path. I fthe watcher failed,
138+
* whenever a file event occurs for the path. If the watcher failed,
139139
* the error parameter is set to `true`.
140140
*
141141
* @returns an `IDisposable` to stop listening to events or `undefined`
142142
* if no events can be watched for the path given the current set of
143143
* recursive watch requests.
144144
*/
145-
subscribe(path: string, callback: (error: boolean, change?: IFileChange) => void): IDisposable | undefined;
145+
subscribe(path: string, callback: (error: true | null, change?: IFileChange) => void): IDisposable | undefined;
146146
}
147147

148148
export interface IRecursiveWatcherOptions {

src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,12 @@ export class NodeJSFileWatcherLibrary extends Disposable {
147147

148148
private doWatchWithExistingWatcher(realPath: string, isDirectory: boolean, disposables: DisposableStore): boolean {
149149
if (isDirectory) {
150-
return false; // only supported for files where we have the full path known upfront
150+
// TODO@bpasero recursive watcher re-use is currently not enabled
151+
// for when folders are watched. this is because the dispatching
152+
// in the recursive watcher for non-recurive requests is optimized
153+
// for file changes where we really only match on the exact path
154+
// and not child paths.
155+
return false;
151156
}
152157

153158
const resource = URI.file(this.request.path);

src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
782782
return true;
783783
}
784784

785-
subscribe(path: string, callback: (error: boolean, change?: IFileChange) => void): IDisposable | undefined {
785+
subscribe(path: string, callback: (error: true | null, change?: IFileChange) => void): IDisposable | undefined {
786786
for (const watcher of this.watchers) {
787787
if (watcher.failed) {
788788
continue; // watcher has already failed
@@ -810,7 +810,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
810810
callback(true /* error */);
811811
}));
812812
disposables.add(Event.once(watcher.onDidFail)(() => callback(true /* error */)));
813-
disposables.add(watcher.subscribe(path, change => callback(false, change)));
813+
disposables.add(watcher.subscribe(path, change => callback(null, change)));
814814

815815
return disposables;
816816
}

src/vs/platform/files/node/watcher/watcherStats.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,31 @@ export function computeStats(
3838
lines.push(`- I/O Handles Impact: total: ${recursiveRequestsStatus.polling + nonRecursiveRequestsStatus.polling + recursiveWatcherStatus.active + nonRecursiveWatcherStatus.active}`);
3939

4040
lines.push(`\n[Recursive Requests (${allRecursiveRequests.length}, suspended: ${recursiveRequestsStatus.suspended}, polling: ${recursiveRequestsStatus.polling})]:`);
41+
const recursiveRequestLines: string[] = [];
4142
for (const request of [nonSuspendedRecursiveRequests, suspendedPollingRecursiveRequests, suspendedNonPollingRecursiveRequests].flat()) {
42-
fillRequestStats(lines, request, recursiveWatcher);
43+
fillRequestStats(recursiveRequestLines, request, recursiveWatcher);
4344
}
45+
lines.push(...alignTextColumns(recursiveRequestLines));
4446

45-
fillRecursiveWatcherStats(lines, recursiveWatcher);
47+
const recursiveWatcheLines: string[] = [];
48+
fillRecursiveWatcherStats(recursiveWatcheLines, recursiveWatcher);
49+
lines.push(...alignTextColumns(recursiveWatcheLines));
4650

4751
lines.push(`\n[Non-Recursive Requests (${allNonRecursiveRequests.length}, suspended: ${nonRecursiveRequestsStatus.suspended}, polling: ${nonRecursiveRequestsStatus.polling})]:`);
52+
const nonRecursiveRequestLines: string[] = [];
4853
for (const request of [nonSuspendedNonRecursiveRequests, suspendedPollingNonRecursiveRequests, suspendedNonPollingNonRecursiveRequests].flat()) {
49-
fillRequestStats(lines, request, nonRecursiveWatcher);
54+
fillRequestStats(nonRecursiveRequestLines, request, nonRecursiveWatcher);
5055
}
56+
lines.push(...alignTextColumns(nonRecursiveRequestLines));
5157

52-
fillNonRecursiveWatcherStats(lines, nonRecursiveWatcher);
58+
const nonRecursiveWatcheLines: string[] = [];
59+
fillNonRecursiveWatcherStats(nonRecursiveWatcheLines, nonRecursiveWatcher);
60+
lines.push(...alignTextColumns(nonRecursiveWatcheLines));
5361

62+
return `\n\n[File Watcher] request stats:\n\n${lines.join('\n')}\n\n`;
63+
}
64+
65+
function alignTextColumns(lines: string[]) {
5466
let maxLength = 0;
5567
for (const line of lines) {
5668
maxLength = Math.max(maxLength, line.split('\t')[0].length);
@@ -65,7 +77,7 @@ export function computeStats(
6577
}
6678
}
6779

68-
return `\n\n[File Watcher] request stats:\n\n${lines.join('\n')}\n\n`;
80+
return lines;
6981
}
7082

7183
function computeRequestStatus(requests: IUniversalWatchRequest[], watcher: ParcelWatcher | NodeJSWatcher): { suspended: number; polling: number } {

src/vs/platform/utilityProcess/electron-main/utilityProcess.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ export class UtilityProcess extends Disposable {
168168
private process: ElectronUtilityProcess | undefined = undefined;
169169
private processPid: number | undefined = undefined;
170170
private configuration: IUtilityProcessConfiguration | undefined = undefined;
171+
private killed = false;
171172

172173
constructor(
173174
@ILogService private readonly logService: ILogService,
@@ -314,18 +315,19 @@ export class UtilityProcess extends Disposable {
314315

315316
// Exit
316317
this._register(Event.fromNodeEventEmitter<number>(process, 'exit')(code => {
317-
this.log(`received exit event with code ${code}`, Severity.Info);
318+
const normalizedCode = this.isNormalExit(code) ? 0 : code;
319+
this.log(`received exit event with code ${normalizedCode}`, Severity.Info);
318320

319321
// Event
320-
this._onExit.fire({ pid: this.processPid!, code, signal: 'unknown' });
322+
this._onExit.fire({ pid: this.processPid!, code: normalizedCode, signal: 'unknown' });
321323

322324
// Cleanup
323325
this.onDidExitOrCrashOrKill();
324326
}));
325327

326328
// Child process gone
327329
this._register(Event.fromNodeEventEmitter<{ details: Details }>(app, 'child-process-gone', (event, details) => ({ event, details }))(({ details }) => {
328-
if (details.type === 'Utility' && details.name === serviceName) {
330+
if (details.type === 'Utility' && details.name === serviceName && !this.isNormalExit(details.exitCode)) {
329331
this.log(`crashed with code ${details.exitCode} and reason '${details.reason}'`, Severity.Error);
330332

331333
// Telemetry
@@ -415,12 +417,24 @@ export class UtilityProcess extends Disposable {
415417
const killed = this.process.kill();
416418
if (killed) {
417419
this.log('successfully killed the process', Severity.Info);
420+
this.killed = true;
418421
this.onDidExitOrCrashOrKill();
419422
} else {
420423
this.log('unable to kill the process', Severity.Warning);
421424
}
422425
}
423426

427+
private isNormalExit(exitCode: number): boolean {
428+
if (exitCode === 0) {
429+
return true;
430+
}
431+
432+
// Treat an exit code of 15 (SIGTERM) as a normal exit
433+
// if we triggered the termination from process.kill()
434+
435+
return this.killed && exitCode === 15 /* SIGTERM */;
436+
}
437+
424438
private onDidExitOrCrashOrKill(): void {
425439
if (typeof this.processPid === 'number') {
426440
UtilityProcess.all.delete(this.processPid);

src/vs/workbench/contrib/chat/browser/chat.contribution.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import { ChatWidgetService } from 'vs/workbench/contrib/chat/browser/chatWidget'
4242
import { ChatCodeBlockContextProviderService } from 'vs/workbench/contrib/chat/browser/codeBlockContextProviderService';
4343
import 'vs/workbench/contrib/chat/browser/contrib/chatHistoryVariables';
4444
import 'vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib';
45-
import { ChatAgentLocation, ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
45+
import { ChatAgentLocation, ChatAgentService, IChatAgentService, IChatAgentNameService, ChatAgentNameService } from 'vs/workbench/contrib/chat/common/chatAgents';
4646
import { CONTEXT_IN_CHAT_SESSION } from 'vs/workbench/contrib/chat/common/chatContextKeys';
4747
import { ChatWelcomeMessageModel } from 'vs/workbench/contrib/chat/common/chatModel';
4848
import { chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
@@ -342,6 +342,7 @@ registerSingleton(IChatWidgetHistoryService, ChatWidgetHistoryService, Instantia
342342
registerSingleton(ILanguageModelsService, LanguageModelsService, InstantiationType.Delayed);
343343
registerSingleton(IChatSlashCommandService, ChatSlashCommandService, InstantiationType.Delayed);
344344
registerSingleton(IChatAgentService, ChatAgentService, InstantiationType.Delayed);
345+
registerSingleton(IChatAgentNameService, ChatAgentNameService, InstantiationType.Delayed);
345346
registerSingleton(IChatVariablesService, ChatVariablesService, InstantiationType.Delayed);
346347
registerSingleton(IVoiceChatService, VoiceChatService, InstantiationType.Delayed);
347348
registerSingleton(IChatCodeBlockContextProviderService, ChatCodeBlockContextProviderService, InstantiationType.Delayed);

src/vs/workbench/contrib/chat/browser/chatListRenderer.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups';
5454
import { ChatMarkdownDecorationsRenderer } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer';
5555
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
5656
import { ChatCodeBlockContentProvider, CodeBlockPart, CodeCompareBlockPart, ICodeBlockData, localFileLanguageId, parseLocalFileData } from 'vs/workbench/contrib/chat/browser/codeBlockPart';
57-
import { ChatAgentLocation, IChatAgentMetadata } from 'vs/workbench/contrib/chat/common/chatAgents';
57+
import { ChatAgentLocation, IChatAgentMetadata, IChatAgentNameService } from 'vs/workbench/contrib/chat/common/chatAgents';
5858
import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_DETECTED_AGENT_COMMAND, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys';
5959
import { IChatProgressRenderableResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
6060
import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
@@ -71,6 +71,8 @@ import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/text
7171
import { TextEdit } from 'vs/editor/common/languages';
7272
import { IChatListItemRendererOptions } from './chat';
7373
import { CancellationTokenSource } from 'vs/base/common/cancellation';
74+
import { autorun, constObservable, IObservable } from 'vs/base/common/observable';
75+
import { isUndefined } from 'vs/base/common/types';
7476

7577
const $ = dom.$;
7678

@@ -146,6 +148,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
146148
@ICommandService private readonly commandService: ICommandService,
147149
@ITextModelService private readonly textModelService: ITextModelService,
148150
@IModelService private readonly modelService: IModelService,
151+
@IChatAgentNameService private readonly chatAgentNameService: IChatAgentNameService,
149152
) {
150153
super();
151154

@@ -367,9 +370,23 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
367370
}
368371

369372
private renderDetail(element: IChatResponseViewModel, templateData: IChatListItemTemplate): void {
370-
let progressMsg: string = '';
373+
let agentName: IObservable<string | undefined> = constObservable(undefined);
374+
371375
if (element.agent && !element.agent.isDefault) {
372-
let usingMsg = chatAgentLeader + element.agent.name;
376+
const name = element.agent.name;
377+
agentName = this.chatAgentNameService.getAgentNameRestriction(element.agent)
378+
.map(allowed => allowed ? name : name); // TODO
379+
}
380+
381+
templateData.elementDisposables.add(autorun(reader => {
382+
this._renderDetail(element, agentName.read(reader), templateData);
383+
}));
384+
}
385+
386+
private _renderDetail(element: IChatResponseViewModel, agentName: string | undefined, templateData: IChatListItemTemplate): void {
387+
let progressMsg: string = '';
388+
if (!isUndefined(agentName)) {
389+
let usingMsg = chatAgentLeader + agentName;
373390
if (element.slashCommand) {
374391
usingMsg += ` ${chatSubcommandLeader}${element.slashCommand.name}`;
375392
}
@@ -381,8 +398,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
381398
}
382399
} else if (element.agentOrSlashCommandDetected) {
383400
const usingMsg: string[] = [];
384-
if (element.agent && !element.agent.isDefault) {
385-
usingMsg.push(chatAgentLeader + element.agent.name);
401+
if (!isUndefined(agentName)) {
402+
usingMsg.push(chatAgentLeader + agentName);
386403
}
387404
if (element.slashCommand) {
388405
usingMsg.push(chatSubcommandLeader + element.slashCommand.name);

src/vs/workbench/contrib/chat/common/chatAgents.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,25 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { timeout } from 'vs/base/common/async';
67
import { CancellationToken } from 'vs/base/common/cancellation';
78
import { Emitter, Event } from 'vs/base/common/event';
89
import { IMarkdownString } from 'vs/base/common/htmlContent';
910
import { Iterable } from 'vs/base/common/iterator';
1011
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
12+
import { IObservable } from 'vs/base/common/observable';
13+
import { observableValue } from 'vs/base/common/observableInternal/base';
14+
import { equalsIgnoreCase } from 'vs/base/common/strings';
1115
import { ThemeIcon } from 'vs/base/common/themables';
1216
import { URI } from 'vs/base/common/uri';
1317
import { ProviderResult } from 'vs/editor/common/languages';
1418
import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
1519
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
1620
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
21+
import { ILogService } from 'vs/platform/log/common/log';
22+
import { IProductService } from 'vs/platform/product/common/productService';
23+
import { asJson, IRequestService } from 'vs/platform/request/common/request';
24+
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
1725
import { CONTEXT_CHAT_ENABLED } from 'vs/workbench/contrib/chat/common/chatContextKeys';
1826
import { IChatProgressResponseContent, IChatRequestVariableData } from 'vs/workbench/contrib/chat/common/chatModel';
1927
import { IRawChatCommandContribution, RawChatParticipantLocation } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes';
@@ -351,3 +359,95 @@ export class MergedChatAgent implements IChatAgent {
351359
return undefined;
352360
}
353361
}
362+
363+
export const IChatAgentNameService = createDecorator<IChatAgentNameService>('chatAgentNameService');
364+
365+
type IChatParticipantRegistry = { [name: string]: string[] };
366+
367+
interface IChatParticipantRegistryResponse {
368+
readonly version: number;
369+
readonly restrictedChatParticipants: IChatParticipantRegistry;
370+
}
371+
372+
export interface IChatAgentNameService {
373+
_serviceBrand: undefined;
374+
getAgentNameRestriction(chatAgentData: IChatAgentData): IObservable<boolean>;
375+
}
376+
377+
export class ChatAgentNameService implements IChatAgentNameService {
378+
379+
private static readonly StorageKey = 'chat.participantNameRegistry';
380+
381+
declare _serviceBrand: undefined;
382+
383+
private readonly url!: string;
384+
private registry = observableValue<IChatParticipantRegistry>(this, Object.create(null));
385+
private disposed = false;
386+
387+
constructor(
388+
@IProductService productService: IProductService,
389+
@IRequestService private readonly requestService: IRequestService,
390+
@ILogService private readonly logService: ILogService,
391+
@IStorageService private readonly storageService: IStorageService
392+
) {
393+
if (!productService.chatParticipantRegistry) {
394+
return;
395+
}
396+
397+
this.url = productService.chatParticipantRegistry;
398+
399+
const raw = storageService.get(ChatAgentNameService.StorageKey, StorageScope.APPLICATION);
400+
401+
try {
402+
this.registry.set(JSON.parse(raw ?? '{}'), undefined);
403+
} catch (err) {
404+
storageService.remove(ChatAgentNameService.StorageKey, StorageScope.APPLICATION);
405+
}
406+
407+
this.refresh();
408+
}
409+
410+
private refresh(): void {
411+
if (this.disposed) {
412+
return;
413+
}
414+
415+
this.update()
416+
.catch(err => this.logService.warn('Failed to fetch chat participant registry', err))
417+
.then(() => timeout(5 * 60 * 1000)) // every 5 minutes
418+
.then(() => this.refresh());
419+
}
420+
421+
private async update(): Promise<void> {
422+
const context = await this.requestService.request({ type: 'GET', url: this.url }, CancellationToken.None);
423+
424+
if (context.res.statusCode !== 200) {
425+
throw new Error('Could not get extensions report.');
426+
}
427+
428+
const result = await asJson<IChatParticipantRegistryResponse>(context);
429+
430+
if (!result || result.version !== 1) {
431+
throw new Error('Unexpected chat participant registry response.');
432+
}
433+
434+
const registry = result.restrictedChatParticipants;
435+
this.registry.set(registry, undefined);
436+
this.storageService.store(ChatAgentNameService.StorageKey, JSON.stringify(registry), StorageScope.APPLICATION, StorageTarget.MACHINE);
437+
}
438+
439+
getAgentNameRestriction(chatAgentData: IChatAgentData): IObservable<boolean> {
440+
const allowList = this.registry.map<string[] | undefined>(registry => registry[chatAgentData.name.toLowerCase()]);
441+
return allowList.map(allowList => {
442+
if (!allowList) {
443+
return true;
444+
}
445+
446+
return allowList.some(id => equalsIgnoreCase(id, id.includes('.') ? chatAgentData.extensionId.value : chatAgentData.extensionPublisher));
447+
});
448+
}
449+
450+
dispose() {
451+
this.disposed = true;
452+
}
453+
}

0 commit comments

Comments
 (0)