dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Unbounded model-request retry for the DeepSeek Harness agent loop, with a live Settings panel.
English · 简体中文
Unbounded model-request retry for the DeepSeek Harness agent loop, with a live Settings panel.
A long unattended run should not die because one request hit a transient network error. This plugin keeps such a request retrying — exponential backoff, with a delay ceiling — until it succeeds or you stop it.
Standard DSH bundle plugin, one-command install. The same package supports both the DSH Web UI and the official desktop client (Electron).
This repository maintains one product line (main). The same package supports both surfaces.
Surface · Install DSH Web UI · Terminal: dsh plugin --profile web add dsh-unlimited-retry Official desktop client (Electron) · No terminal — ask the desktop session to install it with the built-in plugin_manager (below)
The desktop reads the `desktop` profile, and the CLI refuses to touch it by design:
error: profile "desktop" is managed exclusively by the Electron applicationA --profile web install therefore lands in the web profile, and the desktop window reads none of it — the symptom is a Settings page that simply is not there, with nothing in the console.
Instead, say this in a desktop-client session:
Please install dsh-unlimited-retry using this host's built-inplugin_manager. Check the current profile and whether a package of that nameis already installed first; if it is, report its source and version. Then tell
me the result and whether a restart is needed.
plugin_manager is scoped to the current profile (the desktop's is desktop) and uses the host's own package manager to install and register the plugin. No terminal, no cookie construction, no hand-editing a host-managed profile file. Pin a version by asking for dsh-unlimited-retry@.
If Settings does not show the page after a first install, fully quit the client (including the tray) and reopen it rather than stacking a second install.
Why the desktop works: the desktop profile *is* the Web template (base +web-app), and Electron loads the local Web UI URL. Thedsh.clientpanel is
therefore loaded by client-modules there too — no separate client build.Thedsh webcommand line uses its ownwebprofile: restart the host after an
update, then hard-reload the browser (Ctrl+F5, orCmd+Shift+Ron macOS) to
bypass the cache.
DSH ships @deepseek-ai/dsh-llm-retry, but it only *executes* retries — the budget lives on each provider adapter's retryPolicy. Its default mode: "normal" gives up after maxRetries (5 in this build) and then delegates, so the step fails, the turn closes with an error, and the run ends. mode: "always" exists but is all-or-nothing per provider and has no delay ceiling.
This plugin adds a layer *behind* all of them. agent/request-error is a waterfall event and Cordis runs listeners outermost-first, so the last-registered listener is the deepest: every other recovery gets the first word, and only a failure that nobody recovered reaches this plugin.
With the stock 5-retry provider policy the effective order is:
Attempt · Handled by · Behaviour 1 – 5 · the provider's retryPolicy · Its own budget, codes, and delays 6 – ∞ · this plugin · Exponential backoff with jitter, capped at maxDelayMs
Other layers registering on the same event — context-overflow compaction, image offload (dsh-compaction-image-offload), any other retry plugin — sit ahead of this one too. That ordering is the entire reason this plugin registers itself last, and it is what keeps the provider's own policy authoritative.
It is a Host plugin, not a preset plugin, so it applies to every session the profile starts, under every preset. All of the following was verified against the shipped contracts, not assumed:
Situation · Applies · Basis Any agent preset (standard / ptc / minimal / cordis) · ✅ · Host plugin, preset-independent Sibling agents in one composition · ✅ · Each keeps its own ladder An agent mounted inside a preset scope · ✅ · Real Cordis scope-dispatch test headless / ACP / SDK composition (no Web server) · ✅ retries, no panel · One event listener; no Web service needed DSH Web UI · ✅ · dsh.client panel Official desktop client (Electron) · ✅ · Desktop profile = Web template, same Web UI `ABORTED` (you cancelled) · ❌ never retried, by design · A cancellation is not a fault A request the adapter never admitted · ❌ never reaches the event · No agent/request-error is produced A failure before the loop has a turn · ❌ never reaches the event · Same
initialDelayMs, doubles, capped at maxDelayMs, with per-delay randomization so synchronized storms cannot formmaxDelayMs wins any conflict. Ask for 10 ms and you get 10 ms — it is never silently raised because initialDelayMs was largerABORTED is terminal at any configuration (see below)providerRetryAfterMs is respected and capped by maxDelayMs, so one provider cannot park the run indefinitelyretryPolicy (toggleable)Each scheduled retry appends two events, in the same order as dsh-llm-retry:
llm/retry — before the wait, so the log shows a wait that really happened;llm/retry-started — after the wait, when the request is actually re-sent.A retry cancelled by an abort or a plugin unload therefore leaves a llm/retry record with no matching llm/retry-started, which is exactly what happened. mode is recorded as always, and policyKey is the fixed namespace unlimited-retry, which cannot collide with a provider budget.
No limit instead of a fabricated capconfigEditor, surviving a restart and hot-reloading the host half--dsw-alias-* tokens, so light/dark and future rethemes follow automaticallyrole="switch" controls with aria-checked; no DOM outside the component treeABORTED: the one unconditional terminal codeA cancellation is not a fault — the turn was stopped, or the process is shutting down. dsh-llm produces an aborted finish when signal.aborted || code === "ABORTED", which means an ABORTED failure can arrive while the loop's own signal is still live.
Retrying it would re-run work you just cancelled, so the guard runs before excludeCodes and clearing that list does not re-enable it. Code matching is case-insensitive and whitespace-trimmed on both sides, so Aborted, ABORTED and aborted are the same code.
The panel states this rule explicitly, so it never looks like a setting was silently ignored.
autoRetry: true
maxAttempts: null # no attempt limit
initialDelayMs: 1000
maxDelayMs: 60000
jitterRatio: 0.2
retryWithoutProviderPolicy: true
excludeCodes: [] # no configured code is terminal (ABORTED always is)
recordSessionEvents: true
log: true
markerFile: nullNo configured failure code is terminal by default — including auth, quota, billing, and context-overflow codes. That is what *unlimited* means here, and it is announced once at mount so it is a visible decision rather than a silent spend. The only bound is maxDelayMs, so a permanently failing request costs roughly one request per minute, forever.
Two ways to bound it:
that many tries per step and delegates, exactly as if it were not installed.
*Use recommended set* action for the codes where a retry re-sends a request that fails identically:
AUTH, UNAUTHORIZED, FORBIDDEN, BILLING, QUOTA, ACCOUNT_QUOTA, MISSING_CREDENTIAL, INVALID_CREDENTIAL, MODEL_NOT_FOUND, NO_ADAPTER, CONTEXT_WINDOW_EXCEEDED, CONTEXT_OVERFLOW, CONTEXT_LENGTH, INVALID_REQUEST
IMAGE_OFFLOAD_REQUIREDis deliberately absent:dsh-compaction-image-offload
recovers it by shrinking the request and registers ahead of this plugin. If
it declines, retrying is the correct next move.
A cleared numeric field falls back to its default rather than being read as zero — Number(null) is 0, which for a delay is a busy loop and for jitter is no jitter at all.
# Web UI
dsh plugin --profile web add dsh-unlimited-retryFor the official desktop client, use the built-in plugin_manager from a desktop session instead — see Where to install it. The CLI refuses --profile desktop by design.
dsh plugin --profile web add github:xalor888/dsh-unlimited-retry⚠️ The npm name and the github: spec are the same package name — **don't mixthem**; installing both leaves conflicting entries. If you installed via
github:, rundsh plugin --profile web remove dsh-unlimited-retryfirst.
dsh plugin --profile web add link:/absolute/path/to/dsh-unlimited-retryThen open Settings → Retry.
If the panel does not appear, check the host half and the client half separately:
~/.dsh/adapters/unlimited-retry/mounted.json exists and itsversion is the version you meant to install;
/plugins/??dsh-unlimited-retry/client.js&rev=… returns 200in the browser (or the desktop's devtools).
Host half present but client half missing is a cache problem — hard-reload (Ctrl+F5, or Cmd+Shift+R on macOS). If the host half is absent, the plugin never mounted: go back to the install step and confirm which profile you installed into.