From a0fa236ed464b0d14e9ea91edad2507bb372e231 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Mon, 7 Sep 2026 23:42:56 -0700 Subject: [PATCH] fix: retain worker tool activity in narrow classic monitor --- hermes_cli/cli_subagent_monitor.py | 12 ++++++++---- tests/cli/test_subagent_dock_surface.py | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/hermes_cli/cli_subagent_monitor.py b/hermes_cli/cli_subagent_monitor.py index bd3c18c661..c593a3f1ed 100644 --- a/hermes_cli/cli_subagent_monitor.py +++ b/hermes_cli/cli_subagent_monitor.py @@ -130,10 +130,14 @@ def build_monitor_application(monitor, **kwargs): rows = [] for row in monitor.entries: selected = row['subagent_id'] == monitor.selected_id - text = f"{'❯' if selected else ' '} {row['elapsed']}s · {row.get('status') or 'starting'} · {row.get('goal') or row['subagent_id']}" - if row.get('last_tool'): - text += f" · last: {row['last_tool']}" - rows.append(('class:subagent-dock.selected' if selected else '', _clip(text, size.columns) + '\n')) + prefix = f"{row['elapsed']}s · {row.get('status') or 'starting'} · " + activity = f" · last: {row['last_tool']}" if row.get('last_tool') else '' + goal_width = max(0, size.columns - 2 - get_cwidth(prefix + activity)) + goal = _clip(row.get('goal') or row['subagent_id'], goal_width) + text = f"{'❯' if selected else ' '} " + _clip(prefix + goal + activity, max(0, size.columns - 2)) + # Pad selection in terminal cells, not codepoints (task names may be wide). + text += ' ' * max(0, size.columns - get_cwidth(text)) + rows.append(('class:subagent-dock.selected' if selected else '', text + '\n')) return rows or [('', 'No live subagents. Results arrive in the conversation.')] def cursor(): diff --git a/tests/cli/test_subagent_dock_surface.py b/tests/cli/test_subagent_dock_surface.py index a00be9f2dd..c9633e705f 100644 --- a/tests/cli/test_subagent_dock_surface.py +++ b/tests/cli/test_subagent_dock_surface.py @@ -56,3 +56,26 @@ def test_passive_dock_fills_rows_but_keeps_input_live(monkeypatch, columns, rows app.exit() await task asyncio.run(run()) + + +def test_expanded_roster_reserves_activity_before_long_task_names(): + from prompt_toolkit.data_structures import Size + from prompt_toolkit.input import create_pipe_input + from prompt_toolkit.output import DummyOutput + from prompt_toolkit.utils import get_cwidth + from hermes_cli.cli_subagent_monitor import SubagentMonitor, build_monitor_application + + monitor = SubagentMonitor(SimpleNamespace()) + monitor.entries = [dict(subagent_id=str(i), goal='Inspect 界 ' * 30, + elapsed=24, status='running', last_tool='terminal') for i in range(6)] + monitor.selected_id = '0' + output = DummyOutput() + with create_pipe_input() as pipe: + for columns, rows in [(100, 30), (80, 20)]: + output.get_size = lambda: Size(rows=rows, columns=columns) + app = build_monitor_application(monitor, input=pipe, output=output) + fragments = app.layout.current_control.text() + assert len(fragments) == len(monitor.entries) + for _, text in fragments: + assert '24s' in text and 'running' in text and 'last: terminal' in text + assert get_cwidth(text.rstrip('\n')) == columns