From 40f2368875a8174416bb1dc6438bcf7910f32ee3 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Thu, 16 Apr 2026 20:56:47 -0500 Subject: [PATCH] fix(tui): ungate reasoning events so the Thinking panel shows live tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gateway was gating `reasoning.delta` and `reasoning.available` behind `_reasoning_visible(sid)` (true iff `display.show_reasoning: true` or `tool_progress_mode: verbose`). With the default config, neither was true — so reasoning events never reached the TUI, `turn.reasoning` stayed empty, `reasoningTokens` stayed 0, and the Thinking expander showed no token label for the whole turn. Tools still reported tokens because `tool.start` had no such gate. Then `message.complete` fired with `payload.reasoning` populated, the TUI saved it into `msg.thinking`, and the finalized row's expander sprouted "~36 tokens" post-hoc. That's the "tokens appear after the turn" jank. Remove the gate on emission. The TUI is responsible for whether to display reasoning content (detailsMode + collapsed expander already handle that). Token counting becomes continuous throughout the turn, matching how tools work. Also dropped the now-unused `_reasoning_visible` and `_session_show_reasoning` helpers. `show_reasoning` config key stays in place — it's still toggled via `/reasoning show|hide` and read elsewhere for potential future TUI-side gating. --- tui_gateway/server.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tui_gateway/server.py b/tui_gateway/server.py index c8ae0be85c..2fd4f49e8b 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -388,18 +388,10 @@ def _load_enabled_toolsets() -> list[str] | None: return None -def _session_show_reasoning(sid: str) -> bool: - return bool(_sessions.get(sid, {}).get("show_reasoning", False)) - - def _session_tool_progress_mode(sid: str) -> str: return str(_sessions.get(sid, {}).get("tool_progress_mode", "all") or "all") -def _reasoning_visible(sid: str) -> bool: - return _session_show_reasoning(sid) or _session_tool_progress_mode(sid) == "verbose" - - def _tool_progress_enabled(sid: str) -> bool: return _session_tool_progress_mode(sid) != "off" @@ -705,7 +697,7 @@ def _on_tool_progress( if event_type == "tool.started" and name: _emit("tool.progress", sid, {"name": name, "preview": preview or ""}) return - if event_type == "reasoning.available" and preview and _reasoning_visible(sid): + if event_type == "reasoning.available" and preview: _emit("reasoning.available", sid, {"text": str(preview)}) return if event_type.startswith("subagent."): @@ -739,7 +731,7 @@ def _agent_cbs(sid: str) -> dict: ), tool_gen_callback=lambda name: _tool_progress_enabled(sid) and _emit("tool.generating", sid, {"name": name}), thinking_callback=lambda text: _emit("thinking.delta", sid, {"text": text}), - reasoning_callback=lambda text: _reasoning_visible(sid) and _emit("reasoning.delta", sid, {"text": text}), + reasoning_callback=lambda text: _emit("reasoning.delta", sid, {"text": text}), status_callback=lambda kind, text=None: _status_update(sid, str(kind), None if text is None else str(text)), clarify_callback=lambda q, c: _block("clarify.request", sid, {"question": q, "choices": c}), )