Expand description
Cross-batch tool-call loop guard.
The runner already deduplicates within a single LLM batch
(is_dup in runner.rs). This module covers the orthogonal case:
a model emitting the same (tool, arguments) pair across many
sequential batches with no progress between them — the symptom
that produced the 22-identical-Bash(cargo check) screenshot.
Trigger conditions (all required, to avoid the false positives that
killed commit 9339cf1’s removed detect_call_loop):
(name, arguments)exactly matches a prior call’s pair — normalised once via\0separator. No fuzzy / token-streak detection (that was the bit that wrongly caught batches like sshls/cat/sshpass).- Every prior matching call had identical output and identical
success— polling a flaky endpoint where the body changes is real progress, not a loop. - No state-changing tool succeeded with a new key in between —
when an
edit_fileto a previously-untouched file lands, the world has changed and any prior repeats no longer count. (A state-changing call to a key that is already in the window does NOT clear it: that meansedit_fileitself is being spammed with identical args, which is a loop we want to catch.)
The trigger threshold is intentionally low (third attempt blocks) so the model gets two “maybe this time” chances on transient flakes but doesn’t burn a full turn budget repeating itself. The agent clears the window at every user-message boundary, so the per-turn-only semantics are guaranteed by the caller.
Structs§
Enums§
- Loop
Guard Decision - What the runner should do with the call it’s about to dispatch.