From 2f2b2c5cbbb24b65d3bb7e1b951d84a67353311c Mon Sep 17 00:00:00 2001 From: qer Date: Thu, 20 Aug 2026 00:04:16 +0800 Subject: [PATCH] fix(transcript): normalize folded notification text to the live title/body form --- packages/transcript/src/history/groupTurns.ts | 25 ++++++++++++++++++- packages/transcript/test/layers.test.ts | 6 ++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/transcript/src/history/groupTurns.ts b/packages/transcript/src/history/groupTurns.ts index 8d283eda5..88fa56121 100644 --- a/packages/transcript/src/history/groupTurns.ts +++ b/packages/transcript/src/history/groupTurns.ts @@ -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(''); + const closingStart = text.lastIndexOf(''); + 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 ( diff --git a/packages/transcript/test/layers.test.ts b/packages/transcript/test/layers.test.ts index 17a227f84..d7e8aed35 100644 --- a/packages/transcript/test/layers.test.ts +++ b/packages/transcript/test/layers.test.ts @@ -454,7 +454,10 @@ describe('groupMessagesIntoSnapshot (cold path)', () => { }, { role: 'user', - content: [{ type: 'text', text: '' }], + content: [{ + type: 'text', + text: '\nTitle: Background agent completed\nSeverity: info\ninspect done.\n', + }], 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')