fix(transcript): normalize folded notification text to the live title/body form

This commit is contained in:
qer 2026-08-20 00:04:16 +08:00
parent 04255a7aa7
commit 2f2b2c5cbb
2 changed files with 29 additions and 2 deletions

View file

@ -200,7 +200,7 @@ export function groupMessagesIntoSnapshot(
kind: 'text',
frameId: `${step.stepId}.f${step.frames.length + 1}`,
role: 'user',
text: textOf(message),
text: notificationFrameText(textOf(message)),
...(taskId !== undefined ? { taskId } : {}),
});
syncTurnItem(items, current);
@ -280,6 +280,29 @@ export function groupMessagesIntoSnapshot(
return { items, tasks: [], interactions: [], attachments, todos: [], prompts: [], meta: {} };
}
function notificationFrameText(text: string): string {
if (!text.startsWith('<notification')) return text;
const openingEnd = text.indexOf('>');
const closingStart = text.lastIndexOf('</notification>');
if (openingEnd === -1 || closingStart <= openingEnd) return text;
const inner = text.slice(openingEnd + 1, closingStart);
const lines = inner.split('\n');
let title = '';
let lastHeaderIndex = -1;
for (let i = 0; i < lines.length; i++) {
const line = lines[i]!;
if (line.startsWith('Title: ')) {
title = line.slice('Title: '.length);
lastHeaderIndex = i;
} else if (line.startsWith('Severity: ')) {
lastHeaderIndex = i;
}
}
const body = lines.slice(lastHeaderIndex + 1).join('\n').trim();
if (title.length > 0 && body.length > 0) return `${title}\n${body}`;
return title.length > 0 ? title : body.length > 0 ? body : text;
}
function opensOwnTurn(message: HistoryMessage): boolean {
const origin = message.origin as { kind?: unknown; name?: unknown } | undefined;
return (

View file

@ -454,7 +454,10 @@ describe('groupMessagesIntoSnapshot (cold path)', () => {
},
{
role: 'user',
content: [{ type: 'text', text: '<notification id="task:task-9:completed"></notification>' }],
content: [{
type: 'text',
text: '<notification id="task:task-9:completed" category="task" type="task.completed" source_kind="background_task" source_id="task-9">\nTitle: Background agent completed\nSeverity: info\ninspect done.\n</notification>',
}],
toolCalls: [],
origin: { kind: 'task', taskId: 'task-9' } as { kind: string },
},
@ -481,6 +484,7 @@ describe('groupMessagesIntoSnapshot (cold path)', () => {
.flatMap((step) => step.frames)
.filter((f) => f.kind === 'text' && f.role === 'user');
expect(userFrames.map((f) => f.kind === 'text' && f.taskId)).toEqual(['task-9', 'task-8']);
expect(userFrames[0]).toMatchObject({ text: 'Background agent completed\ninspect done.' });
const assistantTexts = turn.steps
.flatMap((step) => step.frames)
.filter((f) => f.kind === 'text' && f.role === 'assistant')