@@ -190,6 +190,71 @@ def test_stream_final_check_hidden(self):
190190 self .assertIn ("Working..." , row .plain )
191191 self .assertNotIn ("Status:" , row .plain )
192192
193+ def test_reasoning_streams_normally (self ):
194+ """While streaming, reasoning content shows up live like any
195+ other text — the collapse only happens in the final history."""
196+ tui , _ = make_tui ()
197+ tui .stream_text = "thinking hard...\n \n answer here"
198+ row = tui ._stream_row ()
199+ self .assertIsNotNone (row )
200+ self .assertIn ("thinking hard..." , row .plain )
201+ self .assertIn ("answer here" , row .plain )
202+
203+ def test_reasoning_collapsed_in_history (self ):
204+ """Once the stream is done, the stored reasoning collapses to a
205+ '...' marker in the history so it stops eating TUI space; the
206+ answer stays fully visible. The stored message is untouched."""
207+ tui , buf = make_tui ()
208+ reasoning = "Let me think: Rayleigh scattering dominates..."
209+ tui .session .last_messages = [
210+ Message (role = "user" , content = "why is the sky blue?" ),
211+ Message (
212+ role = "assistant" ,
213+ content = reasoning + "\n \n The sky is blue due to Rayleigh scattering." ,
214+ reasoning = reasoning ,
215+ ),
216+ ]
217+ tui .console .print (tui ._render_conversation ())
218+ out = buf .getvalue ()
219+ self .assertIn ("💭 ..." , out )
220+ self .assertIn ("Rayleigh scattering." , out )
221+ self .assertNotIn ("Let me think" , out )
222+ # the stored message keeps its reasoning — only the display hides it
223+ self .assertEqual (
224+ tui .session .last_messages [1 ].reasoning , reasoning
225+ )
226+ self .assertIn ("Let me think" , tui .session .last_messages [1 ].text ())
227+
228+ def test_reasoning_collapsed_marker_shows_even_without_answer (self ):
229+ """A reasoning-only assistant message (no answer content) still
230+ shows the collapse marker instead of vanishing entirely."""
231+ tui , buf = make_tui ()
232+ tui .session .last_messages = [
233+ Message (role = "user" , content = "go" ),
234+ Message (
235+ role = "assistant" , content = "pensive thoughts here" ,
236+ reasoning = "pensive thoughts here" ,
237+ ),
238+ ]
239+ tui .console .print (tui ._render_conversation ())
240+ out = buf .getvalue ()
241+ self .assertIn ("💭 ..." , out )
242+ self .assertNotIn ("pensive thoughts" , out )
243+
244+ def test_strip_reasoning (self ):
245+ """_strip_reasoning removes the leading reasoning prefix and
246+ leaves non-matching text untouched."""
247+ from python_agent_harness .tui import _strip_reasoning
248+
249+ self .assertEqual (
250+ _strip_reasoning ("ABCanswer" , "ABC" ), "answer"
251+ )
252+ self .assertEqual (
253+ _strip_reasoning (" ABCanswer" , "ABC" ), "answer"
254+ )
255+ self .assertEqual (_strip_reasoning ("answer" , "ABC" ), "answer" )
256+ self .assertEqual (_strip_reasoning ("x" , "" ), "x" )
257+
193258 def test_long_conversation_bounded (self ):
194259 """A long conversation is capped so rendering stays fast."""
195260 tui , buf = make_tui ()
0 commit comments