My agentic slop goes here. Not intended for anyone else!
1let src = Logs.Src.create "claude.incoming" ~doc:"Incoming messages from Claude CLI"
2module Log = (val Logs.src_log src : Logs.LOG)
3
4(** Control request types for incoming control_request messages *)
5module Control_request = struct
6 (** Can use tool permission request *)
7 module Can_use_tool = struct
8 type t = {
9 tool_name : string;
10 input : Jsont.json;
11 permission_suggestions : Jsont.json list;
12 }
13
14 let tool_name t = t.tool_name
15 let input t = t.input
16 let permission_suggestions t = t.permission_suggestions
17
18 let jsont : t Jsont.t =
19 let make tool_name input permission_suggestions =
20 { tool_name; input; permission_suggestions = Option.value permission_suggestions ~default:[] }
21 in
22 Jsont.Object.map ~kind:"CanUseTool" make
23 |> Jsont.Object.mem "tool_name" Jsont.string ~enc:tool_name
24 |> Jsont.Object.mem "input" Jsont.json ~enc:input
25 |> Jsont.Object.opt_mem "permission_suggestions" (Jsont.list Jsont.json)
26 ~enc:(fun t -> if t.permission_suggestions = [] then None else Some t.permission_suggestions)
27 |> Jsont.Object.finish
28 end
29
30 (** Hook callback request *)
31 module Hook_callback = struct
32 type t = {
33 callback_id : string;
34 input : Jsont.json;
35 tool_use_id : string option;
36 }
37
38 let callback_id t = t.callback_id
39 let input t = t.input
40 let tool_use_id t = t.tool_use_id
41
42 let jsont : t Jsont.t =
43 let make callback_id input tool_use_id = { callback_id; input; tool_use_id } in
44 Jsont.Object.map ~kind:"HookCallback" make
45 |> Jsont.Object.mem "callback_id" Jsont.string ~enc:callback_id
46 |> Jsont.Object.mem "input" Jsont.json ~enc:input
47 |> Jsont.Object.opt_mem "tool_use_id" Jsont.string ~enc:tool_use_id
48 |> Jsont.Object.finish
49 end
50
51 (** Request payload - discriminated by subtype *)
52 type request =
53 | Can_use_tool of Can_use_tool.t
54 | Hook_callback of Hook_callback.t
55 | Unknown of string * Jsont.json
56
57 let request_of_json json =
58 let subtype_codec = Jsont.Object.map ~kind:"Subtype" Fun.id
59 |> Jsont.Object.mem "subtype" Jsont.string ~enc:Fun.id
60 |> Jsont.Object.finish
61 in
62 match Jsont.Json.decode subtype_codec json with
63 | Error _ -> Unknown ("unknown", json)
64 | Ok subtype ->
65 match subtype with
66 | "can_use_tool" ->
67 (match Jsont.Json.decode Can_use_tool.jsont json with
68 | Ok r -> Can_use_tool r
69 | Error _ -> Unknown (subtype, json))
70 | "hook_callback" ->
71 (match Jsont.Json.decode Hook_callback.jsont json with
72 | Ok r -> Hook_callback r
73 | Error _ -> Unknown (subtype, json))
74 | _ -> Unknown (subtype, json)
75
76 (** Full control request message *)
77 type t = {
78 request_id : string;
79 request : request;
80 }
81
82 let request_id t = t.request_id
83 let request t = t.request
84
85 let subtype t =
86 match t.request with
87 | Can_use_tool _ -> "can_use_tool"
88 | Hook_callback _ -> "hook_callback"
89 | Unknown (s, _) -> s
90
91 let jsont : t Jsont.t =
92 let dec json =
93 let envelope_codec =
94 Jsont.Object.map ~kind:"ControlRequestEnvelope" (fun request_id request_json -> (request_id, request_json))
95 |> Jsont.Object.mem "request_id" Jsont.string ~enc:fst
96 |> Jsont.Object.mem "request" Jsont.json ~enc:snd
97 |> Jsont.Object.finish
98 in
99 match Jsont.Json.decode envelope_codec json with
100 | Error err -> failwith ("Failed to decode control_request envelope: " ^ err)
101 | Ok (request_id, request_json) ->
102 { request_id; request = request_of_json request_json }
103 in
104 let enc t =
105 let request_json = match t.request with
106 | Can_use_tool r ->
107 (match Jsont.Json.encode Can_use_tool.jsont r with
108 | Ok j -> j
109 | Error err -> failwith ("Failed to encode Can_use_tool: " ^ err))
110 | Hook_callback r ->
111 (match Jsont.Json.encode Hook_callback.jsont r with
112 | Ok j -> j
113 | Error err -> failwith ("Failed to encode Hook_callback: " ^ err))
114 | Unknown (_, j) -> j
115 in
116 Jsont.Json.object' [
117 Jsont.Json.mem (Jsont.Json.name "type") (Jsont.Json.string "control_request");
118 Jsont.Json.mem (Jsont.Json.name "request_id") (Jsont.Json.string t.request_id);
119 Jsont.Json.mem (Jsont.Json.name "request") request_json;
120 ]
121 in
122 Jsont.map ~kind:"ControlRequest" ~dec ~enc Jsont.json
123end
124
125type t =
126 | Message of Message.t
127 | Control_response of Sdk_control.control_response
128 | Control_request of Control_request.t
129
130let jsont : t Jsont.t =
131 (* Custom decoder that checks the type field and dispatches to the appropriate codec.
132
133 The challenge is that Message can have multiple type values ("user", "assistant",
134 "system", "result"), while control_response and control_request have single type values.
135 Jsont's case_mem discriminator doesn't support multiple tags per case, so we implement
136 a custom decoder/encoder. *)
137
138 let type_field_codec = Jsont.Object.map ~kind:"type_field" Fun.id
139 |> Jsont.Object.opt_mem "type" Jsont.string ~enc:Fun.id
140 |> Jsont.Object.finish
141 in
142
143 let dec json =
144 match Jsont.Json.decode type_field_codec json with
145 | Error _ | Ok None ->
146 (* No type field, try as message *)
147 (match Jsont.Json.decode Message.jsont json with
148 | Ok msg -> Message msg
149 | Error err -> failwith ("Failed to decode message: " ^ err))
150 | Ok (Some typ) ->
151 match typ with
152 | "control_response" ->
153 (match Jsont.Json.decode Sdk_control.control_response_jsont json with
154 | Ok resp -> Control_response resp
155 | Error err -> failwith ("Failed to decode control_response: " ^ err))
156 | "control_request" ->
157 (match Jsont.Json.decode Control_request.jsont json with
158 | Ok req -> Control_request req
159 | Error err -> failwith ("Failed to decode control_request: " ^ err))
160 | "user" | "assistant" | "system" | "result" | _ ->
161 (* Message types *)
162 (match Jsont.Json.decode Message.jsont json with
163 | Ok msg -> Message msg
164 | Error err -> failwith ("Failed to decode message: " ^ err))
165 in
166
167 let enc = function
168 | Message msg ->
169 (match Jsont.Json.encode Message.jsont msg with
170 | Ok json -> json
171 | Error err -> failwith ("Failed to encode message: " ^ err))
172 | Control_response resp ->
173 (match Jsont.Json.encode Sdk_control.control_response_jsont resp with
174 | Ok json -> json
175 | Error err -> failwith ("Failed to encode control response: " ^ err))
176 | Control_request req ->
177 (match Jsont.Json.encode Control_request.jsont req with
178 | Ok json -> json
179 | Error err -> failwith ("Failed to encode control request: " ^ err))
180 in
181
182 Jsont.map ~kind:"Incoming" ~dec ~enc Jsont.json
183
184let pp fmt = function
185 | Message msg -> Format.fprintf fmt "@[<2>Message@ %a@]" Message.pp msg
186 | Control_response resp -> Format.fprintf fmt "@[<2>ControlResponse@ %a@]" Sdk_control.pp (Sdk_control.Response resp)
187 | Control_request req -> Format.fprintf fmt "@[<2>ControlRequest@ { request_id=%S; subtype=%S }@]"
188 (Control_request.request_id req) (Control_request.subtype req)