185 lines · 5.5 KB · TypeScript
RawDownload
/** * ErrorMessageBuilder service - Builds error messages and DetectedError objects. * * Provides utilities for: * - Extracting error messages from tool results * - Finding tool names by ID * - Creating DetectedError objects * - Truncating messages for display */ import { type ContentBlock, type ParsedMessage } from '@main/types';import { randomUUID } from 'crypto'; import { type ExtractedToolResult } from '../analysis/ToolResultExtractor'; import type { TriggerColor } from '@shared/constants/triggerColors'; // =============================================================================// Types// ============================================================================= /** * Represents a detected error from a Claude Code session. */export interface DetectedError {  /** UUID for unique identification */  id: string;  /** Unix timestamp when error was detected */  timestamp: number;  /** Session ID where error occurred */  sessionId: string;  /** Project ID (encoded directory name) */  projectId: string;  /** Path to the JSONL file */  filePath: string;  /** Source of the error - tool name or 'assistant' */  source: string;  /** Error message content */  message: string;  /** Line number in JSONL for deep linking */  lineNumber?: number;  /** Tool use ID for precise deep linking to the specific tool item */  toolUseId?: string;  /** Subagent ID when error originates from a subagent session */  subagentId?: string;  /** Trigger color key for notification dot and highlight */  triggerColor?: TriggerColor;  /** ID of the trigger that produced this notification */  triggerId?: string;  /** Human-readable name of the trigger that produced this notification */  triggerName?: string;  /** Additional context about the error */  context: {    /** Human-readable project name */    projectName: string;    /** Current working directory when error occurred */    cwd?: string;  };} /** * Parameters for creating a DetectedError. */export interface CreateDetectedErrorParams {  sessionId: string;  projectId: string;  filePath: string;  projectName: string;  lineNumber: number;  source: string;  message: string;  timestamp: Date;  cwd?: string;  toolUseId?: string;  subagentId?: string;  triggerColor?: TriggerColor;  triggerId?: string;  triggerName?: string;} // =============================================================================// Error Message Extraction// ============================================================================= /** * Extracts error message from a tool result. */export function extractErrorMessage(result: ExtractedToolResult): string {  if (typeof result.content === 'string') {    return result.content.trim() || 'Unknown error';  }   if (Array.isArray(result.content)) {    const texts: string[] = [];    for (const item of result.content) {      if (item && typeof item === 'object' && 'type' in item) {        const block = item as ContentBlock;        if (block.type === 'text' && 'text' in block) {          texts.push(block.text);        }      }    }    return texts.join('\n').trim() || 'Unknown error';  }   return 'Unknown error';} // =============================================================================// Tool Name Lookup// ============================================================================= /** * Finds tool name from message's tool calls by tool use ID. */function findToolName(message: ParsedMessage, toolUseId: string): string | null {  if (message.toolCalls) {    const toolCall = message.toolCalls.find((tc) => tc.id === toolUseId);    if (toolCall) {      return toolCall.name;    }  }  return null;} /** * Finds tool name by searching tool_use_id in the message context. */export function findToolNameByToolUseId(message: ParsedMessage, toolUseId: string): string | null {  // First check toolCalls  const fromToolCalls = findToolName(message, toolUseId);  if (fromToolCalls) return fromToolCalls;   // Check sourceToolUseID if this message is a tool result  if (message.sourceToolUseID === toolUseId && message.toolUseResult) {    if (typeof message.toolUseResult.toolName === 'string') {      return message.toolUseResult.toolName;    }  }   return null;} // =============================================================================// Message Truncation// ============================================================================= /** * Truncates error message to a reasonable length for display. */function truncateMessage(message: string, maxLength: number = 500): string {  if (message.length <= maxLength) {    return message;  }  return message.slice(0, maxLength) + '...';} // =============================================================================// DetectedError Creation// ============================================================================= /** * Creates a DetectedError object with all required fields. */export function createDetectedError(params: CreateDetectedErrorParams): DetectedError {  return {    id: randomUUID(),    timestamp: params.timestamp.getTime(),    sessionId: params.sessionId,    projectId: params.projectId,    filePath: params.filePath,    source: params.source,    message: truncateMessage(params.message),    lineNumber: params.lineNumber,    toolUseId: params.toolUseId,    subagentId: params.subagentId,    triggerColor: params.triggerColor,    triggerId: params.triggerId,    triggerName: params.triggerName,    context: {      projectName: params.projectName,      cwd: params.cwd,    },  };}