DeepSeek Harness plugin

dsh-finish-reason-patch

Tolerate OpenAI-compatible model streams that end without a terminal finish_reason so DSH turns complete instead of aborting.

Jump to install

Source facts

Repository
Canson666/dsh-finish-reason-patch
Latest update
Aug 20, 2026
Category
Models & Providers
GitHub stars
0
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-21

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/Canson666/dsh-finish-reason-patch
Plugin: dsh-finish-reason-patch
Author: Canson666

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer3 files
README.mdSource · read only

dsh-finish-reason-patch

DSH plugin: stop agent turns from being aborted when an OpenAI-compatible provider (GPT-family models, gateways, proxies) ends a streaming response with full output but without a terminal finish_reason.

The bug it fixes

DSH routes OpenAI-compatible providers through @earendil-works/pi-ai. When the wire stream ends without ever seeing choices[].finish_reason, pi-ai raises Stream ended without finish_reason (openai-completions.js:438). By that point every content block is already closed and delivered — the response is complete, only the terminal marker is missing. pi-ai surfaces that as an error event, the dsh-llm-pi-ai adapter maps it to a terminal finish {kind:'error', code:'TRANSPORT'} chunk, and the agent loop (dsh-agent-loop) throws — aborting a perfectly good model turn. Other agents (e.g. Codex) tolerate the same wire behavior by treating end-of-stream as the end of the response.

The fix

The plugin hooks the llm/stream waterfall — every model call (agent turns, session-title requests, prepared calls, subagents) flows through it via LlmRuntime.streamWithRegistration. When the terminal finish is exactly that error, it is rewritten to a normal stop finish (or tool-calls if tool-call deltas were delivered), so the turn completes instead of aborting. This holds whether or not content was delivered: some OpenAI-compatible gateways stream only usage — or nothing — before ending without a finish_reason (reporting outputTokens: 0), and they are accepted as complete empty responses instead of retry-looping (Codex-style tolerance). All other error finishes (transport resets, rate limits, HTTP errors, ...) pass through untouched, so the existing retry policy still applies to genuinely broken responses.

When it fires you'll see: [finish-reason-patch] provider finished without finish_reason -> mapped to "stop"

Installation

The plugin is a plain Cordis host plugin (exports name / apply, no external dependencies beyond the ctx Cordis passes in).

1. As a persistent profile bundle (recommended, survives restarts)

Ship this folder as a package named dsh-finish-reason-patch:

1. Place the package where your DSH profile can resolve it, e.g. a junction or symlink inside <profile root>/node_modules/dsh-finish-reason-patch pointing at this folder (same pattern as other local DSH plugins). 2. In the profile root package.json, add the package to dsh.profile.bundles and to dependencies (e.g. "dsh-finish-reason-patch": "link:<absolute path to this folder>"). 3. Restart DSH — bundle patches compose at boot. The package's own cordis.patch.yml inserts the plugin row automatically.

2. As a dynamic Cordis plugin (current process only)

Define/run the plugin host body (plugin.mjs) through the Cordis dynamic plugin tools. It stays active until the process exits; re-run after a restart. Effects are identical and idempotent if both routes are active.

Verifying

  • Logic self-test (no network): node selftest.mjs — all 5 cases pass.
  • Live: run a normal conversation with a GPT-class model. Turns that used to

abort now complete, and the log line above appears.

Removal

Remove the bundle entry + dependency from the profile package.json, delete the node_modules junction, restart DSH. No changes are made to any shipped DSH file.

Files

  • plugin.mjs — the plugin (llm/stream waterfall hook + rewrite logic)
  • cordis.patch.yml — bundle patch layer (the profile-tree insert row)
  • package.json — npm metadata (dsh.runtime: host, dsh.bundle.patch)
  • selftest.mjs — local logic self-test
  • LICENSE — MIT