fix: suppress flow console panels in TUI mode; clear header agent on method change

Two review follow-ups:

1) Method panels break Textual TUI (Cursor): forcing suppress_flow_events off
   so the STEPS panel receives events also un-gated the EventListener's Rich
   flow/method panels (ConsoleFormatter.print_panel prints is_flow=True panels
   regardless of verbose), which interleave with Textual and corrupt the TUI.
   print_panel now skips is_flow panels when is_tui_mode() is set (the same
   context the TUI worker already establishes and the tracing listeners already
   honor). Non-TUI/headless flow runs are unaffected. Test:
   test_console_formatter_tui_mode.

2) Flow header showed a stale agent (CodeRabbit): _current_agent persisted
   across methods. It's now cleared when a method starts and when the active
   method changes, so the header never shows the previous method's agent until
   a new agent event arrives. Test: test_flow_method_transitions_clear_current_agent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
This commit is contained in:
Joao Moura
2026-07-08 08:28:26 -07:00
parent ec55c31fed
commit 46cae1a50f
4 changed files with 87 additions and 0 deletions

View File

@@ -737,6 +737,9 @@ FooterKey .footer-key--key {
self._current_method = next(
(s["name"] for s in self._flow_steps if s["status"] == "active"), None
)
# The active method changed; drop its agent so the header doesn't show a
# stale agent until the next method's agent event arrives.
self._current_agent = ""
def _on_crew_done(self, output: str | None) -> None:
with self._lock:
@@ -2036,6 +2039,9 @@ FooterKey .footer-key--key {
with self._lock:
name = event.method_name
self._current_method = name
# Agent is per-method; clear it so the header doesn't show the
# previous method's agent until a new agent event arrives.
self._current_agent = ""
for step in self._flow_steps:
if step["name"] == name:
step["status"] = "active"

View File

@@ -1586,6 +1586,34 @@ def test_current_method_clears_and_falls_back_across_overlap() -> None:
app._unsubscribe()
def test_flow_method_transitions_clear_current_agent() -> None:
app = CrewRunApp(crew_name="Demo")
app._flow = SimpleNamespace()
app._subscribe()
try:
_emit_event(
MethodExecutionStartedEvent(flow_name="Demo", method_name="a", state={})
)
app._current_agent = "Researcher" # an agent ran during method 'a'
# Starting a new method clears the previous method's agent.
_emit_event(
MethodExecutionStartedEvent(flow_name="Demo", method_name="b", state={})
)
assert app._current_agent == ""
app._current_agent = "Writer"
# 'b' ending switches the active method ('a' still active) → agent clears.
_emit_event(
MethodExecutionFinishedEvent(
flow_name="Demo", method_name="b", result=None, state={}
)
)
assert app._current_agent == ""
finally:
app._unsubscribe()
@pytest.mark.asyncio
async def test_declarative_flow_runs_on_tui() -> None:
"""End-to-end: on_mount dispatches _run_flow_worker → flow.kickoff →