ClaudeAgentSDK.Hooks.Output (claude_agent_sdk v0.9.2)

Copy Markdown View Source

Hook output structure and helpers.

Represents the return value from hook callbacks. Hook output controls:

  • Permission decisions (PreToolUse): allow, deny, or ask
  • Additional context (PostToolUse, UserPromptSubmit): inject information for Claude
  • Execution control: continue or stop the agent
  • User feedback: system messages and reasons

Output Fields

  • continue - Whether to continue execution (boolean)
  • stopReason - Message when stopping (string)
  • suppressOutput - Hide from transcript (boolean)
  • systemMessage - User-visible message (string)
  • reason - Claude-visible feedback (string)
  • decision - "block" for some events (string)
  • hookSpecificOutput - Event-specific control (map)

Examples

# Allow a tool
Output.allow("Security check passed")

# Deny a tool
Output.deny("Dangerous command detected")

# Add context after tool execution
Output.add_context("PostToolUse", "Command completed in 2.3s")

# Stop execution
Output.stop("Critical error occurred")

# Combine helpers
Output.deny("Invalid file path")
|> Output.with_system_message("File access restricted")
|> Output.with_reason("Path outside allowed directory")

See: https://docs.anthropic.com/en/docs/claude-code/hooks#hook-output

Summary

Types

Hook-specific output for different event types.

Permission decision for PreToolUse hooks.

PostToolUse hook-specific output.

PreToolUse hook-specific output.

SessionStart hook-specific output.

t()

Complete hook output map.

UserPromptSubmit hook-specific output.

Functions

Creates hook output to add context.

Creates hook output to allow a PreToolUse.

Creates hook output to ask the user for permission.

Marks hook output for asynchronous processing.

Creates hook output to block with decision field.

Creates hook output to continue execution.

Creates hook output to deny a PreToolUse.

Creates hook output to stop execution.

Marks output to be suppressed from transcript.

Converts Elixir output to JSON-compatible map for CLI.

Validates hook output structure.

Sets the timeout for async hook processing.

Adds a reason to hook output.

Adds a system message to hook output.

Modifies tool input before execution (PreToolUse hooks only).

Types

hook_specific_output()

Hook-specific output for different event types.

permission_decision()

@type permission_decision() :: :allow | :deny | :ask

Permission decision for PreToolUse hooks.

post_tool_use_output()

@type post_tool_use_output() :: %{
  hookEventName: String.t(),
  additionalContext: String.t()
}

PostToolUse hook-specific output.

Adds context for Claude to consider:

  • hookEventName - Must be "PostToolUse"
  • additionalContext - Information about tool execution

pre_tool_use_output()

@type pre_tool_use_output() :: %{
  :hookEventName => String.t(),
  :permissionDecision => String.t(),
  :permissionDecisionReason => String.t(),
  optional(:updatedInput) => map()
}

PreToolUse hook-specific output.

Controls whether a tool call proceeds:

  • hookEventName - Must be "PreToolUse"
  • permissionDecision - "allow", "deny", or "ask"
  • permissionDecisionReason - Explanation for the decision
  • updatedInput - Optional modified tool input (via with_updated_input/2)

session_start_output()

@type session_start_output() :: %{
  hookEventName: String.t(),
  additionalContext: String.t()
}

SessionStart hook-specific output.

Adds context when session starts:

  • hookEventName - Must be "SessionStart"
  • additionalContext - Initial context for session

t()

@type t() :: %{
  optional(:continue) => boolean(),
  optional(:stopReason) => String.t(),
  optional(:suppressOutput) => boolean(),
  optional(:systemMessage) => String.t(),
  optional(:reason) => String.t(),
  optional(:decision) => String.t(),
  optional(:hookSpecificOutput) => hook_specific_output(),
  optional(atom()) => term()
}

Complete hook output map.

All fields are optional. The CLI processes these fields to control behavior.

user_prompt_submit_output()

@type user_prompt_submit_output() :: %{
  hookEventName: String.t(),
  additionalContext: String.t()
}

UserPromptSubmit hook-specific output.

Adds context before processing prompt:

  • hookEventName - Must be "UserPromptSubmit"
  • additionalContext - Contextual information to inject

Functions

add_context(event_name, context)

@spec add_context(String.t(), String.t()) :: t()

Creates hook output to add context.

Used with PostToolUse, UserPromptSubmit, or SessionStart hooks.

Parameters

  • event_name - Hook event name ("PostToolUse", "UserPromptSubmit", etc.)
  • context - Contextual information to inject

Examples

Output.add_context("PostToolUse", "Command took 2.3 seconds")
Output.add_context("UserPromptSubmit", "Current time: 10:00 AM")
Output.add_context("SessionStart", "Recent issues: #123, #124")

allow(reason \\ "Approved")

@spec allow(String.t()) :: t()

Creates hook output to allow a PreToolUse.

Parameters

  • reason - Explanation for allowing (default: "Approved")

Examples

Output.allow()
# => %{hookSpecificOutput: %{hookEventName: "PreToolUse", permissionDecision: "allow", ...}}

Output.allow("Security scan passed")

ask(reason)

@spec ask(String.t()) :: t()

Creates hook output to ask the user for permission.

The CLI will prompt the user to confirm the tool use.

Parameters

  • reason - Explanation for asking user (required)

Examples

Output.ask("Confirm deletion of 100 files")
Output.ask("Review this API call before executing")

async(output)

@spec async(t()) :: t()

Marks hook output for asynchronous processing.

When async: true is set, the hook callback can continue processing in the background while Claude continues execution. This is useful for hooks that perform slow operations (e.g., external API calls, logging).

Parameters

  • output - Existing hook output

Examples

# Basic async output
Output.async(%{continue: true})

# Combined with allow
Output.allow("Approved")
|> Output.async()

# With timeout
Output.allow("Starting background check")
|> Output.async()
|> Output.with_async_timeout(30_000)

block(reason)

@spec block(String.t()) :: t()

Creates hook output to block with decision field.

Used for certain hooks to provide feedback to Claude.

Parameters

  • reason - Explanation for blocking

Examples

Output.block("Tool execution failed validation")

continue()

@spec continue() :: t()

Creates hook output to continue execution.

Examples

Output.continue()
# => %{continue: true}

deny(reason)

@spec deny(String.t()) :: t()

Creates hook output to deny a PreToolUse.

Parameters

  • reason - Explanation for denying (required)

Examples

Output.deny("Dangerous command detected")
Output.deny("File path not allowed")

stop(reason)

@spec stop(String.t()) :: t()

Creates hook output to stop execution.

Parameters

  • reason - Explanation for stopping

Examples

Output.stop("Critical error detected")
Output.stop("Resource limit exceeded")

suppress_output(output)

@spec suppress_output(t()) :: t()

Marks output to be suppressed from transcript.

Parameters

  • output - Existing hook output

Examples

Output.allow()
|> Output.suppress_output()

to_json_map(output)

@spec to_json_map(t()) :: map()

Converts Elixir output to JSON-compatible map for CLI.

Converts atom keys to strings recursively.

Examples

iex> Output.to_json_map(%{continue: false, stopReason: "Error"})
%{"continue" => false, "stopReason" => "Error"}

iex> Output.to_json_map(%{hookSpecificOutput: %{hookEventName: "PreToolUse"}})
%{"hookSpecificOutput" => %{"hookEventName" => "PreToolUse"}}

validate(output)

@spec validate(t()) :: :ok | {:error, String.t()}

Validates hook output structure.

Returns :ok if valid, {:error, reason} otherwise.

Examples

iex> Output.validate(%{continue: true})
:ok

iex> Output.validate("not a map")
{:error, "Hook output must be a map"}

with_async_timeout(output, timeout_ms)

@spec with_async_timeout(t(), non_neg_integer()) :: t()

Sets the timeout for async hook processing.

Must be used with async/1. The timeout is specified in milliseconds and defines how long the CLI will wait for the async operation to complete.

Parameters

  • output - Existing hook output (should have async: true)
  • timeout_ms - Timeout in milliseconds

Examples

Output.allow("Processing")
|> Output.async()
|> Output.with_async_timeout(60_000)  # 60 second timeout

with_reason(output, reason)

@spec with_reason(t(), String.t()) :: t()

Adds a reason to hook output.

Reasons are shown to Claude to help it understand what happened.

Parameters

  • output - Existing hook output
  • reason - Claude-visible explanation

Examples

Output.deny("Invalid path")
|> Output.with_reason("Path must be within /allowed directory")

with_system_message(output, message)

@spec with_system_message(t(), String.t()) :: t()

Adds a system message to hook output.

System messages are shown to the user but not to Claude.

Parameters

  • output - Existing hook output
  • message - User-visible message

Examples

Output.deny("Command blocked")
|> Output.with_system_message("Security policy violation")

with_updated_input(output, updated_input)

@spec with_updated_input(t(), map()) :: t()

Modifies tool input before execution (PreToolUse hooks only).

This helper allows hooks to sanitize, validate, or transform tool inputs before Claude executes the tool. The updated input replaces the original input for that tool execution.

Parameters

  • output - Existing hook output
  • updated_input - Map of updated input values

Examples

# Sanitize file paths
Output.allow("Path sanitized")
|> Output.with_updated_input(%{"path" => sanitize_path(input["path"])})

# Add default values
Output.allow("Defaults applied")
|> Output.with_updated_input(Map.put(input, "timeout", 30))

# Validate and transform
Output.allow("Input validated")
|> Output.with_updated_input(%{
  "path" => expand_path(input["path"]),
  "validated" => true
})