How dcg Handles Different AI Agent Protocols: Claude, Codex, and Gemini
dcg inspects a JSON envelope from STDIN, detects the specific AI agent through field heuristics, and returns a protocol-shaped denial so Claude Code, Codex, Gemini, and other supported agents can each interpret the guard decision correctly.
Destructive Command Guard (dcg) from the Dicklesworthstone/destructive_command_guard repository acts as a PreToolUse hook that intercepts tool calls before they reach the shell. Because each AI back-end sends a slightly different JSON payload over standard input, dcg must identify the originating protocol before evaluating or blocking a command. This protocol-aware mediation is implemented almost entirely in src/hook.rs.
Parsing the Inbound JSON Envelope
All supported agents stream a JSON payload to dcg over STDIN. The HookInput struct defined at lines 19-74 of src/hook.rs models every field used by the known back-ends, including optional identifiers like toolCall, event, tool_name, hook_event_name, turn_id, and environment variables. By deserializing into this unified struct, dcg avoids maintaining separate parsers for each agent.
Protocol Detection Heuristics in detect_protocol
The function detect_protocol (lines 85-167) disambiguates the sender by checking fields in a specific order. It returns a variant of the HookProtocol enum (lines 24-41) that downstream code uses to decide how to extract the command and format the denial.
-
Antigravity — The presence of a
toolCallobject containing nestedname/argsimmediately flags the Antigravity protocol. -
Hermes — If
hook_event_nameequals"pre_tool_call"ortool_nameequals"terminal", the payload is treated as Hermes. -
Grok — The payload matches Grok when
hookEventNameequals"pre_tool_use"ortoolNameequals"run_terminal_cmd". -
Copilot — A distinct
eventfield with value"pre-tool-use"or the presence oftool_argstriggers Copilot detection. -
Codex — Codex is identified by shell-tool names such as
bash,powershell,pwsh, orlaunch-processcombined with a non-emptyturn_id; a PowerShell tool name alone also forces Codex detection. -
Claude-compatible — When none of the prior checks match, or when the
CLAUDE_CODEenvironment variable is set,dcgfalls back to the Claude-compatible protocol. -
Gemini — The Gemini protocol is recognized when
tool_nameequals"run_shell_command"andhook_event_nameequals"BeforeTool", or when any combination of Gemini-specific envelope fields appears.
Extracting the Shell Command by Protocol
Once the protocol is known, extract_command_with_protocol (lines 73-104) pulls the actual command string out of the JSON.
- Antigravity →
toolCall.args.CommandLine - Standard payloads →
tool_input.commandortool_args.command - Both JSON-object and JSON-string forms are accepted.
Emitting Protocol-Specific Denial Responses
Denials are authored by write_denial_to (lines 135-190). Depending on the HookProtocol variant, dcg serializes a different output struct to STDOUT so the originating agent can parse the result.
-
ClaudeCompatible — Emits
HookOutputwrapped inhookSpecificOutputwith full ergonomic metadata:ruleId,packId,severity,remediation, andallowOnce*fields. -
Codex — Emits a minimal
HookOutputcontaining onlyhook_event_name,permission_decision, andpermission_decision_reason. Extra ergonomic fields are stripped because Codex’s strict parser discards unknown keys. -
Copilot — Returns
CopilotHookOutputwith top-levelpermissionDecisionandpermissionDecisionReason. -
Gemini — Returns
GeminiHookOutputcontainingdecision,reason, optionalsystemMessage, plus ergonomics. -
Hermes — Returns
HermesHookOutputsupplying bothdecision/reasonandaction/message, since Hermes accepts either format, along with ergonomic extensions.
For every protocol except Codex, dcg injects consistent metadata fields—ruleId, packId, severity, confidence, and remediation—so downstream tooling receives uniform telemetry without breaking any agent parser.
Human-visible messages are built by format_denial_message (lines 80-112) and printed via print_colorful_warning (lines 64-78). For agents that consume JSON payloads, the structured response goes to stdout while the colorful warning goes to stderr.
Fail-Open Safety Behavior
If the input fails to parse or does not represent a supported shell tool, dcg returns an Allow decision. This fail-open design prevents non-zero exits from breaking agent workflows such as Grok, which require hooks to exit cleanly even on unrecognized input.
Runnable Examples for Claude, Codex, Gemini, and More
Claude Code Default Invocation
echo '{"tool_name":"Bash","tool_input":{"command":"git reset --hard"}}' \
| dcg
This produces the full HookOutput JSON on stdout—including ruleId "core.git:reset-hard"—and prints a colored warning to stderr.
Gemini BeforeTool Hook
echo '{
"toolName":"run_shell_command",
"hookEventName":"BeforeTool",
"sessionId":"abc",
"toolInput":{"command":"rm -rf /"}
}' | dcg
dcg emits a GeminiHookOutput JSON object with decision and reason fields, plus ergonomic metadata.
Codex Minimal Response
echo '{"tool_name":"Bash","tool_input":{"command":"git push -f"},"turnId":"12345"}' \
| dcg
Because Codex cannot tolerate unknown fields, the output is a stripped-down HookOutput containing only hook_event_name, permission_decision, and permission_decision_reason.
Copilot Top-Level Denial
echo '{"event":"pre-tool-use","tool_args":{"command":"kubectl delete namespace prod"}}' \
| dcg
This returns CopilotHookOutput with permissionDecision and permissionDecisionReason.
Antigravity CLI via Hermes Mapping
echo '{
"toolCall":{"name":"run_command","args":{"CommandLine":"docker system prune"}}
}' | dcg
Antigravity is detected through its toolCall shape, and dcg emits Hermes-compatible JSON because Antigravity maps to the Hermes protocol internally.
Summary
src/hook.rshouses the unifiedHookInputstruct anddetect_protocollogic that identifies Claude, Codex, Gemini, Hermes, Grok, Copilot, and Antigravity envelopes.- Protocol detection relies on ordered checks of fields like
toolCall,event,tool_name,turn_id, and environment variables. extract_command_with_protocolandwrite_denial_toensure the correct command is pulled and the correct JSON shape is returned.- Codex receives a minimal payload, while Claude-compatible, Gemini, Hermes, and others receive ergonomic metadata fields for richer telemetry.
- A fail-open policy guarantees that parsing errors never block legitimate agent operations.
Frequently Asked Questions
Which file in dcg handles protocol detection?
The core logic resides in src/hook.rs, specifically the detect_protocol function at lines 85-167. It inspects incoming JSON fields and environment variables to return a HookProtocol enum variant that drives the rest of the pipeline.
Why does Codex receive a different denial format than Claude or Gemini?
Codex uses a strict JSON parser that rejects unknown fields. To avoid breaking the agent, dcg strips ergonomic metadata—such as ruleId and severity—and returns a minimal HookOutput containing only hook_event_name, permission_decision, and permission_decision_reason.
How does dcg avoid breaking an agent when the input is not a shell command?
If the payload fails to parse or is not a recognized shell-tool candidate, dcg applies a fail-open strategy and returns an Allow result. This ensures that unsupported or malformed hooks never cause a non-zero exit that would disrupt the agent's flow.
Can dcg distinguish between Gemini and Hermes when fields look similar?
Yes. detect_protocol checks for Gemini-specific combinations such as tool_name == "run_shell_command" paired with hook_event_name == "BeforeTool", while Hermes is identified by hook_event_name == "pre_tool_call" or tool_name == "terminal". The ordered inspection guarantees that the first definitive match wins.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →