# How dcg Handles Different AI Agent Protocols: Claude, Codex, and Gemini

> Learn how dcg inspects AI agent protocols like Claude, Codex, and Gemini. It detects specific agents and returns protocol-shaped denials for accurate interpretation.

- Repository: [Jeff Emanuel/destructive_command_guard](https://github.com/Dicklesworthstone/destructive_command_guard)
- Tags: how-to-guide
- Published: 2026-07-16

---

**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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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 `toolCall` object containing nested `name`/`args` immediately flags the Antigravity protocol.

- **Hermes** — If `hook_event_name` equals `"pre_tool_call"` **or** `tool_name` equals `"terminal"`, the payload is treated as Hermes.

- **Grok** — The payload matches Grok when `hookEventName` equals `"pre_tool_use"` **or** `toolName` equals `"run_terminal_cmd"`.

- **Copilot** — A distinct `event` field with value `"pre-tool-use"` or the presence of `tool_args` triggers Copilot detection.

- **Codex** — Codex is identified by shell-tool names such as `bash`, `powershell`, `pwsh`, or `launch-process` combined with a non-empty `turn_id`; a PowerShell tool name alone also forces Codex detection.

- **Claude-compatible** — When none of the prior checks match, or when the `CLAUDE_CODE` environment variable is set, `dcg` falls back to the Claude-compatible protocol.

- **Gemini** — The Gemini protocol is recognized when `tool_name` equals `"run_shell_command"` and `hook_event_name` equals `"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.command` or `tool_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 `HookOutput` wrapped in `hookSpecificOutput` with full ergonomic metadata: `ruleId`, `packId`, `severity`, `remediation`, and `allowOnce*` fields.

- **Codex** — Emits a minimal `HookOutput` containing only `hook_event_name`, `permission_decision`, and `permission_decision_reason`. Extra ergonomic fields are stripped because Codex’s strict parser discards unknown keys.

- **Copilot** — Returns `CopilotHookOutput` with top-level `permissionDecision` and `permissionDecisionReason`.

- **Gemini** — Returns `GeminiHookOutput` containing `decision`, `reason`, optional `systemMessage`, plus ergonomics.

- **Hermes** — Returns `HermesHookOutput` supplying both `decision`/`reason` and `action`/`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

```bash
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

```bash
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

```bash
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

```bash
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

```bash
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.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/hook.rs) houses the unified `HookInput` struct and `detect_protocol` logic 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_protocol` and `write_denial_to` ensure 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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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.