@@ -36,7 +36,17 @@ export const prettyPrintJson = (value: string): string => {
3636 }
3737}
3838
39- /** Normalized transcript message: two actors (user | assistant), variant drives styling. */
39+ /**
40+ * Normalized transcript message: two actors (user | assistant), variant drives styling.
41+ *
42+ * This is the renderer-facing shape consumed by `<ChatTranscript>`. It is produced
43+ * from canonical `DashboardTranscriptEntryDto` rows by
44+ * `transcriptEntriesToMessages` in `utils/transcript-entries.ts`.
45+ *
46+ * The legacy Claude-shape JSONL parser (`parseTranscriptEntries`) that used to
47+ * live here was removed once all six agents started serving canonical
48+ * transcript entries from the backend.
49+ */
4050export type TranscriptMessage = {
4151 id : string
4252 timestamp : string
@@ -46,159 +56,3 @@ export type TranscriptMessage = {
4656 isError ?: boolean
4757 toolUseId ?: string
4858}
49-
50- function genId ( index : number ) : string {
51- return `msg-${ index } `
52- }
53-
54- function toText ( content : unknown ) : string {
55- if ( typeof content === 'string' ) return content
56- return JSON . stringify ( content ?? '' , null , 2 )
57- }
58-
59- function readToolUseId ( block : Record < string , unknown > ) : string | undefined {
60- for ( const key of [ 'tool_use_id' , 'toolUseId' , 'id' ] ) {
61- const value = block [ key ]
62- if ( typeof value === 'string' && value . trim ( ) ) {
63- return value . trim ( )
64- }
65- }
66- return undefined
67- }
68-
69- function extractTextBlocks ( content : unknown ) : string [ ] {
70- if ( typeof content === 'string' ) {
71- const stripped = stripUserQueryTags ( content ) . trimStart ( )
72- return stripped . trim ( ) ? [ stripped ] : [ ]
73- }
74-
75- if ( ! Array . isArray ( content ) ) {
76- return [ ]
77- }
78-
79- return content
80- . map ( ( block ) => {
81- const item = block as Record < string , unknown >
82- if ( item . type === 'text' && typeof item . text === 'string' ) {
83- return stripUserQueryTags ( item . text ) . trimStart ( )
84- }
85- return ''
86- } )
87- . filter ( ( text ) => text . trim ( ) . length > 0 )
88- }
89-
90- export const parseTranscriptEntries = ( jsonl : string ) : TranscriptMessage [ ] => {
91- const lines = jsonl
92- . split ( '\n' )
93- . map ( ( line ) => line . trim ( ) )
94- . filter ( ( line ) => line . length > 0 )
95-
96- const collected : TranscriptMessage [ ] = [ ]
97-
98- lines . forEach ( ( line , lineIndex ) => {
99- try {
100- const parsed = JSON . parse ( line ) as Record < string , unknown >
101- const type =
102- typeof parsed . type === 'string'
103- ? parsed . type
104- : typeof parsed . role === 'string'
105- ? parsed . role
106- : ''
107- const timestamp =
108- typeof parsed . timestamp === 'string' ? parsed . timestamp : ''
109- const uuid =
110- typeof parsed . uuid === 'string' ? parsed . uuid : genId ( lineIndex )
111- const message = parsed . message as Record < string , unknown > | undefined
112- const messageContent = message ?. content
113-
114- if ( type === 'user' ) {
115- const textBlocks = extractTextBlocks ( messageContent )
116- textBlocks . forEach ( ( text , i ) => {
117- collected . push ( {
118- id : `${ uuid } -${ i } ` ,
119- timestamp,
120- actor : 'user' ,
121- variant : 'chat' ,
122- text,
123- } )
124- } )
125- if ( textBlocks . length > 0 ) return
126-
127- if ( Array . isArray ( messageContent ) ) {
128- const allToolResult = messageContent . every (
129- ( b : unknown ) =>
130- ( b as Record < string , unknown > ) ?. type === 'tool_result' ,
131- )
132- if ( allToolResult ) {
133- messageContent . forEach ( ( block : unknown , i : number ) => {
134- const b = block as Record < string , unknown >
135- const content = b . content
136- const isError = b . is_error === true
137- collected . push ( {
138- id : `${ uuid } -${ i } ` ,
139- timestamp,
140- actor : 'assistant' ,
141- variant : 'tool_result' ,
142- text : toText ( content ) ,
143- isError,
144- toolUseId : readToolUseId ( b ) ,
145- } )
146- } )
147- }
148- }
149- return
150- }
151-
152- if ( type === 'assistant' && Array . isArray ( messageContent ) ) {
153- messageContent . forEach ( ( block : unknown , i : number ) => {
154- const b = block as Record < string , unknown >
155- const blockType = b . type as string | undefined
156- const blockId = `${ uuid } -${ i } `
157-
158- if ( blockType === 'thinking' && typeof b . thinking === 'string' ) {
159- collected . push ( {
160- id : blockId ,
161- timestamp,
162- actor : 'assistant' ,
163- variant : 'thinking' ,
164- text : `Thinking: ${ b . thinking } ` ,
165- } )
166- return
167- }
168- if ( blockType === 'text' && typeof b . text === 'string' ) {
169- collected . push ( {
170- id : blockId ,
171- timestamp,
172- actor : 'assistant' ,
173- variant : 'chat' ,
174- text : b . text ,
175- } )
176- return
177- }
178- if ( blockType === 'tool_use' && typeof b . name === 'string' ) {
179- const input =
180- b . input != null ? JSON . stringify ( b . input , null , 2 ) : ''
181- collected . push ( {
182- id : blockId ,
183- timestamp,
184- actor : 'assistant' ,
185- variant : 'tool_use' ,
186- text : `Tool: ${ b . name } \n${ input } ` ,
187- toolUseId : readToolUseId ( b ) ,
188- } )
189- }
190- } )
191- }
192- } catch {
193- // Skip malformed lines; new format only per plan.
194- }
195- } )
196-
197- collected . sort ( ( a , b ) => {
198- const t = ( a . timestamp || '' ) . localeCompare ( b . timestamp || '' )
199- if ( t !== 0 ) return t
200- return a . id . localeCompare ( b . id )
201- } )
202-
203- return collected
204- }
0 commit comments