export const COMPACTION_SYSTEM_PROMPT = [ "You are a context-compaction model.", "Your output will be given to another AI model as memory of an earlier conversation.", "Compress aggressively, but never discard information that may affect future answers.", "Preserve current goals, user requirements, explicit constraints, decisions already made, unfinished work, project state, technical configuration, model names, versions, filenames, paths, URLs when relevant, commands, ports, identifiers, numeric values, API/interface decisions, errors, causes, attempted and successful fixes, user preferences, and relationships between important entities.", "Remove greetings, acknowledgements, filler, repeated explanations, redundant wording, and obsolete intermediate discussion that no longer affects the current state.", "Never invent information or silently change exact technical values. Do not replace precise facts with vague wording.", "When newer information supersedes older information, preserve the newer state and mention the superseded state only if still relevant.", "Distinguish facts from unresolved assumptions. Keep unresolved questions and tasks. Prefer concise factual statements.", "Output only the requested structured JSON format." ].join("\n"); export const COMPACTION_SCHEMA = { type: "object", additionalProperties: false, properties: { summary: {type: "string"}, current_goals: {type: "array", items: {type: "string"}}, important_facts: {type: "array", items: {type: "string"}}, decisions: {type: "array", items: {type: "string"}}, constraints: {type: "array", items: {type: "string"}}, technical_state: {type: "array", items: {type: "string"}}, open_tasks: {type: "array", items: {type: "string"}}, problems_and_solutions: {type: "array", items: {type: "string"}}, user_preferences: {type: "array", items: {type: "string"}}, important_exact_values: {type: "array", items: {type: "string"}} }, required: ["summary", "current_goals", "important_facts", "decisions", "constraints", "technical_state", "open_tasks", "problems_and_solutions", "user_preferences", "important_exact_values"] }; const FIELDS = COMPACTION_SCHEMA.required; export function estimateTokens(value) { return Math.ceil(String(value || "").length / 4); } export function normalizeCompaction(value, maxChars = 10000) { let parsed = value; if (typeof value === "string") { try { parsed = JSON.parse(value.replace(/^```(?:json)?\s*|\s*```$/gi, "").trim()); } catch { return null; } } if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null; const normalized = {}; for (const field of FIELDS) { if (field === "summary") normalized[field] = typeof parsed[field] === "string" ? parsed[field].trim() : ""; else if (!Array.isArray(parsed[field]) || parsed[field].some(item => typeof item !== "string")) return null; else normalized[field] = parsed[field].map(item => item.trim()).filter(Boolean); } const serialized = JSON.stringify(normalized); const hasContent = normalized.summary || FIELDS.some(field => field !== "summary" && normalized[field].length); if (!hasContent || serialized.length > maxChars) return null; return normalized; } function compactMessage(message) { const lines = [`${message.role === "bot" ? "Assistant" : message.role}: ${String(message.content || "").trim()}`]; if (message.thinking) lines.push(`Assistant reasoning outcome: ${String(message.thinking).slice(0, 1200)}`); if (message.error_message) lines.push(`Error: ${String(message.error_message).slice(0, 1000)}`); if (message.delivery_status && message.delivery_status !== "complete") lines.push(`Delivery status: ${message.delivery_status}`); if (message.sources) { try { const sources = typeof message.sources === "string" ? JSON.parse(message.sources) : message.sources; if (Array.isArray(sources) && sources.length) lines.push(`Sources: ${sources.slice(0, 5).map(source => `${source.title || ""} ${source.url || ""} ${String(source.snippet || "").slice(0, 500)}`).join(" | ")}`); } catch { /* malformed optional metadata must not block compaction */ } } return lines.join("\n"); } export function isCompactionEligibleMessage(message) { return Boolean(message && String(message.content || message.error_message || "").trim()) && (!message.delivery_status || message.delivery_status === "complete" || message.delivery_status === "failed"); } export function formatMessagesForCompaction(messages) { return messages.map(compactMessage).join("\n\n"); } export function buildCompactionRequest(previousSummary, messages) { return [ previousSummary ? `Existing compacted state (update it; do not repeat it blindly):\n${previousSummary}` : "Existing compacted state: none", "", "Newly expired conversation records:", formatMessagesForCompaction(messages), "", "Return exactly one JSON object matching the supplied schema." ].join("\n"); }