diff --git a/clients/terminal/render.py b/clients/terminal/render.py index ecfffcb..2596125 100644 --- a/clients/terminal/render.py +++ b/clients/terminal/render.py @@ -25,8 +25,10 @@ if msg_type == "session_sync": if self.show_events: + sid = msg.get("session_id") profile_id = msg.get("profile_id") or "unknown" - self._print(f"[session {msg.get('session_id')[:8]} | profile {profile_id}]", color="bright_black") + sid_str = f"{sid[:8]}" if sid else "?" + self._print(f"[session {sid_str} | profile {profile_id}]", color="bright_black") return if msg_type == "stream_start": diff --git a/navi/api/websocket.py b/navi/api/websocket.py index 01a8fa3..2f84206 100644 --- a/navi/api/websocket.py +++ b/navi/api/websocket.py @@ -197,11 +197,19 @@ return # client disconnected again — stop here # Stream finished — tell the client to sync session history so it sees # the full saved response (handles any events missed during disconnect). - await websocket.send_json({"type": "session_sync"}) + await websocket.send_json({ + "type": "session_sync", + "session_id": session.id, + "profile_id": session.profile_id, + }) else: # No active run — if this is a reconnect after the agent already finished, # the client needs to reload session history to see the saved response. - await websocket.send_json({"type": "session_sync"}) + await websocket.send_json({ + "type": "session_sync", + "session_id": session.id, + "profile_id": session.profile_id, + }) while True: raw = await websocket.receive_text() diff --git a/tests/clients/test_terminal_ws.py b/tests/clients/test_terminal_ws.py index 911c1b4..532cb25 100644 --- a/tests/clients/test_terminal_ws.py +++ b/tests/clients/test_terminal_ws.py @@ -47,3 +47,25 @@ await asyncio.wait_for(serve(), timeout=10.0) assert stop_event.is_set() + + +def test_renderer_session_sync_without_session_id_does_not_crash(capsys) -> None: + """Regression: session_sync with no session_id must not crash the renderer. + + The server used to send {"type": "session_sync"} without session_id, which + made render.py do None[:8]. The renderer must tolerate a missing id. + """ + renderer = Renderer(show_events=True) + renderer.render({"type": "session_sync"}) # no session_id / profile_id + out = capsys.readouterr().out + assert "[session ? | profile unknown]" in out + + +def test_renderer_session_sync_with_session_id(capsys) -> None: + """When session_id/profile_id are present they render in the header.""" + renderer = Renderer(show_events=True) + renderer.render( + {"type": "session_sync", "session_id": "abcdef1234567890", "profile_id": "navi_code"} + ) + out = capsys.readouterr().out + assert "[session abcdef12 | profile navi_code]" in out