From 18cfaef76ddf6cb35c8ab5da6d5eee447aa503d7 Mon Sep 17 00:00:00 2001 From: Vijay Upadya <41652029+vijayupadya@users.noreply.github.com> Date: Wed, 18 Mar 2026 11:46:34 -0700 Subject: [PATCH] Agent Debug: Enable Claude Code session url and filter untitled sessions (#302903) * Claude and filter * feedback updates --- .../browser/chatDebug/chatDebugHomeView.ts | 15 ++++++-- .../chat/common/chatDebugServiceImpl.ts | 1 + .../test/common/chatDebugServiceImpl.test.ts | 34 +++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugHomeView.ts b/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugHomeView.ts index 8874ddebeaa..1dc8b8b231e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugHomeView.ts +++ b/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugHomeView.ts @@ -17,7 +17,7 @@ import { defaultButtonStyles } from '../../../../../platform/theme/browser/defau import { IChatDebugService } from '../../common/chatDebugService.js'; import { IChatService } from '../../common/chatService/chatService.js'; import { AGENT_DEBUG_LOG_ENABLED_SETTING } from '../../common/promptSyntax/promptTypes.js'; -import { LocalChatSessionUri } from '../../common/model/chatUri.js'; +import { getChatSessionType, isUntitledChatSession, LocalChatSessionUri } from '../../common/model/chatUri.js'; import { IChatWidgetService } from '../chat.js'; import { IPreferencesService } from '../../../../services/preferences/common/preferences.js'; @@ -88,7 +88,12 @@ export class ChatDebugHomeView extends Disposable { // List sessions that have debug event data. // Use the debug service as the source of truth — it includes sessions // whose chat models may have been archived (e.g. when a new chat was started). - const sessionResources = [...this.chatDebugService.getSessionResources()].reverse(); + const cliSessionTypes = new Set(['copilotcli', 'claude-code']); + const sessionResources = [...this.chatDebugService.getSessionResources()].reverse() + // Hide untitled bootstrap sessions for CLI session types (e.g. copilotcli, claude-code). + // These are transient sessions created during async session setup that only contain + // a single "Load Hooks" event and would confuse users. + .filter(r => !cliSessionTypes.has(getChatSessionType(r)) || !isUntitledChatSession(r)); // Sort: active session first if (activeSessionResource) { @@ -122,10 +127,14 @@ export class ChatDebugHomeView extends Disposable { sessionTitle = localize('chatDebug.newSession', "New Chat"); } else if (importedTitle) { sessionTitle = localize('chatDebug.importedSession', "Imported: {0}", importedTitle); - } else if (sessionResource.scheme === 'copilotcli') { + } else if (getChatSessionType(sessionResource) === 'copilotcli') { const pathId = sessionResource.path.replace(/^\//, '').split('-')[0]; const shortId = pathId || sessionResource.authority || sessionResource.toString(); sessionTitle = localize('chatDebug.copilotCliSessionWithId', "Copilot CLI: {0}", shortId); + } else if (getChatSessionType(sessionResource) === 'claude-code') { + const pathId = sessionResource.path.replace(/^\//, '').split('-')[0]; + const shortId = pathId || sessionResource.authority || sessionResource.toString(); + sessionTitle = localize('chatDebug.claudeCodeSessionWithId', "Claude Code: {0}", shortId); } else { sessionTitle = localize('chatDebug.newSession', "New Chat"); } diff --git a/src/vs/workbench/contrib/chat/common/chatDebugServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatDebugServiceImpl.ts index 00ff71eda6a..60d45fe1f8d 100644 --- a/src/vs/workbench/contrib/chat/common/chatDebugServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatDebugServiceImpl.ts @@ -121,6 +121,7 @@ export class ChatDebugServiceImpl extends Disposable implements IChatDebugServic private static readonly _debugEligibleSchemes = new Set([ LocalChatSessionUri.scheme, // vscode-chat-session (local sessions) 'copilotcli', // Copilot CLI background sessions + 'claude-code', // Claude Code CLI sessions ]); private _isDebugEligibleSession(sessionResource: URI): boolean { diff --git a/src/vs/workbench/contrib/chat/test/common/chatDebugServiceImpl.test.ts b/src/vs/workbench/contrib/chat/test/common/chatDebugServiceImpl.test.ts index 386c7a916cf..62e41c7fcb8 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatDebugServiceImpl.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatDebugServiceImpl.test.ts @@ -23,6 +23,7 @@ suite('ChatDebugServiceImpl', () => { const sessionGeneric = URI.parse('vscode-chat-session://local/session'); const nonLocalSession = URI.parse('some-other-scheme://authority/session-1'); const copilotCliSession = URI.parse('copilotcli:/test-session-id'); + const claudeCodeSession = URI.parse('claude-code:/test-session-id'); setup(() => { service = disposables.add(new ChatDebugServiceImpl()); @@ -168,6 +169,16 @@ suite('ChatDebugServiceImpl', () => { assert.strictEqual(firedEvents.length, 1); assert.strictEqual(service.getEvents(copilotCliSession).length, 1); }); + + test('should log events for claude-code sessions', () => { + const firedEvents: IChatDebugEvent[] = []; + disposables.add(service.onDidAddEvent(e => firedEvents.push(e))); + + service.log(claudeCodeSession, 'claude-event', 'details'); + + assert.strictEqual(firedEvents.length, 1); + assert.strictEqual(service.getEvents(claudeCodeSession).length, 1); + }); }); suite('getSessionResources', () => { @@ -492,6 +503,29 @@ suite('ChatDebugServiceImpl', () => { assert.ok(service.getEvents(copilotCliSession).length > 0); }); + test('should invoke providers for claude-code sessions', async () => { + let providerCalled = false; + + const provider: IChatDebugLogProvider = { + provideChatDebugLog: async () => { + providerCalled = true; + return [{ + kind: 'generic', + sessionResource: claudeCodeSession, + created: new Date(), + name: 'claude-provider-event', + level: ChatDebugLogLevel.Info, + }]; + }, + }; + + disposables.add(service.registerProvider(provider)); + await service.invokeProviders(claudeCodeSession); + + assert.strictEqual(providerCalled, true); + assert.ok(service.getEvents(claudeCodeSession).length > 0); + }); + test('newly registered provider should be invoked for active sessions', async () => { // Start an invocation before the provider is registered const firstProvider: IChatDebugLogProvider = {