VS Code Copilot Chat Session Files Technical Guide - NiclasOlofsson/remember-mcp-vscode GitHub Wiki
Last Updated: August 13, 2025
Based on empirical analysis of VS Code Insiders and VS Code Stable
- Overview
- File Location and Discovery
- File Structure and Format
- Session Writing Behavior
- Cross-Platform Implementation
- Programming Interface
- Performance Considerations
- Troubleshooting
- Implementation Examples
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.
- 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
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) orCode - Insiders -
{WorkspaceHash}: 32-character hexadecimal workspace identifier -
{SessionId}: UUID v4 session identifier
%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
~/Library/Application Support/Code/User/workspaceStorage/{hash}/chatSessions/
~/Library/Application Support/Code - Insiders/User/workspaceStorage/{hash}/chatSessions/
~/.config/Code/User/workspaceStorage/{hash}/chatSessions/
~/.config/Code - Insiders/User/workspaceStorage/{hash}/chatSessions/
Since this code runs within a VS Code extension context, use the VS Code Extension API for reliable path discovery:
- Use VS Code Extension API (
vscode.ExtensionContext.storageUri) to get current workspace storage - Navigate up directory structure to find the common root
- Construct paths for both VS Code editions
- Scan all workspace directories for
chatSessionssubdirectories
{
"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
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:
-
requestId: Unique identifier for this conversation turn -
timestamp: Unix timestamp (milliseconds) when request was made -
message.text: The user's input text
-
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)
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
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
}Not all requests have a modelId field - this is normal for:
- System commands
- Agent routing requests
- Error conditions
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
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
- 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
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];
}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;
}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'
);
}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;
}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;
}
}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;
}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}`);
}
}
}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
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
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}`);
}
}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
};
}
}// 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.