(rawFinal: string, streamedThisTurn: string)
| 33 | const normCompare = (s: string) => cleanForDisplay(s).replace(/\s+/g, ' ').trim(); |
| 34 | |
| 35 | export function dedupeFinalAgainstStreamed(rawFinal: string, streamedThisTurn: string): FinalDedupeDecision { |
| 36 | const finalContent = cleanForDisplay(rawFinal ?? ''); |
| 37 | const finalCmp = normCompare(rawFinal ?? ''); |
| 38 | const shownCmp = normCompare(streamedThisTurn ?? ''); |
| 39 | |
| 40 | // (a) Nothing in final: just close the line if we streamed anything. |
| 41 | if (!finalContent) { |
| 42 | return { emit: '', closeLine: !!streamedThisTurn }; |
| 43 | } |
| 44 | |
| 45 | // (b) Nothing was streamed (some backends emit ONLY `final`): print final fresh. |
| 46 | if (!shownCmp) { |
| 47 | return { emit: finalContent, closeLine: true }; |
| 48 | } |
| 49 | |
| 50 | // (c) Already streamed (modulo whitespace): close the line, emit nothing. |
| 51 | if (finalCmp === shownCmp) { |
| 52 | return { emit: '', closeLine: true }; |
| 53 | } |
| 54 | |
| 55 | // (d) Streamed a prefix; print the remaining suffix. |
| 56 | if (finalCmp.startsWith(shownCmp)) { |
| 57 | const suffixLen = finalCmp.length - shownCmp.length; |
| 58 | const remainder = finalContent.slice(finalContent.length - suffixLen); |
| 59 | return { emit: remainder, closeLine: true }; |
| 60 | } |
| 61 | |
| 62 | // (e) Divergence (filter drift, sub-agent timing, etc). DO NOT re-print — this |
| 63 | // was the duplication bug. Close the line and stop. |
| 64 | return { emit: '', closeLine: true }; |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * Detect whether a newly-produced assistant message is redundant against the |
no test coverage detected