Skip to content

Built-in Callback

The built-in callback command is:

agent-hooks callback

It resolves to the built-in app instance at agent_hooks.cli_app.app:app.

This is the "just make it work" path: one local callback target that turns Claude Code and Codex hook payloads into native macOS dialogs and notifications.

One dialog layer

The Allow / Always Allow / Deny choice shows up as a local macOS dialog instead of being trapped inside whichever AI session is waiting.

Less desktop switching

You can respond immediately from the current desktop instead of sweeping across full-screen terminal or editor spaces to find the blocked session.

Local-first setup

The built-in flow uses the system osascript binary. No extra Python packages. No extra macOS dependencies.

What It Looks Like

Claude Code permission request shown as a macOS dialog

Claude Code permission requests become a native local dialog with Deny, Allow Once, and session-scoped Always Allow.

Codex permission request shown as a macOS dialog

Codex PreToolUse requests become the same local dialog flow, with Deny, Allow Once, and optional execpolicy short-circuiting for already-allowed Bash commands.

Quick Setup

Install the CLI:

uv tool install agent-hooks

Put this in ~/.claude/settings.json:

{
  "hooks": {
    "PermissionRequest": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "agent-hooks callback --provider claude-code"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "agent-hooks callback --provider claude-code"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "agent-hooks callback --provider claude-code"
          }
        ]
      }
    ],
    "StopFailure": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "agent-hooks callback --provider claude-code"
          }
        ]
      }
    ]
  }
}

Install the CLI:

uv tool install agent-hooks

If your Codex build still requires the feature flag, add this to ~/.codex/config.toml:

[features]
codex_hooks = true

Put this in ~/.codex/hooks.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "agent-hooks callback --provider codex",
            "timeout": 30
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "agent-hooks callback --provider codex",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Provider Selection

The built-in callback can determine its provider in three ways:

  1. an explicit --provider CLI argument
  2. AGENT_HOOK_PROVIDER
  3. provider inference from the incoming payload when the payload has unique markers

Best practice

Use --provider when the caller is fixed. Inference is helpful for mixed payload testing, but explicit provider selection removes ambiguity in production-like setups.

If you know which provider is calling the hook, using --provider keeps the setup explicit.

Why People Start Here

  • You want one callback command that both Claude Code and Codex can call.
  • You want permission prompts to surface locally on macOS instead of inside the provider UI.
  • You want the fastest path to a usable hook workflow before writing a custom app.

Built-in Event Behavior

Claude Code

The built-in app handles:

  • Notification
  • PermissionRequest
  • Stop
  • StopFailure

Those events are rendered into local notifications or dialogs when appropriate.

Codex

The built-in app registers:

  • SessionStart
  • PreToolUse
  • PostToolUse
  • UserPromptSubmit
  • Stop

Current built-in behavior is intentionally narrow:

  • PreToolUse gets permission dialogs
  • Stop gets notifications
  • SessionStart, PostToolUse, and UserPromptSubmit return empty responses

Important

Empty responses are expected for several Codex events. The built-in app is meant to cover the core interactive path, not to attach custom side effects to every event out of the box.

Codex execpolicy Shortcut

For Codex PreToolUse requests where tool_name == "Bash", Agent Hooks can skip the dialog if local Codex policy has already allowed the command.

The middleware runs:

codex execpolicy check -c model="5.4-mini" --rules ~/.codex/rules/default.rules -- <command ...>

If the top-level JSON decision is allow, the built-in callback returns an empty response immediately.

Current environment knobs:

  • AGENT_HOOK_CODEX_EXECPOLICY_MODEL
  • AGENT_HOOK_CODEX_EXECPOLICY_RULES

Why this shortcut exists

Already-allowed Bash commands should not interrupt local flow again. The execpolicy check lets the built-in callback avoid redundant permission dialogs for commands Codex already considers allowed.

macOS Behavior

The built-in callback uses osascript for dialogs and notifications.

  • On macOS, it opens real local UI
  • On non-macOS platforms, AppleScript actions are skipped
  • If AGENT_HOOK_DISABLE_OSASCRIPT=1, AppleScript actions are skipped even on macOS

Useful during debugging

Disable AppleScript when you want to confirm payload parsing, provider detection, and response formatting without creating modal dialogs during repeated tests.

Logging

Every callback run writes:

  • an application log entry
  • a raw input audit record
  • a rendered response audit record

Audit trail

When a permission dialog or notification does not match expectations, the input and response audit logs are the fastest way to confirm what happened.

See Logging for the file layout and configuration knobs.