dsh-toolbelt
English | 中文
Eight DeepSeek Harness plugins that fill gaps the harness does not cover on its own: a general-purpose persona, a reply-language guard, a per-request vision fallback, two write/exec guards, a cross-agent memory bridge, image-generation tools, and shell-command substitution inside skills.
Every plugin is an independent Cordis plugin with its own entry point, its own typed Config, and its own tests. Mount only what you want.
> Built against @deepseek-ai/dsh@0.1.0-rc.6. The harness is in developer preview and will make breaking changes.
Install
From a local checkout. Build it first — dsh plugin add <path> links the directory and never runs your build, so an unbuilt checkout fails the whole plugin tree at boot with loader entries failed to apply:
git clone https://github.com/cking000bigdemon/dsh-toolbelt
cd dsh-toolbelt && npm install && npm run build && cd ..
dsh plugin --profile <name> add ./dsh-toolbeltDirectly from GitHub — this takes two attempts, by design. A git install fetches source, not build output, so this package builds itself through its prepare script, and pnpm ≥10 refuses to run that until you authorize it:
dsh plugin --profile <name> add github:cking000bigdemon/dsh-toolbelt
# ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED — copy the exact key pnpm prints into
# $DSH_HOME/profiles/<name>/pnpm-workspace.yaml:
#
# allowBuilds:
# dsh-toolbelt@https://codeload.github.com/...: true
#
# then run the same command again.Authorizing a build means this package's code runs on your machine at install time, outside any agent sandbox. Pin a commit (github:cking000bigdemon/dsh-toolbelt#<sha>) so a later push cannot silently change what runs.
Then confirm the layer composed:
dsh --profile <name> --dump-config # look for "# == dsh-toolbelt"Safe by default
Only vision-fallback is enabled on install. It is the one row that cannot surprise you: it does nothing until you send an image to a model that cannot read images.
The other seven change the agent's persona, block tool calls, read files outside the workspace, or execute shell commands. Enable each deliberately in your profile's cordis.patch.yml:
- id: language-guard
disabled: false
config:
targetLanguage: zhA patch replaces a row's whole config value rather than deep-merging, so restate every key you need.
The plugins
| Plugin | Extension point | What it does |
|---|---|---|
vision-fallback | agent/request | A request carrying an image, on a model without image input, is served by an image-capable model for that request only. |
general-agent-prompt | ctx.systemPrompt.section() | Eight persona/discipline sections replacing the coding-assistant identity. Each is true / false / a replacement string. |
language-guard | llm/stream + agent/turn-stopping | Detects non-target-language drift while the reply streams; steers a correction at the turn boundary. |
python-workdir-guard | tools/pre-execute | Denies shell calls reaching for a global Python or pip instead of the project venv, with a model-actionable reason. |
windows-encoding-guard | fs/write-intent, fs/edit-intent | Blocks writes of PowerShell/Python files carrying Windows encoding traps; warns on four softer ones. |
cross-agent-memory | ctx.systemPrompt.context() | Injects Claude Code / Codex memory files as explicitly untrusted context. |
image-generation | ctx.tools.register() | generate_image_gpt and generate_image_gemini; returns a saved path, never base64. |
skill-shell-injection | ctx.skills.registerProvider() | Runs ` !cmd and `` `! ``` blocks inside a SKILL.md before the content reaches the model. |
Each plugin's source file opens with a doc comment covering its design, its config, and its limitations. Read that before enabling.
Notes worth knowing before you enable something
vision-fallbackholds zero mutable state. Switching the model globally and restoring it afterwards is the obvious implementation and it is wrong: the image stays in durable history, so the next request goes to a blind model anyway.agent/requestreplaces the frozen call config for one request, which is both simpler and correct.general-agent-prompt'sruntimesection deliberately ships no built-in text. Only a deployment knows what it bundles, and a wrong claim ("Python is bundled") is worse than no claim, so that section materializes only when you pass a string.windows-encoding-guardguards non-Windows hosts by default. The hazard belongs to the destination interpreter, so a.ps1authored on Linux is still dangerous. It also surfaces a dsh-specific hazard:dsh-fs-localdecodes with a BOM-strippingTextDecoder, so an existing UTF-8 BOM never survives an edit.cross-agent-memoryreads files from your home directory and puts third-party content in front of the model. It is framed as untrusted data and XML-escaped so no closing tag is representable, and{{…}}runs are neutralized so memory content cannot interpolate prompt variables. There is noisProjectTrusted()equivalent in dsh — thetrustProjectflag is yours to wire to a real trust signal.skill-shell-injectionexecutes commands found in files. It only does so under trusted skill roots, memoizes per session so a side-effecting command runs once, and never rescans command output.SkillRegistryexposes no way to intercept an already-registered provider, so this registers its own provider and decorates a provider instance handed to it via config.
Development
npm install
npm test # 342 tests
npm run typecheck
npm run buildTests use the harness's own @deepseek-ai/dsh-agent-loop-testkit and stub adapters. They make zero network calls and write only under os.tmpdir().
Status and limitations
image-generationhas never been exercised against a live provider — no image-generation credential was available during development. Request building and both response shapes are covered by tests against a stubbedfetchImpl; realsize/nacceptance and provider error envelopes are unconfirmed.language-guardkeeps its correction state in memory, so it resets on resume; it registers no slash commands.python-workdir-guardallowspython -c '<program>'and does not resolve variable indirection such asP=pip; $P install x.- Several plugins reach optional seams (
shell,credentials,subprocess,attachments,workspace) structurally throughctx.get()rather thaninject, so they degrade to a no-op when a deployment omits them. Declaring those packages as dependencies would let them use the real types instead.
License
[MIT](LICENSE)