Skip to content

VS Code Copilot Chat Session Files Technical Guide

Niclas Olofsson edited this page Aug 13, 2025 · 1 revision

VS Code Copilot Chat Session Files - Complete Technical Guide

Last Updated: August 13, 2025
Based on empirical analysis of VS Code Insiders and VS Code Stable

Table of Contents

  1. Overview
  2. File Location and Discovery
  3. File Structure and Format
  4. Session Writing Behavior
  5. Cross-Platform Implementation
  6. Programming Interface
  7. Performance Considerations
  8. Troubleshooting
  9. Implementation Examples

Overview

VS Code Copilot chat session files are JSON documents that contain the complete conversation history between users and Copilot agents. Unlike log files that contain operational data, session files store the actual conversational content, making them the primary source for chat analytics and usage tracking.

Key Characteristics

  • Format: JSON with structured request/response data
  • Location: Workspace-specific storage within VS Code's user data
  • Naming: UUID-based filenames (e.g., 5eba223c-15f9-4aea-855d-b7894480893a.json)
  • Writing: Real-time during conversations with intelligent buffering
  • Size: Can range from KB to 100MB+ for long conversations
  • Persistence: Survives VS Code restarts and remain until manually deleted

File Location and Discovery

Storage Path Pattern

Session files follow a consistent pattern across all platforms:

{UserDataRoot}/{Edition}/User/workspaceStorage/{WorkspaceHash}/chatSessions/{SessionId}.json

Components:

  • {UserDataRoot}: Platform-specific application data directory
  • {Edition}: Code (Stable) or Code - Insiders
  • {WorkspaceHash}: 32-character hexadecimal workspace identifier
  • {SessionId}: UUID v4 session identifier

Platform-Specific Locations

Windows

%APPDATA%\Code\User\workspaceStorage\{hash}\chatSessions\
%APPDATA%\Code - Insiders\User\workspaceStorage\{hash}\chatSessions\

Example:

C:\Users\Username\AppData\Roaming\Code - Insiders\User\workspaceStorage\eda44c8ede7e313a77c45273ce6f92c5\chatSessions\5eba223c-15f9-4aea-855d-b7894480893a.json

macOS

~/Library/Application Support/Code/User/workspaceStorage/{hash}/chatSessions/
~/Library/Application Support/Code - Insiders/User/workspaceStorage/{hash}/chatSessions/

Linux

~/.config/Code/User/workspaceStorage/{hash}/chatSessions/
~/.config/Code - Insiders/User/workspaceStorage/{hash}/chatSessions/

Discovery Strategy

Since this code runs within a VS Code extension context, use the VS Code Extension API for reliable path discovery:

  1. Use VS Code Extension API (vscode.ExtensionContext.storageUri) to get current workspace storage
  2. Navigate up directory structure to find the common root
  3. Construct paths for both VS Code editions
  4. Scan all workspace directories for chatSessions subdirectories

File Structure and Format

Top-Level Session Structure

{
  "sessionId": "5eba223c-15f9-4aea-855d-b7894480893a",
  "creationDate": 1723563355820,
  "version": 3,
  "requests": [
    // Array of request objects
  ]
}

Field Descriptions:

  • sessionId: UUID v4 identifier, matches filename
  • creationDate: Unix timestamp (milliseconds) of session creation
  • version: Schema version (currently 3)
  • requests: Array containing all conversation turns

Request Object Structure

Each conversation turn is represented as a request object:

{
  "requestId": "request_82af4373-f3d3-4f60-b9d2-0f1fe051db59",
  "timestamp": 1723563931179,
  "modelId": "copilot/claude-sonnet-4",
  "message": {
    "text": "User's message content here",
    "attachments": []
  },
  "agent": {
    "id": "github.copilot.editsAgent",
    "name": "GitHub Copilot"
  },
  "response": {
    // Response content varies by agent
  }
}

Field Details:

Required Fields

  • requestId: Unique identifier for this conversation turn
  • timestamp: Unix timestamp (milliseconds) when request was made
  • message.text: The user's input text

Optional Fields

  • modelId: AI model used (e.g., "copilot/claude-sonnet-4", "copilot/gpt-4o")
  • agent.id: Agent that processed the request
  • agent.name: Human-readable agent name
  • message.attachments: Array of file attachments
  • response: Agent's response (structure varies by agent type)

Agent Types

Common agent identifiers found in session files:

  • github.copilot.editsAgent: Code editing and file manipulation
  • github.copilot.chatAgent: General chat conversations
  • github.copilot.terminalAgent: Terminal command assistance
  • github.copilot.workspaceAgent: Workspace-level operations

Special Cases

Slash Commands

Commands like /clear, /help may not have an agent field:

{
  "requestId": "request_clear_123",
  "timestamp": 1723563931179,
  "message": {
    "text": "/clear"
  }
  // No agent field for system commands
}

Requests Without Models

Not all requests have a modelId field - this is normal for:

  • System commands
  • Agent routing requests
  • Error conditions

Session Writing Behavior

Real-Time Writing

Based on empirical testing, VS Code writes session data:

✅ When Sessions Are Written:

  • During active conversations (not just at end)
  • After multiple tool interactions
  • With intelligent buffering/debouncing
  • Incrementally as conversations progress

❌ When Sessions Are NOT Written:

  • After every single user input immediately
  • On a fixed time schedule
  • Only when conversations end

Writing Frequency

Observed Pattern:

  • High Activity: More frequent writes during tool-heavy conversations
  • Buffering: Multiple requests may be batched together
  • Debouncing: Rapid sequential actions are grouped
  • File Growth: Sessions grow incrementally, not all-at-once

File Size Implications

  • Small Sessions: Few KB for short conversations
  • Medium Sessions: 1-10MB for typical development sessions
  • Large Sessions: 50-100MB+ for extensive conversations with many tool calls
  • Maximum Observed: 111MB+ files have been encountered in production

Cross-Platform Implementation

Path Discovery Code Example

function discoverSessionStoragePaths(extensionContext: vscode.ExtensionContext): string[] {
    const myExtensionPath = extensionContext.storageUri.fsPath;
    
    // Navigate up to find the common AppData root
    // Path: workspaceStorage/{hash}/extension-name -> AppData/Roaming/
    const appDataRoot = path.dirname(path.dirname(path.dirname(path.dirname(path.dirname(myExtensionPath)))));
    
    // Construct both edition paths
    const stablePath = path.join(appDataRoot, 'Code', 'User', 'workspaceStorage');
    const insidersPath = path.join(appDataRoot, 'Code - Insiders', 'User', 'workspaceStorage');
    
    return [stablePath, insidersPath];
}

File Scanning Implementation

async function scanForSessionFiles(storagePaths: string[]): Promise<string[]> {
    const sessionFiles: string[] = [];
    const sessionPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.json$/i;
    
    for (const storagePath of storagePaths) {
        try {
            await fs.access(storagePath);
            const workspaceDirs = await fs.readdir(storagePath, { withFileTypes: true });
            
            for (const workspaceDir of workspaceDirs) {
                if (!workspaceDir.isDirectory()) continue;
                
                const chatSessionsPath = path.join(storagePath, workspaceDir.name, 'chatSessions');
                
                try {
                    await fs.access(chatSessionsPath);
                    const files = await fs.readdir(chatSessionsPath);
                    
                    for (const fileName of files) {
                        if (sessionPattern.test(fileName)) {
                            sessionFiles.push(path.join(chatSessionsPath, fileName));
                        }
                    }
                } catch {
                    // chatSessions directory doesn't exist
                    continue;
                }
            }
        } catch (error) {
            console.warn(`Could not scan ${storagePath}: ${error}`);
        }
    }
    
    return sessionFiles;
}

Programming Interface

Session Validation

interface CopilotChatSession {
    sessionId: string;
    creationDate: number;
    version: number;
    requests: CopilotRequest[];
}

interface CopilotRequest {
    requestId: string;
    timestamp: number;
    modelId?: string;
    message: {
        text: string;
        attachments?: any[];
    };
    agent?: {
        id: string;
        name?: string;
    };
    response?: any;
}

function validateSession(obj: any): obj is CopilotChatSession {
    return (
        typeof obj?.sessionId === 'string' &&
        typeof obj?.creationDate === 'number' &&
        typeof obj?.version === 'number' &&
        Array.isArray(obj?.requests)
    );
}

function validateRequest(req: any): req is CopilotRequest {
    return (
        typeof req?.requestId === 'string' &&
        typeof req?.timestamp === 'number' &&
        typeof req?.message?.text === 'string'
    );
}

File Watching

function setupSessionWatcher(sessionDirectories: string[], callback: (filePath: string) => void) {
    const pattern = new vscode.RelativePattern(
        vscode.workspace.workspaceFolders?.[0] || vscode.Uri.file(os.homedir()),
        '**/chatSessions/*.json'
    );
    
    const watcher = vscode.workspace.createFileSystemWatcher(pattern);
    
    // Debounce file changes (sessions update in bursts)
    let debounceTimer: NodeJS.Timeout;
    const debounceMs = 3000;
    
    const handleChange = (uri: vscode.Uri) => {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => {
            callback(uri.fsPath);
        }, debounceMs);
    };
    
    watcher.onDidCreate(handleChange);
    watcher.onDidChange(handleChange);
    
    return watcher;
}

Performance Considerations

File Size Management

const MAX_FILE_SIZE_MB = 100;

async function shouldProcessFile(filePath: string): Promise<boolean> {
    try {
        const stats = await fs.stat(filePath);
        const sizeMB = stats.size / (1024 * 1024);
        
        if (sizeMB > MAX_FILE_SIZE_MB) {
            console.warn(`Skipping large session file: ${filePath} (${sizeMB.toFixed(1)}MB)`);
            return false;
        }
        
        return true;
    } catch {
        return false;
    }
}

Batch Processing

async function processSessionsBatch(filePaths: string[], batchSize = 50): Promise<CopilotChatSession[]> {
    const results: CopilotChatSession[] = [];
    
    for (let i = 0; i < filePaths.length; i += batchSize) {
        const batch = filePaths.slice(i, i + batchSize);
        
        const batchPromises = batch.map(async (filePath) => {
            try {
                if (!(await shouldProcessFile(filePath))) return null;
                
                const content = await fs.readFile(filePath, 'utf-8');
                const session = JSON.parse(content);
                
                return validateSession(session) ? session : null;
            } catch {
                return null;
            }
        });
        
        const batchResults = await Promise.all(batchPromises);
        results.push(...batchResults.filter(s => s !== null) as CopilotChatSession[]);
        
        // Progress reporting for large datasets
        if (filePaths.length > 100 && i % 100 === 0) {
            console.log(`Processed ${i + batchSize}/${filePaths.length} session files...`);
        }
    }
    
    return results;
}

Troubleshooting

Common Issues

1. Sessions Not Found

Symptoms: No session files discovered Causes:

  • Wrong VS Code edition (Stable vs Insiders)
  • Workspace not active in Copilot
  • Permissions issues
  • Non-standard VS Code installation

Solutions:

// Verify paths exist
async function diagnosePaths(paths: string[]) {
    for (const path of paths) {
        try {
            await fs.access(path);
            console.log(`✅ Path exists: ${path}`);
            
            const dirs = await fs.readdir(path);
            console.log(`   Found ${dirs.length} workspace directories`);
        } catch {
            console.log(`❌ Path not accessible: ${path}`);
        }
    }
}

2. Large File Handling

Symptoms: Memory issues, slow processing Causes: Session files > 50MB Solutions:

  • Implement file size limits
  • Use streaming JSON parsing for large files
  • Process files in smaller batches

3. Permission Errors

Symptoms: EACCES, EPERM errors Causes: VS Code has files locked, permission restrictions Solutions:

  • Implement retry logic with exponential backoff
  • Check file locks before processing
  • Run with appropriate permissions

Debugging Techniques

Session Content Inspection

async function debugSessionFile(filePath: string) {
    try {
        const stats = await fs.stat(filePath);
        console.log(`File: ${path.basename(filePath)}`);
        console.log(`Size: ${(stats.size / 1024).toFixed(1)}KB`);
        console.log(`Modified: ${stats.mtime.toISOString()}`);
        
        const content = await fs.readFile(filePath, 'utf-8');
        const session = JSON.parse(content);
        
        console.log(`Session ID: ${session.sessionId}`);
        console.log(`Created: ${new Date(session.creationDate).toISOString()}`);
        console.log(`Requests: ${session.requests.length}`);
        console.log(`Version: ${session.version}`);
        
        // Check for recent activity
        if (session.requests.length > 0) {
            const lastRequest = session.requests[session.requests.length - 1];
            console.log(`Last activity: ${new Date(lastRequest.timestamp).toISOString()}`);
        }
    } catch (error) {
        console.error(`Error analyzing ${filePath}: ${error}`);
    }
}

Implementation Examples

Complete Session Scanner Class

export class ChatSessionScanner {
    private readonly logger: ILogger;
    
    constructor(logger: ILogger) {
        this.logger = logger;
    }
    
    async scanAllSessions(storagePaths: string[]): Promise<{
        sessions: CopilotChatSession[];
        stats: {
            totalFiles: number;
            totalRequests: number;
            totalSessions: number;
            scanDuration: number;
            oldestSession?: string;
            newestSession?: string;
        };
    }> {
        const startTime = Date.now();
        
        this.logger.info(`Scanning ${storagePaths.length} storage locations...`);
        
        const allFiles = await this.findAllSessionFiles(storagePaths);
        const sessions = await this.processSessionFiles(allFiles);
        
        const stats = this.calculateStats(sessions, allFiles.length, Date.now() - startTime);
        
        return { sessions, stats };
    }
    
    private async findAllSessionFiles(storagePaths: string[]): Promise<string[]> {
        // Implementation from earlier examples
    }
    
    private async processSessionFiles(filePaths: string[]): Promise<CopilotChatSession[]> {
        // Implementation from earlier examples
    }
    
    private calculateStats(sessions: CopilotChatSession[], totalFiles: number, duration: number) {
        const totalRequests = sessions.reduce((sum, s) => sum + s.requests.length, 0);
        
        let oldestSession: string | undefined;
        let newestSession: string | undefined;
        
        for (const session of sessions) {
            const dateStr = new Date(session.creationDate).toISOString();
            if (!oldestSession || dateStr < oldestSession) {
                oldestSession = dateStr;
            }
            if (!newestSession || dateStr > newestSession) {
                newestSession = dateStr;
            }
        }
        
        return {
            totalFiles,
            totalRequests,
            totalSessions: sessions.length,
            scanDuration: duration,
            oldestSession,
            newestSession
        };
    }
}

Usage Example

// Initialize scanner with Extension API for path discovery
const logger = new ConsoleLogger();
const scanner = new ChatSessionScanner(logger);

// Discover storage paths using Extension API
const storagePaths = discoverSessionStoragePaths(context);

// Scan all sessions  
const { sessions, stats } = await scanner.scanAllSessions(storagePaths);

console.log(`Found ${stats.totalSessions} sessions with ${stats.totalRequests} total requests`);
console.log(`Scan completed in ${stats.scanDuration}ms`);

// Analyze recent activity
const recentSessions = sessions.filter(s => 
    Date.now() - s.creationDate < 24 * 60 * 60 * 1000 // Last 24 hours
);

console.log(`${recentSessions.length} sessions in the last 24 hours`);

This document is based on empirical analysis and reverse engineering of VS Code Copilot session files. Implementation details may change with VS Code updates.

Clone this wiki locally