Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions agent/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,21 @@ def verb_drops_preview(tool_name: str) -> bool:
return tool_name in _TOOL_VERBS_NO_PREVIEW


# Tools that deliver their own complete, purpose-built prompt to the chat via a
# dedicated adapter callback, rather than relying on a generic tool-progress
# bubble. ``clarify`` sends its full question (and any choices) through the
# adapter's ``send_clarify`` callback (see gateway/run.py
# ``_clarify_callback_sync``); the extra "❓ Asking …" tool-progress bubble was a
# verbatim duplicate of that prompt (#69175).
_TOOL_SELF_RENDERED_PROGRESS: frozenset[str] = frozenset({"clarify"})


def tool_renders_own_progress(tool_name: str) -> bool:
"""Whether the tool emits its own chat message and should therefore skip the
generic tool-progress bubble (which would just duplicate it)."""
return tool_name in _TOOL_SELF_RENDERED_PROGRESS


def build_status_phrase(tool_name: str, args: dict | None, max_len: int = 49) -> str | None:
"""Build a short present-tense status phrase for platform status surfaces.

Expand Down
8 changes: 8 additions & 0 deletions gateway/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -19408,6 +19408,14 @@ def progress_callback(event_type: str, tool_name: str = None, preview: str = Non
if event_type not in {"tool.started",}:
return

# Some tools deliver their own complete prompt to the chat via a
# dedicated adapter callback (clarify → send_clarify). Their
# generic tool-progress bubble ("❓ Asking …") is a verbatim
# duplicate of that prompt, so skip it here. (#69175)
from agent.display import tool_renders_own_progress
if tool_renders_own_progress(tool_name):
return

# Suppress tool-progress bubbles once the user has sent `stop`.
# When the LLM response carries N parallel tool calls, the agent
# fires N "tool.started" events back-to-back before checking for
Expand Down
20 changes: 20 additions & 0 deletions tests/agent/test_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
capture_local_edit_snapshot,
extract_edit_diff,
get_cute_tool_message,
get_tool_verb,
redact_tool_args_for_display,
set_tool_preview_max_len,
tool_renders_own_progress,
_render_inline_unified_diff,
_summarize_rendered_diff_sections,
render_edit_diff_with_delta,
Expand Down Expand Up @@ -551,3 +553,21 @@ def test_no_preview_tools_stay_verb_only(self):
from agent.display import build_status_phrase
phrase = build_status_phrase("skills_list", {"category": "devops"})
assert phrase == "is listing skills…"


class TestToolRendersOwnProgress:
"""`clarify` sends its own full prompt via send_clarify, so its generic
tool-progress bubble must be suppressed to avoid a duplicate message
(#69175)."""

def test_clarify_renders_own_progress(self):
assert tool_renders_own_progress("clarify") is True

def test_regular_tools_do_not_render_own_progress(self):
for name in ("terminal", "read_file", "web_search", "delegate_task", "memory"):
assert tool_renders_own_progress(name) is False

def test_clarify_verb_still_defined(self):
# Suppression happens at the progress-bubble layer only; the verb
# mapping (used by the ephemeral status line) is intentionally intact.
assert get_tool_verb("clarify") == "Asking"
Loading