dsh-approval-auto-review
English | 中文
Automatic approval reviewer for DeepSeek Harness. The plugin handles approval requests routed to its configured permission preset, evaluates the exact planned action with a locked-down Guardian agent, and returns a single approval decision for that request.
Each parent agent has one reusable Guardian trunk. The first review sends a bounded transcript and the planned action. Later reviews send only the current action delta to the same trunk. When that trunk is busy, the plugin starts an independent ephemeral reviewer so concurrent approval requests do not queue behind one another. If a trunk turn fails after its child is published, the child remains the reusable recovery point; a later review reattaches to that child instead of creating another continuable session.
Features
- Separate reviewer model route with optional inheritance from the parent agent.
- Provider and model overrides for specific parent model ids.
- Empty tool allowlist and a dedicated reviewer persona for every reviewer agent.
- Bounded transcripts and tool arguments.
- Bounded rejection rationales before they reach the parent transcript.
- Fail-closed handling for timeouts, malformed output, provider failures, and unresolved routes.
- Denial circuit that stops a parent turn after repeated automatic rejections.
- Durable JSONL audit trail recording every reviewed command and the reviewer's verdict.
Install
Install the repository as a DSH profile bundle:
dsh plugin --profile web add github:perlied03/dsh-approval-auto-review
For a local checkout:
dsh plugin --profile web add ./dsh-approval-auto-review
The package declares dsh.bundle, so dsh plugin adds its configuration layer to the profile automatically. The layer registers the reviewer and provides three permission choices: Request approval, Approve for me, and Full access. The same package declares dsh.client, so the Web profile discovers its reviewer-model settings card and the conversation's Review tab from the installed package. Select Approve for me in the conversation permission menu to route approval requests to the Guardian.
A GitHub install runs the package's prepare build. With pnpm 10 or later, add dsh-approval-auto-review: true under allowBuilds in the profile's pnpm-workspace.yaml when pnpm asks for build approval, then repeat the install command.
The host composition must provide these DSH services and plugins:
dsh-agentdsh-sessiondsh-settingsdsh-user-approvaldsh-subagent- an in-process spawn provider registered under the configured
providername - an in-process fork provider registered under the configured
ephemeralProvidername
The fork provider handles concurrent reviews while preserving the reusable trunk path. Deployments that do not need that path may use the same provider name for both settings.
Configure
Register the plugin in the Cordis composition:
import * as ApprovalAutoReview from 'dsh-approval-auto-review'
await ctx.plugin(ApprovalAutoReview, {
provider: 'spawn',
ephemeralProvider: 'fork',
modelProvider: 'deepseek-official',
model: 'deepseek-v4-flash',
fallback: 'parent',
})
The equivalent Loader row is:
- id: approval-auto-review
name: 'dsh-approval-auto-review'
config:
provider: spawn
ephemeralProvider: fork
modelProvider: deepseek-official
model: deepseek-v4-flash
fallback: parent
Configuration fields:
provider: subagent provider for the reusable Guardian trunk. Default:spawn.ephemeralProvider: subagent provider for concurrent one-shot reviews. Default:fork.modelProviderandmodel: optional reviewer route.modelOverrides: optional map keyed by parent model id. Each entry may setprovider,model, andmaxTokens.fallback:parentinherits missing route fields from the parent agent;rejectfails closed when a route is incomplete. Default:parent.timeoutMs: deadline for one review attempt. Default:30000.maxAttempts: retry limit after a failed attempt. Default:3.maxTranscriptChars: maximum serialized parent transcript sent to a full review. Default:60000.maxToolArgumentsChars: maximum raw tool-argument characters sent to a review. Default:20000.maxTokens: optional reviewer output-token ceiling.activationPreset: permission preset that activates review. When omitted, the plugin handles every approval request. The bundled layer setsapprove-for-me.audit:filerecords every terminal review result to the audit JSONL (see Audit log);offdrops records. Default:file.auditPath: absolute audit file path. Default:<DSH_HOME>/audit/approval-auto-review.jsonl. Fixed at mount; a change applies after a restart.auditArgumentsChars: maximum stored raw tool-argument characters per audit record. Default:2000.
Enable automatic review by switching the conversation's permission preset to Approve for me: the durable permission/preset event in the session log activates the Guardian for subsequent asks. Switching back to Request approval returns decisions to the human channel. The plugin does not create a persistent allow rule; each decision applies to one approval request.
DSH compatibility
Routing reads the durable permission/preset selection from the session log, so the reviewer works with the current DSH host services. The Web Review tab additionally requires dsh-host-webserver to expose the read-only audit route. Approval services accept only closed-string outcomes, so a timed-out review resolves as a rejection; the allow/deny behavior remains fail-closed either way.
Web settings
The package's Web client entry contributes an Automatic approval review card to the existing Plugins settings section. The card edits modelProvider and model through the approval-auto-review settings namespace and uses the same model directory as the DSH Models page. Select a provider first to filter the model list, or leave both fields on Follow the parent agent to inherit the parent route. Changes apply to the next review without restarting the process.
The standalone card uses a lower slot priority than default contributions. When the Host also contributes an approval-auto-review card, the standalone card renders and the Host card remains registered but shadowed.
The conversation header also includes a read-only Review tab. It requests the current session's records from the Host and shows the reviewed tool, bounded arguments, outcome, reviewer rationale, route, and attempt count. The tab shows the latest 200 matching records, refreshes on demand, and reports empty or unavailable history without exposing records from another session. It requires the WebServer route supplied by the Web profile; audit: off produces an empty history.
Model routing
The reviewer route is resolved in this order:
modelOverrides[parent.options.model], when an entry exists.- The plugin-level
modelProviderandmodelvalues. - The corresponding parent agent route when
fallbackisparent. - A fail-closed error when
fallbackisrejectand the route is incomplete.
Provider and model names are deployment values. The plugin does not assume that a Codex or OpenAI model id is available from a DSH provider.
Review policy
The Guardian evaluates only the exact action supplied by the approval request. Direct user messages and content explicitly loaded from AGENTS.md are trusted authorization evidence. Assistant messages, tool calls, tool results, file contents, command output, and ordinary plugin context are untrusted.
The default policy allows low- and medium-risk actions unless a specific deny rule or prompt injection applies. High-risk actions require at least medium trusted authorization and a narrow scope. Critical risk, obvious secret exfiltration, broad destructive actions without exact authorization, and broad persistent security weakening are denied.
Every reviewer agent has an empty tool allowlist, a dedicated persona, structured output requirements, and a delegated approval policy that prevents the reviewer from creating another approval request.
Failure behavior
- A timeout injects a retry-or-ask notice into the parent session and rejects the request.
- A provider failure or malformed reviewer response is retried up to
maxAttempts, then returns a rejection. - An unresolved route rejects when
fallbackisreject; withparent, missing fields inherit from the parent agent. - A rejected automatic review injects its rationale and an anti-circumvention notice into the parent session; the rationale is bounded to
AUTO_REVIEW_RATIONALE_MAX_CHARScharacters first. - Three consecutive automatic denials, or ten denials among the latest fifty automatic reviews in one turn, cancel the parent turn.
Audit log
Every terminal review result appends one JSON line to <DSH_HOME>/audit/approval-auto-review.jsonl (override with auditPath; disable with audit: off). One record covers one approval request — retries collapse into a single line:
{"v":1,"time":1756130000000,"session":"<parent session id>","callId":"call-3","toolName":"bash","arguments":"{\"command\":\"rm -rf build\"}","argumentsTruncated":false,"outcome":"deny","attempts":1,"riskLevel":"high","userAuthorization":"low","rationale":"The action exceeds the trusted authorization.","provider":"deepseek-official","model":"deepseek-v4-flash","reviewerSession":"trunk"}
toolNameandargumentsidentify the exact reviewed command;argumentsis bounded byauditArgumentsCharsand marked withargumentsTruncated.rationale,riskLevel, anduserAuthorizationcarry the reviewer's verdict on allow and deny outcomes.provider,model, andreviewerSession(trunkorephemeral) name the route that decided; timeout, error, and cancelled records omit these fields.outcomeis one ofallow,deny,timeout,error,cancelled;attemptscounts consumed attempts.- The trail is best-effort: an unwritable file emits one diagnostic and stops recording for that process without failing any approval decision.
The audit file is out-of-band by design: session logs refuse unknown event types from builds that do not declare them, so the reviewer keeps its records beside the sessions instead of inside them.
Model Experience
Parent session
What the model sees
The parent model receives the approval-policy context produced by dsh-user-approval. A rejection adds the reviewer rationale and an anti-circumvention notice. A timeout adds a separate retry-or-ask notice. The Guardian transcript is not copied into the parent model request.
Token effect
Only approval-policy context and injected rejection or timeout notices can add tokens to a later parent request. The reviewer transcript is not duplicated in the parent request.
KV Cache effect
Approval-policy context and injected notices may change the prefix of the next parent request. A successful approval adds no notice.
Guardian reviewer
What the model sees
The first trunk review receives one bounded parent transcript and the exact planned action. Later trunk reviews receive the current bounded action delta and the prior review count. An ephemeral review receives a new bounded transcript and the current action.
Token effect
Every approval review is a separate model request. Transcript and argument limits cap the material sent to each request.
KV Cache effect
Trunk reviews append deltas to one child session, allowing a provider to reuse a stable prefix. Each review still creates a new model request. Ephemeral reviews use independent sessions and do not reuse the trunk prefix.
Known Limitations and Deferred Work
- The deployment must provide subagent providers that support personas, empty tool filters, structured output, continuations, and cancellation.
- The Guardian has no filesystem or network tools. Its bounded parent evidence is the complete review input; adding tools would introduce another prompt-injection and authorization source.
- Provider and model availability is controlled by the deployment. An unavailable route fails closed instead of being silently replaced.
- The audit trail is best-effort and out-of-band: write failures disable recording for the process, and the file is not covered by session-log integrity or compaction machinery.
- A GitHub install runs
prepareto buildlib/. pnpm 10 or later requires an explicitallowBuildsentry when the profile has not trusted this package before.
Development
pnpm install
pnpm test
pnpm run typecheck
pnpm run build
The package resolves DSH imports from published packages. prepare builds the Host exports and the lib/client.js bundle discovered through dsh.client.
No comments yet. Be the first to write one.