UNPKG

@stackmemoryai/stackmemory

Version:

Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, a

327 lines (325 loc) 9.3 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { logger } from "../../../core/monitoring/logger.js"; class TraceHandlers { constructor(deps) { this.deps = deps; } /** * Get traces with optional filtering */ async handleGetTraces(args) { if (args.analyze) { return this.handleAnalyzeTraces(args); } try { const { limit = 20, pattern, start_time, end_time, include_context = false } = args; const filters = { limit }; if (pattern) { filters.pattern = pattern; } if (start_time) { filters.startTime = new Date(start_time); } if (end_time) { filters.endTime = new Date(end_time); } const traces = await this.deps.traceDetector.getTraces(); if (traces.length === 0) { return { content: [ { type: "text", text: "No traces found matching criteria" } ] }; } const tracesSummary = traces.map((trace) => { const duration = trace.metadata.endTime && trace.metadata.startTime ? trace.metadata.endTime - trace.metadata.startTime : "ongoing"; return { id: trace.id, pattern: trace.compressed?.pattern || "Unknown", toolCount: trace.tools.length, duration: typeof duration === "number" ? `${duration}ms` : duration, status: "completed", startTime: new Date(trace.metadata.startTime).toISOString() }; }); const summaryText = tracesSummary.map( (t) => `${t.id}: ${t.pattern} (${t.toolCount} tools, ${t.duration}) [${t.status}]` ).join("\n"); const result = { content: [ { type: "text", text: `Traces (${traces.length}): ${summaryText}` } ], metadata: { traces: tracesSummary, totalCount: traces.length, filters } }; if (include_context) { result.metadata.fullTraces = traces; } return result; } catch (error) { logger.error( "Error getting traces", error instanceof Error ? error : new Error(String(error)) ); throw error; } } /** * Analyze trace patterns */ async handleAnalyzeTraces(args) { try { const { trace_id, analysis_type = "performance", include_recommendations = true } = args; let analysis; if (trace_id) { const traces = this.deps.traceDetector.getTraces(); const trace = traces.find((t) => t.id === trace_id); if (!trace) { throw new Error(`Trace not found: ${trace_id}`); } analysis = this.analyzeTrace(trace, analysis_type); } else { const traces = this.deps.traceDetector.getTraces(); analysis = this.analyzeRecentTraces(traces, analysis_type); } let analysisText = `Trace Analysis (${analysis_type}): `; switch (analysis_type) { case "performance": analysisText += `Performance Metrics: - Avg duration: ${analysis.avgDuration}ms - Slowest operation: ${analysis.slowestOperation?.name} (${analysis.slowestOperation?.duration}ms) - Tool usage: ${analysis.toolUsageStats} - Bottlenecks: ${analysis.bottlenecks?.join(", ") || "None detected"}`; break; case "patterns": analysisText += `Pattern Analysis: - Common sequences: ${analysis.commonSequences?.join(", ") || "None"} - Repetitive operations: ${analysis.repetitiveOps?.join(", ") || "None"} - Success rate: ${analysis.successRate}% - Failure patterns: ${analysis.failurePatterns?.join(", ") || "None"}`; break; case "errors": analysisText += `Error Analysis: - Error rate: ${analysis.errorRate}% - Common errors: ${analysis.commonErrors?.join(", ") || "None"} - Error sources: ${analysis.errorSources?.join(", ") || "None"} - Recovery patterns: ${analysis.recoveryPatterns?.join(", ") || "None"}`; break; default: analysisText += JSON.stringify(analysis, null, 2); } if (include_recommendations && analysis.recommendations) { analysisText += "\n\nRecommendations:\n"; analysisText += analysis.recommendations.map((rec, i) => `${i + 1}. ${rec}`).join("\n"); } return { content: [ { type: "text", text: analysisText } ], metadata: { analysis, analysisType: analysis_type, traceId: trace_id } }; } catch (error) { logger.error( "Error analyzing traces", error instanceof Error ? error : new Error(String(error)) ); throw error; } } /** * Start browser debugging session */ async handleStartBrowserDebug(args) { try { const { url, headless = false, width = 1280, height = 720, capture_screenshots = true } = args; if (!url) { throw new Error("URL is required for browser debugging"); } const sessionId = `session_${Date.now()}`; logger.info(`Would navigate session ${sessionId} to ${url}`); logger.info("Started browser debug session", { sessionId, url }); return { content: [ { type: "text", text: `Started browser debug session: ${sessionId} Navigated to: ${url}` } ], metadata: { sessionId, url, options: { headless, width, height, capture_screenshots } } }; } catch (error) { logger.error( "Error starting browser debug session", error instanceof Error ? error : new Error(String(error)) ); throw error; } } /** * Take screenshot for debugging */ async handleTakeScreenshot(args) { try { const { session_id, selector, full_page = false } = args; if (!session_id) { throw new Error("Session ID is required"); } const screenshot = { data: "mock-screenshot-data", format: "png" }; return { content: [ { type: "text", text: "Screenshot captured successfully" }, { type: "image", data: screenshot.data, mimeType: "image/png" } ], metadata: { sessionId: session_id, selector, fullPage: full_page, timestamp: Date.now() } }; } catch (error) { logger.error( "Error taking screenshot", error instanceof Error ? error : new Error(String(error)) ); throw error; } } /** * Execute JavaScript in browser for debugging */ async handleExecuteScript(args) { try { const { session_id, script, args: _scriptArgs = [] } = args; if (!session_id) { throw new Error("Session ID is required"); } if (!script) { throw new Error("Script is required"); } const result = { output: "Mock script execution result" }; return { content: [ { type: "text", text: `Script executed successfully: Result: ${JSON.stringify(result, null, 2)}` } ], metadata: { sessionId: session_id, script, result, timestamp: Date.now() } }; } catch (error) { logger.error( "Error executing script", error instanceof Error ? error : new Error(String(error)) ); throw error; } } /** * Stop browser debugging session */ async handleStopBrowserDebug(args) { try { const { session_id } = args; if (!session_id) { throw new Error("Session ID is required"); } logger.info(`Would close session ${session_id}`); logger.info("Stopped browser debug session", { sessionId: session_id }); return { content: [ { type: "text", text: `Stopped browser debug session: ${session_id}` } ], metadata: { sessionId: session_id, timestamp: Date.now() } }; } catch (error) { logger.error( "Error stopping browser debug session", error instanceof Error ? error : new Error(String(error)) ); throw error; } } analyzeTrace(trace, analysisType) { return { type: analysisType, summary: `Analysis of trace ${trace.id}`, toolCount: trace.tools.length, score: trace.score, patterns: trace.compressed?.pattern || "Unknown" }; } analyzeRecentTraces(traces, analysisType) { return { type: analysisType, summary: `Analysis of ${traces.length} recent traces`, totalTraces: traces.length, avgScore: traces.length > 0 ? traces.reduce((sum, t) => sum + t.score, 0) / traces.length : 0, commonPatterns: traces.map((t) => t.compressed?.pattern).filter(Boolean) }; } } export { TraceHandlers };