# DSH runtime guard AgentGuard connects to DSH's native `tools/pre-execute` and `tools/post-execute` waterfalls. It translates each non-AgentGuard tool call into the shared `RuntimeAction` vocabulary, resolves the same effective policy used by the other AgentGuard hosts, runs the shared OSS evaluator, and writes a local audit event. ## Modes The runtime integration accepts three explicit modes: | Mode | Pre-execute | Post-execute | Intended use | |---|---|---|---| | `off` | no listener | no listener | scanner tools only | | `observe` | evaluate and audit; preserve downstream decision | evaluate network responses and audit | explicit audit-only evaluation | | `protect` | apply `allow`, `warn`, native `ask`, or `deny` | audit by default; optionally suppress block-class malicious results | packaged default and real-time protection | The npm bundle composes `protect` by default. A custom DSH composition can restate the protected runtime settings and add optional attribution or policy controls: ```yaml - insert: - id: agentguard-dsh-plugin name: '@goplus/agentguard/dist/dsh/plugin.js' config: runtime: mode: protect failureMode: deny unknownToolDecision: ask postResponseMode: block-malicious attribution: toolOwners: find_dsh_plugin: dsh-find-plugin ownerPolicies: dsh-find-plugin: minimumDecision: require_approval ``` `failureMode` applies only to unexpected evaluator failures in `protect` mode. It defaults to `deny`. Set it to `allow` only for a deliberate compatibility rollout. Audit-file write failures do not erase a successfully evaluated policy decision and do not disable enforcement. At plugin startup AgentGuard logs the configured mode and whether pre-execute enforcement is active. `agentguard_dsh_runtime_summary` also returns `configuredMode`, `preExecuteProtectionActive`, and `configuredPostResponseMode` even when the audit log is empty. Historical `runtimeModes` counts describe observed events; they are not a substitute for the current configured mode. ## Request processing 1. DSH supplies the immutable tool name, parsed arguments, call identity, root-call identity, optional parent token, and calling agent. 2. AgentGuard uses the official session header for the workspace cwd and resolves a relative shell workdir against it. 3. Common command, patch, image, file-search, HTTP, browser, deployment, skill-install, and MCP tools map to the shared action types. Skill-install and MCP actions require approval by the default runtime policy. Unknown tools remain `other`; in `protect` they default to native `ask` and can be configured with `unknownToolDecision: ask|deny|allow`. 4. Network method, headers, and bounded body context are preserved for the shared evaluator. 5. AgentGuard resolves Cloud, cached, or bundled-default policy and evaluates locally. Cloud failure falls back to cached/default policy. 6. In `observe`, the downstream DSH policy is returned unchanged. In `protect`, the AgentGuard result is translated and monotonically merged with the downstream policy so a stronger third-party `ask` or `deny` is never weakened. A final AgentGuard `deny` short-circuits the pre-execute waterfall and does not dispatch the downstream tool body. 7. AgentGuard's own `agentguard_*` tools are excluded to prevent recursive protection. ## Decision mapping | AgentGuard decision | DSH pre-execute result | Behavior | |---|---|---| | `allow` | `allow` | execute | | `warn` | `allow` | execute and retain warning in audit | | `require_approval` | `ask` | use DSH's native approval service | | `block` | `deny` | do not dispatch the tool body | DSH owns the one-shot approval interaction and its durable `approval/asked` plus `approval/decided` pair. AgentGuard does not create a parallel CLI approval entry. Only `allowed-once` resumes that tool call; rejection, cancellation, missing approval channels, headless `never`, a missing approval service, and agent-less calls fail closed in DSH. The reason passed into DSH contains only a bounded risk score, normalized policy metadata, and up to five reason codes. Raw tool input, detector descriptions, evidence, and untrusted control text are excluded. ## Response containment boundary Single AgentGuard CLI shell commands are exempt from AgentGuard's own pre-execute policy evaluation (except `agentguard approve`). Simple shell wrappers are recognized, while explicit executable paths must resolve to the installed AgentGuard CLI. When their post-execute result exposes text or a structured value, the runtime can return an `accept` decision with locally redacted sensitive fields while preserving the exact saved pending Agent JWT activation URL and public key examples. It never weakens a downstream `block` decision. Network results pass through `tools/post-execute`. AgentGuard extracts a bounded response preview plus available status, content type, headers, and byte count, then evaluates response and network-volume anomalies. Post-execute remains audit-only in `observe` and in the default `protect` configuration. Set `postResponseMode: block-malicious` together with `mode: protect` to suppress only results whose final AgentGuard post-execute decision is `block`. The original result value and rendered content are not returned; DSH receives bounded AgentGuard feedback that excludes the untrusted preview and evidence. A stronger downstream block is preserved. Approval-class post results remain audit-only because DSH has no native post-result `ask` or resumable held-result carrier. AgentGuard records the remaining `native-post-result-approval` and `approved-result-resume` gates rather than destroying a result that could not be resumed. Unexpected post evaluator errors fail open for the result path and remain available to downstream DSH policies; this avoids irrecoverable response loss caused by an internal guard failure. ## Audit and summary Events are written to `~/.agentguard/audit.jsonl` with: - native call/root identities and nested-call state; - verified invocation context (`model-direct` or `nested-tool`), top-level/subagent origin, delegation depth, and agent preset when DSH supplies them; - `sourceAttribution: "configured-tool-owner"` plus `sourceOwner` for exact operator-configured tool ownership bindings; - shared decision, risk score, risk level, reason codes, and policy version; - `runtimeMode`, `runtimePhase`, and `enforcementApplied`; - the translated hook decision and disposition; - `sourceAttribution: "unknown"` when no reliable owner binding exists. Clean DSH events remain in this local audit and are not uploaded to AgentGuard Cloud. Events with a decision, non-zero risk, finding, failure, anomaly, or PII summary remain Cloud-eligible. Their Cloud payload uses a bounded semantic summary and bounded status metadata. Pre-execute shell events retain a redacted command preview capped at 2,000 characters so operators can distinguish command arguments, pipelines, and shell syntax; recognized credentials and PII are masked, absolute user-home prefixes are normalized to `~`, and unmaskable sensitive findings fall back to a command name plus static finding title. File actions retain a basename and network actions retain a sanitized destination. Raw tool output, request/response bodies, header values, user-home identities, and evaluator reason text remain local. `runtime.attribution.toolOwners` is an exact, case-sensitive map from a DSH tool name to a stable plugin or package id. It is operator-authored trust metadata, not a tool-name heuristic. Owner ids are bounded and validated, duplicate/ambiguous wildcard matching is not supported, and an unmapped tool remains `unknown`. Do not bind a name when another agent scope may shadow it with a different implementation. `runtime.ownerPolicies` applies only after a call has a matching `configured-tool-owner`. Each owner declares a `minimumDecision` of `allow`, `warn`, `require_approval`, or `block`. This is a monotonic floor: it can strengthen the shared AgentGuard decision but can never weaken it. In particular, `minimumDecision: allow` means “no additional owner restriction”; it does not bypass a warning, approval, or block produced by the shared policy. An elevation adds the bounded `DSH_OWNER_POLICY` reason code to audit and native approval text. In `protect`, pre-execute events set `enforcementApplied: true` and record the applied DSH hook decision. With `postResponseMode: block-malicious`, block-class post-execute events also set it to `true` and record `hookDecisionApplied: block`; approval-class post events remain `false`. DSH session events are the source of truth for the final human approval outcome. `agentguard_dsh_runtime_summary` reads only the bounded final 1 MiB of the audit log and aggregates up to 1,000 recent DSH events. It reports decisions, action types, risks, phases, modes, applied-enforcement count, dispositions, gates, reason-code counts, nested calls, attribution coverage, invocation sources, session origins, and the top configured owners. Raw tool inputs and reason evidence are never returned to the model. An exact optional `sessionId` filter isolates one DSH call tree. ## Security parity DSH does not maintain a separate detector. The normalized action goes through AgentGuard's shared policy and evaluator, so dangerous commands, protected paths, credential access, data exfiltration, outbound-network policy, response anomalies, and remote code execution retain the same semantics as other hosts. Direct unpinned Git package execution through `npx`, `npm exec`, `pnpm dlx`, `yarn dlx`, or `bunx` requires approval. A full 40-character commit pin reduces this to a warning rather than treating remote code as trusted. The host-parity matrix requires equivalent shell, file, and network actions to produce identical decisions, risk scores, risk levels, and reason codes across DSH, Codex, Claude Code, and OpenClaw. Host-specific lifecycle behavior is tested separately. ## Verified lifecycle behavior The real DSH `ToolRuntime`, `ApprovalService`, and `Session` tests cover: - concurrent safe calls; - pre-execute allow, warn, native approval, rejection, and block; - shell, file-write, and network actions; - root and nested calls without duplicate approval; - cancellation, unavailable/headless approval, missing services, and missing agents; - stronger downstream policies; - fail-closed and explicit fail-open evaluator errors; - bounded audit output and explicit unknown attribution; - audit-only approval-class response handling and optional block-class response suppression; - plugin disposal removing the policy listener; - install, update, uninstall, packaged assets, and live HTTP startup. ## Remaining host limitation DSH currently supplies no reliable source-plugin ownership field on the lifecycle event. AgentGuard therefore supports exact operator-configured bindings and otherwise records explicit `unknown` attribution rather than guessing. Plugin-specific trust cannot silently bypass policy. When DSH exposes a stable tool-owner/provider identity, it can supersede configured bindings without changing the shared evaluator.