UNPKG

@debugmcp/mcp-debugger

Version:

Run-time step-through debugging for LLM agents.

121 lines (107 loc) 6 kB
/** * Data retrieval operations for session management including variables, * stack traces, and scopes. */ import { Variable, StackFrame } from './models.js'; import { DebugProtocol } from '@vscode/debugprotocol'; import { SessionState } from './models.js'; import { SessionManagerCore } from './session-manager-core.js'; /** * Data retrieval functionality for session management */ export class SessionManagerData extends SessionManagerCore { async getVariables(sessionId: string, variablesReference: number): Promise<Variable[]> { const session = this._getSessionById(sessionId); this.logger.info(`[SM getVariables ${sessionId}] Entered. variablesReference: ${variablesReference}, Current state: ${session.state}`); if (!session.proxyManager || !session.proxyManager.isRunning()) { this.logger.warn(`[SM getVariables ${sessionId}] No active proxy.`); return []; } if (session.state !== SessionState.PAUSED) { this.logger.warn(`[SM getVariables ${sessionId}] Session not paused. State: ${session.state}.`); return []; } try { this.logger.info(`[SM getVariables ${sessionId}] Sending DAP 'variables' for variablesReference ${variablesReference}.`); const response = await session.proxyManager.sendDapRequest<DebugProtocol.VariablesResponse>('variables', { variablesReference }); this.logger.info(`[SM getVariables ${sessionId}] DAP 'variables' response received. Body:`, response?.body); if (response && response.body && response.body.variables) { const vars = response.body.variables.map((v: DebugProtocol.Variable) => ({ name: v.name, value: v.value, type: v.type || "<unknown_type>", variablesReference: v.variablesReference, expandable: v.variablesReference > 0 })); this.logger.info(`[SM getVariables ${sessionId}] Parsed variables:`, vars.map(v => ({name: v.name, value: v.value, type: v.type}))); return vars; } this.logger.warn(`[SM getVariables ${sessionId}] No variables in response body for reference ${variablesReference}. Response:`, response); return []; } catch (error) { this.logger.error(`[SM getVariables ${sessionId}] Error getting variables:`, error); return []; } } async getStackTrace(sessionId: string, threadId?: number): Promise<StackFrame[]> { const session = this._getSessionById(sessionId); const currentThreadId = session.proxyManager?.getCurrentThreadId(); this.logger.info(`[SM getStackTrace ${sessionId}] Entered. Requested threadId: ${threadId}, Current state: ${session.state}, Actual currentThreadId: ${currentThreadId}`); if (!session.proxyManager || !session.proxyManager.isRunning()) { this.logger.warn(`[SM getStackTrace ${sessionId}] No active proxy.`); return []; } if (session.state !== SessionState.PAUSED) { this.logger.warn(`[SM getStackTrace ${sessionId}] Session not paused. State: ${session.state}.`); return []; } const currentThreadForRequest = threadId || currentThreadId; if (!currentThreadForRequest) { this.logger.warn(`[SM getStackTrace ${sessionId}] No effective thread ID to use.`); return []; } try { this.logger.info(`[SM getStackTrace ${sessionId}] Sending DAP 'stackTrace' for threadId ${currentThreadForRequest}.`); const response = await session.proxyManager.sendDapRequest<DebugProtocol.StackTraceResponse>('stackTrace', { threadId: currentThreadForRequest }); this.logger.info(`[SM getStackTrace ${sessionId}] DAP 'stackTrace' response received. Body:`, response?.body); if (response && response.body && response.body.stackFrames) { const frames = response.body.stackFrames.map((sf: DebugProtocol.StackFrame) => ({ id: sf.id, name: sf.name, file: sf.source?.path || sf.source?.name || "<unknown_source>", line: sf.line, column: sf.column })); this.logger.info(`[SM getStackTrace ${sessionId}] Parsed stack frames (top 3):`, frames.slice(0,3).map(f => ({name:f.name, file:f.file, line:f.line}))); return frames; } this.logger.warn(`[SM getStackTrace ${sessionId}] No stackFrames in response body. Response:`, response); return []; } catch (error) { this.logger.error(`[SM getStackTrace ${sessionId}] Error getting stack trace:`, error); return []; } } async getScopes(sessionId: string, frameId: number): Promise<DebugProtocol.Scope[]> { const session = this._getSessionById(sessionId); this.logger.info(`[SM getScopes ${sessionId}] Entered. frameId: ${frameId}, Current state: ${session.state}`); if (!session.proxyManager || !session.proxyManager.isRunning()) { this.logger.warn(`[SM getScopes ${sessionId}] No active proxy.`); return []; } if (session.state !== SessionState.PAUSED) { this.logger.warn(`[SM getScopes ${sessionId}] Session not paused. State: ${session.state}.`); return []; } try { this.logger.info(`[SM getScopes ${sessionId}] Sending DAP 'scopes' for frameId ${frameId}.`); const response = await session.proxyManager.sendDapRequest<DebugProtocol.ScopesResponse>('scopes', { frameId }); this.logger.info(`[SM getScopes ${sessionId}] DAP 'scopes' response received. Body:`, response?.body); if (response && response.body && response.body.scopes) { this.logger.info(`[SM getScopes ${sessionId}] Parsed scopes:`, response.body.scopes.map(s => ({name: s.name, ref: s.variablesReference, expensive: s.expensive }))); return response.body.scopes; } this.logger.warn(`[GetScopes] No scopes in response body for session ${sessionId}, frameId ${frameId}. Response:`, response); return []; } catch (error) { this.logger.error(`[SM getScopes ${sessionId}] Error getting scopes:`, error); return []; } } }