@@ -4515,7 +4515,10 @@ function listCodexSessions(limit, options = {}) {
45154515 titleReadBytes
45164516 });
45174517 if (summary) {
4518- sessions.push(summary);
4518+ sessions.push({
4519+ ...summary,
4520+ derived: isDerivedSessionFile(filePath)
4521+ });
45194522 }
45204523
45214524 if (sessions.length >= targetCount) {
@@ -4697,6 +4700,7 @@ function listClaudeSessions(limit, options = {}) {
46974700 models,
46984701 __messageCountExact: quickRecords.length > 0 && isSessionSummaryMessageCountExact(fileStat, summaryReadBytes),
46994702 filePath,
4703+ derived: isDerivedSessionFile(filePath),
47004704 keywords,
47014705 capabilities
47024706 });
@@ -4723,7 +4727,10 @@ function listClaudeSessions(limit, options = {}) {
47234727 titleReadBytes
47244728 });
47254729 if (summary) {
4726- sessions.push(summary);
4730+ sessions.push({
4731+ ...summary,
4732+ derived: isDerivedSessionFile(filePath)
4733+ });
47274734 }
47284735
47294736 if (sessions.length >= targetCount) {
@@ -4745,7 +4752,10 @@ function listClaudeSessions(limit, options = {}) {
47454752 titleReadBytes
47464753 });
47474754 if (summary) {
4748- sessions.push(summary);
4755+ sessions.push({
4756+ ...summary,
4757+ derived: isDerivedSessionFile(filePath)
4758+ });
47494759 }
47504760 seen.add(filePath);
47514761 }
@@ -6324,6 +6334,28 @@ function buildSessionPlainText(messages) {
63246334 return lines.join('\n');
63256335}
63266336
6337+ function getDerivedSessionMetaPath(filePath) {
6338+ if (!filePath) return '';
6339+ const base = filePath.toLowerCase().endsWith('.jsonl')
6340+ ? filePath.slice(0, -5)
6341+ : filePath;
6342+ return `${base}.meta.json`;
6343+ }
6344+
6345+ function isDerivedSessionFile(filePath) {
6346+ const metaPath = getDerivedSessionMetaPath(filePath);
6347+ if (!metaPath) return false;
6348+ try {
6349+ if (fs.existsSync(metaPath)) {
6350+ return true;
6351+ }
6352+ } catch (_) {
6353+ return false;
6354+ }
6355+ const base = path.basename(filePath || '', path.extname(filePath || ''));
6356+ return /-\d{8}-\d{6}-[0-9a-f]{6}$/i.test(base);
6357+ }
6358+
63276359function resolveStateMaxMessages(state) {
63286360 if (!state || typeof state !== 'object') {
63296361 return MAX_EXPORT_MESSAGES;
@@ -6669,6 +6701,20 @@ async function readSessionDetail(params = {}) {
66696701 sessionId,
66706702 cwd: extracted.cwd || '',
66716703 updatedAt: extracted.updatedAt || '',
6704+ derived: (() => {
6705+ try {
6706+ const metaPath = filePath.toLowerCase().endsWith('.jsonl')
6707+ ? `${filePath.slice(0, -5)}.meta.json`
6708+ : `${filePath}.meta.json`;
6709+ if (fs.existsSync(metaPath)) {
6710+ return true;
6711+ }
6712+ } catch (_) {
6713+ return false;
6714+ }
6715+ const base = path.basename(filePath || '', path.extname(filePath || ''));
6716+ return /-\d{8}-\d{6}-[0-9a-f]{6}$/i.test(base);
6717+ })(),
66726718 totalMessages: hasExactTotalMessages ? extracted.totalMessages : null,
66736719 clipped: typeof extracted.clipped === 'boolean'
66746720 ? extracted.clipped
@@ -6696,6 +6742,15 @@ async function readSessionPlain(params = {}) {
66966742 return { error: 'Session file not found' };
66976743 }
66986744
6745+ const rawMaxMessages = params.maxMessages;
6746+ const maxMessages = rawMaxMessages === Infinity || rawMaxMessages === 'all'
6747+ ? Infinity
6748+ : (
6749+ Number.isFinite(Number(rawMaxMessages))
6750+ ? Math.max(1, Math.floor(Number(rawMaxMessages)))
6751+ : 50
6752+ );
6753+
66996754 let extracted;
67006755 if (source === 'gemini') {
67016756 let json;
@@ -6716,15 +6771,19 @@ async function readSessionPlain(params = {}) {
67166771 const text = extractMessageText(extractGeminiMessageText(entry.content ?? entry.message ?? entry.text));
67176772 if (!text && role !== 'system') continue;
67186773 messages.push({ role, text });
6774+ if (maxMessages !== Infinity && messages.length >= maxMessages) {
6775+ break;
6776+ }
67196777 }
67206778 extracted = {
67216779 sessionId: typeof json.sessionId === 'string' && json.sessionId.trim() ? json.sessionId.trim() : path.basename(filePath, '.json'),
67226780 cwd: typeof json.projectRoot === 'string' ? json.projectRoot : '',
6723- messages
6781+ messages,
6782+ truncated: maxMessages !== Infinity && rawMessages.length > messages.length
67246783 };
67256784 } else {
67266785 try {
6727- extracted = await extractMessagesFromFile(filePath, source, { maxMessages: Infinity });
6786+ extracted = await extractMessagesFromFile(filePath, source, { maxMessages });
67286787 } catch (e) {
67296788 extracted = null;
67306789 }
@@ -6738,7 +6797,7 @@ async function readSessionPlain(params = {}) {
67386797 if (fallbackRecords.length === 0) {
67396798 return { error: 'Session file is empty' };
67406799 }
6741- extracted = extractMessagesFromRecords(fallbackRecords, source, { maxMessages: Infinity });
6800+ extracted = extractMessagesFromRecords(fallbackRecords, source, { maxMessages });
67426801 }
67436802 }
67446803
@@ -6757,7 +6816,8 @@ async function readSessionPlain(params = {}) {
67576816 sessionId,
67586817 title: sessionId,
67596818 filePath,
6760- text
6819+ text,
6820+ clipped: maxMessages !== Infinity && !!(extracted && extracted.truncated)
67616821 };
67626822}
67636823
@@ -6998,7 +7058,7 @@ async function convertSessionToDerived(params = {}) {
69987058 target,
69997059 truncated: !!extracted.truncated,
70007060 maxMessages: maxMessagesLabel,
7001- session: summary || {
7061+ session: summary ? { ...summary, derived: true } : {
70027062 source: target,
70037063 sourceLabel: target === 'codex' ? 'Codex' : 'Claude Code',
70047064 sessionId: derivedSessionId,
@@ -7015,6 +7075,7 @@ async function convertSessionToDerived(params = {}) {
70157075 reasoningOutputTokens: 0,
70167076 __messageCountExact: true,
70177077 filePath: outputPath,
7078+ derived: true,
70187079 keywords: [],
70197080 capabilities: {}
70207081 }
0 commit comments