const ROOT_JSON_KEY = '__historyRootJson'; const VERSION_META_FIELDS = ['toolCalls', 'responseEdits', 'responseSegments', 'error']; function validateAndRepairTree(rootMessage) { const repair = (msg) => { if (!msg) return; if (msg.content === undefined || msg.content === null) { msg.content = ''; } if (msg.versions && Array.isArray(msg.versions)) { for (const version of msg.versions) { if (version.content === undefined || version.content === null) { version.content = ''; } if (version.tail && Array.isArray(version.tail)) { for (const tailMsg of version.tail) { repair(tailMsg); } } } } }; repair(rootMessage); return rootMessage; } function cloneAndRepairTree(rootMessage) { return validateAndRepairTree(JSON.parse(JSON.stringify(rootMessage))); } function getActiveVersion(message) { if (!message) return null; const versions = Array.isArray(message.versions) ? message.versions : []; if (!versions.length) { message.versions = [{ content: message.content ?? '', tail: [], timestamp: Date.now() }]; message.currentVersionIdx = 0; return message.versions[0]; } const currentVersionIdx = Number.isInteger(message.currentVersionIdx) ? Math.max(0, Math.min(message.currentVersionIdx, versions.length - 1)) : 0; message.currentVersionIdx = currentVersionIdx; if (!Array.isArray(message.versions[currentVersionIdx].tail)) { message.versions[currentVersionIdx].tail = []; } if (message.versions[currentVersionIdx].content === undefined || message.versions[currentVersionIdx].content === null) { message.versions[currentVersionIdx].content = message.content ?? ''; } return message.versions[currentVersionIdx]; } function cloneVersionMetaValue(value) { if (value === undefined) return undefined; return JSON.parse(JSON.stringify(value)); } function syncMessageFromActiveVersion(message) { if (!message) return message; const currentVersion = getActiveVersion(message); if (!currentVersion) return message; message.content = currentVersion.content ?? message.content ?? ''; VERSION_META_FIELDS.forEach((key) => { if (key in currentVersion) { message[key] = cloneVersionMetaValue(currentVersion[key]); } else { delete message[key]; } }); return message; } function cloneJson(value) { return JSON.parse(JSON.stringify(value)); } export function extractFlatHistory(rootMessage) { if (!rootMessage) return []; const toFlatEntry = (message) => { const cloned = cloneJson(message); if (cloned.content === undefined || cloned.content === null) { cloned.content = ''; } syncMessageFromActiveVersion(cloned); if (Array.isArray(cloned.versions)) { cloned.versions = cloned.versions.map((version) => ({ ...version, tail: [], })); } return cloned; }; const history = [toFlatEntry(rootMessage)]; const currentVerIdx = rootMessage.currentVersionIdx ?? 0; if (!Array.isArray(rootMessage.versions) || currentVerIdx >= rootMessage.versions.length) { return history; } const currentTail = rootMessage.versions[currentVerIdx]?.tail; if (currentTail && Array.isArray(currentTail)) { const walkTail = (tail) => { for (let i = 0; i < tail.length; i++) { const msg = tail[i]; if (msg?.content === undefined || msg?.content === null) { msg.content = ''; } syncMessageFromActiveVersion(msg); history.push(toFlatEntry(msg)); const ver = msg.versions?.[msg.currentVersionIdx ?? 0]; if (ver?.tail && Array.isArray(ver.tail)) { walkTail(ver.tail); } if (msg.role === 'user' && Array.isArray(msg.versions) && msg.versions.length > 1) { break; } } }; walkTail(currentTail); } return history; } export function serializeSessionForClient(session) { const rootMessage = Array.isArray(session?.history) && session.history[0] ? cloneAndRepairTree(session.history[0]) : null; return { id: session?.id, name: session?.name, created: session?.created, history: rootMessage ? extractFlatHistory(rootMessage) : [], model: session?.model || null, updatedAt: session?.updatedAt || null, [ROOT_JSON_KEY]: rootMessage ? JSON.stringify(rootMessage) : null, }; }