const DEFAULT_GENERATION_CONTEXT_TTL_MS = 60000; export function normalizeGenerationType(type = "normal") { const normalized = String(type || "normal").trim(); return normalized || "normal"; } export function classifyGenerationKind(type = "normal", params = {}) { const generationType = normalizeGenerationType(type); if (params?.automatic_trigger || params?.quiet_prompt) { return "skip"; } if (generationType === "quiet" || generationType === "impersonate") { return "skip"; } if ( generationType === "swipe" || generationType === "regenerate" || generationType === "continue" ) { return "no-new-user"; } return "fresh"; } export function isVisibleUserGenerationMessage(message, { index = null, chat = null, isSystemMessage = null } = {}) { if (!message?.is_user) return false; if (message?.extra?.isSmallSys) return false; if (typeof isSystemMessage === "function" && isSystemMessage(message, { index, chat })) { return false; } if (message?.is_system) return false; return true; } export function resolveGenerationParentUserFloor( chat, context = {}, { phase = "", isSystemMessage = null } = {}, ) { if (!Array.isArray(chat) || chat.length === 0) return null; const generationType = normalizeGenerationType(context?.type || context?.generationType || "normal"); const findVisibleUserBefore = (startIndex) => { for (let index = Math.min(chat.length - 1, Math.floor(Number(startIndex))); index >= 0; index--) { if (isVisibleUserGenerationMessage(chat[index], { index, chat, isSystemMessage })) return index; } return null; }; const findLastVisibleNonSystemIndex = () => { for (let index = chat.length - 1; index >= 0; index--) { const message = chat[index]; if (!message) continue; if (message?.extra?.isSmallSys) continue; if (typeof isSystemMessage === "function" && isSystemMessage(message, { index, chat })) continue; if (message?.is_system) continue; return index; } return null; }; if (generationType === "swipe") { const swipedFloor = Number(context?.swipedAssistantFloor); if (Number.isFinite(swipedFloor)) return findVisibleUserBefore(swipedFloor - 1); const lastVisible = findLastVisibleNonSystemIndex(); return Number.isFinite(lastVisible) ? findVisibleUserBefore(lastVisible - 1) : null; } if (generationType === "regenerate") { const lastVisible = findLastVisibleNonSystemIndex(); if (!Number.isFinite(lastVisible)) return null; if (isVisibleUserGenerationMessage(chat[lastVisible], { index: lastVisible, chat, isSystemMessage })) { return lastVisible; } return findVisibleUserBefore(lastVisible - 1); } if (generationType === "continue") { const lastVisible = findLastVisibleNonSystemIndex(); if (!Number.isFinite(lastVisible)) return null; return findVisibleUserBefore(lastVisible - (chat[lastVisible]?.is_user ? 0 : 1)); } return findVisibleUserBefore(chat.length - 1); } function clonePlain(value, fallback = null) { if (!value || typeof value !== "object") return fallback; try { return structuredClone(value); } catch (_error) { try { return JSON.parse(JSON.stringify(value)); } catch (_jsonError) { return fallback; } } } export function createGenerationContextTracker(deps = {}) { let current = null; let pendingSwipe = null; let sequence = 0; const now = () => typeof deps.now === "function" ? Number(deps.now()) || Date.now() : Date.now(); const ttlMs = () => Number.isFinite(Number(deps.ttlMs)) ? Math.max(1, Number(deps.ttlMs)) : DEFAULT_GENERATION_CONTEXT_TTL_MS; const getChatId = () => String(deps.getCurrentChatId?.() || "").trim(); function noteSwipe(messageId = null, meta = null) { const parsed = Number(messageId); pendingSwipe = { assistantFloor: Number.isFinite(parsed) ? Math.floor(parsed) : null, meta: clonePlain(meta, null), chatId: getChatId(), at: now(), }; return pendingSwipe; } function begin(type = "normal", params = {}, { dryRun = false, phase = "" } = {}) { if (dryRun) return null; const at = now(); const generationType = normalizeGenerationType(type); const kind = classifyGenerationKind(generationType, params); const context = { id: `${at}:${++sequence}`, type: generationType, kind, chatId: getChatId(), params: clonePlain(params, {}), dryRun: false, startedAt: at, updatedAt: at, phase: String(phase || ""), swipedAssistantFloor: generationType === "swipe" && Number.isFinite(pendingSwipe?.assistantFloor) ? pendingSwipe.assistantFloor : null, swipeMeta: generationType === "swipe" ? clonePlain(pendingSwipe?.meta, null) : null, expectedMutation: "", expectedMutationAt: 0, }; pendingSwipe = null; current = context; return { ...context }; } function update(type = "normal", params = {}, { dryRun = false, phase = "" } = {}) { if (dryRun) return null; const at = now(); const generationType = normalizeGenerationType(type); const kind = classifyGenerationKind(generationType, params); if (!current || current.type !== generationType || current.chatId !== getChatId()) { return begin(generationType, params, { dryRun, phase }); } current = { ...current, type: generationType, kind, params: clonePlain(params, current.params || {}), updatedAt: at, afterCommandsAt: String(phase || "") === "GENERATION_AFTER_COMMANDS" ? at : current.afterCommandsAt || 0, phase: String(phase || current.phase || ""), }; return { ...current }; } function get({ allowStale = false } = {}) { if (!current) return null; const age = now() - Number(current.updatedAt || current.startedAt || 0); if (!allowStale && age > ttlMs()) { current = null; return null; } const activeChatId = getChatId(); if (current.chatId && activeChatId && current.chatId !== activeChatId) { current = null; return null; } return { ...current }; } function markExpectedMutation(kind = "", payload = {}) { if (!current) return null; current = { ...current, expectedMutation: String(kind || ""), expectedMutationAt: now(), expectedMutationPayload: clonePlain(payload, {}), updatedAt: now(), }; return { ...current }; } function clear(reason = "") { const previous = current; current = null; pendingSwipe = null; return previous ? { ...previous, clearReason: String(reason || "") } : null; } return { begin, update, get, clear, noteSwipe, markExpectedMutation, }; }