pi-deadloop
v0.3.0
Published
Detect and alert on agentic reasoning loops and tool call repetition in pi-coding-agent
Downloads
458
Maintainers
Readme
pi-deadloop
Detect and alert on agentic reasoning loops and tool call repetition patterns.
Installation
pi install npm:pi-deadloopWhat it detects
1. Tool call repetition
Same sequence of tool calls repeated (e.g., read(fileA) → edit(fileA) → read(fileA) → edit(fileA)), indicating the agent is stuck in a loop rather than making progress.
2. Reasoning stagnation
The agent's thinking content is highly similar across consecutive turns (≥4 turns, Jaccard similarity ≥85%), suggesting it's circling on the same problem with the same approach.
3. File read repetition
The same file is read 4+ times across the turn window. Catches the "I need to read more context" loop where the agent keeps re-examining the same source file without making changes. Threshold is deliberately set to 4+ to avoid false positives from legitimate research.
4. Search expansion spiral
grep/find calls with progressively broader or varying patterns on the same topic. Catches the "let me grep for X" → "let me grep for something similar" → "let me search elsewhere" spiral.
Setup
Add to your package.json:
{
"peerDependencies": {
"@mariozechner/pi-coding-agent": ">=0.70.0"
}
}Register the extension:
import piLoopDetectExtension from "pi-loop-detector";
// Default config (all 4 detectors enabled, conservative thresholds)
piLoopDetectExtension(pi);
// Customize: disable noisy detector
piLoopDetectExtension(pi, {
enableSearchSpiralDetection: false, // disable if too noisy
});Behavior
When a loop is detected, the extension sends a warning message to the conversation with actionable context, prunes the problematic turns from the sliding window, and suggests a different approach. The agent sees the message and responds accordingly.
Configuration
| Option | Default | Description |
|--------|---------|-------------|
| windowSize | 10 | Sliding window of turns to analyze |
| reasoningStuckThreshold | 4 | Consecutive similar-thinking turns before flagging |
| reasoningStuckThresholdSimilarity | 0.85 | Jaccard similarity threshold (0-1) for reasoning stagnation detection |
| repeatSequenceMinLength | 6 | Min tool calls in a repeating pattern (pattern × 3 reps) |
| repeatPatternMinReps | 3 | Min repetitions of the same tool pattern |
| enableReasoningDetection | true | Enable reasoning stagnation detection |
| enableToolRepetitionDetection | true | Enable tool call repetition detection |
| enableReadRepetitionDetection | true | Enable file read repetition detection (4+ reads) |
| enableSearchSpiralDetection | true | Enable search expansion spiral detection |
Runtime Configuration
Modify detection settings mid-session via the deadloop command:
/deadloop # Show current status and config
/deadloop windowSize=12 # Increase detection window
/deadloop enableSearchSpiralDetection=false # Disable a specific detector
/deadloop windowSize=12 enableReadRepetitionDetection=false # Multiple optionsThis is useful for tuning thresholds without restarting the agent. Changes apply immediately to the current session.
Debugging
Run /deadloop to see the current detection state, window size, and all configuration values. Pass key=value pairs to modify settings mid-session (see Runtime Configuration).
Design rationale
The extension hooks into turn_end events, providing the full assistant message including thinking content and tool call details. Four independent detectors run on the sliding window:
- Tool repetition — builds an ordered sequence of tool calls, looks for repeating subsequences of length 2–3, requires ≥3 repetitions
- Reasoning stagnation — extracts thinking text, normalizes it (strips paths/code), uses Jaccard similarity with >85% threshold, requires ≥4 consecutive turns
- File read repetition — counts
read()calls per file path, triggers at 4+ reads (high threshold to avoid false positives) - Search expansion spiral — tracks
grep/findcalls, detects same-pattern-multi-path (≥3 paths) or progressively-expanding-patterns
When a detector triggers, a message is sent via pi.sendMessage() with actionable context.
The approach is deliberately conservative — thresholds are set high to avoid false positives from legitimate work patterns (iterative debugging, research, refactoring). All detectors are independently configurable so you can tune or disable them per project needs. Changes apply immediately at runtime via the deadloop command.
