dsh-approve-for-me
中文说明见 README.zh.md。
A DeepSeek Harness (DSH) host plugin that adds an approve-for-me approval mode.
When enabled, every approval request that reaches the DSH answerer chain is judged by an LLM safety reviewer against a summary of the prior conversation. The model answers ALLOW or REJECT. Destructive high-risk commands — rm -r, rm -rf, drop table, drop database, and friends — skip the model entirely and are sent to the normal interactive user prompt.
The plugin does not add a new
approve-for-mepolicy enum (that is hardcoded in the host packagedsh-user-approval). It works inside the existingaskapproval policy: the plugin's enabled state is the mode switch. The DSH approval audit events (approval/asked+approval/decided) are still recorded normally.
Behavior
- Listens on
approval/requestwithprepend: true, so it runs before the interactive (user-prompt) answerer. - Extracts the concrete action text for the request:
commandfrom bash-family tool-call arguments,- tool arguments JSON for other tools,
- or the request
reasonas a fallback.
- If the action (or the request reason) matches a high-risk pattern →
next(), i.e. the normal user approval prompt. - Otherwise it sends the LLM:
- the conversation summary (most recent ~12k chars of user messages, assistant tool activity, and tool calls),
- the requested action (tool name, reason, command/args).
- The model must reply with exactly one word:
ALLOWorREJECT.ALLOW→allowed-onceREJECT→rejected- unparseable / timeout / model error / no model service →
next()(falls back to asking the user; never silently allows, never silently rejects)
High-risk patterns
The following are matched as exact patterns against the extracted action text (case-insensitive regex, no g flag):
rm -r,rm -rf,rm -fr,rm -R,rm -rfv, ... (anyrmwith anrflag)rm ... /(recursive/force remove targeting the filesystem root)drop table,drop database,drop schematruncate table ...delete from ...mkfs,mkfs.ext4, ...dd if=shred,wipefsgit push -f/git push --forcegit reset --hardchmod -R 777,chown -R- fork bomb
:(){ :|:& };:
Install (static, survives restart)
Option A: dsh plugin add (recommended)
The package declares dsh.bundle.patch, so dsh plugin add installs it and
adds it to the profile's dsh.profile.bundles automatically — no manual
patch editing.
Clone the repo, then from the directory that contains it run:
git clone https://github.com/shifan3/dsh-approve-for-me.git
dsh plugin --profile web add ./dsh-approve-for-me
Or by absolute path:
dsh plugin --profile web add /path/to/dsh-approve-for-me
Or, once it is published to npm:
dsh plugin --profile web add dsh-approve-for-me
Then restart dsh web. The command runs pnpm add inside
~/.dsh/profiles/web/, then reconciles dsh.profile.bundles against the
installed package. The row that activates the plugin lives in this repo's
cordis.patch.yml.
Option B: manual home patch layer
If you prefer not to use dsh plugin, clone the repo into your DSH home and
insert the row into ~/.dsh/cordis.patch.yml yourself (applies to every
profile):
mkdir -p ~/.dsh/plugins
git clone https://github.com/shifan3/dsh-approve-for-me.git ~/.dsh/plugins/approve-for-me
Then create or extend ~/.dsh/cordis.patch.yml with:
- insert:
- id: approve-for-me
name: '../../plugins/approve-for-me/lib/index.js'
config:
enabled: true
provider: deepseek-official
model: deepseek-v4-flash
maxTokens: 512
summaryMaxChars: 12000
timeoutMs: 60000
Then restart dsh web. The relative name resolves against each profile
directory (~/.dsh/profiles/<name>/), so ../../plugins/... reaches
~/.dsh/plugins/... for the shipped web, tui, and cc-tui profiles.
If you previously installed via Option B and then switch to Option A, remove the
approve-for-meinsert from~/.dsh/cordis.patch.ymlfirst so the row is not defined twice.
Enable / disable / configure
- Disable: set
enabled: falsein the row config and restart.- Option A: add an id-targeted override to the profile patch
(
~/.dsh/profiles/web/cordis.patch.yml):- id: approve-for-me config: enabled: false - Option B: edit the insert block in
~/.dsh/cordis.patch.yml.
- Option A: add an id-targeted override to the profile patch
(
- Uninstall:
- Option A:
dsh plugin --profile web remove dsh-approve-for-me, then restart. - Option B: remove the
insertblock (or the whole patch file), then restart.
- Option A:
- Config keys (all optional):
enabled(defaulttrue)provider(defaultdeepseek-official)model(defaultdeepseek-v4-flash)maxTokens(default512)summaryMaxChars(default12000)timeoutMs(default60000)reasoningEffort(defaultoff; keep itoffso the verdict is not consumed by reasoning tokens)
Install (dynamic, per-run)
If you prefer not to write to ~/.dsh, you can load host-code.js as a dynamic Cordis Host package (does not survive restart):
- Web UI: open the Cordis panel → new Plugin (id prefix such as
afm) → pastehost-code.jsas the Host code → run. - With
cordis_define/cordis_run: readhost-code.jsand pass it ascode.host.
Files
lib/index.js— static host plugin (ESM, no runtime dependencies).host-code.js— dynamic Cordis Host package variant (same logic, sandbox-safe subset).cordis.patch.yml— bundle patch (declared viadsh.bundle.patch) that activates the plugin row.
Limitations
- The dynamic variant runs in the DSH dynamic-host vm sandbox: no
setTimeout/AbortController, so its timeout relies on the LLM adapter'sstreamIdleTimeoutMs(default 300s). The static variant uses a realAbortSignal.timeout. - The session approval policy still displays as
ask; the plugin is a prepended answerer, not a new policy enum. - Only one approve-for-me answerer should be active: if you load both the static and the dynamic variant, the later-prepended one wins.
License
MIT
No comments yet. Be the first to write one.