@@ -3,8 +3,7 @@ import type { DomainSessionMessage } from '@/domain/types'
33import {
44 tryParseToolPayload ,
55 tryParseToolResult ,
6- extractLastAssistantMessage ,
7- enrichMessages ,
6+ filterEmptyMessages ,
87 groupChatItems ,
98 buildChatItems ,
109} from '../chat-messages'
@@ -111,95 +110,35 @@ describe('tryParseToolResult', () => {
111110 } )
112111} )
113112
114- // ---- extractLastAssistantMessage ----
113+ // ---- filterEmptyMessages ----
115114
116- describe ( 'extractLastAssistantMessage' , ( ) => {
117- it ( 'extracts last_assistant_message from a system event payload' , ( ) => {
118- const payload = JSON . stringify ( {
119- value : { last_assistant_message : 'Hello from the assistant' } ,
120- } )
121- expect ( extractLastAssistantMessage ( payload ) ) . toBe ( 'Hello from the assistant' )
122- } )
123-
124- it ( 'returns null when last_assistant_message is missing' , ( ) => {
125- const payload = JSON . stringify ( { value : { other_field : 'x' } } )
126- expect ( extractLastAssistantMessage ( payload ) ) . toBeNull ( )
127- } )
128-
129- it ( 'returns null when last_assistant_message is whitespace-only' , ( ) => {
130- const payload = JSON . stringify ( { value : { last_assistant_message : ' ' } } )
131- expect ( extractLastAssistantMessage ( payload ) ) . toBeNull ( )
132- } )
133-
134- it ( 'returns null when value is not an object' , ( ) => {
135- const payload = JSON . stringify ( { value : 'string-value' } )
136- expect ( extractLastAssistantMessage ( payload ) ) . toBeNull ( )
137- } )
138-
139- it ( 'returns null for invalid JSON' , ( ) => {
140- expect ( extractLastAssistantMessage ( 'not json' ) ) . toBeNull ( )
141- } )
142-
143- it ( 'returns null for a JSON array' , ( ) => {
144- expect ( extractLastAssistantMessage ( '[1]' ) ) . toBeNull ( )
145- } )
146- } )
147-
148- // ---- enrichMessages ----
149-
150- describe ( 'enrichMessages' , ( ) => {
151- it ( 'enriches empty assistant messages from a preceding system event' , ( ) => {
152- const messages = [
153- makeMsg ( {
154- eventType : 'system' ,
155- payload : JSON . stringify ( {
156- value : { last_assistant_message : 'Extracted text' } ,
157- } ) ,
158- } ) ,
159- makeMsg ( { eventType : 'assistant' , payload : '' } ) ,
160- ]
161-
162- const result = enrichMessages ( messages )
163- expect ( result ) . toHaveLength ( 2 )
164- // The system event passes through
165- expect ( result [ 0 ] . eventType ) . toBe ( 'system' )
166- // The assistant message is enriched
167- expect ( result [ 1 ] . eventType ) . toBe ( 'assistant' )
168- expect ( result [ 1 ] . payload ) . toBe ( 'Extracted text' )
169- } )
170-
171- it ( 'drops empty assistant messages with no nearby system event' , ( ) => {
115+ describe ( 'filterEmptyMessages' , ( ) => {
116+ it ( 'drops empty assistant messages' , ( ) => {
172117 const messages = [
173118 makeMsg ( { eventType : 'user' , payload : 'hello' } ) ,
174119 makeMsg ( { eventType : 'assistant' , payload : '' } ) ,
175120 ]
176121
177- const result = enrichMessages ( messages )
122+ const result = filterEmptyMessages ( messages )
178123 expect ( result ) . toHaveLength ( 1 )
179124 expect ( result [ 0 ] . eventType ) . toBe ( 'user' )
180125 } )
181126
182- it ( 'drops empty assistant messages when system event lacks last_assistant_message ' , ( ) => {
127+ it ( 'drops whitespace-only assistant messages' , ( ) => {
183128 const messages = [
184- makeMsg ( {
185- eventType : 'system' ,
186- payload : JSON . stringify ( { value : { some_other : 'data' } } ) ,
187- } ) ,
188- makeMsg ( { eventType : 'assistant' , payload : '' } ) ,
129+ makeMsg ( { eventType : 'assistant' , payload : ' ' } ) ,
189130 ]
190131
191- const result = enrichMessages ( messages )
192- // System passes through, empty assistant is dropped
193- expect ( result ) . toHaveLength ( 1 )
194- expect ( result [ 0 ] . eventType ) . toBe ( 'system' )
132+ const result = filterEmptyMessages ( messages )
133+ expect ( result ) . toHaveLength ( 0 )
195134 } )
196135
197- it ( 'passes through non-empty assistant messages unchanged ' , ( ) => {
136+ it ( 'passes through non-empty assistant messages' , ( ) => {
198137 const messages = [
199138 makeMsg ( { eventType : 'assistant' , payload : 'I have content' } ) ,
200139 ]
201140
202- const result = enrichMessages ( messages )
141+ const result = filterEmptyMessages ( messages )
203142 expect ( result ) . toHaveLength ( 1 )
204143 expect ( result [ 0 ] . payload ) . toBe ( 'I have content' )
205144 } )
@@ -211,49 +150,10 @@ describe('enrichMessages', () => {
211150 makeMsg ( { eventType : 'lifecycle' , payload : 'started' } ) ,
212151 ]
213152
214- const result = enrichMessages ( messages )
153+ const result = filterEmptyMessages ( messages )
215154 expect ( result ) . toHaveLength ( 3 )
216155 expect ( result . map ( m => m . eventType ) ) . toEqual ( [ 'user' , 'tool_use' , 'lifecycle' ] )
217156 } )
218-
219- it ( 'looks back up to 3 positions for a system event' , ( ) => {
220- const messages = [
221- makeMsg ( {
222- eventType : 'system' ,
223- payload : JSON . stringify ( {
224- value : { last_assistant_message : 'Found it' } ,
225- } ) ,
226- } ) ,
227- makeMsg ( { eventType : 'user' , payload : 'filler1' } ) ,
228- makeMsg ( { eventType : 'user' , payload : 'filler2' } ) ,
229- makeMsg ( { eventType : 'assistant' , payload : '' } ) ,
230- ]
231-
232- const result = enrichMessages ( messages )
233- const assistant = result . find ( m => m . eventType === 'assistant' )
234- expect ( assistant ) . toBeDefined ( )
235- expect ( assistant ! . payload ) . toBe ( 'Found it' )
236- } )
237-
238- it ( 'does not look back more than 3 positions' , ( ) => {
239- const messages = [
240- makeMsg ( {
241- eventType : 'system' ,
242- payload : JSON . stringify ( {
243- value : { last_assistant_message : 'Too far' } ,
244- } ) ,
245- } ) ,
246- makeMsg ( { eventType : 'user' , payload : 'a' } ) ,
247- makeMsg ( { eventType : 'user' , payload : 'b' } ) ,
248- makeMsg ( { eventType : 'user' , payload : 'c' } ) ,
249- makeMsg ( { eventType : 'assistant' , payload : '' } ) ,
250- ]
251-
252- const result = enrichMessages ( messages )
253- // The empty assistant should be dropped since system event is 4 positions back
254- const assistant = result . find ( m => m . eventType === 'assistant' )
255- expect ( assistant ) . toBeUndefined ( )
256- } )
257157} )
258158
259159// ---- groupChatItems ----
@@ -371,12 +271,7 @@ describe('buildChatItems', () => {
371271 const messages = [
372272 makeMsg ( { eventType : 'user' , payload : 'Fix the bug' } ) ,
373273 makeMsg ( { eventType : 'lifecycle' , payload : 'session_started' } ) ,
374- makeMsg ( {
375- eventType : 'system' ,
376- payload : JSON . stringify ( {
377- value : { last_assistant_message : 'I will fix the bug.' } ,
378- } ) ,
379- } ) ,
274+ makeMsg ( { eventType : 'system' , payload : '{}' } ) ,
380275 makeMsg ( { eventType : 'assistant' , payload : '' } ) ,
381276 makeMsg ( {
382277 eventType : 'tool_use' ,
@@ -391,31 +286,25 @@ describe('buildChatItems', () => {
391286
392287 const items = buildChatItems ( messages )
393288
394- // lifecycle and system events are filtered out
289+ // lifecycle, system, and empty assistant are filtered out
395290 const kinds = items . map ( i => i . kind )
396- expect ( kinds ) . toEqual ( [ 'message' , 'message' , ' tool_call', 'message' ] )
291+ expect ( kinds ) . toEqual ( [ 'message' , 'tool_call' , 'message' ] )
397292
398293 // First message is the user message
399294 if ( items [ 0 ] . kind === 'message' ) {
400295 expect ( items [ 0 ] . message . eventType ) . toBe ( 'user' )
401296 expect ( items [ 0 ] . message . payload ) . toBe ( 'Fix the bug' )
402297 }
403298
404- // Second message is the enriched assistant
405- if ( items [ 1 ] . kind === 'message' ) {
406- expect ( items [ 1 ] . message . eventType ) . toBe ( 'assistant' )
407- expect ( items [ 1 ] . message . payload ) . toBe ( 'I will fix the bug.' )
408- }
409-
410299 // Tool call is grouped
411- if ( items [ 2 ] . kind === 'tool_call' ) {
412- expect ( items [ 2 ] . group . toolResult ) . not . toBeNull ( )
300+ if ( items [ 1 ] . kind === 'tool_call' ) {
301+ expect ( items [ 1 ] . group . toolResult ) . not . toBeNull ( )
413302 }
414303
415- // Final assistant message
416- if ( items [ 3 ] . kind === 'message' ) {
417- expect ( items [ 3 ] . message . eventType ) . toBe ( 'assistant' )
418- expect ( items [ 3 ] . message . payload ) . toBe ( 'Done fixing.' )
304+ // Final assistant message (with content)
305+ if ( items [ 2 ] . kind === 'message' ) {
306+ expect ( items [ 2 ] . message . eventType ) . toBe ( 'assistant' )
307+ expect ( items [ 2 ] . message . payload ) . toBe ( 'Done fixing.' )
419308 }
420309 } )
421310
@@ -433,23 +322,13 @@ describe('buildChatItems', () => {
433322 }
434323 } )
435324
436- it ( 'includes enriched empty assistant messages derived from system events ' , ( ) => {
325+ it ( 'drops empty assistant messages' , ( ) => {
437326 const messages = [
438- makeMsg ( {
439- eventType : 'system' ,
440- payload : JSON . stringify ( {
441- value : { last_assistant_message : 'Enriched reply' } ,
442- } ) ,
443- } ) ,
444327 makeMsg ( { eventType : 'assistant' , payload : '' } ) ,
445328 ]
446329
447330 const items = buildChatItems ( messages )
448- expect ( items ) . toHaveLength ( 1 )
449- if ( items [ 0 ] . kind === 'message' ) {
450- expect ( items [ 0 ] . message . eventType ) . toBe ( 'assistant' )
451- expect ( items [ 0 ] . message . payload ) . toBe ( 'Enriched reply' )
452- }
331+ expect ( items ) . toHaveLength ( 0 )
453332 } )
454333
455334 it ( 'returns empty array when given no messages' , ( ) => {
0 commit comments