@@ -1861,20 +1861,63 @@ describe("Queued message processing after condense", () => {
18611861
18621862 // Make condense fast + deterministic
18631863 vi . spyOn ( task as any , "getSystemPrompt" ) . mockResolvedValue ( "system" )
1864- const submitSpy = vi . spyOn ( task , "submitUserMessage " ) . mockResolvedValue ( undefined )
1864+ const saySpy = vi . spyOn ( task , "say " ) . mockResolvedValue ( undefined )
18651865
18661866 // Queue a message during condensing
18671867 task . messageQueueService . addMessage ( "queued text" , [ "img1.png" ] )
18681868
1869- // Use fake timers to capture setTimeout(0) in processQueuedMessages
1870- vi . useFakeTimers ( )
18711869 await task . condenseContext ( )
18721870
1873- // Flush the microtask that submits the queued message
1874- vi . runAllTimers ( )
1875- vi . useRealTimers ( )
1871+ // Verify the message was shown in UI
1872+ expect ( saySpy ) . toHaveBeenCalledWith ( "user_feedback" , "queued text" , [ "img1.png" ] )
18761873
1877- expect ( submitSpy ) . toHaveBeenCalledWith ( "queued text" , [ "img1.png" ] )
1874+ // Verify the content was added to userMessageContent
1875+ expect ( task . userMessageContent . length ) . toBeGreaterThan ( 0 )
1876+ const textBlock = task . userMessageContent . find (
1877+ ( block ) => block . type === "text" && ( block as any ) . text ?. includes ( "queued text" ) ,
1878+ )
1879+ expect ( textBlock ) . toBeDefined ( )
1880+
1881+ // Verify queue was emptied
1882+ expect ( task . messageQueueService . isEmpty ( ) ) . toBe ( true )
1883+ } )
1884+
1885+ it ( "processes image-only queued messages correctly" , async ( ) => {
1886+ const provider = createProvider ( )
1887+ const task = new Task ( {
1888+ provider,
1889+ apiConfiguration : apiConfig ,
1890+ task : "initial task" ,
1891+ startTask : false ,
1892+ } )
1893+
1894+ vi . spyOn ( task as any , "getSystemPrompt" ) . mockResolvedValue ( "system" )
1895+ const saySpy = vi . spyOn ( task , "say" ) . mockResolvedValue ( undefined )
1896+
1897+ // Queue a message with ONLY images (no text) - this is the bug scenario
1898+ // Images must be in data URL format for formatResponse.imageBlocks to parse them correctly
1899+ const testImages = [
1900+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" ,
1901+ "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRof" ,
1902+ ]
1903+ task . messageQueueService . addMessage ( "" , testImages )
1904+
1905+ await task . condenseContext ( )
1906+
1907+ // Verify the message was shown in UI (with empty text but images)
1908+ expect ( saySpy ) . toHaveBeenCalledWith ( "user_feedback" , "" , testImages )
1909+
1910+ // Verify image blocks were added to userMessageContent
1911+ const imageBlocks = task . userMessageContent . filter ( ( block ) => block . type === "image" )
1912+ expect ( imageBlocks . length ) . toBe ( 2 )
1913+
1914+ // Verify no text block was added for empty text
1915+ const textBlocks = task . userMessageContent . filter (
1916+ ( block ) => block . type === "text" && ( block as any ) . text ?. includes ( "<user_message>" ) ,
1917+ )
1918+ expect ( textBlocks . length ) . toBe ( 0 )
1919+
1920+ // Verify queue was emptied
18781921 expect ( task . messageQueueService . isEmpty ( ) ) . toBe ( true )
18791922 } )
18801923
@@ -1898,29 +1941,23 @@ describe("Queued message processing after condense", () => {
18981941 vi . spyOn ( taskA as any , "getSystemPrompt" ) . mockResolvedValue ( "system" )
18991942 vi . spyOn ( taskB as any , "getSystemPrompt" ) . mockResolvedValue ( "system" )
19001943
1901- const spyA = vi . spyOn ( taskA , "submitUserMessage " ) . mockResolvedValue ( undefined )
1902- const spyB = vi . spyOn ( taskB , "submitUserMessage " ) . mockResolvedValue ( undefined )
1944+ const saySpyA = vi . spyOn ( taskA , "say " ) . mockResolvedValue ( undefined )
1945+ const saySpyB = vi . spyOn ( taskB , "say " ) . mockResolvedValue ( undefined )
19031946
19041947 taskA . messageQueueService . addMessage ( "A message" )
19051948 taskB . messageQueueService . addMessage ( "B message" )
19061949
19071950 // Condense in task A should only drain A's queue
1908- vi . useFakeTimers ( )
19091951 await taskA . condenseContext ( )
1910- vi . runAllTimers ( )
1911- vi . useRealTimers ( )
19121952
1913- expect ( spyA ) . toHaveBeenCalledWith ( "A message" , undefined )
1914- expect ( spyB ) . not . toHaveBeenCalled ( )
1953+ expect ( saySpyA ) . toHaveBeenCalledWith ( "user_feedback" , "A message" , undefined )
1954+ expect ( saySpyB ) . not . toHaveBeenCalledWith ( "user_feedback" , expect . anything ( ) , expect . anything ( ) )
19151955 expect ( taskB . messageQueueService . isEmpty ( ) ) . toBe ( false )
19161956
19171957 // Now condense in task B should drain B's queue
1918- vi . useFakeTimers ( )
19191958 await taskB . condenseContext ( )
1920- vi . runAllTimers ( )
1921- vi . useRealTimers ( )
19221959
1923- expect ( spyB ) . toHaveBeenCalledWith ( "B message" , undefined )
1960+ expect ( saySpyB ) . toHaveBeenCalledWith ( "user_feedback" , "B message" , undefined )
19241961 expect ( taskB . messageQueueService . isEmpty ( ) ) . toBe ( true )
19251962 } )
19261963} )
0 commit comments