// Package format provides chat log serialization. // // The chat log uses JSON Lines format: one JSON object per line. // Each line is a complete, self-contained block. // // For streaming, blocks can be partial (Partial=true). The GUI shows // partial blocks immediately; when the final block arrives (same ID, // Partial=false), it replaces the partial version. package format import ( "encoding/json" ) // Block represents a single chat log entry. type Block struct { Role string `json:"role"` // user, assistant, context, reasoning, call, tool, error, info, retry, stalled ID string `json:"id"` // 8-char hex block ID Content string `json:"content,omitempty"` // Block content (text, args for call, output for tool) Name string `json:"name,omitempty"` // Tool/function name (for call blocks) Format string `json:"format,omitempty"` // Output format hint (for tool blocks) Partial bool `json:"partial,omitempty"` // True if streaming (not final) } // MarshalBlock returns a JSONL line for the block (with trailing newline). func MarshalBlock(b Block) []byte { data, _ := json.Marshal(b) return append(data, '\n') } // UnmarshalBlock parses a JSONL line into a Block. func UnmarshalBlock(line []byte) (Block, error) { var b Block err := json.Unmarshal(line, &b) return b, err } // RenderBlock returns human-readable text for a block (for log file). // Partial blocks are skipped (only final blocks render). func RenderBlock(b Block) string { if b.Partial { return "" } switch b.Role { case "user": return "You: " + b.Content + "\n\n" case "assistant": return b.Content + "\n\n" case "context": return "" // Hidden from rendered log case "reasoning": return "" // Hidden from rendered log case "call": if b.Content != "" { return "→ " + b.Name + " " + b.Content + "\n\n" } return "→ " + b.Name + "\n\n" case "tool": return b.Content + "\n\n" case "bypass": return "⚠ bypass requested (needs approval): " + b.Content + "\n\n" case "bypass-resolved": return "bypass " + b.Content + "\n\n" case "error": return "Error: " + b.Content + "\n\n" case "info": return "Info: " + b.Content + "\n\n" case "retry": return "Retry: " + b.Content + "\n\n" case "stalled": return "[Stalled]\n\n" default: return b.Content + "\n\n" } }