package cc.unitmesh.agent.render import cc.unitmesh.agent.Platform import cc.unitmesh.agent.tool.ToolType import cc.unitmesh.agent.tool.impl.docql.DocQLSearchStats import cc.unitmesh.devins.llm.Message import cc.unitmesh.devins.llm.MessageRole import cc.unitmesh.llm.compression.TokenInfo /** * Shared data models for Renderer implementations. * Used by both ComposeRenderer and JewelRenderer. */ /** * Information about a tool call for display purposes. */ data class ToolCallInfo( val toolName: String, val description: String, val details: String? = null ) /** * Internal display info for formatting tool calls. */ data class ToolCallDisplayInfo( val toolName: String, val description: String, val details: String? ) /** * Task information from plan management tool. */ data class TaskInfo( val taskName: String, val status: TaskStatus, val summary: String = "", val timestamp: Long = Platform.getCurrentTimestamp(), val startTime: Long = Platform.getCurrentTimestamp() ) /** * Task status enum with display names. */ enum class TaskStatus(val displayName: String) { PLANNING("Planning"), WORKING("Working"), COMPLETED("Completed"), BLOCKED("Blocked"), CANCELLED("Cancelled"); companion object { fun fromString(status: String): TaskStatus { return entries.find { it.name.equals(status, ignoreCase = true) } ?: WORKING } } } /** * Base timeline item for chronological rendering. * This is the shared base class for timeline items in both ComposeRenderer and JewelRenderer. * * **Important**: When using `copy()` on data class instances, the `id` and `timestamp` * default parameters are NOT re-evaluated. This means copied items will retain the * original `id` and `timestamp` unless explicitly overridden: * ```kotlin * val item1 = TimelineItem.MessageItem(role = MessageRole.USER, content = "Hello") * val item2 = item1.copy(content = "World") // item2.id == item1.id (same ID!) * // To get a new ID: * val item3 = item1.copy(content = "World", id = TimelineItem.generateId()) * ``` */ sealed class TimelineItem( open val timestamp: Long = Platform.getCurrentTimestamp(), open val id: String = generateId() ) { /** * Message item for user/assistant/system messages. * Supports both simple role+content and full Message object. */ data class MessageItem( val message: Message? = null, val role: MessageRole = message?.role ?: MessageRole.USER, val content: String = message?.content ?: "", val tokenInfo: TokenInfo? = null, override val timestamp: Long = message?.timestamp ?: Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) { /** * Secondary constructor for simple role+content usage (JewelRenderer). */ constructor( role: MessageRole, content: String, tokenInfo: TokenInfo? = null, timestamp: Long = Platform.getCurrentTimestamp(), id: String = generateId() ) : this( message = null, role = role, content = content, tokenInfo = tokenInfo, timestamp = timestamp, id = id ) } /** * Combined tool call and result item - displays both in a single compact row. * This is the primary way to display tool executions. */ data class ToolCallItem( val toolName: String, val description: String = "", val params: String = "", val fullParams: String? = null, val filePath: String? = null, val toolType: ToolType? = null, val success: Boolean? = null, // null means still executing val summary: String? = null, val output: String? = null, val fullOutput: String? = null, val executionTimeMs: Long? = null, // DocQL-specific search statistics val docqlStats: DocQLSearchStats? = null, override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) /** * Error item for displaying errors. */ data class ErrorItem( val message: String, override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) /** * Info item for displaying informational messages (non-error, non-warning). * Used for status updates, progress information, database context, etc. */ data class InfoItem( val message: String, override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) /** * Task completion item. */ data class TaskCompleteItem( val success: Boolean, val message: String, val iterations: Int = 0, override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) /** * Terminal output item for shell command results. */ data class TerminalOutputItem( val command: String, val output: String, val exitCode: Int, val executionTimeMs: Long, override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) /** * Live terminal session - connected to a PTY process for real-time output. * This is only used on platforms that support PTY (JVM with JediTerm). * * When the session completes, exitCode and executionTimeMs will be set. * The UI can use these to show completion status without creating a separate TerminalOutputItem. */ data class LiveTerminalItem( val sessionId: String, val command: String, val workingDirectory: String?, val ptyHandle: Any?, // Platform-specific: on JVM this is a PtyProcess val exitCode: Int? = null, // null = still running, non-null = completed val executionTimeMs: Long? = null, // null = still running val output: String? = null, // Captured output when completed (optional) override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) { /** * Check if the terminal session is still running */ fun isRunning(): Boolean = exitCode == null /** * Check if the terminal session completed successfully */ fun isSuccess(): Boolean = exitCode == 0 } /** * Agent-generated sketch block item for displaying interactive visualizations. * Used for chart, nanodsl, mermaid, and other renderable code blocks generated by SubAgents. */ data class AgentSketchBlockItem( val agentName: String, val language: String, val code: String, val metadata: Map = emptyMap(), override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) /** * Thinking/reasoning content item for displaying agent's internal thoughts. * Used by ACP agents and other agents that expose their thinking process. */ data class ThinkingItem( val content: String, override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) /** * ChatDB execution step item for displaying database query execution steps. * Each step can be expanded/collapsed and shows detailed information. */ data class ChatDBStepItem( val stepType: ChatDBStepType, val status: ChatDBStepStatus, val title: String, val details: Map = emptyMap(), val error: String? = null, override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) /** * Multimodal analysis item for displaying vision model analysis progress and results. * Shows image thumbnails, analysis progress, and streaming results. */ data class MultimodalAnalysisItem( val images: List, val prompt: String, val visionModel: String, val status: MultimodalAnalysisStatus, val progress: String? = null, val streamingResult: String = "", val finalResult: String? = null, val error: String? = null, val executionTimeMs: Long? = null, override val timestamp: Long = Platform.getCurrentTimestamp(), override val id: String = generateId() ) : TimelineItem(timestamp, id) companion object { /** * Thread-safe counter for generating unique IDs. * Uses timestamp + random component to avoid collisions across threads/instances. */ private val random = kotlin.random.Random /** * Generates a unique ID for timeline items. * Uses timestamp + random component for thread-safety without requiring atomic operations. */ fun generateId(): String = "${Platform.getCurrentTimestamp()}-${random.nextInt(0, Int.MAX_VALUE)}" } } /** * ChatDB execution step types */ enum class ChatDBStepType(val displayName: String, val icon: String) { FETCH_SCHEMA("Fetch Database Schema", "๐Ÿ“Š"), SCHEMA_LINKING("Schema Linking", "๐Ÿ”—"), GENERATE_SQL("Generate SQL Query", "๐Ÿค–"), VALIDATE_SQL("Validate SQL", "โœ“"), REVISE_SQL("Revise SQL", "๐Ÿ”„"), /** Dry run to validate SQL without executing (uses transaction rollback) */ DRY_RUN("Dry Run Validation", "๐Ÿงช"), /** Waiting for user approval before executing write operation */ AWAIT_APPROVAL("Awaiting Approval", "?"), EXECUTE_SQL("Execute SQL Query", "โšก"), /** Execute write operation (INSERT, UPDATE, DELETE, DDL) */ EXECUTE_WRITE("Execute Write Operation", "!"), GENERATE_VISUALIZATION("Generate Visualization", "๐Ÿ“ˆ"), FINAL_RESULT("Query Result", "โœ…") } /** * ChatDB execution step status */ enum class ChatDBStepStatus(val displayName: String) { PENDING("Pending"), IN_PROGRESS("In Progress"), SUCCESS("Success"), WARNING("Warning"), ERROR("Error"), /** User approval is required */ AWAITING_APPROVAL("Awaiting Approval"), /** User approved the operation */ APPROVED("Approved"), /** User rejected the operation */ REJECTED("Rejected") } /** * Image information for multimodal analysis display. */ data class ImageInfo( val id: String, val name: String, val path: String? = null, val mimeType: String = "image/png", val uploadedUrl: String? = null, val displaySize: String = "", val compressionSavings: String? = null ) /** * Status of multimodal analysis. */ enum class MultimodalAnalysisStatus(val displayName: String) { /** Images are being compressed */ COMPRESSING("Compressing images..."), /** Images are being uploaded to cloud storage */ UPLOADING("Uploading to cloud..."), /** Vision model is analyzing images */ ANALYZING("Analyzing with vision model..."), /** Streaming response in progress */ STREAMING("Receiving analysis..."), /** Analysis completed successfully */ COMPLETED("Analysis complete"), /** Analysis failed */ FAILED("Analysis failed") }