Repository navigation
Releases
v0.2.0 — computed fields, so a card can show what the arguments cannot
Added
fields[].valueaccepts a function:(exec) => string | Promise<string>, resolved by the host before the request is built. A literal string works too, and a field without avaluekeeps reading its own argument as before.diff.before/diff.after/diff.pathaccept a function as well as a parameter name, so a proposal can diff what is on disk against what the call would write. A stringpaththat is not a parameter name is now used as a literal path.service.list()reportscomputedfor every mount, so/dsh-hitl/statusanswers "which mounts need the host to compute a field".
Changed
- A failing resolver degrades instead of blocking. A throw or a timeout — bounded by
resolveTimeoutMs, 5 s by default — turns that one field into a one-line notice; the card still opens and the human still decides. If the resolution machinery itself breaks, the proposal falls back to the call's raw arguments rather than denying the call. diffis validated at mount time. A malformed one is reported and dropped instead of silently rendering as a markdown box.- Each side of a diff is cut past
maxDiffLines(2000 lines) with a notice, so a whole-file diff cannot become an unbounded SSE frame. - Both READMEs document computed fields; the appendix line counts and case counts are updated.
Install
# From npm
dsh plugin --profile <your profile> add dsh-hitl
# Straight from GitHub
dsh plugin --profile <your profile> add github:YunpengDon/dsh-hitl167 zero-dependency cases pass.
v0.1.2 — a 94.5 kB package, staged releases, two pipeline fixes
A smaller package, an off-package tour, and two pipeline fixes.
Changed
- The published package is about six times smaller: 599.8 kB → 94.5 kB packed (851.9 kB → 314.4 kB unpacked, 29 → 21 files). The READMEs no longer embed the screenshots — they link the landing page, whose own repository carries the images at full resolution in both languages.
docs/stays in this repository as source material and is deliberately not shipped. - Releases are staged, not published. Pushing a
v*tag runs.github/workflows/release.yml: it self-checks, verifies the tag againstpackage.json, and runsnpm stage publishover OIDC trusted publishing. The version then waits in npm's staging area until a maintainer approves it with 2FA — a tampered workflow can upload, but it cannot publish.
Fixed
modify.mode: allow-and-informno longer parks revisions in a leaking map. The user's edit was held in aMapkeyed bycallId, so a call that was allowed and then never reachedtools/post-execute— an aborted turn, a process torn down mid-execution — left its entry behind for the life of the process. It is aWeakMapkeyed by the execution object now, so the entry dies with the call that owns it, and a call without an id is labelled by tool name in the dropped-context warning instead of printingundefined.whenUnavailable: 'wait'without a countdown warns at mount time. It is legal, but it blocks an unattended call for as long as its session lives; the mount still installs, and the warning is the whole point.
Install
# From npm
dsh plugin --profile <your profile> add dsh-hitl
# Straight from GitHub
dsh plugin --profile <your profile> add github:YunpengDon/dsh-hitl153 zero-dependency cases pass.
v0.1.1 — slimmer package, npm install documented
Slimmer package, and a canonical install from npm.
Changed
- The tarball is half the size. The README's 2360px retina captures were 1.3 MB of a 1.2 MB package — every install paid for the documentation. They are now exported at 1000px (still wider than the ~860px GitHub and npm render at, and still lossless PNG): 1.2 MB → 599.8 kB packed, 1.6 MB → 851.9 kB unpacked, with no format change and no link change.
- English copy finished. Six sample values inside the English README's config snippets (
title: '这条命令要执行吗?'and friends) were still Chinese; each document now shows its own language. - npm documented as the canonical install, with the GitHub and local-directory installs beside it.
Install
# From npm
dsh plugin --profile <your profile> add dsh-hitl
# Straight from GitHub
dsh plugin --profile <your profile> add github:YunpengDon/dsh-hitlUnchanged
No runtime code changed in this release: index.js, client.js and lib/ are identical to v0.1.0. The 151 zero-dependency cases still pass, and prepublishOnly runs them before any publish can land.
v0.1.0 — human-in-the-loop gate for DSH tool calls
Human-in-the-loop for any DeepSeek Harness tool: mount a tool behind a decision card so a human approves, edits, or rejects the call before it runs.
Highlights
- Mount in one line — a
cordis.patch.ymlentry, orctx.hitl.protect('bash', { ... }, ctx)from another plugin. - A decision card before execution — title + pending proposal (side-by-side diff / text / rendered markdown) + 【Approve】【Modify】【Reject】, with an optional rejection-feedback box whose words reach the agent with the rejection.
- Modify becomes a revision request — editing the proposal flips the primary button, and the edited text goes back to the agent instead of silently rewriting the call (DSH cannot rewrite arguments before execution).
- Countdown with three endings — approve / reject / report the timeout to the agent. A timeout is nobody's decision, so the panel lingers a few seconds saying what happened instead of vanishing.
- Subagent pass-through — a tool call inside an in-process subagent (
spawn/fork) is gated too, and the request is filed both under the child Session and under the Session you are reading: the card appears where you are, labelled 「from subagent Session …」 with a jump button. - A frame-level fallback notice — on the plugins page, inside the settings modal, or with no Session selected, a top-layer notice keeps the decision reachable (approve / reject / jump, plus the count and remaining time).
- Bilingual panel — follows the application language (
zh/en) and switches live; mounting plugins keep their own copy.
Install
dsh plugin --profile <your profile> add github:YunpengDon/dsh-hitlZero dependencies, zero build: index.js (host half) + client.js (browser half) + lib/, all plain JS. Refresh the page once after installing (the decision token is injected per page load).
Checks
151 zero-dependency cases via npm test — protocol frames and uplink validation, the countdown/hold state machine, default proposal derivation, matcher compilation, the browser half's pure logic (markdown, diff, clock, filing, seat visibility), and a guard that keeps the two READMEs and their screenshots honest.