Stop an agent that repeats itself

Agent buildEasy15 min

Your task

A coding agent sometimes gets stuck: it reads the same file over and over, or keeps running a test that keeps failing, burning tokens until someone notices. A loop guard watches its tool calls and stops the run.

Write loop_guard(calls, max_steps, max_repeats, max_failures). calls is the run's tool calls in order, each [tool, args, ok]: the tool name, the call's arguments as a string (compared exactly), and whether the call succeeded. Tool names and args can contain any character, | and : included. The three limits are at least 1.

For the call at index i (the run's step i + 1), apply these checks in order:

  1. "step_limit": if i >= max_steps, the run has used all its steps; stop before this call.
  2. "repeat": if this exact call, the same tool with the same args, has already run max_repeats times earlier in the run (in a row or not), stop before it.
  3. Otherwise the call runs. If it failed and it is the max_failures-th failed call in a row, stop after it with "failing". A successful call resets that count.

Return [i, reason] for the call at which the guard stopped the run, or [-1, "ok"] if the run ended without being stopped.

Examples:

  • loop_guard([["read", "a.py", True], ["read", "a.py", True], ["read", "a.py", True]], 10, 2, 3) returns [2, "repeat"]: the third identical read.
  • loop_guard([["test", "", False], ["edit", "a.py", True], ["test", "", False], ["test", "", False]], 10, 5, 2) returns [3, "failing"]: the edit reset the count, then two failures in a row.
  • loop_guard([["ls", ".", True], ["read", "b.py", True], ["grep", "foo", True]], 2, 5, 5) returns [2, "step_limit"].

Examples

loop_guard(calls: list[list], max_steps: int, max_repeats: int, max_failures: int) -> list
Example 1
Inputloop_guard([["read", "a.py", True], ["read", "a.py", True], ["read", "a.py", True]], 10, 2, 3)
Output[2, "repeat"]
Example 2
Inputloop_guard([["test", "", False], ["edit", "a.py", True], ["test", "", False], ["test", "", False]], 10, 5, 2)
Output[3, "failing"]
Example 3
Inputloop_guard([["ls", ".", True], ["read", "b.py", True], ["grep", "foo", True]], 2, 5, 5)
Output[2, "step_limit"]