Banner
@dsh-plugin/dsh-approve-for-me
A DSH (DeepSeek Harness) plugin that reviews and auto-approves command execution, adding an "Approve For Me" sandbox permission option.
English | 简体中文
[!WARNING]
The auto-review model is not perfect and may misjudge or miss cases. Do not treat it as a security guarantee; keep human review and least-privilege controls in place for high-risk, destructive, or sensitive-data operations.
[!IMPORTANT]
This package is all-in-one: it includes not only the Host-side auto-review logic but also ships a browser-side (Web GUI) real-time review status bar
(the ./client sub-path + dsh.client metadata). Installing @dsh-plugin/dsh-approve-for-me gives you both host and UI functionality at once,
with no need to install a separate front-end plugin.
A DSH (DeepSeek Harness) plugin that provides codex-like auto-review of executed commands and adds an
"Approve For Me" option to the sandbox permission choices.
Features
1. Auto-review of executed commands (codex-like)
The plugin registers an approval/request answerer (prepended before the UI answerer) that auto-resolves approval prompts by mode:
| Mode | Behavior |
|---|
off (default) | Everything goes to the original approval chain (Web GUI dialog); behavior unchanged |
auto | Automatically allows when an approve rule matches; automatically denies when a deny rule matches (deny takes priority); everything else is handed back to the user |
full-auto | All approval prompts are automatically allowed (equivalent to codex --full-auto) |
never | All approval prompts are automatically denied (CI / unattended, fail-closed) |
review | Lightweight-model review (see below) — reviewing permission escalations when approve-for-me is selected; reviewing before every tool call when strict-review (Approve For Me - Strict Mode) is selected; other presets keep native manual approval |
In auto mode the rules match against: the tool name (toolName) + the approval reason (reason, which for sandbox escalations includes the model's justification) + the actual command text recovered from the tool/call events in the session log. Rules are regex strings (with the im flags; ^...$ matched per line); invalid regexes are skipped automatically.
2. "Approve For Me" and Approve For Me - Strict Mode = lightweight-model review (review mode with a preset selected, ported from codex guardian)
Implemented following the guardian (auto-approval review) design of the codex CLI: when no preset is selected, all approvals are still handled by the native UI; the two review presets govern different scopes:
| Preset | Review timing | Behavior after approval |
|---|
approve-for-me / Approve For Me | Only when a tool requests a permission escalation | The review model approves or rejects that permission escalation |
strict-review / Approve For Me - Strict Mode | Before every tool call executes (including calls without a permission escalation) | Continues into the original tool and sandbox policy only when approved; if that call later triggers a permission escalation, the same ruling is reused without calling the review model again |
- Review prompt construction (isomorphic to codex
codex-rs/core/src/guardian/prompt.rs):
- system (fixed safety policy): evidence handling (only user messages are trusted, everything else is treated as untrusted evidence, truncation markers should make this more cautious), user authorization levels (high/medium/low/unknown), risk levels (low/medium/high/critical), outcome policy (low/medium→allow; high→allow only when authorization ≥ medium; critical→deny), and the
{{ policy }} slot into which your extra rules are injected;
- user: a compact transcript (
[N] role: text, keeping the first and last user messages as anchors, filling the rest of the user messages newest-to-oldest, and counting the most recent assistant/tool-call/tool-result items against their respective budgets and a cap on recent entries) + the session id + >>> APPROVAL REQUEST START planned-action JSON (tool, callId, reason, justification, actual command) + >>> APPROVAL REQUEST END;
- output contract: strict JSON
{ risk_level, user_authorization, outcome, rationale } (low-risk cases may reply directly with {"outcome":"allow"}), with a small amount of outer prose tolerated; non-JSON is treated as a failure.
- fail-closed: review timeout (default 30s), LLM call failure, or unparseable output → deny by default (configurable
reviewFallback: "ask" to hand off to a human); may be retried reviewMaxAttempts times.
- Denial circuit breaker (ported from codex's guardian circuit breaker): after
reviewCircuitMaxConsecutive (default 3) consecutive denials within the same turn, or reviewCircuitMaxRecent (default 10) denials within the window, control is handed back to a human (forwarded to the UI dialog) to keep the review model from blocking an entire round; it resets automatically on turn switch.
- UI state and rulings: in normal mode the in-progress and completed states are driven by the native
approval/asked / approval/decided events, and the review opinion is written via the log-only hook/result. Strict Mode uses hook/invoked / hook/result to show "reviewing" and the conclusion next to the corresponding tool call; when denied, the tool body does not execute. None of these events enter the model context, and they do not rely on plugin custom events that cannot be registered, so they do not block session reloads. No extra messages are injected by default; reviewNotify: true can show the final ruling of an ordinary permission review at the next model step.
3. The new approve-for-me sandbox permission option (Approve For Me)
- Appends
approve-for-me to ESCALATION_TARGETS, and applies in-place patches to every registered tool definition that carries sandbox_permissions (pwsh / bash / fs, etc.):
- adds
approve-for-me to the sandbox_permissions enum, explaining its use in the description;
- wraps
execute: when a call carries sandbox_permissions: "approve-for-me" (still requiring justification), it is auto-rewritten to the real mode (grantMode, default danger-full-access) and an [approve-for-me] marker is stamped into the justification; the original tool's approveEscalation flow runs as-is, and the answerer auto-approves once it sees the marker.
- All tool patches are replayed on every
tools/change, independent of this plugin's load order.
- Key security point: the sandbox mode itself is never bypassed — every grant still goes through the original
approveEscalation strict widening check + approval channel; only the "ask a human" step is auto-answered (rules / full-auto / lightweight model); mode: "never" takes priority over everything.
4. The approve-for-me and strict-review permission presets (GUI-visible)
permissionPresets builds the dropdown options (schemastery enum) when the service initializes, and profile row configs are not merged (an id-located patch replaces the whole row's config). This plugin's bundle patch (cordis.patch.yml) now ships with a permission row override: installing the plugin automatically writes approve-for-me and strict-review into the preset table, with no need to edit the profile's cordis.patch.yml by hand. The override row is ordered "before Full Access" and restates the base three presets as well (because row configs are not merged):
- id: permission
name: '@deepseek-ai/dsh-permission-presets'
config:
presets:
read-only:
sandbox: read-only
approval: ask
workspace-write:
sandbox: workspace-write
approval: ask
approve-for-me:
sandbox: workspace-write
approval: ask
name: Approve For Me
description: Review each permission escalation automatically.
strict-review:
sandbox: workspace-write
approval: ask
name: Approve For Me - Strict Mode
description: Review every tool call automatically before it executes.
danger-full-access:
sandbox: danger-full-access
approval: never
If a deployment need overrides this row from the profile (e.g. adding/adjusting presets beyond dsh-base), restate the whole row and keep this plugin's two presets (see above); row configs are not merged automatically.
Note: the runtime table write inside this plugin's apply() only makes the new presets visible in the in-session /permission dialog; the settings-page dropdown must rely on the row config above (applied automatically).
- The Web GUI's sandbox-permission dropdown shows "Approve For Me" and "Approve For Me - Strict Mode";
- Can be switched via
/permission approve-for-me or /permission strict-review, or via permissionPresets.set (UI);
- In
mode: review, the review model is only invoked once the session has selected one of the review presets above; other presets continue with native manual approval.
- In the rule-based modes (
off / auto / full-auto / never), approve-for-me keeps its original full-auto semantics; strict-review only activates pre-execution review in review mode.
5. System-prompt context section
Explains the current auto-approval mode to the model (off / auto / full-auto / never / review / preset active); when review has no preset selected, it explicitly states that native manual approval is still in effect.
Configuration
Requires Node.js 24.9+ and dsh-loader 2.x. Supported dsh range: [0.1.5,0.1.7-rc.2). The 0.1.7-rc.2 release itself is excluded: use @deepseek-ai/dsh-experimental-auto-review from that release. Uninstall this bundle before switching an upgraded profile to the official plugin. Both host and browser entries skip legacy behavior on unsupported newer hosts. dsh-auxiliary can select a dedicated model for the official reviewer.
Use the loader's unified form: Settings → Plugin configuration before 0.1.6-alpha.2, or the plugin detail page from that release. The card starts collapsed. Saving replaces the policy scope, releasing its patches, listeners and presets before the updated policy becomes active. Defaults remain mode: off and fail-closed review behavior.
Config-api stores values at <profile>/config/dsh-approve-for-me/config.json, without exposeAllNamespaces. Uninstalling retains the file. Cordis row values can seed initial defaults; an existing file takes precedence. To migrate 1.x, copy your old values into the new form. No files are moved automatically.
The following options show the Cordis bootstrap defaults (the form exposes the same fields):
approve-for-me:
mode: review # off | auto | full-auto | never | review
# review mode (lightweight-model review):
reviewProvider: deepseek # optional; defaults to inheriting the requesting agent's provider
reviewModel: deepseek-chat # optional; defaults to inheriting the requesting agent's model (a cheap one is recommended)
reviewPolicy: | # optional; extra rules injected into the {{ policy }} slot of the safety policy
- Always deny any command that touches ~/keys
reviewTimeoutMs: 30000
reviewMaxAttempts: 2
reviewFallback: deny # deny (fail-closed, default) | ask (hand off to a human)
reviewCircuitMaxConsecutive: 3
reviewCircuitMaxRecent: 10
reviewNotify: false # optional: show one final ruling at the next model step; does not write a deferred "reviewing" line
# rule-based mode (auto, etc.):
approve: ["^git ", "^npm (install|run)", "pnpm"]
deny: ["rm -rf", "format"]
grantMode: danger-full-access # the mode actually granted for approve-for-me
presetName: approve-for-me
presetSandbox: workspace-write # base mode of the review preset; a full-permission request is still a reviewable escalation
presetApproval: ask
strictPresetName: strict-review
strictPresetSandbox: workspace-write
strictPresetApproval: ask
Installation
# in a dsh profile directory (e.g. the web profile)
dsh plugin --profile web add file:C:/Users/jkl-9/IdeaProjects/dsh-approve-for-me
# or with a relative path from this directory
dsh plugin --profile web add file:../dsh-approve-for-me
Then enable it in cordis.patch.yml (see above). It takes effect after restarting dsh web.
Structure
lib/index.js cordis plugin entry (answerer / tool patches / presets / system-prompt section / review calls)
lib/policy.js pure decision logic (rule modes + review prompt construction + strict JSON parsing + denial circuit breaker; no DSH dependency)
test/policy.test.js unit tests for the decision logic
test/plugin.smoke.test.js plugin wiring smoke tests (fake ctx + fake ctx.llm)
Testing
node --check lib/index.js lib/policy.js
node test/policy.test.js
node test/plugin.smoke.test.js
Under the sandbox, the child-process fork of node --test test/ is rejected (EPERM), so run individual test files directly.
Notes and limitations
- The auto-review model is not perfect and cannot replace human judgment, least privilege, sandbox isolation, backups, or other security controls; keep human double-checking for operations involving sensitive data, production environments, or irreversible actions.
- Approval events are written to the session audit by
ApprovalService as usual (approval/asked + approval/decided), so auto-resolutions leave a trace too; review rulings (risk/auth/rationale) are written to the plugin log and the corresponding session-flow status line.
- Strict Mode rejections return a standard
deny at tools/pre-execute and the tool body does not run; approved calls still pass through the original sandbox and other tool policies.
sandbox_permissions: "approve-for-me" without a justification errors out directly (consistent with the original paired validation).
- In
review mode with a preset selected, an explicit sandbox_permissions: "approve-for-me" (carrying the [approve-for-me] marker) is treated as the user's advance consent to that one action and is allowed through directly without the review model; with no preset selected it still goes to native approval.
- Strict Mode does not apply the above marker bypass: every call is first ruled on once by the review model, and later permission escalations with the same callId just reuse that result to avoid re-reviewing.
- The review model's output is used only for ruling; its response is not written back into the session history.