# client-resume-button [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) [![DSH](https://img.shields.io/badge/DSH-%3E%3D0.2.0--rc.2-blue)](https://github.com/deepseek-ai/deepseek-harness) Adds a **Continue** button to the DeepSeek Harness composer. When the previous turn was interrupted, one click asks the model to pick that turn back up. [中文](./README.md) | English --- ## Preview ![preview](./docs/screenshot.png) The circled button is **Continue**, sitting left of the model selector. It collapses when you start typing or attach a file, and expands back when the composer is cleared. ## What it does The button is visible when both hold: - The previous turn ended with `aborted` (user pressed stop), `interrupted` (crash), or `error` - The primary submit button is disabled — meaning the composer is empty, no attachment is pending upload, and no turn is running `aborted` only counts when the sub-reason is the user pressing stop; a timeout or policy cancel does not. On click it sends two plugin-origin notices to the model: one asking it to continue, one reminding it to check for leftover processes and not repeat completed steps. ## Install ``` Plugins → Add plugin → enter client-resume-button → install → restart DSH ``` Command-line equivalent (`--profile` is the profile you actually run: `desktop` for the desktop client, `web` for `dsh web`): ```sh dsh plugin --profile desktop add client-resume-button ``` **Restart DSH afterwards** — the browser half loads at startup only.
Other install methods From the Git repository (requires reaching `github.com`): ```sh dsh plugin --profile desktop add github:ddy-007/dsh-client-resume-button ``` If `github.com` is unreachable, a third-party mirror such as `ghproxy.net` can relay the clone. A mirror is a convenience for working around network restrictions, not a recommended path — it routes your install through a third party whose supply-chain trust this project has not assessed. Prefer the npm package or a direct GitHub connection. ```sh dsh plugin --profile desktop add https://ghproxy.net/https://github.com/ddy-007/dsh-client-resume-button.git ``` While editing the source, use `link:` so you do not reinstall on every change: ```sh dsh plugin --profile desktop add "link:" ```
## Uninstall ```sh dsh plugin --profile desktop remove client-resume-button ``` ## Verification ```sh npm test # contract tests: projection fold, button visibility, internal-contract probe npm run record # one printed fold per state npm run verify # mounts the real projection registry in a throwaway DSH_HOME ``` The suite covers three layers: the fold in `lib/projection.js` (no installed dependencies, no network), the `shouldShowButton` visibility rule, and the `probeContracts` internal-contract check. The latter two load `lib/client.js` in Node through `tests/support/load-client.mjs` — the host bundles React inside `app.asar` and there is no resolvable React locally, so they cover the decision, not the rendering. `npm run verify` needs the SDK (`pnpm install`): it mounts a real Cordis context and the real projection registry, appends real events to a real `Session`, then reads the snapshot the client renders from. The event shapes in the suite come from real session logs rather than invented approximations. CI (`.github/workflows/ci.yml`) runs four jobs: projection tests, isolated interruption states, an `npm install` against the real harness, and a packed-contents check. ## Limitations - **The button sits left of the model selector**, not next to the primary submit button. The closer `conversation.input.activity` slot is a `single` slot already taken by the voice-input plugin; claiming it prevents the app from starting. - **Starting a new turn dismisses it.** The interruption mark is cleared on the next `turn/start` — once you send anything after interrupting, the chance to continue that turn is gone. - **It cannot restore the interrupted output.** That partial content was never persisted. The plugin starts a new turn; it does not resume a token stream. ## Compatibility Verified on DSH `0.2.0-rc.2` and `0.2.1-rc.2`. `dsh.engines.dsh` declares `>=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-rc.1 <0.3.0-0`; below that floor, or at `0.3.0` (including `0.3.0-rc.1` and other prereleases of it), the install is refused. The range is written in two segments rather than `>=0.2.0-rc.2 <0.3.0` because the single-segment form silently excludes every `0.2.1-*` prerelease: node-semver only admits a prerelease version when *some comparator itself* shares its full `major.minor.patch` tuple and carries a prerelease tag. With one segment, `0.2.1-rc.2` is excluded and `npm install` fails with `ERESOLVE`. DSH's own compatibility gate evaluates with `includePrerelease: true`, which hides the problem — so the two-segment form is a fix, not a style preference. For the same reason, every `@deepseek-ai/*` peer range names a version line and none uses `"*"`. These packages keep a long-stale `latest` tag (the harness-matched line sits on `next`), so a wildcard resolves to an old `0.0.x` graph that includes packages never published to npm, and the install fails outright. Relies on these internal contracts, which may change across DSH releases: the `turn/end` reason enum, the `conversation.input.right` slot, the `_primary` class name, `agent.inbox.prepend` / `agent.steer`, and `window.__ModuleLoader__.load`. ## License MIT