DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Loop Brake — DSH Plugin for DeepSeek Harness
← Plugins
L

dsh-loop-brake

Loop Brake

DeepSeek Harness plugin: a circuit breaker that denies the Nth identical tool call in a row, by exact hash of tool name and canonical arguments

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:jwilson411/dsh-loop-brake#08eb25770caa657530b326dbd7dc06e0c0b32a8e
READMECompatibilityVersions

Compatibility and provenance

Loop Brake is published as dsh-loop-brake and currently resolves to version 0.1.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
github
Registry updated
8/31/2026

Versions

0.1.0stable
8/31/2026

Related plugins

Loading related plugins…

Latest
0.1.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
any
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
8/31/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in security-access.

Doctor@linxin666/dsh-doctorTransactional rescue mode for DSH profiles with a supervised launcher, isolated recovery capsule, deterministic repairs, health monitoring, and a local Web recovery consolePocketdsh-pocketPut DeepSeek Harness in your pocket: one package, one settings page, and scan a QR code on your phone to access DSH on your computer in sync (LAN + public network, real-time screen mirroring).DSCODE@toddzheng024/dscode-bundleA complete DeepSeek coding agent with persistent shell, Ultra collaboration and automatic permission review.Auto Reviewdsh-auto-reviewSecond-model AI auto-review for DeepSeek Harness approval requests: a read-only reviewer subagent decides allow/deny on the approval answerer chain, with fail-closed fallback and full session-log audit.

README

dsh-loop-brake

A DeepSeek Harness function plugin: a circuit breaker for identical tool calls.

An agent gets stuck. It calls search with {"q":"weather"}, reads the result, decides it needs to call search with {"q":"weather"}, and does. Nothing is wrong with any single call, so nothing stops it, and the loop runs until a budget, a turn limit, or a human notices.

This plugin notices on the third one.

LoopBrakeError: loop brake: tool "search" called 3 times in a row with identical
arguments (call 9f2c1a7b3e04…, limit 3); the call was denied before it ran.
Change the arguments, call a different tool, or stop.

The tool body does not run. The error carries code: 'LOOP_BRAKE', the count, the limit, and twelve hex characters of the call hash, so the denial is legible in a log without the arguments themselves being copied into one.

What it is not

This is not an LLM similarity judge. It is not embeddings, not a fuzzy match, not a semantic "are these two calls basically the same" question put to a model, not a workflow engine, and not a second agent loop watching the first one. There is no model call anywhere in this package and no threshold to tune.

Exact hash match only. Two calls are the same call when

sha256(toolName + "\n" + canonicalJson(args))

produces the same digest, and are different calls otherwise. Canonicalization sorts object keys at every nesting level and nothing else — so {"a":1,"b":2} and {"b":2,"a":1} are one call, and {"q":"weather"} and {"q":"weather "} are two.

That boundary is worth stating plainly, because it decides what this catches:

  • An agent repeating a call byte for byte — caught, on the Nth try.
  • An agent looping while a timestamp, a nonce, a page cursor, or a retry counter changes in its arguments — not caught. Every call hashes differently, and this plugin has no opinion about whether they mean the same thing.
  • An agent alternating between two calls forever — not caught. The counter is a consecutive-repeat streak, so A, B, A, B never reaches three of anything.

A brake that fired on "these look similar" would need a model to decide what similar means, which is a model call in front of every tool call in the composition, with a false-positive rate and a bill. The exact-match version costs microseconds, cannot hallucinate, and is explainable to whoever reads the denial at three in the morning. It catches the single most common shape of a stuck agent and it is honest about the rest.

Install

dsh plugin --profile default add github:jwilson411/dsh-loop-brake

The installer reads dsh.bundle.patch from the package manifest and appends this package to the profile's ordered bundle list. Its cordis.patch.yml carries one insert row, id: loop-brake, with no config — the defaults.

Pin the tools package at 0.1.1-rc.2; that is the release candidate this plugin is developed and tested against.

Configure

One knob, in the plugin's row in the composed patch:

- id: loop-brake
  config:
    maxRepeats: 5
KeyDefaultMeaning
maxRepeats3Consecutive identical calls a session may make before the next one is denied. Counting includes the denied call, so at 3 two identical calls run and the third is refused.

Must be an integer of at least 1. An unusable value is rejected when the plugin applies, not at the first call: a brake with different teeth than the one a profile asked for is discovered, if ever, only when a loop is already burning tokens. 1 denies every repeat immediately, including the first call of any streak — a debugging setting rather than a deployment one.

The environment fallback DSH_LOOP_BRAKE_MAX_REPEATS is read only when the config key is absent. The patch row is the deployment's stated intent, so it wins over an ambient variable.

An id-targeted patch replaces the row's whole config rather than merging into it, so an override must restate every field it means to keep.

Counting

Counts are per session, and per session the plugin keeps one current hash and one count — a streak, not a histogram.

  • Call A, A → the streak is at 2.
  • Call B → the A streak is gone; B is at 1.
  • Call A → back to 1, not 3.

Two sessions never share a count: two agents legitimately making the same call are not one agent looping. A session is the explicit sessionId a caller passes, else the calling agent's id from the execution input, else default.

Memory does not grow with session length — one slot per session, not one entry per distinct call ever made.

The error

error.code       // 'LOOP_BRAKE'
error.toolName   // 'search'
error.count      // 3 — which consecutive repeat this was, counting this call
error.maxRepeats // 3
error.hashPrefix // '9f2c1a7b3e04' — the first 12 hex characters, and no more
error.sessionId  // the session whose streak tripped
error.plugin     // 'dsh-loop-brake'

LoopBrakeError is thrown, not returned. A brake a caller can drive past by ignoring a return value is not a brake, and the failure this exists to stop is precisely the case where nobody is reading return values carefully.

The arguments are never put in the error. They are the thing most likely to hold a path, a query, or a credential the caller passed in, and the error message is the thing most likely to be logged, shown, or handed back to a model.

How it attaches

apply patches the injected tool registry in two places and returns the LoopBrake instance, so a host that wants to inspect or reset it can.

  • ctx.tools.register is patched, so every definition registered for the lifetime of this fiber carries a braked execute. This is the mandatory path and the only one this plugin assumes exists.
  • ctx.tools.execute is patched when the runtime exposes it, which catches what the register patch cannot: a tool registered before this plugin applied.

One host call passing through both seams is counted once, keyed by its call id. A pair of wrappers that both counted would bite at half the configured limit.

Both patches are undone on dispose, so stopping or reloading the plugin leaves the registry exactly as it was found — and forgets every streak, which is the right default. A brake that remembered across a reload would deny the first call of a fresh run for something the previous run did.

No tool is registered. This plugin adds nothing to the model's tool surface. A brake the model can call is a brake it can be talked into arguing with, and there is nothing a status tool could report that the denial does not already say.

Using the library directly

The counting half is exported on its own for a host that owns its call sites:

import { LoopBrake, decorateTool, wrapExecute } from 'dsh-loop-brake/loop-brake'

const brake = new LoopBrake({ maxRepeats: 3 })

// Wrap one execute…
const guarded = wrapExecute(execute, brake, { tool: 'search', sessionId: 's1' })

// …or copy a whole definition with its execute braked.
const braked = decorateTool(definition, brake)

decorateTool returns a copy; the original definition is left alone. Also exported: callHash, canonicalJson, hashPrefix, sessionKey, normalizeMaxRepeats, resolveMaxRepeats, LoopBrakeError, InvalidMaxRepeatsError.

Dependencies

None at runtime. The shipped source imports node:crypto and nothing else — it opens no socket, reads no file, and spawns no process, all of which the test suite asserts rather than promises. @deepseek-ai/cordis and @deepseek-ai/dsh-tools are peer dependencies, provided by the harness.

Node >=22.14.0.

Tests

npm install
npm test

No network, no credentials, no model weights. CI runs the same suite on Node 22.x and 24.x.

License

MIT. Copyright (c) 2026 jwilson411.