My agentic slop goes here. Not intended for anyone else!
1(** Client interface for interacting with Claude.
2
3 This module provides the high-level client API for sending messages to
4 Claude and receiving responses. It handles the bidirectional streaming
5 protocol, permission callbacks, and hooks.
6
7 {2 Basic Usage}
8
9 {[
10 Eio.Switch.run @@ fun sw ->
11 let client = Client.create ~sw ~process_mgr () in
12 Client.query client "What is 2+2?";
13
14 let messages = Client.receive_all client in
15 List.iter (function
16 | Message.Assistant msg ->
17 Printf.printf "Claude: %s\n" (Message.Assistant.text msg)
18 | _ -> ()
19 ) messages
20 ]}
21
22 {2 Features}
23
24 - {b Message Streaming}: Messages are streamed lazily via {!Seq.t}
25 - {b Permission Control}: Custom permission callbacks for tool usage
26 - {b Hooks}: Intercept and modify tool execution
27 - {b Dynamic Control}: Change settings mid-conversation
28 - {b Resource Management}: Automatic cleanup via Eio switches
29
30 {2 Message Flow}
31
32 1. Create a client with {!create}
33 2. Send messages with {!query} or {!send_message}
34 3. Receive responses with {!receive} or {!receive_all}
35 4. Continue multi-turn conversations by sending more messages
36 5. Client automatically cleans up when the switch exits
37
38 {2 Advanced Features}
39
40 - Permission discovery mode for understanding required permissions
41 - Mid-conversation model switching and permission mode changes
42 - Server capability introspection *)
43
44(** The log source for client operations *)
45val src : Logs.Src.t
46
47type t
48(** The type of Claude clients. *)
49
50val create :
51 ?options:Options.t ->
52 sw:Eio.Switch.t ->
53 process_mgr:_ Eio.Process.mgr ->
54 unit -> t
55(** [create ?options ~sw ~process_mgr ()] creates a new Claude client.
56
57 @param options Configuration options (defaults to {!Options.default})
58 @param sw Eio switch for resource management
59 @param process_mgr Eio process manager for spawning the Claude CLI *)
60
61val query : t -> string -> unit
62(** [query t prompt] sends a text message to Claude.
63
64 This is a convenience function for simple string messages. For more
65 complex messages with tool results or multiple content blocks, use
66 {!send_message} instead. *)
67
68val send_message : t -> Message.t -> unit
69(** [send_message t msg] sends a message to Claude.
70
71 Supports all message types including user messages with tool results. *)
72
73val send_user_message : t -> Message.User.t -> unit
74(** [send_user_message t msg] sends a user message to Claude. *)
75
76val receive : t -> Message.t Seq.t
77(** [receive t] returns a lazy sequence of messages from Claude.
78
79 The sequence yields messages as they arrive from Claude, including:
80 - {!constructor:Message.Assistant} - Claude's responses
81 - {!constructor:Message.System} - System notifications
82 - {!constructor:Message.Result} - Final result with usage statistics
83
84 Control messages (permission requests, hook callbacks) are handled
85 internally and not yielded to the sequence. *)
86
87val receive_all : t -> Message.t list
88(** [receive_all t] collects all messages into a list.
89
90 This is a convenience function that consumes the {!receive} sequence.
91 Use this when you want to process all messages at once rather than
92 streaming them. *)
93
94val interrupt : t -> unit
95(** [interrupt t] sends an interrupt signal to stop Claude's execution. *)
96
97val discover_permissions : t -> t
98(** [discover_permissions t] enables permission discovery mode.
99
100 In discovery mode, all tool usage is logged but allowed. Use
101 {!get_discovered_permissions} to retrieve the list of permissions
102 that were requested during execution.
103
104 This is useful for understanding what permissions your prompt requires. *)
105
106val get_discovered_permissions : t -> Permissions.Rule.t list
107(** [get_discovered_permissions t] returns permissions discovered during execution.
108
109 Only useful after enabling {!discover_permissions}. *)
110
111val with_permission_callback : t -> Permissions.callback -> t
112(** [with_permission_callback t callback] updates the permission callback.
113
114 Allows dynamically changing the permission callback without recreating
115 the client. *)
116
117(** {1 Dynamic Control Methods}
118
119 These methods allow you to change Claude's behavior mid-conversation
120 without recreating the client. This is useful for:
121
122 - Adjusting permission strictness based on user feedback
123 - Switching to faster/cheaper models for simple tasks
124 - Adapting to changing requirements during long conversations
125 - Introspecting server capabilities
126
127 {2 Example: Adaptive Permission Control}
128
129 {[
130 (* Start with strict permissions *)
131 let client = Client.create ~sw ~process_mgr
132 ~options:(Options.default
133 |> Options.with_permission_mode Permissions.Mode.Default) ()
134 in
135
136 Client.query client "Analyze this code";
137 let _ = Client.receive_all client in
138
139 (* User approves, switch to auto-accept edits *)
140 Client.set_permission_mode client Permissions.Mode.Accept_edits;
141
142 Client.query client "Now refactor it";
143 let _ = Client.receive_all client in
144 ]}
145
146 {2 Example: Model Switching for Efficiency}
147
148 {[
149 (* Use powerful model for complex analysis *)
150 let client = Client.create ~sw ~process_mgr
151 ~options:(Options.default |> Options.with_model "claude-sonnet-4-5") ()
152 in
153
154 Client.query client "Design a new architecture for this system";
155 let _ = Client.receive_all client in
156
157 (* Switch to faster model for simple tasks *)
158 Client.set_model client "claude-haiku-4";
159
160 Client.query client "Now write a README";
161 let _ = Client.receive_all client in
162 ]}
163
164 {2 Example: Server Introspection}
165
166 {[
167 let info = Client.get_server_info client in
168 Printf.printf "Claude CLI version: %s\n"
169 (Sdk_control.Server_info.version info);
170 Printf.printf "Capabilities: %s\n"
171 (String.concat ", " (Sdk_control.Server_info.capabilities info));
172 ]} *)
173
174val set_permission_mode : t -> Permissions.Mode.t -> unit
175(** [set_permission_mode t mode] changes the permission mode mid-conversation.
176
177 This allows switching between permission modes without recreating the client:
178 - {!Permissions.Mode.Default} - Prompt for all permissions
179 - {!Permissions.Mode.Accept_edits} - Auto-accept file edits
180 - {!Permissions.Mode.Plan} - Planning mode with restricted execution
181 - {!Permissions.Mode.Bypass_permissions} - Skip all permission checks
182
183 @raise Failure if the server returns an error *)
184
185val set_model : t -> Model.t -> unit
186(** [set_model t model] switches to a different AI model mid-conversation.
187
188 Common models:
189 - [`Sonnet_4_5] - Most capable, balanced performance
190 - [`Opus_4] - Maximum capability for complex tasks
191 - [`Haiku_4] - Fast and cost-effective
192
193 @raise Failure if the model is invalid or unavailable *)
194
195val set_model_string : t -> string -> unit
196(** [set_model_string t model] switches to a different AI model using a string.
197
198 This is a convenience function that parses the string using {!Model.of_string}.
199
200 @raise Failure if the model is invalid or unavailable *)
201
202val get_server_info : t -> Sdk_control.Server_info.t
203(** [get_server_info t] retrieves server capabilities and metadata.
204
205 Returns information about:
206 - Server version string
207 - Available capabilities
208 - Supported commands
209 - Available output styles
210
211 Useful for feature detection and debugging.
212
213 @raise Failure if the server returns an error *)