Skip to content

Commit e5913be

Browse files
committed
feat: add diagnostics/debug logging for ChatSessionCustomizationProvider
- Add ChatSessionCustomizationResult wrapper with diagnostics (scannedPaths, skippedFiles) - Add ChatSessionCustomizationChangeEvent with changedTypes - Log customization provider results to Chat Debug panel via IChatDebugService - Add resolve provider for expandable file list in Agent Debug Logs - Add Plugins to ChatSessionCustomizationType - Extend Chat Customization filter to include customization-provider events - Remove redundant aiCustomizationDebugPanel.ts - Update all internal types, protocol DTOs, and tests
1 parent e9dbff5 commit e5913be

13 files changed

Lines changed: 323 additions & 345 deletions

src/vs/sessions/AI_CUSTOMIZATIONS.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ src/vs/workbench/contrib/chat/browser/aiCustomization/
1616
├── aiCustomizationManagementEditorInput.ts # Singleton input
1717
├── aiCustomizationListWidget.ts # Search + grouped list + harness toggle
1818
├── aiCustomizationListWidgetUtils.ts # List item helpers (truncation, etc.)
19-
├── aiCustomizationDebugPanel.ts # Debug diagnostics panel
2019
├── aiCustomizationWorkspaceService.ts # Core VS Code workspace service impl
2120
├── customizationHarnessService.ts # Core harness service impl (agent-gated)
2221
├── customizationCreatorService.ts # AI-guided creation flow

src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHostCustomizationHarness.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js';
1212
import { AICustomizationManagementSection, type IStorageSourceFilter } from '../../../../workbench/contrib/chat/common/aiCustomizationWorkspaceService.js';
1313
import { PromptsStorage } from '../../../../workbench/contrib/chat/common/promptSyntax/service/promptsService.js';
1414
import { PromptsType } from '../../../../workbench/contrib/chat/common/promptSyntax/promptTypes.js';
15-
import { type IHarnessDescriptor, type IExternalCustomizationItem, type IExternalCustomizationItemProvider } from '../../../../workbench/contrib/chat/common/customizationHarnessService.js';
15+
import { type IHarnessDescriptor, type IExternalCustomizationItem, type IExternalCustomizationItemProvider, type IExternalCustomizationResult, type IExternalCustomizationChangeEvent } from '../../../../workbench/contrib/chat/common/customizationHarnessService.js';
1616
import { SessionClientState } from '../../../../platform/agentHost/common/state/sessionClientState.js';
1717
import { type IAgentInfo, type ICustomizationRef, type ISessionCustomization, CustomizationStatus } from '../../../../platform/agentHost/common/state/sessionState.js';
1818
import { BUILTIN_STORAGE } from '../../chat/common/builtinPromptsStorage.js';
@@ -44,8 +44,8 @@ function toStatusString(status: CustomizationStatus | undefined): 'loading' | 'l
4444
* status and enabled state.
4545
*/
4646
export class RemoteAgentCustomizationItemProvider extends Disposable implements IExternalCustomizationItemProvider {
47-
private readonly _onDidChange = this._register(new Emitter<void>());
48-
readonly onDidChange: Event<void> = this._onDidChange.event;
47+
private readonly _onDidChange = this._register(new Emitter<IExternalCustomizationChangeEvent>());
48+
readonly onDidChange: Event<IExternalCustomizationChangeEvent> = this._onDidChange.event;
4949

5050
private _agentCustomizations: readonly ICustomizationRef[];
5151
private _sessionCustomizations: readonly ISessionCustomization[] | undefined;
@@ -61,7 +61,7 @@ export class RemoteAgentCustomizationItemProvider extends Disposable implements
6161
this._register(this._clientState.onDidChangeSessionState(({ state }) => {
6262
if (state.customizations !== this._sessionCustomizations) {
6363
this._sessionCustomizations = state.customizations;
64-
this._onDidChange.fire();
64+
this._onDidChange.fire({});
6565
}
6666
}));
6767
}
@@ -72,30 +72,34 @@ export class RemoteAgentCustomizationItemProvider extends Disposable implements
7272
*/
7373
updateAgentCustomizations(customizations: readonly ICustomizationRef[]): void {
7474
this._agentCustomizations = customizations;
75-
this._onDidChange.fire();
75+
this._onDidChange.fire({});
7676
}
7777

78-
async provideChatSessionCustomizations(_token: CancellationToken): Promise<IExternalCustomizationItem[]> {
78+
async provideChatSessionCustomizations(_token: CancellationToken): Promise<IExternalCustomizationResult> {
7979
// When a session is active, prefer session-level data (includes status)
8080
if (this._sessionCustomizations) {
81-
return this._sessionCustomizations.map(sc => ({
82-
uri: URI.isUri(sc.customization.uri) ? sc.customization.uri : URI.parse(sc.customization.uri),
83-
type: 'plugin',
84-
name: sc.customization.displayName,
85-
description: sc.customization.description,
86-
status: toStatusString(sc.status),
87-
statusMessage: sc.statusMessage,
88-
enabled: sc.enabled,
89-
}));
81+
return {
82+
items: this._sessionCustomizations.map(sc => ({
83+
uri: URI.isUri(sc.customization.uri) ? sc.customization.uri : URI.parse(sc.customization.uri),
84+
type: 'plugin',
85+
name: sc.customization.displayName,
86+
description: sc.customization.description,
87+
status: toStatusString(sc.status),
88+
statusMessage: sc.statusMessage,
89+
enabled: sc.enabled,
90+
})),
91+
};
9092
}
9193

9294
// Baseline: agent-level customizations (no status info)
93-
return this._agentCustomizations.map(ref => ({
94-
uri: URI.isUri(ref.uri) ? ref.uri : URI.parse(ref.uri as unknown as string),
95-
type: 'plugin',
96-
name: ref.displayName,
97-
description: ref.description,
98-
}));
95+
return {
96+
items: this._agentCustomizations.map(ref => ({
97+
uri: URI.isUri(ref.uri) ? ref.uri : URI.parse(ref.uri as unknown as string),
98+
type: 'plugin',
99+
name: ref.displayName,
100+
description: ref.description,
101+
})),
102+
};
99103
}
100104
}
101105

src/vs/workbench/api/browser/mainThreadChatAgents2.ts

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { DeferredPromise } from '../../../base/common/async.js';
77
import { CancellationToken } from '../../../base/common/cancellation.js';
88
import { Emitter, Event } from '../../../base/common/event.js';
9+
import { generateUuid } from '../../../base/common/uuid.js';
910
import { IMarkdownString } from '../../../base/common/htmlContent.js';
1011
import { Disposable, DisposableMap, IDisposable } from '../../../base/common/lifecycle.js';
1112
import { revive } from '../../../base/common/marshalling.js';
@@ -41,12 +42,13 @@ import { ILanguageModelToolsService } from '../../contrib/chat/common/tools/lang
4142
import { IExtHostContext, extHostNamedCustomer } from '../../services/extensions/common/extHostCustomers.js';
4243
import { IExtensionService } from '../../services/extensions/common/extensions.js';
4344
import { Dto } from '../../services/extensions/common/proxyIdentifier.js';
44-
import { ExtHostChatAgentsShape2, ExtHostContext, IChatSessionCustomizationItemDto, IChatSessionCustomizationProviderMetadataDto, IChatNotebookEditDto, IChatParticipantMetadata, IChatProgressDto, IChatSessionContextDto, ICustomAgentDto, IDynamicChatAgentProps, IExtensionChatAgentMetadata, IInstructionDto, ISkillDto, MainContext, MainThreadChatAgentsShape2 } from '../common/extHost.protocol.js';
45+
import { ExtHostChatAgentsShape2, ExtHostContext, IChatSessionCustomizationChangeEventDto, IChatSessionCustomizationProviderMetadataDto, IChatNotebookEditDto, IChatParticipantMetadata, IChatProgressDto, IChatSessionContextDto, ICustomAgentDto, IDynamicChatAgentProps, IExtensionChatAgentMetadata, IInstructionDto, ISkillDto, MainContext, MainThreadChatAgentsShape2 } from '../common/extHost.protocol.js';
4546
import { NotebookDto } from './mainThreadNotebookDto.js';
4647
import { isUntitledChatSession } from '../../contrib/chat/common/model/chatUri.js';
47-
import { ICustomizationHarnessService, IExternalCustomizationItem, IExternalCustomizationItemProvider, IHarnessDescriptor } from '../../contrib/chat/common/customizationHarnessService.js';
48+
import { ICustomizationHarnessService, IExternalCustomizationChangeEvent, IExternalCustomizationItem, IExternalCustomizationItemProvider, IExternalCustomizationResult, IHarnessDescriptor } from '../../contrib/chat/common/customizationHarnessService.js';
4849
import { AICustomizationManagementSection } from '../../contrib/chat/common/aiCustomizationWorkspaceService.js';
4950
import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';
51+
import { ChatDebugLogLevel, IChatDebugResolvedEventContent, IChatDebugService } from '../../contrib/chat/common/chatDebugService.js';
5052

5153
interface AgentData {
5254
dispose: () => void;
@@ -107,7 +109,9 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
107109
private readonly _promptFileContentRegistrations = this._register(new DisposableMap<number, DisposableMap<string, IDisposable>>());
108110

109111
private readonly _customizationProviders = this._register(new DisposableMap<number, IDisposable>());
110-
private readonly _customizationProviderEmitters = this._register(new DisposableMap<number, Emitter<void>>());
112+
private readonly _customizationProviderEmitters = this._register(new DisposableMap<number, Emitter<IExternalCustomizationChangeEvent>>());
113+
private readonly _customizationDebugDetails = new Map<string, IExternalCustomizationResult>();
114+
private readonly _lastChangedTypes = new Map<number, readonly string[] | undefined>();
111115

112116
private readonly _pendingProgress = new Map<string, { progress: (parts: IChatProgress[]) => void; chatSession: IChatModel | undefined }>();
113117
private readonly _proxy: ExtHostChatAgentsShape2;
@@ -131,10 +135,19 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
131135
@ILanguageModelToolsService private readonly _languageModelToolsService: ILanguageModelToolsService,
132136
@ICustomizationHarnessService private readonly _customizationHarnessService: ICustomizationHarnessService,
133137
@IConfigurationService private readonly _configurationService: IConfigurationService,
138+
@IChatDebugService private readonly _chatDebugService: IChatDebugService,
134139
) {
135140
super();
136141
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatAgents2);
137142

143+
// Register a resolve provider for customization discovery debug events.
144+
this._register(this._chatDebugService.registerProvider({
145+
provideChatDebugLog: async () => undefined,
146+
resolveChatDebugLogEvent: async (eventId) => {
147+
return this._resolveCustomizationEvent(eventId);
148+
}
149+
}));
150+
138151
// When the provider API kill-switch is toggled off, dispose all registered providers
139152
this._register(this._configurationService.onDidChangeConfiguration(e => {
140153
if (e.affectsConfiguration('chat.customizations.providerApi.enabled')) {
@@ -611,18 +624,18 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
611624
return;
612625
}
613626

614-
const emitter = new Emitter<void>();
627+
const emitter = new Emitter<IExternalCustomizationChangeEvent>();
615628
this._customizationProviderEmitters.set(handle, emitter);
616629

617630
// Build the item provider that calls back to the ExtHost
618631
const itemProvider: IExternalCustomizationItemProvider = {
619632
onDidChange: emitter.event,
620633
provideChatSessionCustomizations: async (token) => {
621-
const items = await this._proxy.$provideChatSessionCustomizations(handle, token);
622-
if (!items) {
634+
const result = await this._proxy.$provideChatSessionCustomizations(handle, token);
635+
if (!result) {
623636
return undefined;
624637
}
625-
return items.map((item: IChatSessionCustomizationItemDto): IExternalCustomizationItem => ({
638+
const items = result.items.map((item): IExternalCustomizationItem => ({
626639
uri: URI.revive(item.uri),
627640
type: item.type,
628641
name: item.name,
@@ -631,6 +644,18 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
631644
badge: item.badge,
632645
badgeTooltip: item.badgeTooltip,
633646
}));
647+
const mapped: IExternalCustomizationResult = {
648+
items,
649+
diagnostics: result.diagnostics ? {
650+
scannedPaths: result.diagnostics.scannedPaths?.map(p => URI.revive(p)),
651+
skippedFiles: result.diagnostics.skippedFiles?.map(f => ({
652+
uri: URI.revive(f.uri),
653+
reason: f.reason,
654+
})),
655+
} : undefined,
656+
};
657+
this._logCustomizationResult(handle, metadata.label, mapped);
658+
return mapped;
634659
},
635660
};
636661

@@ -642,6 +667,7 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
642667
case 'instructions': return AICustomizationManagementSection.Instructions;
643668
case 'prompt': return AICustomizationManagementSection.Prompts;
644669
case 'hook': return AICustomizationManagementSection.Hooks;
670+
case 'plugins': return AICustomizationManagementSection.Plugins;
645671
default: return type;
646672
}
647673
});
@@ -668,11 +694,83 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
668694
this._customizationProviderEmitters.deleteAndDispose(handle);
669695
}
670696

671-
$onDidChangeCustomizations(handle: number): void {
697+
$onDidChangeCustomizations(handle: number, event: IChatSessionCustomizationChangeEventDto): void {
698+
this._lastChangedTypes.set(handle, event.changedTypes);
672699
const emitter = this._customizationProviderEmitters.get(handle);
673700
if (emitter) {
674-
emitter.fire();
701+
emitter.fire({ changedTypes: event.changedTypes });
702+
}
703+
}
704+
705+
private _logCustomizationResult(handle: number, label: string, result: IExternalCustomizationResult): void {
706+
const sessionResource = this._chatDebugService.activeSessionResource;
707+
if (!sessionResource) {
708+
return;
709+
}
710+
711+
const changedTypes = this._lastChangedTypes.get(handle);
712+
this._lastChangedTypes.delete(handle);
713+
714+
const eventId = generateUuid();
715+
this._customizationDebugDetails.set(eventId, result);
716+
717+
// Evict oldest entries when the map exceeds the cap.
718+
if (this._customizationDebugDetails.size > 10_000) {
719+
const first = this._customizationDebugDetails.keys().next().value;
720+
if (first !== undefined) {
721+
this._customizationDebugDetails.delete(first);
722+
}
723+
}
724+
725+
const parts: string[] = [];
726+
parts.push(`${result.items.length} loaded`);
727+
if (result.diagnostics?.skippedFiles?.length) {
728+
parts.push(`${result.diagnostics.skippedFiles.length} skipped`);
729+
}
730+
if (result.diagnostics?.scannedPaths?.length) {
731+
parts.push(`${result.diagnostics.scannedPaths.length} paths scanned`);
675732
}
733+
if (changedTypes?.length) {
734+
parts.push(`changed: [${changedTypes.join(', ')}]`);
735+
}
736+
737+
this._chatDebugService.log(
738+
sessionResource,
739+
`Customization discovery (${label})`,
740+
parts.join(' | '),
741+
ChatDebugLogLevel.Info,
742+
{ id: eventId, category: 'customization-provider' },
743+
);
744+
}
745+
746+
private _resolveCustomizationEvent(eventId: string): IChatDebugResolvedEventContent | undefined {
747+
const result = this._customizationDebugDetails.get(eventId);
748+
if (!result) {
749+
return undefined;
750+
}
751+
752+
return {
753+
kind: 'fileList',
754+
discoveryType: 'customization-provider',
755+
files: [
756+
...result.items.map(item => ({
757+
uri: item.uri,
758+
name: item.name,
759+
status: 'loaded' as const,
760+
storage: item.groupKey,
761+
})),
762+
...(result.diagnostics?.skippedFiles ?? []).map(f => ({
763+
uri: f.uri,
764+
name: f.uri.path.split('/').pop(),
765+
status: 'skipped' as const,
766+
skipReason: f.reason,
767+
})),
768+
],
769+
sourceFolders: result.diagnostics?.scannedPaths?.map(uri => ({
770+
uri,
771+
storage: 'external',
772+
})),
773+
};
676774
}
677775
}
678776

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ export interface MainThreadChatAgentsShape2 extends IChatAgentProgressShape, IDi
15891589
$onDidChangePromptFiles(handle: number): void;
15901590
$registerChatSessionCustomizationProvider(handle: number, chatSessionType: string, metadata: IChatSessionCustomizationProviderMetadataDto, extension: ExtensionIdentifier): void;
15911591
$unregisterChatSessionCustomizationProvider(handle: number): void;
1592-
$onDidChangeCustomizations(handle: number): void;
1592+
$onDidChangeCustomizations(handle: number, event: IChatSessionCustomizationChangeEventDto): void;
15931593
$registerAgentCompletionsProvider(handle: number, id: string, triggerCharacters: string[]): void;
15941594
$unregisterAgentCompletionsProvider(handle: number, id: string): void;
15951595
$updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void;
@@ -1657,7 +1657,7 @@ export interface ExtHostChatAgentsShape2 {
16571657
$releaseSession(sessionResource: UriComponents): void;
16581658
$detectChatParticipant(handle: number, request: Dto<IChatAgentRequest>, context: { history: IChatAgentHistoryEntryDto[] }, options: { participants: IChatParticipantMetadata[]; location: ChatAgentLocation }, token: CancellationToken): Promise<IChatParticipantDetectionResult | null | undefined>;
16591659
$providePromptFiles(handle: number, type: PromptsType, context: IPromptFileContext, token: CancellationToken): Promise<Dto<IPromptFileResource>[] | undefined>;
1660-
$provideChatSessionCustomizations(handle: number, token: CancellationToken): Promise<IChatSessionCustomizationItemDto[] | undefined>;
1660+
$provideChatSessionCustomizations(handle: number, token: CancellationToken): Promise<IChatSessionCustomizationResultDto | undefined>;
16611661
$setRequestTools(requestId: string, tools: UserSelectedTools): void;
16621662
$setYieldRequested(requestId: string, value: boolean): void;
16631663
$acceptActiveChatSession(sessionResource: UriComponents | undefined): void;
@@ -1693,6 +1693,25 @@ export interface IChatSessionCustomizationItemDto {
16931693
readonly badge?: string;
16941694
readonly badgeTooltip?: string;
16951695
}
1696+
1697+
export interface IChatSessionCustomizationSkippedFileDto {
1698+
readonly uri: UriComponents;
1699+
readonly reason: string;
1700+
}
1701+
1702+
export interface IChatSessionCustomizationDiagnosticsDto {
1703+
readonly scannedPaths?: readonly UriComponents[];
1704+
readonly skippedFiles?: readonly IChatSessionCustomizationSkippedFileDto[];
1705+
}
1706+
1707+
export interface IChatSessionCustomizationResultDto {
1708+
readonly items: IChatSessionCustomizationItemDto[];
1709+
readonly diagnostics?: IChatSessionCustomizationDiagnosticsDto;
1710+
}
1711+
1712+
export interface IChatSessionCustomizationChangeEventDto {
1713+
readonly changedTypes?: readonly string[];
1714+
}
16961715
export interface IChatParticipantMetadata {
16971716
participant: string;
16981717
command?: string;

0 commit comments

Comments
 (0)