From a7b6cd2cdadf45369e44265bcc5b8ca71e03d328 Mon Sep 17 00:00:00 2001 From: Michael Lam Date: Tue, 5 May 2026 15:39:38 -0700 Subject: [PATCH] fix: simplify compact activity summaries --- DESIGN.md | 2 +- static/style.css | 2 -- static/ui.js | 20 ++------------------ tests/test_ui_tool_call_cleanup.py | 24 ++++++++++++++++++++++++ 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 2954385d..a0aefcfe 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -140,7 +140,7 @@ Use almost no shadows in the transcript. Shadows are reserved for popovers, drop ### Tool/thinking activity group -Collapsed by default in settled history and during live runs. Summary line uses one disclosure for internals, e.g. `Activity: thinking + 4 tools · read_file, patch, terminal`. Expanding reveals thinking and individual tool cards together. Thinking and tools should not create separate transcript rows unless there is an error or approval state that needs attention. +Collapsed by default in settled history and during live runs. Summary line uses one disclosure for internals and stays intentionally terse, e.g. `Activity: 4 tools`. It should not repeat the always-present thinking area, list individual tool names, or add a second trailing count badge. Expanding reveals thinking and individual tool cards together. Thinking and tools should not create separate transcript rows unless there is an error or approval state that needs attention. ### Tool card diff --git a/static/style.css b/static/style.css index d5e8fe61..21df92e8 100644 --- a/static/style.css +++ b/static/style.css @@ -1753,9 +1753,7 @@ body.resizing{user-select:none;cursor:col-resize;} .tool-call-group-summary{width:100%;display:flex;align-items:center;gap:var(--space-2);padding:var(--space-1) var(--space-3);border:0;background:transparent;color:var(--muted);cursor:pointer;text-align:left;font:inherit;font-size:var(--font-size-xs);line-height:1.4;border-radius:var(--radius-card);} .tool-call-group-summary:hover{background:var(--surface-subtle-hover);color:var(--text);} .tool-call-group-label{font-weight:600;color:var(--muted);} -.tool-call-group-list{opacity:.72;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;} .tool-call-group-duration{margin-left:auto;opacity:.62;font-variant-numeric:tabular-nums;white-space:nowrap;} -.tool-call-group-count{opacity:.56;font-variant-numeric:tabular-nums;} .tool-call-group-chevron{opacity:.45;display:inline-flex;transition:transform .16s ease;} .tool-call-group:not(.tool-call-group-collapsed) .tool-call-group-chevron{transform:rotate(90deg);} .tool-call-group-body{display:block;padding-left:var(--space-3);} diff --git a/static/ui.js b/static/ui.js index 167b1dc7..84e0ee0d 100644 --- a/static/ui.js +++ b/static/ui.js @@ -3805,7 +3805,7 @@ function ensureActivityGroup(inner, opts){ group.setAttribute('data-tool-call-group','1'); group.setAttribute('data-agent-activity-group','1'); if(live) group.setAttribute('data-live-tool-call-group','1'); - group.innerHTML=`
`; + group.innerHTML=`
`; const anchor=opts.anchor||null; if(anchor&&anchor.parentElement===inner) anchor.insertAdjacentElement('afterend', group); else inner.appendChild(group); @@ -4890,27 +4890,12 @@ function _syncToolCallGroupSummary(group){ if(!group) return; const cards=Array.from(group.querySelectorAll('.tool-card-row .tool-card')); const toolCount=cards.length; - const thinkingCount=group.querySelectorAll('.agent-activity-thinking .thinking-card').length; - const names=cards.map(card=>{ - const el=card.querySelector('.tool-card-name'); - return el?String(el.textContent||'').trim():''; - }).filter(Boolean); - const uniqueNames=[...new Set(names)]; const label=group.querySelector('.tool-call-group-label'); - const list=group.querySelector('.tool-call-group-list'); - const badge=group.querySelector('.tool-call-group-count'); const durationEl=group.querySelector('.tool-call-group-duration'); - const parts=[]; - if(thinkingCount) parts.push('thinking'); - if(uniqueNames.length) parts.push(uniqueNames.slice(0,5).join(', ')+(uniqueNames.length>5?'…':'')); - const total=toolCount+thinkingCount; if(label){ - if(thinkingCount&&toolCount) label.textContent=`Activity: thinking + ${toolCount} tool${toolCount===1?'':'s'}`; - else if(thinkingCount) label.textContent='Activity: thinking'; - else if(toolCount) label.textContent=`Activity: ${toolCount} tool${toolCount===1?'':'s'}`; + if(toolCount) label.textContent=`Activity: ${toolCount} tool${toolCount===1?'':'s'}`; else label.textContent='Activity'; } - if(list) list.textContent=parts.join(' · ')||'tools / thinking'; if(durationEl){ if(group.getAttribute('data-live-tool-call-group')==='1'){ const activeText=_activityElapsedLabel(group); @@ -4924,7 +4909,6 @@ function _syncToolCallGroupSummary(group){ durationEl.style.display=durationText?'':'none'; } } - if(badge) badge.textContent=String(total); } // ── Live tool card helpers (called during SSE streaming) ── diff --git a/tests/test_ui_tool_call_cleanup.py b/tests/test_ui_tool_call_cleanup.py index a374ebed..7d157f15 100644 --- a/tests/test_ui_tool_call_cleanup.py +++ b/tests/test_ui_tool_call_cleanup.py @@ -128,6 +128,30 @@ class TestToolCallGroupingStatic: "The expand/collapse control must expose aria-expanded." ) + def test_activity_summary_omits_redundant_trailing_count_badge(self): + helper = _function_body(UI_JS, "ensureActivityGroup") + sync_fn = _function_body(UI_JS, "_syncToolCallGroupSummary") + assert "tool-call-group-count" not in helper, ( + "Compact Activity summaries already state tool counts in the label; " + "do not render a second trailing count badge." + ) + assert "tool-call-group-count" not in sync_fn, ( + "The summary sync path should not update a hidden/removed trailing count badge." + ) + + def test_activity_summary_keeps_header_compact_without_tool_names_or_thinking_prefix(self): + helper = _function_body(UI_JS, "ensureActivityGroup") + sync_fn = _function_body(UI_JS, "_syncToolCallGroupSummary") + assert "tool-call-group-list" not in helper, ( + "The compact Activity row should not allocate a secondary tool-name/thinking summary span." + ) + assert "tool-call-group-list" not in sync_fn, ( + "The summary sync path should not populate a redundant tool-name/thinking list." + ) + assert "Activity: thinking +" not in sync_fn, ( + "When tools are present, thinking is expected and should not be repeated in the label." + ) + def test_live_tool_cards_use_grouping_only_when_simplified(self): live_fn = _function_body(UI_JS, "appendLiveToolCard") settled_fn = _function_body(UI_JS, "renderMessages")