Skip to content

feat: add Codex CLI support#49

Open
manuel-alvarez-alvarez wants to merge 5 commits into
mainfrom
feat/codex-cli-support
Open

feat: add Codex CLI support#49
manuel-alvarez-alvarez wants to merge 5 commits into
mainfrom
feat/codex-cli-support

Conversation

@manuel-alvarez-alvarez

Copy link
Copy Markdown
Member

Summary

Adds OpenAI Codex CLI as a supported coding agent alongside Claude Code, giving it the same Datadog AI Guard protection. Codex's hook contract is nearly identical to Claude Code's (same permissionDecision:"deny" / decision:"block" / reason payloads), so the decision-shaping layer is shared; the genuinely new pieces are the transcript translator (Codex uses OpenAI Responses-API rollout JSONL, not Anthropic blocks) and the installer (wires into ~/.codex/hooks.json).

What's included

  • src/aiguard/codex/CodexHandler (dispatch + SessionStart/SubagentStart/SubagentStop/Stop spans, PreToolUse deny, PostToolUse block, UserPromptSubmit), translate.py (rollout response_items → AI Guard messages), installer.py (idempotent merge into ~/.codex/hooks.json, version gate >= 0.117.0).
  • src/aiguard/hooks/common.py — extracted the agent-neutral blocked-tool payload, investigate-link builder, and common span tags; ClaudeHandler refactored to use them (payloads byte-unchanged, all Claude tests still pass).
  • UserPromptSubmit — Codex's analog of Claude's UserPromptExpansion: evaluates the prompt and resolves/injects explicit $skill and /prompts: definitions.
  • Wiring_build_handler, SUPPORTED_AGENTS, ai-guard.spec hidden imports, paths.codex_config_dir/codex_hooks_path (honor $CODEX_HOME), constants.
  • docker/codex/ — reference container + compose service, configured for Codex's Rust HTTP client (proxy + CODEX_CA_CERTIFICATE).
  • Docs — README badge flipped to ready with a trust note; AGENTS.md updated.
  • Tests — mirror the Claude suite (unit + integration); 298 passed, ruff clean, PyInstaller bundle verified to include the lazy Codex imports.

Notable design points / caveats

  • Skills: Codex loads an auto-activated skill's SKILL.md with no tool call and no hook event, so — unlike Claude's Skill-tool gate at PreToolUse — only explicitly-invoked ($name) skills are screened today. A SessionStart skill audit is a possible follow-up.
  • CODEX_MIN_VERSION = 0.117.0 is the release that introduced PreToolUse/PostToolUse; pinned but not verified against the exact introducing build.
  • Trust: Codex requires the user to approve a non-managed command hook on first run; the installer cannot pre-trust it (documented in README).
  • Not live-tested end-to-end: the dev machine only has Codex 0.34.0 (predates hooks). Verified via the unit/integration suite (faked AI Guard client) and the bundled-binary import check.

🤖 Generated with Claude Code

Add OpenAI Codex CLI as a supported coding agent alongside Claude Code,
giving it the same AI Guard protection.

- src/aiguard/codex/: CodexHandler (hook dispatch + lifecycle/PreToolUse/
  PostToolUse/UserPromptSubmit), translate.py (Codex rollout JSONL in the
  OpenAI Responses-API item shape -> AI Guard messages), and CodexInstaller
  (idempotent merge into ~/.codex/hooks.json, version gate >= 0.117.0).
- src/aiguard/hooks/common.py: extract the agent-neutral blocked-tool payload,
  investigate-link builder, and common span tags shared by both handlers;
  ClaudeHandler refactored to use them (payloads unchanged).
- UserPromptSubmit resolves explicit $skill / /prompts: references and injects
  the definition for evaluation (Codex's analog of Claude's UserPromptExpansion).
- Wiring: _build_handler, SUPPORTED_AGENTS, ai-guard.spec hidden imports,
  paths (codex_config_dir/codex_hooks_path honoring $CODEX_HOME), constants.
- docker/codex/ reference container + compose service (Rust-client proxy/CA env).
- Tests mirror the Claude suite (unit + integration); full suite green.
@manuel-alvarez-alvarez manuel-alvarez-alvarez requested a review from a team as a code owner June 23, 2026 07:39
@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Jun 23, 2026

Copy link
Copy Markdown

Pipelines

Fix all issues with BitsAI

⚠️ Warnings

🚦 1 Pipeline job failed

Test | Lint   View in Datadog   GitHub Actions

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: accf1b5 | Docs | Datadog PR Page | Give us feedback!

Codex's bubblewrap sandbox can't create user namespaces inside Docker
(bwrap: No permissions to create a new namespace). The container is the
isolation boundary, so set sandbox_mode=danger-full-access and
approval_policy=never in the reference config.toml.
Real Codex rollouts record apply_patch (and other freeform tools) as
custom_tool_call / custom_tool_call_output items, not function_call. The
translator only handled function_call*, so apply_patch — the file-edit tool —
was silently dropped from the trajectory sent to AI Guard.

- translate: map custom_tool_call -> assistant tool_call (payload is in `input`,
  a raw string, passed through as arguments) and custom_tool_call_output -> tool.
- tests: add CodexRolloutWriter builders + a real-transcript fixture
  (rollout_wasm.jsonl, a trimmed codex-tui 0.140.0 session) and an end-to-end
  test asserting apply_patch and an empty function_call_output are captured.
Codex splits the system prompt and the first user turn across separate rollout
items. Fold them back so AI Guard sees coherent turns:

- session_meta.base_instructions.text (the base system prompt) is prepended into
  the developer-derived system message.
- the injected <environment_context> user turn is merged with the user's first
  real prompt into one user message with multiple content parts.

These are the only two merges — no general same-role coalescing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant