DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Circuit Breaker — DeepSeek Harness 插件(DSH Plugin)
← Plugins
C

dsh-circuit-breaker

Circuit Breaker

在 DeepSeek Harness 中阻止失控的代理循环:拒绝重复的相同工具调用,并在模型之外(指令无法触及的地方)强制执行每个代理的调用上限。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:pricklywiggles/dsh-circuit-breaker#8aa4ffeb0e3df25e186291be8ef8e1f9478a494a
README兼容性版本

兼容性与来源证明

Circuit Breaker 以 dsh-circuit-breaker 发布,当前版本为 0.1.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
github
Registry 更新时间
2026/9/2

版本

0.1.0stable
2026/9/2

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
any
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/9/2
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

相关插件

继续浏览 agents-orchestration 分类下经过校验的插件。

Headless@deepseek-ai/dsh-headlessdsh one-shot bundle:基于 dsh-base 的直接核心 Agent/Session 运行器,不包含 Host、HTTP 或浏览器层Experimental Agent Team Web Profile@deepseek-ai/dsh-experimental-agent-team-web-profile用于 Agent Teams Remote 和 UI 插件的实验性 Web 配置层Subagent Codex@deepseek-ai/dsh-subagent-codex基于官方 app-server 协议的一次性 Codex 子代理提供程序Subagent Claude Code@deepseek-ai/dsh-subagent-claude-code基于官方 Agent SDK 的一次性 Claude Code 子代理提供方

README

dsh-circuit-breaker

A loop guard for DeepSeek Harness. It denies a tool call once the agent has already made that exact call several times, and caps how many calls one agent can make. The check runs in code, outside the model.

Why this exists

I run Qwen3.8-27B locally as the agent behind a DeepSeek Harness box. Two of its subagents went into degenerate repetition on the same afternoon. Here is what the session transcripts showed.

Agent AAgent B
Runtime33 min68 min, until I killed it
Tool calls6261,227
Searches1,200, of which 74 distinct1,043
Worst repeatone query 555 timesone query 1,000 times
Failed callsnonenone

Every call succeeded. Search returned results each time. Nothing errored, so nothing alerted, and the only reason I caught either one was that I happened to look. Both started repeating around step 11 to 28, so this is not context exhaustion, and neither recovered on its own.

The part that convinced me to write this: Agent B had read a briefing telling it "Budget: 12 web_search calls" and "never re-issue a query you have already run". It then made 1,043 searches. Telling the model to stop does not work, because a model in this state has stopped following instructions in any useful sense. The repetition happens below the level where instructions apply.

So the guard has to be code that never asks the model's opinion.

Why this happens, and why it is not only Qwen

Qwen3.8 was my trigger, and the vendor treats this as a known failure. The model card's own non-thinking preset sets presence_penalty: 1.5 to hold back repetition, and calls out language mixing as the cost of pushing it higher. The architecture gives it a reason. Qwen3.8 runs three Gated DeltaNet linear-attention layers for every full-attention one, and the linear layers compress history into a small recurrent state rather than attending over every past token. When that state drifts, the model can lock onto its own recent output. llama.cpp shipped a real arithmetic bug in exactly that path (the key_gdiff fix, PR #19324, merged February 2026) whose symptom was looping and degraded output that got worse deeper into the context. My build postdates the fix, so it was not my cause, but it shows the shape of the problem. In this model family a numerical slip in the engine surfaces as a loop.

The deeper reason is not specific to Qwen at all. Repetition is a self-reinforcing attractor. Each time a sequence repeats, the probability of repeating it again goes up, and the published work on this finds the state holds through added sampling randomness and through changed prompts. That is the mechanism behind the part that surprised me most. Telling a looping agent that it is looping sends it straight back into the loop. The instruction lands in a context already dominated by the pattern.

Two things make agent loops worse than the chat-repetition most people have seen. The repeated unit is a whole tool call, not a word, so llama.cpp's anti-repetition penalty never catches it. That penalty scans the last --repeat-last-n tokens, 64 by default, and two copies of a tool call are thousands of tokens apart. And an agent that loops keeps taking real actions, so it burns time and a model slot while every prompt-level guardrail you wrote sails past it.

None of that is unique to Qwen. Any local model driven as an agent can land in the same attractor, and the mainstream hosted models are not immune either. This guard works on the pattern of tool calls, not on anything about the model, so it does the same job whatever you run behind it. Qwen is just what made me write it.

What it does

The plugin registers a guard through ctx.tools.guard(), which DSH runs before every tool execution. Returning a string denies the call and hands that string back to the model. Denials are monotonic in DSH, so nothing downstream can re-allow a call the guard refused.

It denies on either of two conditions:

  • The same tool has already run with the same significant arguments duplicateLimit times inside a sliding per-agent window.
  • The agent has passed maxCallsPerAgent total calls, which catches loops that vary their arguments enough to slip past duplicate detection.

The denial text explains what happened and tells the model to stop or change approach, so a working model can recover, and the whole exchange lands in the transcript where a human can read it later.

Here is a real one. I asked an agent to run echo cbprobe4 ten times. The sixth call was denied. It tried bash -c and sh -c variants, then stopped and said:

Every step executed the unchanged echo cbprobe4. If you needed all 10 to be byte-identical tool calls, that isn't possible in this session because of the breaker.

That is the behavior I want. It stopped, and it told the user why.

Install

dsh plugin --profile web add github:pricklywiggles/dsh-circuit-breaker

Restart the profile afterwards.

The package ships plain ESM with no build step. That matters more than it sounds. DSH's own docs warn that a GitHub-installed plugin needing a build also needs its users to add an allowBuilds entry to their pnpm-workspace.yaml, which grants that package permission to execute code at install time. This one asks for nothing.

Pin a commit if you want to know exactly what you are running:

dsh plugin --profile web add github:pricklywiggles/dsh-circuit-breaker#<sha>

Configuration

Every setting has a default that works. To change one, target the plugin's row id in a cordis patch layer, either your profile's cordis.patch.yml or $DSH_HOME/cordis.patch.yml:

- id: circuit-breaker
  config:
    duplicateLimit: 6
    maxCallsPerAgent: 300
    incidentLog: /workspace/.circuit-breaker-incidents.jsonl

A circuit-breaker: section in settings.yaml does not work, and I tested it. That namespace reaches plugins that read settings for themselves, not a bundle plugin's config. The patch-layer override is the path that does.

KeyDefaultWhat it does
enabledtrueMaster switch
duplicateLimit6Deny after this many identical calls in the window
window200How many recent calls to remember per agent
maxCallsPerAgent300Lifetime call cap per agent object; once hit, that agent is stopped for good. Sized for unattended subagents, which get a fresh cap per run. 0 disables it
exempttodo_write, ask_user_question, exit_plan_modeTools the guard ignores
only[]If set, guard only these tools. Overrides exempt
ignoreArgsdescription, explanation, reason, thought, purposeArgument names excluded when comparing two calls
denyMessagesee sourceDenial text. Supports {tool}, {count}, {limit}
incidentLog"" (off)Append-only JSONL recording the first denial of each kind per agent, so a supervisor can notice a tripped agent. See below

Picking a duplicateLimit

The default sits far above normal behavior on purpose. Re-reading a file or re-listing a directory a few times is ordinary work and should not be punished. Running the same search 555 times is not ordinary. At 6, a real loop dies in seconds and healthy agents never notice the plugin is installed.

Lower it if you want tighter control and can live with the occasional false positive. Raise it if your agents legitimately poll something.

Things I got wrong building this

The first version never fired. The guard was invoked on all ten calls of my test and denied none of them. DSH's bash tool takes a free-text description argument next to command, and the model rewrites it every time: "Run probe step 1", "step 2", and so on. Ten byte-identical commands produced ten distinct comparison keys.

ignoreArgs exists because of that. It strips annotation-only arguments before comparing. If your tools take a similar field, add it to the list, or the breaker will sit there doing nothing. No unit test of mine would have caught this, because I wrote the fixtures myself and my fixtures did not lie about their own arguments.

Denied calls are not counted. A call that never ran must not push its own count higher or evict a real entry from the window. Getting that wrong makes the breaker latch permanently once it trips.

How it works

State is a bounded ring of recent call keys per agent, not a running tally. No turn-boundary detection is needed, memory cannot grow without limit, and a legitimate repeat from earlier ages out rather than counting toward a future denial.

Keys sort object properties before serializing, so argument order never changes the result. Arguments that will not serialize are allowed through. The guard cannot judge them, and failing open beats blocking real work.

Counters live in a WeakMap keyed by the agent object. A subagent's state disappears with the subagent, and agents in a parallel batch never interfere with each other.

The bundled patch mounts the guard on the host plane, so it covers background subagents. Those are the ones that run unattended long enough to loop, which is how both of mine survived for half an hour. Register it through an agent's own context instead if you want it scoped to one agent.

A denial is not a kill

The guard denies calls; it cannot terminate an agent, because DSH's guard API has no abort hook. That splits outcomes in two:

  • A model that can still read sees the denial, stops, and reports. Its completion reaches whoever launched it through the normal channel. The duplicateLimit tier fires early (six repeats) precisely because that is the window where a model is most likely to still be reachable.
  • A model in true degenerate repetition ignores the denial the same way it ignored its own briefing. The research on repetition attractors matches what I saw: the repeated pattern in context self-reinforces, and it persists through added randomness and through changed prompts. Such an agent keeps emitting the same call and collecting denials forever. It is now harmless, every call denied before execution, but it never completes, so nothing is ever returned to its parent, and on a single-slot model server it still competes for inference until something external stops it.

incidentLog exists for that second case. Set it to a writable path in the patch layer above. On the first denial of each kind per agent, the plugin appends one JSON line:

{"time":"2026-09-01T23:10:07Z","kind":"cap","tool":"bash","count":300,"limit":300,"agent":"agent-007"}

agent is the agent's uuid. Verified live on dsh 0.1.1-rc.2: it is the same id that list_agents reports and interrupt_agent accepts, and it also names the agent's session directory, so an incident maps straight onto the tools a supervisor already has. If a future dsh changes the agent object's shape and no id field is recognized, the entry carries agentKeys instead so you can map it yourself. Writes are append-only and fail open: a bad path never affects the guard.

The supervision pattern this enables, used by the research skills on the box this was built for: a parent that has launched subagents reads the incident file on each of its turns. A tripped child that has not delivered within a few minutes gets interrupt_agent and its work re-dispatched once, with the replacement told that the repeated line of investigation is exhausted. A second trip on the same work item means the item itself is probably unsatisfiable (in my loops, a hallucinated premise), so stop retrying and record the gap.

Limits

This bounds the damage. It does not fix the model. A loop still means the agent failed at its task. What changes is that the failure is fast and visible rather than slow and silent.

A loop that varies its arguments every single time will get past duplicate detection, and only the call cap will stop it. DSH guards are synchronous, so the guard path does no I/O beyond the optional one-line incident append and keeps no state across processes.

License

MIT