Fail-closed runtime tool-call loop guard for DeepSeek Harness.
Install
# from npm (prebuilt)
dsh plugin --profile web add @goodandready/dsh-agent-loop-guard
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:GooDAnDReaDY/dsh-agent-loop-guard
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
⚡ Overview & Problem Solved
When autonomous AI coding agents encounter unexpected tool failures, ambiguous instructions, or hallucination loops, they often enter infinite retry cycles — repeatedly reading the same directory, editing identical lines without progress, or echoing identical output lines across streaming turns. These loops rapidly exhaust token budgets, lock execution threads, and waste API credits without ever completing user goals.
@goodandready/dsh-agent-loop-guard is a native host runtime plugin for DeepSeek Harness that intercepts and breaks loops before they spiral out of control:
- Progress-Aware Tool-Call Guard: Employs epoch-based state tracking so that repeated calls are blocked only when no meaningful state changes or new evidence appear. Legitimate productive iterations (e.g.
read➔edit➔read➔edit) remain fully unrestricted. - Assistant Output Loop Breaker: Detects text-only repetition (identical streaming lines or multi-line markdown blocks) across turns and steps, cleanly canceling runaway generation without damaging session history.
- Answer-Only Graceful Fallback: Instead of terminating abruptly, the plugin triggers structured DSH denials that guide the LLM into answering with text explaining the bottleneck.
- Zero Core Modifications: Implemented purely through Cordis lifecycle hooks (
ctx.tools.guard,agent/pre-step,tools/execute,session/event).
🏗️ Architecture
graph TD
subgraph DSH ["DeepSeek Harness Core Runtime"]
Turn["Agent Turn Execution<br/>(LLM Reasoning & Tool Calls)"]
Stream["Assistant Output Stream<br/>(text-delta & block-end)"]
UserStop["User Message Analysis<br/>(Stop words: stop, halt, прекрати)"]
end
subgraph LoopGuard ["@goodandready/dsh-agent-loop-guard"]
ToolInterceptor["ctx.tools.guard Interceptor<br/>(denyReason evaluation)"]
ProgressEpoch["Progress Epoch Tracker<br/>(Result & Argument Fingerprints)"]
OutputState["AssistantOutputGuardState<br/>(Line & Block Deduplication)"]
SafeLogger["Sanitized Event Logger<br/>(Token/Secret Redaction)"]
end
subgraph Actions ["Enforcement Actions"]
Allow["Allow Tool Execution<br/>(Fresh state or valid progress)"]
Deny["Answer-Only Denial<br/>(Mandates text summary from agent)"]
Cancel["Session Cancel<br/>(keepInbox: false on runaway text)"]
end
Turn -->|Pre-step & Tool call| ToolInterceptor
ToolInterceptor --> ProgressEpoch
ProgressEpoch -->|Productive Result Diff| Allow
ProgressEpoch -->|Duplicate / Budget Exceeded| Deny
Stream --> OutputState
OutputState -->|Exceeds Line/Block Repeat Limit| Cancel
UserStop -->|Immediate Latch| Deny
Deny -.-> SafeLogger
Cancel -.-> SafeLogger
✨ Full Feature Breakdown
1. Progress-Aware Tool Execution Tracking
Unlike naive counters that blindly limit tool invocations, dsh-agent-loop-guard distinguishes between productive iteration and stagnant loops:
- Canonical Fingerprinting: Creates deterministic JSON signatures of tool arguments (
callFingerprint) and results (resultFingerprint). - Progress Epochs: When an edit or command yields a different result, the session transitions to a new progress epoch, resetting the failure budget.
- Granular HTTP/VCS Safety: Distinct endpoints and HTTP methods (e.g., Gitea API operations or curl scripts) are never collapsed under a shared base URL.
- Progress Tool Whitelist: Designated progress tools (
todo_write, etc.) maintain independent no-progress budgets so task checklists do not trigger false positives.
2. Guard Violation Codes & Enforcement Modes
When a loop is detected, the guard rejects the call with structured diagnostics and enforces Answer-Only Mode for the rest of the turn:
| Guard Code | Trigger Condition | Default Limit | Guard Action |
|---|---|---|---|
LOOP_GUARD_STOP |
User sent stop command (stop, halt, cancel, стоп, остановись, прекрати, ответь, петля) |
Immediate | Rejects tool call; requires immediate text reply |
LOOP_GUARD_DUPLICATE |
Identical call arguments with identical result to previous attempt | 1 retry | Blocks exact repeat; demands alternative approach |
LOOP_GUARD_REPEAT |
Repeated calls within current group without state change | maxCallsPerRepeatGroup (5) |
Prevents spinning on single tool |
LOOP_GUARD_LIMIT |
Total non-productive tool attempts since last successful progress | maxToolAttemptsPerTurn (64) |
Caps turn exploratory budget |
LOOP_GUARD_PROGRESS_LIMIT |
Successive invocations of progress tools without content changes | maxProgressToolCallsPerTurn (16) |
Prevents infinite todo-writing loops |
LOOP_GUARD_OUTPUT |
Assistant generated identical lines or multi-line blocks | maxRepeatedAssistantLines (5) |
Cancels session cleanly via agent.cancel() |
3. Assistant Output Stream Guard
Runaway LLM generation can manifest as repetitive narration without tool calls. The output guard monitors streaming text in real time:
- Line Normalization: Collapses carriage returns and whitespace to catch formatted repetition.
- Multi-Line Block Detection: Hashes blocks up to
maxAssistantBlockChars(16,384 bytes) to detect cyclical paragraph generation. - Active Tool Immunity: While tool calls are executing, output cancellation is temporarily suppressed to avoid false alarms during long tasks.
- Lossless Deduplication: Cleanses streaming chunks and message buffers without losing valid context.
4. Enterprise Privacy & Token Redaction
All loop guard warning logs automatically redact sensitive credentials:
- Bearer tokens, passwords, cookies, and query parameter secrets (
token=,api-key=,secret=) are replaced with[redacted]before reaching logs. - Deeply nested argument trees are bounded to prevent memory leaks during massive JSON payloads.
5. Native WebUI Settings, Telemetry & One-Click Updater
- Integrated Settings Card: Placed directly inside DSH Web UI under Settings ➔ Plugins ➔ Plugin Settings (
settings.plugin.item, order 95). - Native DSH Design System: Styled exclusively using standard DSH design variables (
--dsw-alias-*), accessible<label>/<input>associations (htmlFor/id), and smooth chevron rotation without external UI dependencies. - Live Protection Telemetry: Real-time counters of blocked loops (
LOOP_GUARD_DUPLICATE,LOOP_GUARD_REPEAT,LOOP_GUARD_LIMIT,LOOP_GUARD_OUTPUT) with instant statistics reset via/api/@goodandready/dsh-agent-loop-guard/telemetry. - One-Click In-Place Updater: Seamless version verification against the npm registry and one-click upgrades via authenticated loopback API (
/api/@goodandready/dsh-agent-loop-guard/update).
📦 Installation
Install via DeepSeek Harness CLI:
dsh plugin --profile web add @goodandready/dsh-agent-loop-guard
Restart DSH Web UI and reload your workspace.
⚙️ Configuration
Configure via config.yaml or through the Web UI settings:
# config.yaml
dsh-agent-loop-guard:
maxToolAttemptsPerTurn: 64
maxProgressToolCallsPerTurn: 16
progressToolNames:
- todo_write
maxCallsPerRepeatGroup: 5
blockExactDuplicates: true
assistantOutputGuard: true
maxRepeatedAssistantLines: 5
maxRepeatedAssistantBlocks: 5
maxAssistantBlockChars: 16384
Settings Reference Table
| Key | Type | Default | Description |
|---|---|---|---|
maxToolAttemptsPerTurn |
number |
64 |
Maximum un-productive tool attempts allowed before forcing an answer. Set to 0 to disable aggregate budget. |
maxProgressToolCallsPerTurn |
number |
16 |
Maximum consecutive calls to progress tools (todo_write) without substantive progress. |
progressToolNames |
array |
["todo_write"] |
Array of tool names considered progress markers. |
maxCallsPerRepeatGroup |
number |
5 |
Maximum tool calls allowed within the same repeat group without producing a new result. |
maxCallsPerToolPerTurn |
number |
5 |
(Deprecated) Legacy alias for maxCallsPerRepeatGroup. If both are specified, maxCallsPerRepeatGroup takes precedence. Omitted from UI card to avoid confusion. |
strictTools |
array |
[] |
Array of tool names subjected to lower repeat tolerance (e.g. mutating tools). |
strictToolLimit |
number |
3 |
Maximum repeat count allowed for tools specified in strictTools. |
blockExactDuplicates |
boolean |
true |
Immediately block consecutive identical calls with zero state change. |
dryRunMode |
boolean |
false |
Audit mode: logs violations and tracks telemetry without aborting tool calls. |
assistantOutputGuard |
boolean |
true |
Enable real-time detection and cancellation of repetitive assistant output loops. |
maxRepeatedAssistantLines |
number |
5 |
Threshold of consecutive identical output lines to trigger session cancellation. |
maxRepeatedAssistantBlocks |
number |
5 |
Threshold of repeated multi-line markdown blocks before aborting generation. |
maxAssistantBlockChars |
number |
16384 |
Maximum byte length captured for block fingerprinting. |
🧪 Testing & Verification
Run the full automated test suite covering all loop denial paths, streaming output guards, and progress epochs:
npm test
npm run check
📄 License
- License: MIT © GooDAnDReaDY
- Changelog: Detailed release history and migration notes are maintained in CHANGELOG.md.
Links
More in this category
kenryu42/cc-safety-net★ 1749
Pre-execution guard that blocks destructive Git and filesystem commands and access to secrets such as SSH keys and .env files before a tool call runs.
toby-bridges/api-relay-audit★ 887
Runs local security audits of AI API relays and LLM proxies from DeepSeek Harness, producing Markdown reports for prompt injection, model substitution signals, tool-call rewriting, error leakage, stream integrity, and profile-gated Web3 risks.
SeaOf0/dsh-redteam-model★ 741
Authorized-security DSH collection: nine work modes (redteam coordinator, pentest, code audit, binary analysis, attack-defense, AV evasion, incident response, cloud security, CTF solving) and fifteen runtime plugins, managed from a settings page with one-click deploy, install, update and uninstall.
howmp/dsh-pentest★ 608
Authorized pentest mode for DeepSeek Harness — exploration chain, assets and findings with a Web view.
ADWMC/helm-d#helmd★ 443
Security-analysis plugin for DeepSeek Harness: 33 tools spanning six security domains, evidence/reporting and toolbox, plus the case lifecycle, routing and tool ledger; a session-bound case workflow with an evidence chain, E-id validation and reproducible findings; 362 on-demand references; three hot-swappable persona profiles; and a four-tab workbench (armor / case / intercept / about) with case-state recovery after context compaction.
PerryLink/dsh-auto-review★ 234
Second-model auto-review on the approval answerer chain: a read-only reviewer subagent returns structured allow/deny verdicts with reasons, fail-closed by default.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.