My agentic slop goes here. Not intended for anyone else!
1open Ezjsonm 2 3let src = Logs.Src.create "claude.sdk_control" ~doc:"Claude SDK control protocol" 4module Log = (val Logs.src_log src : Logs.LOG) 5 6module JU = Json_utils 7 8module Request = struct 9 type interrupt = { 10 subtype : [`Interrupt]; 11 } 12 13 type permission = { 14 subtype : [`Can_use_tool]; 15 tool_name : string; 16 input : value; 17 permission_suggestions : Permissions.Update.t list option; 18 blocked_path : string option; 19 } 20 21 type initialize = { 22 subtype : [`Initialize]; 23 hooks : (string * value) list option; 24 } 25 26 type set_permission_mode = { 27 subtype : [`Set_permission_mode]; 28 mode : Permissions.Mode.t; 29 } 30 31 type hook_callback = { 32 subtype : [`Hook_callback]; 33 callback_id : string; 34 input : value; 35 tool_use_id : string option; 36 } 37 38 type mcp_message = { 39 subtype : [`Mcp_message]; 40 server_name : string; 41 message : value; 42 } 43 44 type t = 45 | Interrupt of interrupt 46 | Permission of permission 47 | Initialize of initialize 48 | Set_permission_mode of set_permission_mode 49 | Hook_callback of hook_callback 50 | Mcp_message of mcp_message 51 52 let interrupt () = Interrupt { subtype = `Interrupt } 53 54 let permission ~tool_name ~input ?permission_suggestions ?blocked_path () = 55 Permission { 56 subtype = `Can_use_tool; 57 tool_name; 58 input; 59 permission_suggestions; 60 blocked_path; 61 } 62 63 let initialize ?hooks () = 64 Initialize { subtype = `Initialize; hooks } 65 66 let set_permission_mode ~mode = 67 Set_permission_mode { subtype = `Set_permission_mode; mode } 68 69 let hook_callback ~callback_id ~input ?tool_use_id () = 70 Hook_callback { 71 subtype = `Hook_callback; 72 callback_id; 73 input; 74 tool_use_id; 75 } 76 77 let mcp_message ~server_name ~message = 78 Mcp_message { 79 subtype = `Mcp_message; 80 server_name; 81 message; 82 } 83 84 let to_json = function 85 | Interrupt _ -> 86 `O [("subtype", `String "interrupt")] 87 | Permission p -> 88 let fields = [ 89 ("subtype", `String "can_use_tool"); 90 ("tool_name", `String p.tool_name); 91 ("input", p.input); 92 ] in 93 let fields = match p.permission_suggestions with 94 | Some suggestions -> 95 ("permission_suggestions", 96 `A (List.map Permissions.Update.to_json suggestions)) :: fields 97 | None -> fields 98 in 99 let fields = match p.blocked_path with 100 | Some path -> ("blocked_path", `String path) :: fields 101 | None -> fields 102 in 103 `O fields 104 | Initialize i -> 105 let fields = [("subtype", `String "initialize")] in 106 let fields = match i.hooks with 107 | Some hooks -> 108 ("hooks", `O hooks) :: fields 109 | None -> fields 110 in 111 `O fields 112 | Set_permission_mode s -> 113 `O [ 114 ("subtype", `String "set_permission_mode"); 115 ("mode", Permissions.Mode.to_json s.mode); 116 ] 117 | Hook_callback h -> 118 let fields = [ 119 ("subtype", `String "hook_callback"); 120 ("callback_id", `String h.callback_id); 121 ("input", h.input); 122 ] in 123 let fields = match h.tool_use_id with 124 | Some id -> ("tool_use_id", `String id) :: fields 125 | None -> fields 126 in 127 `O fields 128 | Mcp_message m -> 129 `O [ 130 ("subtype", `String "mcp_message"); 131 ("server_name", `String m.server_name); 132 ("message", m.message); 133 ] 134 135 let of_json = function 136 | `O fields -> 137 let subtype = JU.assoc_string "subtype" fields in 138 (match subtype with 139 | "interrupt" -> 140 Interrupt { subtype = `Interrupt } 141 | "can_use_tool" -> 142 let tool_name = JU.assoc_string "tool_name" fields in 143 let input = List.assoc "input" fields in 144 let permission_suggestions = 145 match List.assoc_opt "permission_suggestions" fields with 146 | Some (`A lst) -> 147 Some (List.map Permissions.Update.of_json lst) 148 | _ -> None 149 in 150 let blocked_path = JU.assoc_string_opt "blocked_path" fields in 151 Permission { 152 subtype = `Can_use_tool; 153 tool_name; 154 input; 155 permission_suggestions; 156 blocked_path; 157 } 158 | "initialize" -> 159 let hooks = 160 match List.assoc_opt "hooks" fields with 161 | Some (`O hooks) -> Some hooks 162 | _ -> None 163 in 164 Initialize { subtype = `Initialize; hooks } 165 | "set_permission_mode" -> 166 let mode = List.assoc "mode" fields |> Permissions.Mode.of_json in 167 Set_permission_mode { subtype = `Set_permission_mode; mode } 168 | "hook_callback" -> 169 let callback_id = JU.assoc_string "callback_id" fields in 170 let input = List.assoc "input" fields in 171 let tool_use_id = JU.assoc_string_opt "tool_use_id" fields in 172 Hook_callback { 173 subtype = `Hook_callback; 174 callback_id; 175 input; 176 tool_use_id; 177 } 178 | "mcp_message" -> 179 let server_name = JU.assoc_string "server_name" fields in 180 let message = List.assoc "message" fields in 181 Mcp_message { 182 subtype = `Mcp_message; 183 server_name; 184 message; 185 } 186 | _ -> raise (Invalid_argument ("Unknown request subtype: " ^ subtype))) 187 | _ -> raise (Invalid_argument "Request.of_json: expected object") 188 189 let pp fmt = function 190 | Interrupt _ -> 191 Fmt.pf fmt "@[<2>Interrupt@]" 192 | Permission p -> 193 Fmt.pf fmt "@[<2>Permission@ { tool = %S;@ blocked_path = %a }@]" 194 p.tool_name Fmt.(option string) p.blocked_path 195 | Initialize i -> 196 Fmt.pf fmt "@[<2>Initialize@ { hooks = %s }@]" 197 (if Option.is_some i.hooks then "present" else "none") 198 | Set_permission_mode s -> 199 Fmt.pf fmt "@[<2>SetPermissionMode@ { mode = %a }@]" 200 Permissions.Mode.pp s.mode 201 | Hook_callback h -> 202 Fmt.pf fmt "@[<2>HookCallback@ { id = %S;@ tool_use_id = %a }@]" 203 h.callback_id Fmt.(option string) h.tool_use_id 204 | Mcp_message m -> 205 Fmt.pf fmt "@[<2>McpMessage@ { server = %S }@]" 206 m.server_name 207end 208 209module Response = struct 210 type success = { 211 subtype : [`Success]; 212 request_id : string; 213 response : value option; 214 } 215 216 type error = { 217 subtype : [`Error]; 218 request_id : string; 219 error : string; 220 } 221 222 type t = 223 | Success of success 224 | Error of error 225 226 let success ~request_id ?response () = 227 Success { 228 subtype = `Success; 229 request_id; 230 response; 231 } 232 233 let error ~request_id ~error = 234 Error { 235 subtype = `Error; 236 request_id; 237 error; 238 } 239 240 let to_json = function 241 | Success s -> 242 let fields = [ 243 ("subtype", `String "success"); 244 ("request_id", `String s.request_id); 245 ] in 246 let fields = match s.response with 247 | Some resp -> ("response", resp) :: fields 248 | None -> fields 249 in 250 `O fields 251 | Error e -> 252 `O [ 253 ("subtype", `String "error"); 254 ("request_id", `String e.request_id); 255 ("error", `String e.error); 256 ] 257 258 let of_json = function 259 | `O fields -> 260 let subtype = JU.assoc_string "subtype" fields in 261 let request_id = JU.assoc_string "request_id" fields in 262 (match subtype with 263 | "success" -> 264 let response = List.assoc_opt "response" fields in 265 Success { 266 subtype = `Success; 267 request_id; 268 response; 269 } 270 | "error" -> 271 let error = JU.assoc_string "error" fields in 272 Error { 273 subtype = `Error; 274 request_id; 275 error; 276 } 277 | _ -> raise (Invalid_argument ("Unknown response subtype: " ^ subtype))) 278 | _ -> raise (Invalid_argument "Response.of_json: expected object") 279 280 let pp fmt = function 281 | Success s -> 282 Fmt.pf fmt "@[<2>Success@ { request_id = %S;@ response = %s }@]" 283 s.request_id (if Option.is_some s.response then "present" else "none") 284 | Error e -> 285 Fmt.pf fmt "@[<2>Error@ { request_id = %S;@ error = %S }@]" 286 e.request_id e.error 287end 288 289type control_request = { 290 type_ : [`Control_request]; 291 request_id : string; 292 request : Request.t; 293} 294 295type control_response = { 296 type_ : [`Control_response]; 297 response : Response.t; 298} 299 300type t = 301 | Request of control_request 302 | Response of control_response 303 304let create_request ~request_id ~request = 305 Request { 306 type_ = `Control_request; 307 request_id; 308 request; 309 } 310 311let create_response ~response = 312 Response { 313 type_ = `Control_response; 314 response; 315 } 316 317let to_json = function 318 | Request r -> 319 `O [ 320 ("type", `String "control_request"); 321 ("request_id", `String r.request_id); 322 ("request", Request.to_json r.request); 323 ] 324 | Response r -> 325 `O [ 326 ("type", `String "control_response"); 327 ("response", Response.to_json r.response); 328 ] 329 330let of_json = function 331 | `O fields -> 332 let type_ = JU.assoc_string "type" fields in 333 (match type_ with 334 | "control_request" -> 335 let request_id = JU.assoc_string "request_id" fields in 336 let request = List.assoc "request" fields |> Request.of_json in 337 Request { 338 type_ = `Control_request; 339 request_id; 340 request; 341 } 342 | "control_response" -> 343 let response = List.assoc "response" fields |> Response.of_json in 344 Response { 345 type_ = `Control_response; 346 response; 347 } 348 | _ -> raise (Invalid_argument ("Unknown control type: " ^ type_))) 349 | _ -> raise (Invalid_argument "of_json: expected object") 350 351let pp fmt = function 352 | Request r -> 353 Fmt.pf fmt "@[<2>ControlRequest@ { id = %S;@ request = %a }@]" 354 r.request_id Request.pp r.request 355 | Response r -> 356 Fmt.pf fmt "@[<2>ControlResponse@ { %a }@]" 357 Response.pp r.response 358 359let log_request req = 360 Log.debug (fun m -> m "SDK control request: %a" Request.pp req) 361 362let log_response resp = 363 Log.debug (fun m -> m "SDK control response: %a" Response.pp resp)