Skip to content
dsh.fish
Bundle

dsh-auto-reconnect

Client-only auto-reconnect for the DSH web client: wakes the connection controller on visibility/focus/interaction events and on terminal disconnects — the blind spots of @deepseek-ai/dsh-client-connection

Source
tqcq
License
MIT
Updated
Updated yesterday

Readme

# dsh-auto-reconnect

English | [中文](README.zh.md)

Auto-reconnect for the DeepSeek Harness (DSH) web client. The official connection controller has backoff reconnect built in, but it sleeps through two real-world scenarios — this plugin wakes it. Client-only, zero build, no official package touched.

## Install / Uninstall

```sh
dsh plugin --profile web add github:tqcq/dsh-auto-reconnect
```

Then restart dsh-web — adding or removing plugins always needs a restart; a browser refresh alone is not enough.

Upgrade (re-running `add` won't move the resolved commit; `update` re-resolves):

```sh
dsh plugin --profile web update dsh-auto-reconnect
```

Uninstall:

```sh
dsh plugin --profile web remove dsh-auto-reconnect
```

Runtime tweak, no reinstall needed (then reload the page):

```js
localStorage["dsh-auto-reconnect"] = JSON.stringify({ minIntervalMs: 3000, retryDelayMs: 3000, maxAutoRetries: 20, log: true });
```

## Two blind spots in the official controller (source-audited)

Audited against the actually-served `@deepseek-ai/dsh-client-connection` 0.1.2-rc.1:

1. Terminal `disconnected` sleeps forever. Once the official 500ms→10s backoff tops out, the controller enters a terminal state and only wakes on the browser `online` event or a manual tap. Phone sleep/wake keeps `navigator.onLine === true` the whole time, so `online` never fires — every wake-up needs a manual tap.
2. Zombie `connected`. The official client has no visibilitychange / focus listener and no heartbeat. A silently dropped TCP leaves the WebSocket in OPEN while the state still reports `connected`.

## What this plugin does

Listens to `visibilitychange(→visible)`, `focus`, `pointerdown`, `keydown`, `pageshow(persisted)`, and subscribes to the connection state store:

- Event wakes: call `connection.reconnect()` only when state ≠ `connected` (resets backoff, retries immediately) — never interrupts a healthy connection.
- bfcache restore (`persisted`): sockets are dead by definition, reconnect unconditionally.
- Transition into terminal `disconnected`: delayed auto-retry (default 3s), budgeted to 20 consecutive attempts, reset on `connected`. Covers "page already visible and focused, no browser event will ever fire again" (2026-09-11 field report: a hard refresh after a host restart landed in terminal state, still needed a manual tap).

Every trigger logs with a `[dsh-auto-reconnect]` prefix (DevTools console forensics), throttled to 3s by default.

## Compatibility

- Field-verified on host `@deepseek-ai/dsh` 0.1.5-rc.2 (boot activation assertions all green).
- Client contract audited against `dsh-client-connection` 0.1.2-rc.1 source.
- Shape mismatch on `ctx.connection` → warn and stay idle, never breaks the page: graceful degradation on API drift.

## Limitations

The general form of zombie `connected` (background tab NAT-dropped, no OS sleep) cannot be detected client-side: the RPC unary channel is separate HTTP, so a successful probe does not prove the WebSocket is alive. The real fix is a host-side heartbeat + dead-connection sweep (prior art: the deprecated enterhalf/dsh-web-network-optimizer) — a separate plugin if ever needed.

## Tests

```bash
node tests/client_test.js   # 14 groups, vm-sandboxed DOM/connection stubs, no browser
```

## Forking notes

- The in-repo `cordis.patch.yml` must stay `[]`: a comment-only YAML file parses to null and kills profile boot.
- Client-code-only changes need just a browser hard refresh, no dsh-web restart.

License: MIT (see LICENSE).

Install

dsh plugin --profile web add github:tqcq/dsh-auto-reconnect

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source