mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix: prevent duplicate update prompt spam in gateway watcher (#8343)
The _watch_update_progress() poll loop never deleted .update_prompt.json after forwarding the prompt to the user, causing the same prompt to be re-sent every poll cycle (2s). Two fixes: 1. Delete .update_prompt.json after forwarding — the update process only polls for .update_response, it doesn't need the prompt file to persist. 2. Guard re-sends with _update_prompt_pending check — belt-and-suspenders to prevent duplicates even under race conditions. Add regression test asserting the prompt is sent exactly once.
This commit is contained in:
parent
7a67b13506
commit
4eecaf06e4
2 changed files with 61 additions and 2 deletions
|
|
@ -403,6 +403,56 @@ class TestWatchUpdateProgress:
|
|||
|
||||
# Should not crash; legacy notification handles this case
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_prompt_forwarded_only_once(self, tmp_path):
|
||||
"""Regression: prompt must not be re-sent on every poll cycle.
|
||||
|
||||
Before the fix, the watcher never deleted .update_prompt.json after
|
||||
forwarding, causing the same prompt to be sent every poll_interval.
|
||||
"""
|
||||
runner = _make_runner()
|
||||
hermes_home = tmp_path / "hermes"
|
||||
hermes_home.mkdir()
|
||||
|
||||
pending = {"platform": "telegram", "chat_id": "111", "user_id": "222",
|
||||
"session_key": "agent:main:telegram:dm:111"}
|
||||
(hermes_home / ".update_pending.json").write_text(json.dumps(pending))
|
||||
(hermes_home / ".update_output.txt").write_text("")
|
||||
|
||||
mock_adapter = AsyncMock()
|
||||
runner.adapters = {Platform.TELEGRAM: mock_adapter}
|
||||
|
||||
# Write the prompt file up front (before the watcher starts).
|
||||
# The watcher should forward it exactly once, then delete it.
|
||||
prompt = {"prompt": "Would you like to configure new options now? Y/n",
|
||||
"default": "n", "id": "dup-test"}
|
||||
(hermes_home / ".update_prompt.json").write_text(json.dumps(prompt))
|
||||
|
||||
async def finish_after_polls():
|
||||
# Wait long enough for multiple poll cycles to occur, then
|
||||
# simulate a response + completion.
|
||||
await asyncio.sleep(1.0)
|
||||
(hermes_home / ".update_response").write_text("n")
|
||||
await asyncio.sleep(0.3)
|
||||
(hermes_home / ".update_exit_code").write_text("0")
|
||||
|
||||
with patch("gateway.run._hermes_home", hermes_home):
|
||||
task = asyncio.create_task(finish_after_polls())
|
||||
await runner._watch_update_progress(
|
||||
poll_interval=0.1,
|
||||
stream_interval=0.2,
|
||||
timeout=10.0,
|
||||
)
|
||||
await task
|
||||
|
||||
# Count how many times the prompt text was sent
|
||||
all_sent = [str(c) for c in mock_adapter.send.call_args_list]
|
||||
prompt_sends = [s for s in all_sent if "configure new options" in s]
|
||||
assert len(prompt_sends) == 1, (
|
||||
f"Prompt was sent {len(prompt_sends)} times (expected 1). "
|
||||
f"All sends: {all_sent}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Message interception for update prompts
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue