import * as fs from "fs" import * as path from "path" import * as os from "os" import type { Message } from "../agent/conversation" export interface HistorySnapshot { sessionId: string timestamp: string reason: "manual" | "auto" messageCount: number tokenEstimate: number messages: Message[] } const SESSIONS_DIR = () => { const dir = path.join(os.homedir(), ".llmtune", "sessions") if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }) return dir } export function getHistoryDir(sessionId: string): string { const dir = path.join(SESSIONS_DIR(), sessionId) if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }) return dir } export function saveRawHistory( sessionId: string, messages: Message[], reason: "manual" | "auto" = "manual" ): string { const dir = getHistoryDir(sessionId) const filePath = path.join(dir, "history.json") const tokenEstimate = messages.reduce((total, msg) => { const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) return total + Math.ceil(content.length / 4) }, 0) const snapshot: HistorySnapshot = { sessionId, timestamp: new Date().toISOString(), reason, messageCount: messages.length, tokenEstimate, messages, } fs.writeFileSync(filePath, JSON.stringify(snapshot, null, 2), "utf-8") return filePath } export function loadRawHistory(sessionId: string): HistorySnapshot | null { const filePath = path.join(getHistoryDir(sessionId), "history.json") if (!fs.existsSync(filePath)) return null try { return JSON.parse(fs.readFileSync(filePath, "utf-8")) as HistorySnapshot } catch { return null } } export function saveCompactionMeta( sessionId: string, meta: { compactedAt: string tokensBefore: number tokensAfter: number tokensSaved: number messagesBefore: number messagesAfter: number } ): void { const filePath = path.join(getHistoryDir(sessionId), "compaction-meta.json") const history: typeof meta[] = [] if (fs.existsSync(filePath)) { try { const existing = JSON.parse(fs.readFileSync(filePath, "utf-8")) if (Array.isArray(existing)) history.push(...existing) } catch { /* ignore */ } } history.push(meta) fs.writeFileSync(filePath, JSON.stringify(history, null, 2), "utf-8") } export function loadCompactionHistory(sessionId: string): Array<{ compactedAt: string tokensBefore: number tokensAfter: number tokensSaved: number messagesBefore: number messagesAfter: number }> { const filePath = path.join(getHistoryDir(sessionId), "compaction-meta.json") if (!fs.existsSync(filePath)) return [] try { return JSON.parse(fs.readFileSync(filePath, "utf-8")) } catch { return [] } }