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.

Llm Quota Retry — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
L

dsh-llm-quota-retry

Llm Quota Retry

Keep a DeepSeek Harness agent step alive through an exhausted account quota: after the base provider retry policy gives up on a QUOTA failure, retry the same request once per hour without an attempt limit, and publish the entering, leaving, and current state of that long-term retry to other plugins.

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

npx -y @deepseek-ai/dsh plugin --profile web add github:windwhiterain/dsh-llm-quota-retry#a69732d70b0fc4d55c269c452edde2d21d8a81af
READMECompatibilityVersions

Compatibility and provenance

Llm Quota Retry is published as dsh-llm-quota-retry and currently resolves to version 0.2.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
9/29/2026

Versions

0.2.0stable
9/29/2026

Related plugins

Loading related plugins…

Latest
0.2.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
GitHub
★ 1
Weekly downloads
0
Last push
10/14/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

Related plugins

More verified plugins in models-usage.

Whale Widgetdsh-whale-widgetDeepSeek balance whale widget in the bottom-right corner of the DSH Web interface: balance/today’s usage/peak-off-peak pricing, customizable bubble click sequence (text/balance/today/peak-off-peak/image/random phrases and parallel weighted selection), per-line styles and fonts, floating quick editinUsage@linxin666/dsh-usageUsage statistics plugin for the dsh web GUI: per-provider balance and coding-plan quota detection plus a live token usage ledger, with a compact price glance card above the sidebar's Settings rowClient Ui Model Capabilities@linxin666/dsh-client-ui-model-capabilitiesPer-model capability declarations (image input and reasoning efforts) for custom pi-ai providers, edited in place on the Models settings cards; writes the official llm-pi-ai settings namespace, no DSH source changes.Connect Workbuddydsh-connect-workbuddyConnect locally logged-in WorkBuddy models to DeepSeek Harness, with read-only credit overview and model management.

README

dsh-llm-quota-retry

A DeepSeek Harness host plugin that keeps an agent step alive through an exhausted account quota or balance.

When a model request fails because the account is out of allowance, the ordinary recovery policy gives up once its retry budget is spent and the turn ends. This plugin takes over at that point and retries the same request inside the same open turn once per configured interval — one hour by default, with no attempt limit — until a request succeeds or the turn is cancelled. It is on by default for every session: turn it off per session through the composer chip or /quota-retry, or for the whole deployment with defaultEnabled: false.

That covers three shapes of the same condition: the canonical QUOTA code, and an AUTH or RATE_LIMIT failure whose text names a spent usage limit. The other two are how metered providers answer — a 403 permission error from one that meters a rolling window, a 429 rate limit from one that meters a Token Plan (已达到 Token Plan 用量上限:…, in Chinese) — and a plugin that keyed on QUOTA alone would miss exactly the cases it exists for.

Install

The package is a Cordis bundle with a Web composer half:

From the directory that contains your checkout of this repository:

dsh plugin --profile web add ./dsh-llm-quota-retry

Installation links the package into the profile and records it in dsh.profile.bundles, so the bundle layer is read on every later start. The host row applies without a restart: the profile patch reloads live, the row activates, and the bundle reports enabled with an active fiber.

The composer chip needs one host restart the first time. The dsh.client manifest is read by the client-module scan, which caches its verdict per package for the lifetime of the host — a package first scanned before it declared dsh.client stays cached as "not a client package", and no later rescan clears that entry. Restart dsh web once after installing, or after adding dsh.client to an already-installed package, and the chip appears.

Composition order

Compose the llm-quota-retry row after @deepseek-ai/dsh-llm-retry.

That order is the contract, not a preference. dsh-llm-retry runs earlier in the agent/request-error waterfall, spends its own retry budget first, and calls the next listener once that budget is exhausted. By the time a quota failure reaches this plugin, every earlier recovery policy has already declined it — which is exactly what "after the base quick retries" means. The bundle's own cordis.patch.yml appends the row to the composition root, so a bundle install lands in the right place automatically.

What this plugin does not touch

The plugin is strictly additive for every failure except an exhausted allowance, and for that too in every session whose switch is off:

  • Every other failure delegates unchanged — rate limits, server errors, timeouts, transports, and context overflows keep their ordinary handling, including compaction's own recovery. So does a credential the provider will keep rejecting, and so does ordinary throttling: an AUTH or RATE_LIMIT failure is read as a spent allowance only when its text names one, so a wrong or revoked API key still fails fast and a burst of concurrent requests still keeps its sub-minute retries, instead of either waiting an hour between attempts that cannot succeed.
  • A session whose switch is off delegates an exhausted allowance unchanged. Turning the switch off is the opt-out; every session starts on.
  • The native quick retry is never removed. dsh-llm-retry still runs first and its configured retryPolicy still governs. Note that QUOTA is not in the default retryableCodes, so by default an exhausted allowance triggers no native retry at all and this plugin is the only thing that answers it; a provider that lists QUOTA (or runs mode: always) keeps its own retries, and this plugin wakes up only after they are spent.
  • One configuration is superseded rather than merely left alone. retryPolicy.mode: always has no eligible-code list and no budget, and it asks downstream first, so for an exhausted allowance the hourly schedule replaces its own sub-minute backoff. Every other configuration is untouched.

The per-session switch

Long-term retry is on until the switch is turned off. Three paths move the same switch:

  • Composer chip — beside the model, permission, and compaction chips, with a menu offering On, Off, and Default.
  • /quota-retry <on|off|default|status> — the one write path the chip also uses, so a typed line and a click can never disagree.
  • The setting is per session and durable through the llm_quota_retry storage domain, so it survives a host restart. Without a usable storage domain it degrades to a process-local value instead of failing the switch.

A session with no record of its own follows defaultEnabled, which is true unless the row sets it to false. A forked or delegated session takes its nearest ancestor's explicit setting once, as a snapshot: the value applies to that session from then on, and default clears it back to the deployment default rather than re-inheriting.

Turning the switch off while a session is waiting ends the entry immediately — the pending timer is aborted rather than left to wake an hour later — and reports left with reason disabled.

Configuration

KeyDefaultMeaning
longTermDelayMs3600000Interval between long-term attempts, in milliseconds. Must be a positive finite number no greater than 2147483647.
defaultEnabledtrueThe switch a session starts from before it owns a setting of its own. false makes long-term retry opt-in.

Unknown keys and invalid values throw at activation, so a misconfigured row fails loudly instead of silently retrying on a wrong schedule.

A row that turns the default off makes long-term retry opt-in for the whole deployment; the row below also shortens the interval.

- id: llm-quota-retry
  name: 'dsh-llm-quota-retry'
  config:
    longTermDelayMs: 1800000
    defaultEnabled: false

Reading the state from another plugin

Two read paths are published.

The live retry state, as the service ctx.llmQuotaRetry:

export const inject = ['llmQuotaRetry']

export function apply(ctx) {
  ctx.on('session/event', (session) => {
    if (!ctx.llmQuotaRetry.isRetrying(session)) return
    const state = ctx.llmQuotaRetry.stateOf(session)
    // state.pending       — an hourly wait is armed right now
    // state.attempts      — long-term attempts scheduled for this entry
    // state.enteredAt     — epoch ms the entry opened
    // state.nextAttemptAt — epoch ms of the armed attempt, while pending
    // state.provider, state.turn, state.step — the failing request
  })
}

isRetrying(session) answers whether the session is in long-term retry. stateOf(session) returns a frozen snapshot, or undefined when the session is not in long-term retry. Both accept the Harness Session. This state is in-memory: it exists only while a wait is live.

The per-session switch, as the quotaRetry Session projection:

const facts = ctx.sessionProjections.stateOf(session, 'quotaRetry')
// facts.enabled        — the effective switch
// facts.defaultEnabled — the row's configured default
// facts.source         — 'default' | 'session' | 'inherited'

The projection is the client-visible path, so it is also what the composer chip reads.

Subscribing to the transitions

Two Cordis events carry the timing of every transition.

llm-quota-retry/entered

Emitted once per entry, when the first quota failure takes the session over and its first hourly wait is armed.

FieldMeaning
session, agentThe session and agent whose request failed.
failureThe LlmFailure that opened the entry.
provider, turn, stepLocation of the failing request.
enteredAtEpoch ms the entry opened.
nextAttemptAtEpoch ms of the first long-term attempt.
attempts1 on entry.
pendingtrue — the wait is armed.
retryingtrue.

llm-quota-retry/left

Emitted when an entry ends: a model request in the session succeeded, or the switch was turned off.

FieldMeaning
sessionThe session whose entry ended.
reasonrecovered or disabled.
provider, turn, stepLocation of the last failed request.
enteredAtEpoch ms the entry opened.
leftAtEpoch ms the entry ended.
durationMsleftAt - enteredAt.
attemptsLong-term attempts scheduled during the entry.
export function apply(ctx) {
  ctx.on('llm-quota-retry/entered', ({ session, nextAttemptAt }) => {
    // ...
  })
  ctx.on('llm-quota-retry/left', ({ session, reason, durationMs }) => {
    // ...
  })
}

Behavior and limits

  • The trigger is an exhausted allowance. The canonical QUOTA code always counts; an AUTH or RATE_LIMIT failure counts only when its text names a spent usage limit, quota, balance, or credits, in English or Chinese. Everything else reaches the next listener untouched.
  • The session stays busy. A long-term wait holds the open step, so the turn does not reach quiescence and the session reports as running until a request succeeds or the turn is cancelled.
  • Each attempt is a billed provider request. The schedule is unbounded by design: nothing but a successful request, cancellation, the switch going off, or unloading the plugin stops it.
  • Cancelling a turn keeps the entry. The pending wait aborts, pending becomes false, and no left is announced; the entry and its enteredAt survive, and the next quota failure in that session re-arms the schedule. Only success or turning the switch off ends an entry.
  • The composer chip shows the switch, not live progress. Session projections recompute on session events, and the retry state changes between them, so a live "retrying now" badge would be up to an hour stale. Use the service or the two events for live progress.
  • The wording checks compensate for the harness. A Harness release that classifies a spent allowance as QUOTA — which @deepseek-ai/dsh-llm does as of the fix(llm): classify a spent usage allowance as quota, not auth change, for a rolling window a provider reports as a 403 — needs no wording check at all. A Token Plan provider still arrives as RATE_LIMIT: that harness classifier reads English wording only, so a Chinese body keeps its 429 code and would take its turn down here. The checks keep this plugin correct against both hosts.
  • It consumes no model-visible context. Nothing here reaches the model, and the plugin writes no session events, so it adds no log vocabulary that another build would have to know.

Hot reload

Listing index.js, client.js, and lib/policy.js in the profile's hmr roots makes an edit to either host file replace the module generation in a running host: the plugin is disposed and re-imported, and apply() re-runs. The in-memory retry state does not survive that — a reload drops a pending wait and its entry, exactly as a restart does. The durable per-session switch does survive, because it lives in the storage domain. Editing client.js of an already-registered client row is picked up as a new artifact generation; only adding or changing the dsh.client manifest itself needs a restart, because the client-module scan caches that verdict per package.

Client bundle scope

The shell concatenates every dynamic client bundle of a batch into one classic script, so a top-level declaration in client.js joins the same lexical scope as every other bundle's. A name two bundles both declare is a SyntaxError that fails the whole batch, which takes down every composer contribution in it — not just this one.

client.js therefore declares nothing at the top level: a single IIFE owns every name and only the window.__ModuleLoader__.load(...) call inside it reaches the shell. probe/client-probe.mjs enforces this by evaluating the bundle twice into one scope, which is exactly what a batch does.

Probe

probe/probe.mjs drives apply() against a fake Cordis context and checks every decision, event, state transition, command outcome, inherited setting, and projection value without a Harness:

node probe/probe.mjs

probe/client-probe.mjs loads the composer half through a stub module loader and checks its slot registration, dictionary, menu contents, selected option, and the exact /quota-retry line it submits:

node probe/client-probe.mjs

Neither probe can produce a provider response that reports an exhausted quota. Confirming the composed behavior against a real provider requires either an account that actually runs out of balance or an OpenAI-compatible endpoint that answers 402 with insufficient-balance wording.

License

MIT