Skip to content

Commit 744cf45

Browse files
committed
fix(cli): normalize full-scan fast previews
1 parent 9784ee6 commit 744cf45

2 files changed

Lines changed: 118 additions & 1 deletion

File tree

cli.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4789,7 +4789,14 @@ function extractSessionDetailPreviewFromFileFast(filePath, source, messageLimit)
47894789

47904790
if (position > 0) {
47914791
latest.clipped = latest.clipped || position > 0;
4792-
}
4792+
return latest;
4793+
}
4794+
const normalizedMessages = removeLeadingSystemMessage(latest.messages);
4795+
latest.messages = normalizedMessages.length > safeMessageLimit
4796+
? normalizedMessages.slice(-safeMessageLimit)
4797+
: normalizedMessages;
4798+
latest.totalMessages = normalizedMessages.length;
4799+
latest.clipped = latest.totalMessages > latest.messages.length;
47934800
return latest;
47944801
} catch (_) {
47954802
return null;

tests/unit/session-detail-preview-fast.test.mjs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,22 @@ test('extractSessionDetailPreviewFromFileFast preserves clipped previews after f
8585
return content;
8686
}
8787
return '';
88+
},
89+
removeLeadingSystemMessage(messages) {
90+
if (!Array.isArray(messages)) {
91+
return [];
92+
}
93+
let startIndex = 0;
94+
while (startIndex < messages.length) {
95+
const item = messages[startIndex];
96+
const role = item && typeof item.role === 'string' ? item.role : '';
97+
if (role === 'system') {
98+
startIndex += 1;
99+
continue;
100+
}
101+
break;
102+
}
103+
return startIndex > 0 ? messages.slice(startIndex) : messages;
88104
}
89105
});
90106

@@ -98,3 +114,97 @@ test('extractSessionDetailPreviewFromFileFast preserves clipped previews after f
98114
fs.rmSync(tmpDir, { recursive: true, force: true });
99115
}
100116
});
117+
118+
test('extractSessionDetailPreviewFromFileFast normalizes full-scan previews before returning', () => {
119+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codexmate-fast-preview-full-'));
120+
const filePath = path.join(tmpDir, 'session.jsonl');
121+
122+
try {
123+
const records = [
124+
{
125+
type: 'response_item',
126+
payload: {
127+
type: 'message',
128+
role: 'system',
129+
content: 'bootstrap'
130+
},
131+
timestamp: '2025-04-12T00:00:00.000Z'
132+
},
133+
{
134+
type: 'response_item',
135+
payload: {
136+
type: 'message',
137+
role: 'user',
138+
content: 'hello'
139+
},
140+
timestamp: '2025-04-12T00:00:01.000Z'
141+
},
142+
{
143+
type: 'response_item',
144+
payload: {
145+
type: 'message',
146+
role: 'assistant',
147+
content: 'world'
148+
},
149+
timestamp: '2025-04-12T00:00:02.000Z'
150+
}
151+
];
152+
fs.writeFileSync(filePath, records.map((record) => JSON.stringify(record)).join('\n') + '\n', 'utf-8');
153+
154+
const extractSessionDetailPreviewFromFileFast = instantiateExtractSessionDetailPreviewFromFileFast({
155+
fs,
156+
getFileStatSafe(targetPath) {
157+
try {
158+
return fs.statSync(targetPath);
159+
} catch (_) {
160+
return null;
161+
}
162+
},
163+
FAST_SESSION_DETAIL_PREVIEW_FILE_BYTES: 32,
164+
FAST_SESSION_DETAIL_PREVIEW_MAX_BYTES: 4096,
165+
FAST_SESSION_DETAIL_PREVIEW_CHUNK_BYTES: 256,
166+
DEFAULT_SESSION_DETAIL_MESSAGES: 300,
167+
toIsoTime(value, fallback = '') {
168+
return typeof value === 'string' && value ? value : fallback;
169+
},
170+
normalizeRole(role) {
171+
const normalized = String(role || '').trim().toLowerCase();
172+
return normalized === 'user' || normalized === 'assistant' || normalized === 'system'
173+
? normalized
174+
: '';
175+
},
176+
extractMessageText(content) {
177+
return typeof content === 'string' ? content : '';
178+
},
179+
removeLeadingSystemMessage(messages) {
180+
if (!Array.isArray(messages)) {
181+
return [];
182+
}
183+
let startIndex = 0;
184+
while (startIndex < messages.length) {
185+
const item = messages[startIndex];
186+
const role = item && typeof item.role === 'string' ? item.role : '';
187+
if (role === 'system') {
188+
startIndex += 1;
189+
continue;
190+
}
191+
break;
192+
}
193+
return startIndex > 0 ? messages.slice(startIndex) : messages;
194+
}
195+
});
196+
197+
const preview = extractSessionDetailPreviewFromFileFast(filePath, 'codex', 5);
198+
199+
assert(preview, 'full-scan fast preview should still return a preview');
200+
assert.deepStrictEqual(
201+
preview.messages.map((item) => item.role),
202+
['user', 'assistant'],
203+
'full-scan previews should strip leading system/bootstrap messages'
204+
);
205+
assert.strictEqual(preview.totalMessages, 2, 'full-scan previews should restore an exact total');
206+
assert.strictEqual(preview.clipped, false, 'full-scan previews should not stay clipped when all messages fit');
207+
} finally {
208+
fs.rmSync(tmpDir, { recursive: true, force: true });
209+
}
210+
});

0 commit comments

Comments
 (0)