You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(agent-runner): loop-breaker for stuck repeat-loops + web_fetch raw-HTML bug (#229)
* fix(agent-runner): loop-breaker for stuck repeat-loops + web_fetch returns raw HTML
Live incident: a Project (research 5 AI startups, draft outreach emails) burned
its ENTIRE 80-step budget on checkpoint 1 without ever finishing it. Root cause
was two compounding bugs, both fixed:
1. web_fetch returned response.text verbatim for ANY html content-type — raw
markup ("<!DOCTYPE html><html lang=...") is unusable for an LLM trying to
extract facts from a page. The agent kept re-fetching the same URLs hoping
for different content and got identical garbage every time. Fixed with a
stdlib-only HTML-to-text extractor (no new dependency — bs4 is installed in
this venv but not a pinned requirement, so depending on it would be a CI
risk): strips script/style/head/comments, converts block tags to newlines,
unescapes entities, and caps output at 12k chars so a single fetch can't
blow a step's context budget. Verified against the actual URL from the
incident (legible text instead of raw markup).
2. Even with readable results, nothing detected that the agent was making the
same call over and over. Qwen's own "return checkpoint_done when satisfied"
self-check never caught it — after 80 identical-ish steps it was still
issuing near-duplicate web_search/web_fetch calls. Added a deterministic
loop-breaker: NoProgressDetected fires when the same (skill, result)
signature repeats _REPEAT_THRESHOLD=3 times within a checkpoint, pausing
early with an honest, specific reason ("'web_fetch' returned the same
result 3x... without new information") instead of grinding through the
rest of the budget. Doesn't depend on the model noticing its own loop —
this is deterministic, seeded from prior history on resume too so a
crash-restart can't reset the counter and re-earn 3 more free repeats.
Two existing tests (test_execute_checkpoint_step_budget_exhausted,
test_run_agent_step_budget_exhausted_pauses_not_blocks) were mocking a
"repeat the same result forever" scenario to test pure budget exhaustion —
now correctly caught earlier by the loop-breaker instead. Fixed both to use
distinct per-step results so they test what they say they test; added two new
tests proving the loop-breaker fires within ~3 repeats (not 60) and does NOT
false-positive on genuine step-by-step progress.
Verified: full test suite (2469 passed, 0 failed, 79 skipped/env-gated), ruff
clean, ran the fixed web_fetch against the real incident URL.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: regenerate D-1 skill manifest for web_fetch.py change
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Mickael Farina <farina.mickael@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
0 commit comments