DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

00wz /

00wz/dsh-subagent-gate

Verified

Ask a human before a delegated subagent widens its sandbox — a DSH plugin that re-arms child approval to `ask` and routes the request to you.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@b7e76979

dsh-subagent-gate

Ask a human before a delegated subagent widens its sandbox.

DeepSeek Harness pins every delegated child session to approval: "never" (see captureDelegatedPolicyOverrides in @deepseek-ai/dsh-subagent), so an escalation request coming from a subagent is rejected before anybody is asked — a parent can be asked, its child cannot.

This plugin re-arms exactly that one knob to ask for children whose subtree still has a human in the loop, and answers the resulting approval request by asking the person. Nothing else changes: the sandbox mode, the permission preset table and the parent's own behaviour stay stock.

Host-only plugin: no client bundle, no UI strings, no remote methods.

How it works

agent/created ─────► Coverage ─────► Knobs   (child: never → ask)
approval/request ──► Coverage ─────► Pipeline ─────► Outcome ─────► Audit
                                        │
                                        └─ stages: human
                                                   rules / denylist / trust /
                                                   judge / autoapprove (planned)
  • Managed children only. A child is managed when some ancestor runs under ask. Children of a danger-full-access / never session are left alone: they already inherit full access and have nothing to escalate.
  • Sandbox untouched. Only the child's approval policy changes. The gate decides whether a one-off widening happens; it never decides how far the child can reach on its own.
  • Only approvals are gated. The plugin hooks approval/request and nothing else: ordinary tool calls — including calls that the sandbox denies outright — never reach it.
  • Fail closed. Rejection, cancellation, timeout, an unavailable answerer or any internal error resolve to rejected. allow is produced only by allowed-once from the person.
  • One card at a time. Concurrent asks are serialized FIFO; a queued ask has not started its timeout yet.
  • allowed-once only. One confirmation grants one call and nothing is remembered. Leaf grants ("allow this child for its session") are a planned stage, not a core change.

Install

# npm
dsh plugin --profile <profile> add dsh-subagent-gate

# or from a packed tarball
npm pack
dsh plugin --profile <profile> add ./dsh-subagent-gate-0.1.3.tgz

Install a packed tarball, not a link: on the source directory: a linked package resolves its @deepseek-ai/* peers from outside the profile and fails to load.

Configuration

$DSH_HOME/dsh-subagent-gate/config.json — created on the first on/off; defaults apply while the file is absent:

{
  "version": 1,
  "enabled": true,
  "forwardTo": "root",
  "directProbeMs": 5000,
  "humanTimeoutMs": 0,
  "onTimeout": "reject",
  "onNoAnswerer": "reject",
  "provenance": true,
  "promptSuffix": "",
  "audit": true,
  "stages": [{ "id": "human", "enabled": true, "order": 100 }]
}
key meaning
forwardTo target strategy id for the confirmation card (see below)
directProbeMs budget for the one-time auto probe; 0 disables probing
humanTimeoutMs 0 waits for the human indefinitely; otherwise a timeout resolves per onTimeout
onTimeout / onNoAnswerer reject (default) or allow
provenance prefix the ask with [subagent <id> · depth <n>] and pass a display line
promptSuffix extra text appended to the model-facing note
audit append one JSONL record per decision to audit.jsonl

Target strategies

id path
root (default) the nearest non-subagent ancestor — the session the user is looking at, so the card is always visible
self the child's own session (visible only while that session is open)
auto probe self once per process for directProbeMs, then fall back to root and stay there
any other looked up in the target registry; an unknown id falls back to root

root is the default because the stock Web UI binds the approval card to the session that is currently open: a subagent listed in a sidebar tree is still a different session view.

Commands

/subagent-gate status     # mode, target, timeouts, armed children, config path
/subagent-gate on|off     # enable/disable; "off" restores armed children

Architecture

A stage is the only extension point the core knows about:

{
  id: "human",
  order: 100,
  failClosed: true,              // a throw denies (default); false abstains
  appliesTo(ctx) { … },          // cheap scope filter
  decide(ctx) { return "allow" | "deny" | "ask" | "abstain"
                     | { decision, final: false }; }
}

final: false enables an appeal chain: a judge can deny non-finally and the human stage after it is still asked — the "ask the human only after the judge said no" mode. With no final verdict the collected signals resolve strictly (deny > ask > allow).

file role
src/index.js cordis wiring: listeners, command, fail-closed boundary
src/coverage.js who is managed, root resolution, 8-hop cycle-safe walk
src/knobs.js never → ask, restore, remember
src/pipeline.js ordered stages, final/non-final verdicts, strictest resolution
src/ask.js the human stage: routing, probe/fallback, timeout, provenance
src/targets.js target-strategy registry — the confirmation-UX extension point
src/queue.js one card at a time
src/audit.js append-only JSONL
src/config.js defaults, normalization, persistence
src/prompt.js the model-facing note for armed children

Extension points

  1. Stages — pipeline.upsert({ id, order, decide }) plus a config entry. rules, denylist, trust, judge, autoapprove are planned as stages.
  2. Target strategies — service.registerTarget(id, (ctx, gate) => ({ agent, path, probe? })) so another plugin can add a transport (modal, notification, webhook, a dedicated approvals session) without touching this code.
  3. promptSuffix — extra model-facing wording without a rebuild. Add rules there only after an observed failure, not speculatively.

Compatibility

Written against DSH 0.2.x and tested on 0.2.2-rc.1. It uses public seams only: agent/created, approval/request, approval.setPolicy, approval.overrideOf, systemPrompt.context, commands.register.

Limitations

  • A card is bound to the session you are viewing — hence the root default.
  • Approval asks only. Calls that would bypass the sandbox without asking (for example an MCP tool whose server does the I/O in its own process) are not seen by this plugin. Closing that needs a per-call stage on tools/pre-execute.
  • One model-facing sentence contradicts the host. DSH appends an unconditional delegation note to every child ("your permission scope cannot be widened … rejected automatically"). It is true only while the child is pinned to never — the very thing this plugin changes — so the gate states the real contract and stops there. The escalation procedure itself ("narrowest wider mode", "one-shot retry of the exact command", "one sentence of justification") is already in the model-facing tool schema, identical for parents and children. The clean fix belongs upstream: emit the delegation note only when the policy is never, the way the never policy sentence already is.

Development

node test/run.mjs     # 57 assertions, no host required
npm pack              # produces the installable tarball

Roadmap

  1. rules — declarative allow/deny on tool + args.
  2. denylist — dangerous-command patterns with normalized matching.
  3. trust — an identical call already approved in this session.
  4. judge — LLM auto-approval; with final: false on denial the human stage becomes "ask only after the judge rejected".
  5. autoapprove — pattern whitelist, so routine widenings never reach a person.
  6. Leaf grants — "allow this child for the rest of its session".
  7. Optional scope filter — restrict the gate to selected workspaces/roots.

License

MIT — see LICENSE.

—/ 5

No ratings yet

Verified DSH bundle

Commit b7e76979c780

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout