My aggregated monorepo of OCaml code, automaintained
at doc-fixes 357 lines 11 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** Fully typed hook callbacks. 7 8 Hooks allow you to intercept and control events in Claude Code sessions, 9 using fully typed OCaml values instead of raw JSON. 10 11 {1 Overview} 12 13 This module provides a high-level, type-safe interface to hooks. Each hook 14 type has: 15 - Fully typed input records using {!Tool_input.t} 16 - Fully typed output records 17 - Helper functions for common responses 18 - Conversion functions to/from wire format ({!Proto.Hooks}) 19 20 {1 Example Usage} 21 22 {[ 23 open Eio.Std 24 25 (* Block dangerous bash commands *) 26 let block_rm_rf input = 27 if input.Hooks.PreToolUse.tool_name = "Bash" then 28 match Tool_input.get_string input.tool_input "command" with 29 | Some cmd when String.contains cmd "rm -rf" -> 30 Hooks.PreToolUse.deny ~reason:"Dangerous command" () 31 | _ -> Hooks.PreToolUse.continue () 32 else Hooks.PreToolUse.continue () 33 34 let hooks = 35 Hooks.empty 36 |> Hooks.on_pre_tool_use ~pattern:"Bash" block_rm_rf 37 38 let options = Claude.Options.create ~hooks () in 39 let client = Claude.Client.create ~options ~sw ~process_mgr () in 40 ]} *) 41 42val src : Logs.Src.t 43(** The log source for hooks *) 44 45(** {1 Hook Types} *) 46 47(** PreToolUse hook - fires before tool execution *) 48module PreToolUse : sig 49 (** {2 Input} *) 50 51 type input = { 52 session_id : string; 53 transcript_path : string; 54 tool_name : string; 55 tool_input : Tool_input.t; 56 } 57 (** Input provided to PreToolUse hooks. *) 58 59 (** {2 Output} *) 60 61 type decision = 62 | Allow 63 | Deny 64 | Ask 65 (** Permission decision for tool usage. *) 66 67 type output = { 68 decision : decision option; 69 reason : string option; 70 updated_input : Tool_input.t option; 71 } 72 (** Output from PreToolUse hooks. *) 73 74 (** {2 Response Builders} *) 75 76 val allow : ?reason:string -> ?updated_input:Tool_input.t -> unit -> output 77 (** [allow ?reason ?updated_input ()] creates an allow response. 78 @param reason Optional explanation for allowing 79 @param updated_input Optional modified tool input *) 80 81 val deny : ?reason:string -> unit -> output 82 (** [deny ?reason ()] creates a deny response. 83 @param reason Optional explanation for denying *) 84 85 val ask : ?reason:string -> unit -> output 86 (** [ask ?reason ()] creates an ask response to prompt the user. 87 @param reason Optional explanation for asking *) 88 89 val continue : unit -> output 90 (** [continue ()] creates a continue response with no decision. *) 91 92 (** {2 Callback Type} *) 93 94 type callback = input -> output 95 (** Callback function type for PreToolUse hooks. *) 96 97 (** {2 Conversion Functions} *) 98 99 val input_of_proto : Proto.Hooks.PreToolUse.Input.t -> input 100 (** [input_of_proto proto] converts wire format input to typed input. *) 101 102 val output_to_proto : output -> Proto.Hooks.PreToolUse.Output.t 103 (** [output_to_proto output] converts typed output to wire format. *) 104end 105 106(** PostToolUse hook - fires after tool execution *) 107module PostToolUse : sig 108 (** {2 Input} *) 109 110 type input = { 111 session_id : string; 112 transcript_path : string; 113 tool_name : string; 114 tool_input : Tool_input.t; 115 tool_response : Jsont.json; (* Response varies by tool *) 116 } 117 (** Input provided to PostToolUse hooks. 118 Note: [tool_response] remains as {!type:Jsont.json} since response schemas 119 vary by tool. *) 120 121 (** {2 Output} *) 122 123 type output = { 124 block : bool; 125 reason : string option; 126 additional_context : string option; 127 } 128 (** Output from PostToolUse hooks. *) 129 130 (** {2 Response Builders} *) 131 132 val continue : ?additional_context:string -> unit -> output 133 (** [continue ?additional_context ()] creates a continue response. 134 @param additional_context Optional context to add to the transcript *) 135 136 val block : 137 ?reason:string -> ?additional_context:string -> unit -> output 138 (** [block ?reason ?additional_context ()] creates a block response. 139 @param reason Optional explanation for blocking 140 @param additional_context Optional context to add to the transcript *) 141 142 (** {2 Callback Type} *) 143 144 type callback = input -> output 145 (** Callback function type for PostToolUse hooks. *) 146 147 (** {2 Conversion Functions} *) 148 149 val input_of_proto : Proto.Hooks.PostToolUse.Input.t -> input 150 (** [input_of_proto proto] converts wire format input to typed input. *) 151 152 val output_to_proto : output -> Proto.Hooks.PostToolUse.Output.t 153 (** [output_to_proto output] converts typed output to wire format. *) 154end 155 156(** UserPromptSubmit hook - fires when user submits a prompt *) 157module UserPromptSubmit : sig 158 (** {2 Input} *) 159 160 type input = { 161 session_id : string; 162 transcript_path : string; 163 prompt : string; 164 } 165 (** Input provided to UserPromptSubmit hooks. *) 166 167 (** {2 Output} *) 168 169 type output = { 170 block : bool; 171 reason : string option; 172 additional_context : string option; 173 } 174 (** Output from UserPromptSubmit hooks. *) 175 176 (** {2 Response Builders} *) 177 178 val continue : ?additional_context:string -> unit -> output 179 (** [continue ?additional_context ()] creates a continue response. 180 @param additional_context Optional context to add to the transcript *) 181 182 val block : ?reason:string -> unit -> output 183 (** [block ?reason ()] creates a block response. 184 @param reason Optional explanation for blocking *) 185 186 (** {2 Callback Type} *) 187 188 type callback = input -> output 189 (** Callback function type for UserPromptSubmit hooks. *) 190 191 (** {2 Conversion Functions} *) 192 193 val input_of_proto : Proto.Hooks.UserPromptSubmit.Input.t -> input 194 (** [input_of_proto proto] converts wire format input to typed input. *) 195 196 val output_to_proto : output -> Proto.Hooks.UserPromptSubmit.Output.t 197 (** [output_to_proto output] converts typed output to wire format. *) 198end 199 200(** Stop hook - fires when conversation stops *) 201module Stop : sig 202 (** {2 Input} *) 203 204 type input = { 205 session_id : string; 206 transcript_path : string; 207 stop_hook_active : bool; 208 } 209 (** Input provided to Stop hooks. *) 210 211 (** {2 Output} *) 212 213 type output = { 214 block : bool; 215 reason : string option; 216 } 217 (** Output from Stop hooks. *) 218 219 (** {2 Response Builders} *) 220 221 val continue : unit -> output 222 (** [continue ()] creates a continue response. *) 223 224 val block : ?reason:string -> unit -> output 225 (** [block ?reason ()] creates a block response. 226 @param reason Optional explanation for blocking *) 227 228 (** {2 Callback Type} *) 229 230 type callback = input -> output 231 (** Callback function type for Stop hooks. *) 232 233 (** {2 Conversion Functions} *) 234 235 val input_of_proto : Proto.Hooks.Stop.Input.t -> input 236 (** [input_of_proto proto] converts wire format input to typed input. *) 237 238 val output_to_proto : output -> Proto.Hooks.Stop.Output.t 239 (** [output_to_proto output] converts typed output to wire format. *) 240end 241 242(** SubagentStop hook - fires when a subagent stops *) 243module SubagentStop : sig 244 (** {2 Input} *) 245 246 type input = Stop.input 247 (** Same structure as Stop.input *) 248 249 (** {2 Output} *) 250 251 type output = Stop.output 252 (** Same structure as Stop.output *) 253 254 (** {2 Response Builders} *) 255 256 val continue : unit -> output 257 (** [continue ()] creates a continue response. *) 258 259 val block : ?reason:string -> unit -> output 260 (** [block ?reason ()] creates a block response. 261 @param reason Optional explanation for blocking *) 262 263 (** {2 Callback Type} *) 264 265 type callback = input -> output 266 (** Callback function type for SubagentStop hooks. *) 267 268 (** {2 Conversion Functions} *) 269 270 val input_of_proto : Proto.Hooks.SubagentStop.Input.t -> input 271 (** [input_of_proto proto] converts wire format input to typed input. *) 272 273 val output_to_proto : output -> Proto.Hooks.SubagentStop.Output.t 274 (** [output_to_proto output] converts typed output to wire format. *) 275end 276 277(** PreCompact hook - fires before message compaction *) 278module PreCompact : sig 279 (** {2 Input} *) 280 281 type input = { 282 session_id : string; 283 transcript_path : string; 284 } 285 (** Input provided to PreCompact hooks. *) 286 287 (** {2 Callback Type} *) 288 289 type callback = input -> unit 290 (** Callback function type for PreCompact hooks. 291 PreCompact hooks have no output - they are notification-only. *) 292 293 (** {2 Conversion Functions} *) 294 295 val input_of_proto : Proto.Hooks.PreCompact.Input.t -> input 296 (** [input_of_proto proto] converts wire format input to typed input. *) 297end 298 299(** {1 Hook Configuration} *) 300 301type t 302(** Hook configuration. 303 304 Hooks are configured using a builder pattern: 305 {[ 306 Hooks.empty 307 |> Hooks.on_pre_tool_use ~pattern:"Bash" bash_handler 308 |> Hooks.on_post_tool_use post_handler 309 ]} *) 310 311val empty : t 312(** [empty] is an empty hook configuration with no callbacks. *) 313 314val on_pre_tool_use : ?pattern:string -> PreToolUse.callback -> t -> t 315(** [on_pre_tool_use ?pattern callback config] adds a PreToolUse hook. 316 @param pattern Optional regex pattern to match tool names (e.g., "Bash|Edit") 317 @param callback Function to invoke on matching events *) 318 319val on_post_tool_use : ?pattern:string -> PostToolUse.callback -> t -> t 320(** [on_post_tool_use ?pattern callback config] adds a PostToolUse hook. 321 @param pattern Optional regex pattern to match tool names 322 @param callback Function to invoke on matching events *) 323 324val on_user_prompt_submit : UserPromptSubmit.callback -> t -> t 325(** [on_user_prompt_submit callback config] adds a UserPromptSubmit hook. 326 @param callback Function to invoke on prompt submission *) 327 328val on_stop : Stop.callback -> t -> t 329(** [on_stop callback config] adds a Stop hook. 330 @param callback Function to invoke on conversation stop *) 331 332val on_subagent_stop : SubagentStop.callback -> t -> t 333(** [on_subagent_stop callback config] adds a SubagentStop hook. 334 @param callback Function to invoke on subagent stop *) 335 336val on_pre_compact : PreCompact.callback -> t -> t 337(** [on_pre_compact callback config] adds a PreCompact hook. 338 @param callback Function to invoke before message compaction *) 339 340(** {1 Internal - for client use} *) 341 342val get_callbacks : 343 t -> 344 (Proto.Hooks.event * (string option * (Jsont.json -> Proto.Hooks.result)) 345 list) 346 list 347(** [get_callbacks config] returns hook configuration in format suitable for 348 registration with the CLI. 349 350 This function converts typed callbacks into wire format handlers that: 351 - Parse JSON input using Proto.Hooks types 352 - Convert to typed input using input_of_proto 353 - Invoke the user's typed callback 354 - Convert output back to wire format using output_to_proto 355 356 This is an internal function used by {!Client} - you should not need to 357 call it directly. *)