import fsSync from 'node:fs'; import path from 'node:path'; import readline from 'node:readline'; import { CODEX_SESSIONS_DIR } from './codex-config.js'; import { readMobileSessionMessages } from './mobile-session-index.js'; import { filterDeletedMessages, readDeletedMessageIds } from './codex-data-hidden-state.js'; import { extractContent, isVisibleUserMessage, sanitizeVisibleUserMessage, walkJsonlFiles } from './codex-data-parser.js'; async function findSessionFile(cache, sessionId) { const cached = cache.sessionById.get(sessionId)?.filePath; if (cached) { return cached; } const files = await walkJsonlFiles(CODEX_SESSIONS_DIR); return files.find((file) => path.basename(file).includes(sessionId)) || null; } function paginateMessages(messages, { limit = 120, offset = null, latest = true } = {}) { const total = messages.length; const count = Number(limit) || 0; const hasOffset = offset !== null && offset !== undefined; const start = hasOffset ? Math.max(0, Number(offset) || 0) : latest && count ? Math.max(0, total - count) : 0; const end = count ? start + count : undefined; return { messages: messages.slice(start, end), total, offset: start, hasMore: end ? end < total : false, hasMoreBefore: start > 0 }; } export async function readSessionMessagesFromCache(cache, sessionId, { limit = 120, offset = null, latest = true } = {}) { const filePath = await findSessionFile(cache, sessionId); const mobileMessages = await readMobileSessionMessages(sessionId); const deletedIds = await readDeletedMessageIds(sessionId); if (!filePath) { return paginateMessages(filterDeletedMessages(mobileMessages, deletedIds), { limit, offset, latest }); } const messages = []; const stream = fsSync.createReadStream(filePath, { encoding: 'utf8' }); const rl = readline.createInterface({ input: stream, crlfDelay: Infinity }); for await (const line of rl) { if (!line.trim()) { continue; } try { const entry = JSON.parse(line); const timestamp = entry.timestamp || null; if (entry.type === 'event_msg' && isVisibleUserMessage(entry.payload)) { messages.push({ id: `${entry.timestamp || messages.length}-user`, role: 'user', content: sanitizeVisibleUserMessage(entry.payload.message), timestamp }); } if ( entry.type === 'response_item' && entry.payload?.type === 'message' && entry.payload.role === 'assistant' && entry.payload.phase !== 'commentary' ) { const content = extractContent(entry.payload.content); if (content.trim()) { messages.push({ id: entry.payload.id || `${entry.timestamp || messages.length}-assistant`, role: entry.payload.role || 'assistant', content, timestamp }); } } } catch { // Skip malformed rows. } } for (const message of mobileMessages) { if (!messages.some((item) => item.id === message.id)) { messages.push(message); } } messages.sort((a, b) => new Date(a.timestamp || 0) - new Date(b.timestamp || 0)); return paginateMessages(filterDeletedMessages(messages, deletedIds), { limit, offset, latest }); }