Skip to content

Ayubjon/loopstop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

loopstop

Detect degenerate loops in LLM agent tool-call trajectories — and break them before they burn tokens.

Autonomous agents get stuck. They call the same tool with the same arguments over and over, bounce between two actions forever, or keep re-running a query that returns the same useless result. loopstop is a tiny, zero-dependency library + CLI that spots these patterns so your runtime can stop early, and so your CI/eval harness can fail a trajectory that loops.

loopstop CLI demo

  • 🔁 Three detectors — consecutive repeats, A⇄B oscillation cycles, and no-progress (same call → same result).
  • 🧩 Format-agnostic — reads OpenAI, Anthropic, and LangChain-style step shapes out of the box.
  • 🛡️ Runtime guard — a streaming LoopGuard you drop inside your agent loop.
  • 🚦 CI gate — exits non-zero when a trajectory loops, so it plugs into evals.
  • 📦 Zero dependencies, ESM, Node ≥ 18. No API keys, no network, fully offline.

how it works

Install

npm install loopstop
# or run the CLI without installing:
npx loopstop trajectory.jsonl

CLI usage

Pipe in a trajectory (JSON array, JSONL, or an object with a steps/trajectory/messages array):

loopstop examples/stuck.jsonl
● LOOPING  4 steps analyzed
  verdict: stop   severity: █████░░░░░ 0.50

  consecutive-repeat
    Tool call repeated 4× in a row (indices 0-3).
  no-progress
    Same call and result observed 4× — the agent is making no progress.

Exit code is 1 when a loop is found and 0 when it's clean — drop it straight into CI:

loopstop run.jsonl --quiet || echo "agent looped!"

Get machine-readable output:

cat run.jsonl | loopstop --json

Options

Flag Default Description
--consecutive <n> 3 flag N identical calls in a row
--cycle-repeats <n> 2 flag a cycle repeated N times
--max-period <n> 8 longest oscillation period to look for
--no-progress <n> 3 flag same call+result seen N times
--ignore-args off match on tool name only, ignore arguments
--json off output the full report as JSON
--quiet off print nothing; rely on the exit code

Library usage

One-shot analysis

import { analyze } from 'loopstop';

const report = analyze([
  { tool: 'search', args: { q: 'paris' }, result: 'no results' },
  { tool: 'search', args: { q: 'paris' }, result: 'no results' },
  { tool: 'search', args: { q: 'paris' }, result: 'no results' },
]);

report.looping;    // true
report.verdict;    // 'stop'
report.severity;   // 0.50
report.findings;   // [{ type: 'consecutive-repeat', ... }, { type: 'no-progress', ... }]

Live guard inside an agent loop

LoopGuard keeps a bounded window of recent steps so it stays cheap even on long runs:

import { LoopGuard } from 'loopstop';

const guard = new LoopGuard({ consecutive: 3 });

while (true) {
  const call = await model.nextToolCall();
  const verdict = guard.push(call);     // push the step as it happens
  if (verdict.looping) {
    console.warn('Loop detected:', verdict.topFinding.message);
    break;                              // stop before the next model call
  }
  await runTool(call);
}

Supported step shapes

A "step" is one tool call. loopstop reads whichever field names your runtime uses:

  • tool name: tool, name, tool_name, function, action
  • arguments: args, arguments, input, parameters, params
  • result (optional): result, output, observation, response

Argument objects are compared with stable, key-order-independent hashing, so {a:1, b:2} and {b:2, a:1} count as the same call.

What each detector catches

Detector Pattern Example
consecutive-repeat the same call N times in a row search → search → search
cycle a short pattern oscillating at the tail list → read → list → read → …
no-progress same call and same result repeatedly a query that keeps returning "no results"

The no-progress detector only fires when results are present — without a result it has no evidence either way.

Why

Most agent frameworks only offer a blunt max_turns cap. That stops runaways eventually, but wastes every turn until the cap, and tells you nothing about why it stalled. loopstop recognizes the actual loop the moment it forms, names the pattern, and hands you a severity score — useful both as a runtime kill-switch and as an offline trajectory linter.

Development

npm test   # runs the node:test suite (no build step)

Support

loopstop is free and MIT-licensed. If it saved you some wasted tokens and you'd like to say thanks, an optional crypto tip is always welcome (never expected):

Please send only on the Ethereum (ERC-20) network.

License

MIT © 2026 Ayubjon

About

Zero-dependency loop detector for LLM agent tool-call trajectories — catch consecutive repeats, A-B-A-B oscillation, and no-progress cycles, then break the loop before it burns tokens.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors