dsh-llm-network-retry: Keeping a conversation alive during network outages

Introduction

One of the most common failures when running agent tasks is that a conversation is interrupted halfway due to a network drop—closing the lid, disconnecting VPN, or weak network handshake timeouts. The built-in @deepseek-ai/dsh-llm-retry in DSH does retry, but the budget comes from the provider’s retryPolicy, with a default normal limit of a maximum of 5 times, giving up after about 10 seconds. The result is that the turn ends, and you must manually click “Continue.”

elfgzp/dsh-llm-network-retry is a plugin specifically designed for this scenario: unbounded retries for connection-type failures until the network recovers; other errors remain bounded, allowing real 400s, authentication failures, or context length exceeded to fail immediately.

Below is an introduction to its positioning, installation, configuration, and boundaries. Facts are derived from the plugin’s GitHub README and package.json.

What is this

One-line positioning: it attaches to the agent loop’s retry waterfall, adding an unbounded retry chain for failures of the “network disconnected” type.

  • Plugin name: dsh-llm-network-retry
  • Maintainer: elfgzp
  • Category: Model Inference
  • Version: 1.2.0
  • License: MIT
  • Stars: 0

It implements the unbounded_connection_retries rule found in Codex CLI: only errors of the “network disconnected” type, such as ConnectionFailed, are retried unboundedly, doubling from 5s to 60s; the rest remain bounded. Once installed, it takes effect without the need for configuration.

Core Features

Separating retry budgets to avoid burning tokens on dead ends

The difference between this and the built-in llm-retry is not “whether it can retry,” but where the budget comes from:

Built-in llm-retry This plugin
Default budget provider retryPolicy, default max 5 times Unbounded for connection failures; 5 times for others
Default backoff 500 ms → 10 s Unbounded 5 s → 60 s; others 500 ms → 10 s
Approx. time to give up ~10 seconds Unbounded for connections; others ~10 seconds
10s network drop Turn ends, manual “Continue” needed Automatically reconnects

In other words, the plugin changes the judgment of “whether it is worth waiting”: dead-end chains shouldn’t be waited on, but live chains should continue waiting until recovery.

Avoid building duplicate connection chains

The plugin is prepended before the built-in executor. Connection-type failures are handled entirely by it as a single infinite chain, rather than “built-in 5 times + this plugin another.” Providers that have already declared mode: always are handed over entirely to it, without stacking. This avoids two executors building retry chains and fighting for the budget.

Every retry is a real billing request

The unbounded mode will keep retrying until success or until you cancel. This is its design purpose, but it is worth noting: every retry during the wait is a real request.

For the round interrupted by a crash, take a separate path

After the process is killed, crashes, or close timeouts, the agent-loop will detect an unclosed log entry at the end of the logs on the next startup and append a synthetic turn/end{interrupted} to close it. This failure is unrelated to the network and cannot be intercepted at the request level.

The plugin fixes three things:

  1. A prompt bar below the input box indicating “Last run interrupted (Round 3)” and listing tool calls (bash, write) with unknown results, so you can verify the real state before continuing.
  2. On startup, scan all sessions. A small yellow dot appears in the sidebar for sessions that ended with a crash; hovering shows “Round N interrupted, these tool call results are unknown: …”.
  3. When opening such a session, automatically continue the last round without needing to click a button.

The trigger condition is “the last round in the log is interrupted,” regardless of the startup source. This allows it to do three things at once: crashed sessions will resume, normal sessions from three months ago won’t move, and when a crash is reproducible, it won’t loop (the next restart will no longer auto-resume, instead leaving it to human judgment).

Two implementation details are worth noting: the scan is read-only (open(id, 'read')), does not take write privileges, has limited concurrency defaulting to 4, skips failed sessions individually without affecting others, and is cancelable; the continue message uses agent.followup() instead of sessionController.prompt() because the latter writes the message source as user-rpc, making it indistinguishable in the logs as coming from the plugin.

Decision Table

Which errors to retry and which not to retry is determined by the table below:

Failure code Behavior
TRANSPORT / TIMEOUT / NETWORK Unbounded, 5 s → 60 s
SERVER / RATE_LIMIT / EMPTY_RESPONSE Bounded, default 5 times, 500 ms → 10 s
AUTH / QUOTA / CONTEXT_WINDOW_EXCEEDED / INVALID_REQUEST No retry, immediately passed to downstream
Current round cancelled No retry, and does not notify downstream
Codes not listed Passed to downstream (retry if preset: always)

The server’s Retry-After header will be respected—as long as it does not exceed the strategy’s upper limit; if exceeded, local backoff is used, and the strategy does not terminate there.

Installation and Enablement

Requires DeepSeek Harness 0.2.x.

Web Interface: Sidebar → Plugins → Add Plugin → Install 3rd party plugin, paste the repository address:

https://github.com/elfgzp/dsh-llm-network-retry

After confirmation, enable Network Retry in “Installed Plugins.”

To let the agent install: In a session where the Plugin Manager tool is enabled, say:

Install and enable this plugin from https://github.com/elfgzp/dsh-llm-network-retry

Command Line:

dsh plugin --profile <profile> add "github:elfgzp/dsh-llm-network-retry#main"

After installation, just start chatting. Retry lines will appear in the chat during network outages. If you modify config or update the plugin code, reloading the plugin can be achieved by toggling it once (turning it off and then on). The only exception is newly added browser-side features, which require a Harness restart.

Configuration

The default value is the shape of Codex; leaving config empty works:

- insert:
    - id: llm-network-retry
      name: dsh-llm-network-retry
      config:
        preset: codex
        maxRetries: 5
        unboundedConnectionRetries: true
        maxUnboundedWaitMs: 0
        providers: []
        models: []
        honorRetryAfter: true
        respectProviderPolicy: true
        requestBackoff:
          initialDelayMs: 500
          maxDelayMs: 10000
          jitterRatio: 0.1
          multiplier: 2
        connectionBackoff:
          initialDelayMs: 5000
          maxDelayMs: 60000
          jitterRatio: 0.1
          multiplier: 2

Three presets:

  • codex (default): Unbounded for connection-type failures, bounded for others. Use this for daily use.
  • always: Unbounded retry as long as it’s not a fatal error. Suitable for long tasks where you’d rather wait an hour than break.
  • normal: Never unbounded in any situation; equivalent to the default behavior of the built-in llm-retry, only with the budget managed by this plugin.

Typos in field names will result in an error immediately upon activation and will not silently fail.

Typical Usage

After installation, no extra configuration is needed; it works directly in chat. During a network outage, the chat will render native retry lines, with a style roughly like:

↻ Retrying… 3/∞ · in 12s Connection reset

When opening a session interrupted by a crash, you will see a prompt bar. Selecting “Continue last task” will allow the model to proceed sequentially: first verify real status (files, Git, remote, services) → only retry read-only or idempotent operations → confirm side effects before retrying those with side effects → ask if the status is unclear. This is exactly the deliberate design of the plugin: when the process dies, there might be git push, npm publish, or deployments flying, so blindly replaying is not possible.

Use Cases and Notes

Suitable for DSH users who frequently run long tasks and are affected by network switching, especially scenarios where network drop windows (closing lid, VPN, weak network) should not interrupt a conversation.

A few notes to keep in mind:

  • The plugin runs with the permissions of the current dsh process. Before installing third-party plugins, you should check the source code and license (MIT).
  • It only covers the layer of model request failures. Tool calls and shell command failures have their own timeout and retry semantics, which the plugin does not touch.
  • Code that directly calls ctx.llm.stream() bypasses the agent loop and therefore does not enjoy retries here (the built-in executor has the same boundaries).
  • Crash recovery only handles the last round in the logs; earlier interrupted rounds have been overwritten by subsequent rounds, and the page will not ask again.
  • The community directory is an independent site with no official affiliation to DeepSeek or Hypersphere; this is not an official app store.

Conclusion

The value of this plugin is focused: it turns failures like “network disconnected” that could self-heal from requiring manual “continue” into automatic reconnection; meanwhile, it does not change the behavior of errors that should truly fail fast. The default configuration is the shape of Codex; adjust presets and backoff as needed.

Directory page: https://www.skillhub.cn/plugins/elfgzp/dsh-llm-network-retry

GitHub: https://github.com/elfgzp/dsh-llm-network-retry

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye