🪔 dsh-genie
Wishes that outlive the session.
DeepSeek Harness can already write its own plugins.
It just can't keep them. This is the other half.
English | 中文
The gap
DeepSeek Harness ships a self-referential toolset — cordis_inspect, cordis_define,
cordis_run — that lets the agent write a plugin and hot-load it into the live process.
You describe a feature, the agent builds it, and it works right now.
Then you restart, and it's gone. From the official README of that toolset:
Dynamic packages live only in the shared DSH process memory. […] They create no Plugin file, install no package, change no
cordis.ymlor personal/project configuration, do not survive restart, and cannot be promoted automatically. To keep an experiment, ask the Agent to implement a normal local, project, or repository Plugin through the regular development workflow.
dsh-genie is that "regular development workflow", collapsed into one tool call.
The loop
you ▸ every time I say "ship it", stage everything and commit with a generated message
▸ agent writes the plugin, cordis_run mounts it ← ships with DSH
▸ you try it. it works.
▸ "keep it"
▸ genie_keep dyn-1 → dsh-wish-ship-it ← this package
▸ restart
▸ still there.
Two extra facts that make this pleasant:
- No pnpm, no network, no build authorization. A granted wish is written straight into
$DSH_HOME/genie/and linked where the launcher already resolves modules. Compare that to the normal path for a git-installed plugin, which needs apreparescript from the author and anallowBuildsgrant from you — arbitrary code execution at install time, outside every sandbox the agent runs in. - It's just files. Every wish is a readable directory with a
package.json, acordis.patch.yml, and anindex.js. Read it, edit it,git initit, publish it. dsh-genie never rewrites a wish after granting it.
Install
dsh plugin --profile web add github:swaylq/dsh-genie
Then restart dsh. That's it — plain JavaScript, no build step, so there is no prepare
script and nothing to authorize.
Already have your own @deepseek-ai/dsh-tool-cordis row? See Composition.
What you get
genie_keep |
Take a dynamic package the agent just proved out and make it permanent. Only appears when the dynamic-package runner is composed. |
genie_wish |
Same, for code the agent wrote directly — no prototype step. |
genie_list |
Every wish on this machine, and whether it's live in this profile. |
genie_revoke |
Unlist and unlink a wish. Keeps the source unless you ask otherwise. |
/wish |
The human view: what's granted, what's active, where the code lives. |
The bundle also mounts @deepseek-ai/dsh-tool-cordis, DeepSeek Harness's own
self-modification toolset. It ships inside every install but the stock web profile
doesn't mount it — its runner already is, so switching it on costs no install.
How it works
Three facts about the launcher, none of them private API:
- A profile resolves bundles through two anchors — the dsh installation, then the profile
directory. Node's ordinary parent walk from
<profile>/package.jsonreaches$DSH_HOME/profiles/node_modules, the flat fallback directory the launcher maintains so in-box plugins resolve from any profile. - That directory is append-only in practice.
healProfilesModuleFallbackkeeps correct links, re-points moved ones, and never prunes what it doesn't own — so a link planted there survives every later boot. dsh pluginwon't reclaim the layer. Its reconcile pass only removes a bundle whose name is (or was) a profiledependencieskey. dsh-genie writes todsh.profile.bundlesand never todependencies, sodsh plugin add anything-elseleaves your wishes alone.
So granting a wish is: write the package → symlink it into the fallback → append one name to the profile's ordered bundle list. No installer, no registry, no build.
One wrinkle worth knowing, because it bites everyone once: Node resolves a package's imports
from its real directory, not from the symlink used to find it. A wish in $DSH_HOME/genie/
would walk genie → $DSH_HOME → ~ and never see the fallback, so dsh-genie plants
$DSH_HOME/genie/node_modules → $DSH_HOME/profiles/node_modules beside the wishes. That
puts every in-box package back on the walk. For the same reason dsh-genie itself declares no
runtime dependency at all — it loads @deepseek-ai/dsh-tools through the profile, the way
the launcher resolves its own bundles, so it works from a link: checkout, a git install, an
npm install, or a directory you dropped in by hand.
Trust stance
Read this before you install it, not after.
A granted wish is ordinary code that runs at every boot, with the harness's full context. That is the point of the package, and it is also the whole risk. dsh-genie does not add a capability your agent lacked — an agent with bash access could already write these files — but it does make it one tool call instead of a visible sequence of them, which is exactly the kind of convenience worth being explicit about.
What the design does about it:
- Nothing runs in the session that wrote it. A grant writes files and edits one manifest. The code loads on the next restart, which only you can perform. That restart is the checkpoint, and every tool result says so.
- Nothing is executed to be validated. Generated modules are parse-checked with
node --check, which parses without evaluating. A wish that doesn't parse installs nothing. - Everything is listable and reversible.
/wishandgenie_listshow every wish and its source path;genie_revokeremoves it. Revoke only acceptsdsh-wish-*names, so a mistyped argument cannot unlistdsh-base. - Wish names are one validated path segment. No scopes, no dots, no traversal.
What it does not do: sandbox the wish, review the code for you, or ask a second time. If you would not run a script an agent wrote without reading it, read the wish before you restart — the tool result hands you the exact path for that reason.
Upstream's stance on the toolset this builds on is the same one, and worth repeating: the vm
sandbox behind cordis_run "isolates globals but is not a security boundary […] treat this
toolset like bash access."
Composition
The bundle inserts two rows: genie-tool-cordis and genie. If something else in your profile
already mounts @deepseek-ai/dsh-tool-cordis, both registrations claim the same tool names and
the load fails loudly (as DSH intends). Disable ours from your profile's cordis.patch.yml:
- id: genie-tool-cordis
disabled: true
Config on the genie row:
| key | default | meaning |
|---|---|---|
allowUpdate |
true |
whether mode: "update" may overwrite an existing wish |
Caveats
- A kept dynamic package changes realms.
cordis_definebodies run in a vm sandbox where Node globals are absent or redirected to Cordis services. A kept wish runs in ordinary Node, so a body written against those facades may need adjusting.genie_keeptells the model to say so when the code touches them; it cannot rewrite the code for you. - Layer order is append. A wish patches on top of
dsh-base, the mode bundle, and every plugin installed before it. A patch replaces a row's wholeconfigrather than deep-merging, so a wish that overrides an existing row must restate every key that row needs. - DSH is a developer preview. Upstream says breaking changes are coming. This package leans on documented launcher behavior rather than internals, but "documented" and "stable" are not the same thing yet.
- The client half is stored, not mounted.
genie_keepsaves aclient.jsbeside the host half for reference; wiring a browser half into the web UI is still manual work.
Development
npm test # 19 tests, real throwaway Harness homes, no network
lamp.js is the whole install mechanism and imports nothing from DSH — it is plain node:fs
and testable on its own. index.js is the plugin surface.
License
Built on DeepSeek Harness (MIT) and Cordis. Not affiliated with DeepSeek.
No comments yet. Be the first to write one.