7e250ce03e
__init__ 659 -> 39 (_init_display_options / _init_model_routing / _init_runtime_state); main 651 -> 403 (_install_single_query_signal_handlers, _collect_kanban_task_images, _route_single_query_images, _run_quiet_single_query as module-level helpers); run 384 -> 293 (_tui_shutdown); load_cli_config 366 -> 115 (_cli_config_defaults, _mirror_config_to_env); cli_tui_mixin: _tui_build_layout 352 -> 196 (_tui_build_input_area, _tui_set_base_style), _tui_handle_enter 333 -> 192 (_tui_enter_overlay -> bool). All regions are zero-back-ref lifts, ast.dump-identical bodies, unresolved-free-names check clean. Two source-inspection tests repointed at the helper that now owns the code.
111 lines
4.2 KiB
Python
111 lines
4.2 KiB
Python
"""`hermes chat -Q` must not leak presentation output into stdout (#93220).
|
|
|
|
The quiet single-query branch lives inline in ``cli.py``'s main flow (no
|
|
standalone function to call), so these pin the branch's required statements
|
|
at the source level — the established convention for behavior with no
|
|
runtime mirror (see the install.ps1 source-text tests). Deleting any
|
|
neutralization reintroduces a leak:
|
|
|
|
- ``reasoning_callback`` → streaming ``┌─ Reasoning ─┐`` box
|
|
- ``tool_complete_callback`` → full file diffs via render_edit_diff_with_delta
|
|
- ``tool_progress_callback`` → MoA reference blocks (printed before the
|
|
mode check in _on_tool_progress)
|
|
- ``tool_start_callback`` → snapshot capture paired with tool_complete
|
|
- ``agent.tool_progress_mode`` → executor's direct progress prints
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import cli as cli_mod
|
|
|
|
_QUIET_ANCHOR = "# Quiet mode: suppress banner, spinner, tool previews."
|
|
|
|
|
|
def _quiet_branch() -> str:
|
|
"""Return the source slice of the quiet single-query branch."""
|
|
source = Path(cli_mod.__file__).read_text(encoding="utf-8")
|
|
start = source.index(_QUIET_ANCHOR)
|
|
# A generous window covers the whole branch body up to the
|
|
# run_conversation call and beyond.
|
|
return source[start : start + 8000]
|
|
|
|
|
|
def test_quiet_branch_clears_reasoning_callback():
|
|
branch = _quiet_branch()
|
|
assert "cli.agent.reasoning_callback = None" in branch, (
|
|
"-Q must clear the reasoning callback on the live agent, or the "
|
|
"streaming Reasoning box renders into captured stdout (#93220)."
|
|
)
|
|
|
|
|
|
def test_quiet_branch_clears_inline_diff_callbacks():
|
|
branch = _quiet_branch()
|
|
for attr in (
|
|
"cli.agent.tool_start_callback = None",
|
|
"cli.agent.tool_complete_callback = None",
|
|
):
|
|
assert attr in branch, (
|
|
f"-Q must set `{attr}`: the inline-diff callbacks print full "
|
|
"file diffs via render_edit_diff_with_delta and are gated by "
|
|
"neither quiet_mode nor tool_progress_mode (#93220)."
|
|
)
|
|
|
|
|
|
def test_quiet_branch_clears_tool_progress_callback():
|
|
branch = _quiet_branch()
|
|
assert "cli.agent.tool_progress_callback = None" in branch, (
|
|
"-Q must clear tool_progress_callback: _on_tool_progress prints "
|
|
"MoA reference blocks before its tool_progress_mode check (#93220)."
|
|
)
|
|
|
|
|
|
def test_quiet_branch_syncs_tool_progress_off_to_agent():
|
|
branch = _quiet_branch()
|
|
assert 'cli.agent.tool_progress_mode = "off"' in branch, (
|
|
"-Q must sync tool_progress_mode='off' to the live agent so the "
|
|
"tool_executor rendering path stays silent (#93220)."
|
|
)
|
|
|
|
|
|
def test_suppress_status_output_gates_quiet_tool_messages():
|
|
"""The executor's [tool]/[done] fallback must stay silent under -Q.
|
|
|
|
``_should_emit_quiet_tool_messages`` is the gate for the quiet-mode
|
|
KawaiiSpinner fallback in agent/tool_executor.py; with the rendering
|
|
callbacks neutralized it would otherwise print ``[tool]``/``[done]``
|
|
lines straight into -Q's captured stdout (#93220).
|
|
"""
|
|
from run_agent import AIAgent
|
|
|
|
agent = object.__new__(AIAgent)
|
|
agent.quiet_mode = True
|
|
agent.tool_progress_callback = None
|
|
agent.platform = "cli"
|
|
|
|
agent.suppress_status_output = False
|
|
assert agent._should_emit_quiet_tool_messages() is True
|
|
|
|
agent.suppress_status_output = True
|
|
assert agent._should_emit_quiet_tool_messages() is False
|
|
|
|
|
|
def test_quiet_branch_neutralizations_precede_run_conversation():
|
|
branch = _quiet_branch()
|
|
# The turn itself runs inside ``_run_quiet_single_query``; the quiet branch
|
|
# must finish neutralizing the callbacks before it hands off to that helper.
|
|
import inspect
|
|
|
|
assert "run_conversation(" in inspect.getsource(cli_mod._run_quiet_single_query)
|
|
run_idx = branch.index("_run_quiet_single_query(cli,")
|
|
for attr in (
|
|
"cli.agent.reasoning_callback = None",
|
|
"cli.agent.tool_progress_callback = None",
|
|
"cli.agent.tool_start_callback = None",
|
|
"cli.agent.tool_complete_callback = None",
|
|
):
|
|
assert branch.index(attr) < run_idx, (
|
|
f"`{attr}` must run before the run_conversation hand-off in the quiet branch"
|
|
)
|