My agentic slop goes here. Not intended for anyone else!

claudeio

+1
claudeio/claude.opam
···
"fmt"
"logs"
"ezjsonm"
+
"jsont" {>= "0.2.0"}
"alcotest" {with-test}
"odoc" {with-doc}
]
+1
claudeio/dune-project
···
fmt
logs
ezjsonm
+
(jsont (>= 0.2.0))
(alcotest :with-test)))
+3
claudeio/lib/claude.ml
···
+
module Model = Model
module Content_block = Content_block
module Message = Message
module Control = Control
module Permissions = Permissions
module Hooks = Hooks
+
module Sdk_control = Sdk_control
+
module Structured_output = Structured_output
module Options = Options
module Transport = Transport
module Client = Client
+9
claudeio/lib/claude.mli
···
module Options = Options
(** Configuration options for Claude sessions. *)
+
module Model = Model
+
(** Claude AI model identifiers with type-safe variants. *)
+
module Content_block = Content_block
(** Content blocks for messages (text, tool use, tool results, thinking). *)
···
module Hooks = Hooks
(** Hooks system for event interception. *)
+
+
module Sdk_control = Sdk_control
+
(** SDK control protocol for dynamic configuration. *)
+
+
module Structured_output = Structured_output
+
(** Structured output support using JSON Schema. *)
module Transport = Transport
(** Low-level transport layer for CLI communication. *)
+80 -1
claudeio/lib/client.ml
···
hook_callbacks : (string, Hooks.callback) Hashtbl.t;
mutable next_callback_id : int;
mutable session_id : string option;
+
control_responses : (string, Ezjsonm.value) Hashtbl.t;
+
control_mutex : Eio.Mutex.t;
+
control_condition : Eio.Condition.t;
}
let handle_control_request t control_msg =
···
(* Handle control responses (e.g., initialize response) *)
let request_id = Json_utils.find_string json ["response"; "request_id"] in
Log.debug (fun m -> m "Received control response for request_id: %s" request_id);
-
(* Don't yield control responses as messages, just loop *)
+
(* Store the response and signal waiting threads *)
+
Eio.Mutex.use_rw ~protect:false t.control_mutex (fun () ->
+
Hashtbl.replace t.control_responses request_id json;
+
Eio.Condition.broadcast t.control_condition
+
);
loop ()
| _ ->
···
hook_callbacks;
next_callback_id = 0;
session_id = None;
+
control_responses = Hashtbl.create 16;
+
control_mutex = Eio.Mutex.create ();
+
control_condition = Eio.Condition.create ();
} in
(* Register hooks and send initialize if hooks are configured *)
···
let with_permission_callback t callback =
{ t with permission_callback = Some callback }
+
+
(* Helper to send a control request and wait for response *)
+
let send_control_request t ~request_id request =
+
let open Ezjsonm in
+
(* Send the control request *)
+
let control_msg = Sdk_control.create_request ~request_id ~request in
+
let json = Sdk_control.to_json control_msg in
+
Log.info (fun m -> m "Sending control request: %s" (value_to_string json));
+
Transport.send t.transport json;
+
+
(* Wait for the response with timeout *)
+
let max_wait = 10.0 in (* 10 seconds timeout *)
+
let start_time = Unix.gettimeofday () in
+
+
let rec wait_for_response () =
+
Eio.Mutex.use_rw ~protect:false t.control_mutex (fun () ->
+
match Hashtbl.find_opt t.control_responses request_id with
+
| Some response_json ->
+
(* Remove it from the table *)
+
Hashtbl.remove t.control_responses request_id;
+
response_json
+
| None ->
+
let elapsed = Unix.gettimeofday () -. start_time in
+
if elapsed > max_wait then
+
raise (Failure (Printf.sprintf "Timeout waiting for control response: %s" request_id))
+
else (
+
(* Release mutex and wait for signal *)
+
Eio.Condition.await_no_mutex t.control_condition;
+
wait_for_response ()
+
)
+
)
+
in
+
+
let response_json = wait_for_response () in
+
Log.debug (fun m -> m "Received control response: %s" (value_to_string response_json));
+
+
(* Parse the response *)
+
let response = find response_json ["response"] |> Sdk_control.Response.of_json in
+
match response with
+
| Sdk_control.Response.Success s -> s.response
+
| Sdk_control.Response.Error e ->
+
raise (Failure (Printf.sprintf "Control request failed: %s" e.error))
+
+
let set_permission_mode t mode =
+
let request_id = Printf.sprintf "set_perm_mode_%f" (Unix.gettimeofday ()) in
+
let request = Sdk_control.Request.set_permission_mode ~mode in
+
let _response = send_control_request t ~request_id request in
+
Log.info (fun m -> m "Permission mode set to: %a" Permissions.Mode.pp mode)
+
+
let set_model t model =
+
let model_str = Model.to_string model in
+
let request_id = Printf.sprintf "set_model_%f" (Unix.gettimeofday ()) in
+
let request = Sdk_control.Request.set_model ~model:model_str in
+
let _response = send_control_request t ~request_id request in
+
Log.info (fun m -> m "Model set to: %a" Model.pp model)
+
+
let set_model_string t model_str =
+
set_model t (Model.of_string model_str)
+
+
let get_server_info t =
+
let request_id = Printf.sprintf "get_server_info_%f" (Unix.gettimeofday ()) in
+
let request = Sdk_control.Request.get_server_info () in
+
match send_control_request t ~request_id request with
+
| Some response_data ->
+
let server_info = Sdk_control.Server_info.of_json response_data in
+
Log.info (fun m -> m "Retrieved server info: %a" Sdk_control.Server_info.pp server_info);
+
server_info
+
| None ->
+
raise (Failure "No response data from get_server_info request")
+193 -3
claudeio/lib/client.mli
···
+
(** Client interface for interacting with Claude.
+
+
This module provides the high-level client API for sending messages to
+
Claude and receiving responses. It handles the bidirectional streaming
+
protocol, permission callbacks, and hooks.
+
+
{2 Basic Usage}
+
+
{[
+
Eio.Switch.run @@ fun sw ->
+
let client = Client.create ~sw ~process_mgr () in
+
Client.query client "What is 2+2?";
+
+
let messages = Client.receive_all client in
+
List.iter (function
+
| Message.Assistant msg ->
+
Printf.printf "Claude: %s\n" (Message.Assistant.text msg)
+
| _ -> ()
+
) messages
+
]}
+
+
{2 Features}
+
+
- {b Message Streaming}: Messages are streamed lazily via {!Seq.t}
+
- {b Permission Control}: Custom permission callbacks for tool usage
+
- {b Hooks}: Intercept and modify tool execution
+
- {b Dynamic Control}: Change settings mid-conversation
+
- {b Resource Management}: Automatic cleanup via Eio switches
+
+
{2 Message Flow}
+
+
1. Create a client with {!create}
+
2. Send messages with {!query} or {!send_message}
+
3. Receive responses with {!receive} or {!receive_all}
+
4. Continue multi-turn conversations by sending more messages
+
5. Client automatically cleans up when the switch exits
+
+
{2 Advanced Features}
+
+
- Permission discovery mode for understanding required permissions
+
- Mid-conversation model switching and permission mode changes
+
- Server capability introspection *)
+
(** The log source for client operations *)
val src : Logs.Src.t
type t
+
(** The type of Claude clients. *)
-
val create :
-
?options:Options.t ->
-
sw:Eio.Switch.t ->
+
val create :
+
?options:Options.t ->
+
sw:Eio.Switch.t ->
process_mgr:_ Eio.Process.mgr ->
unit -> t
+
(** [create ?options ~sw ~process_mgr ()] creates a new Claude client.
+
+
@param options Configuration options (defaults to {!Options.default})
+
@param sw Eio switch for resource management
+
@param process_mgr Eio process manager for spawning the Claude CLI *)
val query : t -> string -> unit
+
(** [query t prompt] sends a text message to Claude.
+
+
This is a convenience function for simple string messages. For more
+
complex messages with tool results or multiple content blocks, use
+
{!send_message} instead. *)
+
val send_message : t -> Message.t -> unit
+
(** [send_message t msg] sends a message to Claude.
+
+
Supports all message types including user messages with tool results. *)
+
val send_user_message : t -> Message.User.t -> unit
+
(** [send_user_message t msg] sends a user message to Claude. *)
val receive : t -> Message.t Seq.t
+
(** [receive t] returns a lazy sequence of messages from Claude.
+
+
The sequence yields messages as they arrive from Claude, including:
+
- {!Message.Assistant} - Claude's responses
+
- {!Message.System} - System notifications
+
- {!Message.Result} - Final result with usage statistics
+
+
Control messages (permission requests, hook callbacks) are handled
+
internally and not yielded to the sequence. *)
+
val receive_all : t -> Message.t list
+
(** [receive_all t] collects all messages into a list.
+
+
This is a convenience function that consumes the {!receive} sequence.
+
Use this when you want to process all messages at once rather than
+
streaming them. *)
val interrupt : t -> unit
+
(** [interrupt t] sends an interrupt signal to stop Claude's execution. *)
val discover_permissions : t -> t
+
(** [discover_permissions t] enables permission discovery mode.
+
+
In discovery mode, all tool usage is logged but allowed. Use
+
{!get_discovered_permissions} to retrieve the list of permissions
+
that were requested during execution.
+
+
This is useful for understanding what permissions your prompt requires. *)
+
val get_discovered_permissions : t -> Permissions.Rule.t list
+
(** [get_discovered_permissions t] returns permissions discovered during execution.
+
+
Only useful after enabling {!discover_permissions}. *)
+
val with_permission_callback : t -> Permissions.callback -> t
+
(** [with_permission_callback t callback] updates the permission callback.
+
+
Allows dynamically changing the permission callback without recreating
+
the client. *)
+
+
(** {1 Dynamic Control Methods}
+
+
These methods allow you to change Claude's behavior mid-conversation
+
without recreating the client. This is useful for:
+
+
- Adjusting permission strictness based on user feedback
+
- Switching to faster/cheaper models for simple tasks
+
- Adapting to changing requirements during long conversations
+
- Introspecting server capabilities
+
+
{2 Example: Adaptive Permission Control}
+
+
{[
+
(* Start with strict permissions *)
+
let client = Client.create ~sw ~process_mgr
+
~options:(Options.default
+
|> Options.with_permission_mode Permissions.Mode.Default) ()
+
in
+
+
Client.query client "Analyze this code";
+
let _ = Client.receive_all client in
+
+
(* User approves, switch to auto-accept edits *)
+
Client.set_permission_mode client Permissions.Mode.Accept_edits;
+
+
Client.query client "Now refactor it";
+
let _ = Client.receive_all client in
+
]}
+
+
{2 Example: Model Switching for Efficiency}
+
+
{[
+
(* Use powerful model for complex analysis *)
+
let client = Client.create ~sw ~process_mgr
+
~options:(Options.default |> Options.with_model "claude-sonnet-4-5") ()
+
in
+
+
Client.query client "Design a new architecture for this system";
+
let _ = Client.receive_all client in
+
+
(* Switch to faster model for simple tasks *)
+
Client.set_model client "claude-haiku-4";
+
+
Client.query client "Now write a README";
+
let _ = Client.receive_all client in
+
]}
+
+
{2 Example: Server Introspection}
+
+
{[
+
let info = Client.get_server_info client in
+
Printf.printf "Claude CLI version: %s\n"
+
(Sdk_control.Server_info.version info);
+
Printf.printf "Capabilities: %s\n"
+
(String.concat ", " (Sdk_control.Server_info.capabilities info));
+
]} *)
+
+
val set_permission_mode : t -> Permissions.Mode.t -> unit
+
(** [set_permission_mode t mode] changes the permission mode mid-conversation.
+
+
This allows switching between permission modes without recreating the client:
+
- {!Permissions.Mode.Default} - Prompt for all permissions
+
- {!Permissions.Mode.Accept_edits} - Auto-accept file edits
+
- {!Permissions.Mode.Plan} - Planning mode with restricted execution
+
- {!Permissions.Mode.Bypass_permissions} - Skip all permission checks
+
+
@raise Failure if the server returns an error *)
+
+
val set_model : t -> Model.t -> unit
+
(** [set_model t model] switches to a different AI model mid-conversation.
+
+
Common models:
+
- [`Sonnet_4_5] - Most capable, balanced performance
+
- [`Opus_4] - Maximum capability for complex tasks
+
- [`Haiku_4] - Fast and cost-effective
+
+
@raise Failure if the model is invalid or unavailable *)
+
+
val set_model_string : t -> string -> unit
+
(** [set_model_string t model] switches to a different AI model using a string.
+
+
This is a convenience function that parses the string using {!Model.of_string}.
+
+
@raise Failure if the model is invalid or unavailable *)
+
+
val get_server_info : t -> Sdk_control.Server_info.t
+
(** [get_server_info t] retrieves server capabilities and metadata.
+
+
Returns information about:
+
- Server version string
+
- Available capabilities
+
- Supported commands
+
- Available output styles
+
+
Useful for feature detection and debugging.
+
+
@raise Failure if the server returns an error *)
+1 -1
claudeio/lib/dune
···
(library
(public_name claude)
(name claude)
-
(libraries eio eio.unix ezjsonm fmt logs))
+
(libraries eio eio.unix ezjsonm fmt logs jsont))
+65 -21
claudeio/lib/message.ml
···
end
module Assistant = struct
+
type error = [
+
| `Authentication_failed
+
| `Billing_error
+
| `Rate_limit
+
| `Invalid_request
+
| `Server_error
+
| `Unknown
+
]
+
+
let error_to_string = function
+
| `Authentication_failed -> "authentication_failed"
+
| `Billing_error -> "billing_error"
+
| `Rate_limit -> "rate_limit"
+
| `Invalid_request -> "invalid_request"
+
| `Server_error -> "server_error"
+
| `Unknown -> "unknown"
+
+
let error_of_string = function
+
| "authentication_failed" -> `Authentication_failed
+
| "billing_error" -> `Billing_error
+
| "rate_limit" -> `Rate_limit
+
| "invalid_request" -> `Invalid_request
+
| "server_error" -> `Server_error
+
| "unknown" | _ -> `Unknown
+
type t = {
content : Content_block.t list;
model : string;
+
error : error option;
}
-
-
let create ~content ~model = { content; model }
+
+
let create ~content ~model ?error () = { content; model; error }
let content t = t.content
let model t = t.model
+
let error t = t.error
let get_text_blocks t =
List.filter_map (function
···
String.concat "\n" (get_text_blocks t)
let to_json t =
+
let msg_fields = [
+
("content", `A (List.map Content_block.to_json t.content));
+
("model", `String t.model);
+
] in
+
let msg_fields = match t.error with
+
| Some err -> ("error", `String (error_to_string err)) :: msg_fields
+
| None -> msg_fields
+
in
`O [
("type", `String "assistant");
-
("message", `O [
-
("content", `A (List.map Content_block.to_json t.content));
-
("model", `String t.model);
-
]);
+
("message", `O msg_fields);
]
let of_json = function
| `O fields ->
let message = List.assoc "message" fields in
-
let content, model = match message with
+
let content, model, error = match message with
| `O msg_fields ->
-
let content =
+
let content =
match List.assoc "content" msg_fields with
| `A blocks -> List.map Content_block.of_json blocks
| _ -> raise (Invalid_argument "Assistant.of_json: invalid content")
in
let model = JU.assoc_string "model" msg_fields in
-
content, model
+
let error =
+
match JU.assoc_string_opt "error" msg_fields with
+
| Some err_str -> Some (error_of_string err_str)
+
| None -> None
+
in
+
content, model, error
| _ -> raise (Invalid_argument "Assistant.of_json: invalid message")
in
-
{ content; model }
+
{ content; model; error }
| _ -> raise (Invalid_argument "Assistant.of_json: expected object")
let pp fmt t =
···
total_cost_usd : float option;
usage : Usage.t option;
result : string option;
+
structured_output : value option;
}
-
let create ~subtype ~duration_ms ~duration_api_ms ~is_error ~num_turns
-
~session_id ?total_cost_usd ?usage ?result () =
+
let create ~subtype ~duration_ms ~duration_api_ms ~is_error ~num_turns
+
~session_id ?total_cost_usd ?usage ?result ?structured_output () =
{ subtype; duration_ms; duration_api_ms; is_error; num_turns;
-
session_id; total_cost_usd; usage; result }
+
session_id; total_cost_usd; usage; result; structured_output }
let subtype t = t.subtype
let duration_ms t = t.duration_ms
···
let total_cost_usd t = t.total_cost_usd
let usage t = t.usage
let result t = t.result
+
let structured_output t = t.structured_output
let to_json t =
let fields = [
···
| Some result -> ("result", `String result) :: fields
| None -> fields
in
+
let fields = match t.structured_output with
+
| Some output -> ("structured_output", output) :: fields
+
| None -> fields
+
in
`O fields
let of_json = function
···
let total_cost_usd = JU.assoc_float_opt "total_cost_usd" fields in
let usage = Option.map Usage.of_json (List.assoc_opt "usage" fields) in
let result = JU.assoc_string_opt "result" fields in
+
let structured_output = List.assoc_opt "structured_output" fields in
{ subtype; duration_ms; duration_api_ms; is_error; num_turns;
-
session_id; total_cost_usd; usage; result }
+
session_id; total_cost_usd; usage; result; structured_output }
| _ -> raise (Invalid_argument "Result.of_json: expected object")
let pp fmt t =
···
let user_with_tool_result ~tool_use_id ~content ?is_error () =
User (User.create_with_tool_result ~tool_use_id ~content ?is_error ())
-
let assistant ~content ~model = Assistant (Assistant.create ~content ~model)
-
let assistant_text ~text ~model =
-
Assistant (Assistant.create ~content:[Content_block.text text] ~model)
+
let assistant ~content ~model ?error () = Assistant (Assistant.create ~content ~model ?error ())
+
let assistant_text ~text ~model ?error () =
+
Assistant (Assistant.create ~content:[Content_block.text text] ~model ?error ())
let system ~subtype ~data = System (System.create ~subtype ~data)
let system_init ~session_id =
···
let data = System.Data.of_assoc [("error", `String error)] in
System (System.create ~subtype:"error" ~data)
-
let result ~subtype ~duration_ms ~duration_api_ms ~is_error ~num_turns
-
~session_id ?total_cost_usd ?usage ?result () =
-
Result (Result.create ~subtype ~duration_ms ~duration_api_ms ~is_error
-
~num_turns ~session_id ?total_cost_usd ?usage ?result ())
+
let result ~subtype ~duration_ms ~duration_api_ms ~is_error ~num_turns
+
~session_id ?total_cost_usd ?usage ?result ?structured_output () =
+
Result (Result.create ~subtype ~duration_ms ~duration_api_ms ~is_error
+
~num_turns ~session_id ?total_cost_usd ?usage ?result ?structured_output ())
let to_json = function
| User t -> User.to_json t
+59 -33
claudeio/lib/message.mli
···
module Assistant : sig
(** Messages from Claude assistant. *)
-
+
+
type error = [
+
| `Authentication_failed (** Authentication with Claude API failed *)
+
| `Billing_error (** Billing or account issue *)
+
| `Rate_limit (** Rate limit exceeded *)
+
| `Invalid_request (** Request was invalid *)
+
| `Server_error (** Internal server error *)
+
| `Unknown (** Unknown error type *)
+
]
+
(** The type of assistant message errors based on Python SDK error types. *)
+
+
val error_to_string : error -> string
+
(** [error_to_string err] converts an error to its string representation. *)
+
+
val error_of_string : string -> error
+
(** [error_of_string s] parses an error string. Unknown strings become [`Unknown]. *)
+
type t
(** The type of assistant messages. *)
-
-
val create : content:Content_block.t list -> model:string -> t
-
(** [create ~content ~model] creates an assistant message.
+
+
val create : content:Content_block.t list -> model:string -> ?error:error -> unit -> t
+
(** [create ~content ~model ?error ()] creates an assistant message.
@param content List of content blocks in the response
-
@param model The model identifier used for the response *)
-
+
@param model The model identifier used for the response
+
@param error Optional error that occurred during message generation *)
+
val content : t -> Content_block.t list
(** [content t] returns the content blocks of the assistant message. *)
-
+
val model : t -> string
(** [model t] returns the model identifier. *)
+
+
val error : t -> error option
+
(** [error t] returns the optional error that occurred during message generation. *)
val get_text_blocks : t -> string list
(** [get_text_blocks t] extracts all text content from the message. *)
···
type t
(** The type of result messages. *)
-
val create :
-
subtype:string ->
-
duration_ms:int ->
-
duration_api_ms:int ->
-
is_error:bool ->
-
num_turns:int ->
-
session_id:string ->
-
?total_cost_usd:float ->
-
?usage:Usage.t ->
-
?result:string ->
+
val create :
+
subtype:string ->
+
duration_ms:int ->
+
duration_api_ms:int ->
+
is_error:bool ->
+
num_turns:int ->
+
session_id:string ->
+
?total_cost_usd:float ->
+
?usage:Usage.t ->
+
?result:string ->
+
?structured_output:Ezjsonm.value ->
unit -> t
(** [create ~subtype ~duration_ms ~duration_api_ms ~is_error ~num_turns
~session_id ?total_cost_usd ?usage ?result ()] creates a result message.
···
@param session_id Unique session identifier
@param total_cost_usd Optional total cost in USD
@param usage Optional usage statistics as JSON
-
@param result Optional result string *)
+
@param result Optional result string
+
@param structured_output Optional structured JSON output from Claude *)
val subtype : t -> string
(** [subtype t] returns the subtype of the result. *)
···
val result : t -> string option
(** [result t] returns the optional result string. *)
-
+
+
val structured_output : t -> Ezjsonm.value option
+
(** [structured_output t] returns the optional structured JSON output. *)
+
val to_json : t -> Ezjsonm.value
(** [to_json t] converts the result message to its JSON representation. *)
···
(** [user_with_tool_result ~tool_use_id ~content ?is_error ()] creates a user message
containing a tool result. *)
-
val assistant : content:Content_block.t list -> model:string -> t
-
(** [assistant ~content ~model] creates an assistant message. *)
+
val assistant : content:Content_block.t list -> model:string -> ?error:Assistant.error -> unit -> t
+
(** [assistant ~content ~model ?error ()] creates an assistant message. *)
-
val assistant_text : text:string -> model:string -> t
-
(** [assistant_text ~text ~model] creates an assistant message with only text content. *)
+
val assistant_text : text:string -> model:string -> ?error:Assistant.error -> unit -> t
+
(** [assistant_text ~text ~model ?error ()] creates an assistant message with only text content. *)
val system : subtype:string -> data:System.Data.t -> t
(** [system ~subtype ~data] creates a system message. *)
···
val system_error : error:string -> t
(** [system_error ~error] creates a system error message. *)
-
val result :
-
subtype:string ->
-
duration_ms:int ->
-
duration_api_ms:int ->
-
is_error:bool ->
-
num_turns:int ->
-
session_id:string ->
-
?total_cost_usd:float ->
-
?usage:Result.Usage.t ->
-
?result:string ->
+
val result :
+
subtype:string ->
+
duration_ms:int ->
+
duration_api_ms:int ->
+
is_error:bool ->
+
num_turns:int ->
+
session_id:string ->
+
?total_cost_usd:float ->
+
?usage:Result.Usage.t ->
+
?result:string ->
+
?structured_output:Ezjsonm.value ->
unit -> t
(** [result ~subtype ~duration_ms ~duration_api_ms ~is_error ~num_turns
~session_id ?total_cost_usd ?usage ?result ()] creates a result message. *)
+50 -7
claudeio/lib/options.ml
···
let src = Logs.Src.create "claude.options" ~doc:"Claude configuration options"
module Log = (val Logs.src_log src : Logs.LOG)
+
type setting_source = User | Project | Local
+
type t = {
allowed_tools : string list;
disallowed_tools : string list;
···
append_system_prompt : string option;
permission_mode : Permissions.Mode.t option;
permission_callback : Permissions.callback option;
-
model : string option;
+
model : Model.t option;
cwd : Eio.Fs.dir_ty Eio.Path.t option;
env : (string * string) list;
continue_conversation : bool;
···
extra_args : (string * string option) list;
debug_stderr : Eio.Flow.sink_ty Eio.Flow.sink option;
hooks : Hooks.config option;
+
max_budget_usd : float option;
+
fallback_model : Model.t option;
+
setting_sources : setting_source list option;
+
max_buffer_size : int option;
+
user : string option;
+
output_format : Structured_output.t option;
}
let default = {
···
extra_args = [];
debug_stderr = None;
hooks = None;
+
max_budget_usd = None;
+
fallback_model = None;
+
setting_sources = None;
+
max_buffer_size = None;
+
user = None;
+
output_format = None;
}
let create
···
?(extra_args = [])
?debug_stderr
?hooks
+
?max_budget_usd
+
?fallback_model
+
?setting_sources
+
?max_buffer_size
+
?user
+
?output_format
() =
{ allowed_tools; disallowed_tools; max_thinking_tokens;
system_prompt; append_system_prompt; permission_mode;
permission_callback; model; cwd; env;
continue_conversation; resume; max_turns;
permission_prompt_tool_name; settings; add_dirs;
-
extra_args; debug_stderr; hooks }
+
extra_args; debug_stderr; hooks;
+
max_budget_usd; fallback_model; setting_sources;
+
max_buffer_size; user; output_format }
let allowed_tools t = t.allowed_tools
let disallowed_tools t = t.disallowed_tools
···
let extra_args t = t.extra_args
let debug_stderr t = t.debug_stderr
let hooks t = t.hooks
+
let max_budget_usd t = t.max_budget_usd
+
let fallback_model t = t.fallback_model
+
let setting_sources t = t.setting_sources
+
let max_buffer_size t = t.max_buffer_size
+
let user t = t.user
+
let output_format t = t.output_format
let with_allowed_tools tools t = { t with allowed_tools = tools }
let with_disallowed_tools tools t = { t with disallowed_tools = tools }
···
let with_permission_mode mode t = { t with permission_mode = Some mode }
let with_permission_callback callback t = { t with permission_callback = Some callback }
let with_model model t = { t with model = Some model }
+
let with_model_string model t = { t with model = Some (Model.of_string model) }
let with_cwd cwd t = { t with cwd = Some cwd }
let with_env env t = { t with env }
let with_continue_conversation continue t = { t with continue_conversation = continue }
···
let with_extra_args args t = { t with extra_args = args }
let with_debug_stderr sink t = { t with debug_stderr = Some sink }
let with_hooks hooks t = { t with hooks = Some hooks }
+
let with_max_budget_usd budget t = { t with max_budget_usd = Some budget }
+
let with_fallback_model model t = { t with fallback_model = Some model }
+
let with_fallback_model_string model t = { t with fallback_model = Some (Model.of_string model) }
+
let with_setting_sources sources t = { t with setting_sources = Some sources }
+
let with_no_settings t = { t with setting_sources = Some [] }
+
let with_max_buffer_size size t = { t with max_buffer_size = Some size }
+
let with_user user t = { t with user = Some user }
+
let with_output_format format t = { t with output_format = Some format }
let to_json t =
let fields = [] in
···
| None -> fields
in
let fields = match t.model with
-
| Some m -> ("model", `String m) :: fields
+
| Some m -> ("model", `String (Model.to_string m)) :: fields
| None -> fields
in
let fields =
···
try Some (Permissions.Mode.of_json (List.assoc "permission_mode" fields))
with Not_found -> None
in
-
let model =
-
try Some (get_string (List.assoc "model" fields))
+
let model =
+
try Some (Model.of_string (get_string (List.assoc "model" fields)))
with Not_found -> None
in
let env =
···
add_dirs = [];
extra_args = [];
debug_stderr = None;
-
hooks = None; }
+
hooks = None;
+
max_budget_usd = None;
+
fallback_model = None;
+
setting_sources = None;
+
max_buffer_size = None;
+
user = None;
+
output_format = None; }
| _ -> raise (Invalid_argument "Options.of_json: expected object")
let pp fmt t =
···
Fmt.(option string) t.system_prompt
Fmt.(option string) t.append_system_prompt
Fmt.(option Permissions.Mode.pp) t.permission_mode
-
Fmt.(option string) t.model
+
Fmt.(option Model.pp) t.model
Fmt.(list (pair string string)) t.env
let log_options t =
+177 -10
claudeio/lib/options.mli
···
(** Configuration options for Claude sessions.
-
-
This module provides configuration options for controlling Claude's
-
behavior, including tool permissions, system prompts, models, and
-
execution environment. *)
+
+
This module provides comprehensive configuration options for controlling
+
Claude's behavior, including tool permissions, system prompts, models,
+
execution environment, cost controls, and structured outputs.
+
+
{2 Overview}
+
+
Options control all aspects of Claude's behavior:
+
- {b Permissions}: Which tools Claude can use and how permission is granted
+
- {b Models}: Which AI model to use and fallback options
+
- {b Environment}: Working directory, environment variables, settings
+
- {b Cost Control}: Budget limits to prevent runaway spending
+
- {b Hooks}: Intercept and modify tool execution
+
- {b Structured Output}: JSON schema validation for responses
+
- {b Session Management}: Continue or resume conversations
+
+
{2 Builder Pattern}
+
+
Options use a functional builder pattern - each [with_*] function returns
+
a new options value with the specified field updated:
+
+
{[
+
let options = Options.default
+
|> Options.with_model "claude-sonnet-4-5"
+
|> Options.with_max_budget_usd 1.0
+
|> Options.with_permission_mode Permissions.Mode.Accept_edits
+
]}
+
+
{2 Common Configuration Scenarios}
+
+
{3 CI/CD: Isolated, Reproducible Builds}
+
+
{[
+
let ci_config = Options.default
+
|> Options.with_no_settings (* Ignore user config *)
+
|> Options.with_max_budget_usd 0.50 (* 50 cent limit *)
+
|> Options.with_permission_mode
+
Permissions.Mode.Bypass_permissions
+
|> Options.with_model "claude-haiku-4"
+
]}
+
+
{3 Production: Cost Control with Fallback}
+
+
{[
+
let prod_config = Options.default
+
|> Options.with_model "claude-sonnet-4-5"
+
|> Options.with_fallback_model "claude-haiku-4"
+
|> Options.with_max_budget_usd 10.0 (* $10 daily limit *)
+
|> Options.with_max_buffer_size 5_000_000
+
]}
+
+
{3 Development: User Settings with Overrides}
+
+
{[
+
let dev_config = Options.default
+
|> Options.with_setting_sources [User; Project]
+
|> Options.with_max_budget_usd 1.0
+
|> Options.with_permission_mode Permissions.Mode.Default
+
]}
+
+
{3 Structured Output: Type-Safe Responses}
+
+
{[
+
let schema = Ezjsonm.(`O [
+
("type", `String "object");
+
("properties", `O [
+
("count", `O [("type", `String "integer")]);
+
("has_tests", `O [("type", `String "boolean")]);
+
]);
+
])
+
let format = Structured_output.of_json_schema schema
+
+
let analysis_config = Options.default
+
|> Options.with_output_format format
+
|> Options.with_allowed_tools ["Read"; "Glob"; "Grep"]
+
]}
+
+
{2 Advanced Options}
+
+
{3 Budget Control}
+
+
Use {!with_max_budget_usd} to set hard spending limits. Claude will
+
terminate the session if the budget is exceeded, preventing runaway costs.
+
+
{3 Settings Isolation}
+
+
Use {!with_setting_sources} or {!with_no_settings} to control which
+
configuration files are loaded:
+
- [User] - ~/.claude/config
+
- [Project] - .claude/ in project root
+
- [Local] - Current directory settings
+
- [Some \[\]] (via {!with_no_settings}) - No settings, fully isolated
+
+
This is critical for reproducible builds in CI/CD environments.
+
+
{3 Model Fallback}
+
+
Use {!with_fallback_model} to specify an alternative model when the
+
primary model is unavailable or overloaded. This improves reliability. *)
open Ezjsonm
(** The log source for options operations *)
val src : Logs.Src.t
+
(** {1 Types} *)
+
+
type setting_source = User | Project | Local
+
(** Setting source determines which configuration files to load.
+
- [User]: Load user-level settings from ~/.claude/config
+
- [Project]: Load project-level settings from .claude/ in project root
+
- [Local]: Load local settings from current directory *)
+
type t
(** The type of configuration options. *)
···
?append_system_prompt:string ->
?permission_mode:Permissions.Mode.t ->
?permission_callback:Permissions.callback ->
-
?model:string ->
+
?model:Model.t ->
?cwd:Eio.Fs.dir_ty Eio.Path.t ->
?env:(string * string) list ->
?continue_conversation:bool ->
···
?extra_args:(string * string option) list ->
?debug_stderr:Eio.Flow.sink_ty Eio.Flow.sink ->
?hooks:Hooks.config ->
+
?max_budget_usd:float ->
+
?fallback_model:Model.t ->
+
?setting_sources:setting_source list ->
+
?max_buffer_size:int ->
+
?user:string ->
+
?output_format:Structured_output.t ->
unit -> t
(** [create ?allowed_tools ?disallowed_tools ?max_thinking_tokens ?system_prompt
?append_system_prompt ?permission_mode ?permission_callback ?model ?cwd ?env
?continue_conversation ?resume ?max_turns ?permission_prompt_tool_name ?settings
-
?add_dirs ?extra_args ?debug_stderr ()]
+
?add_dirs ?extra_args ?debug_stderr ?hooks ?max_budget_usd ?fallback_model
+
?setting_sources ?max_buffer_size ?user ()]
creates a new configuration.
@param allowed_tools List of explicitly allowed tool names
@param disallowed_tools List of explicitly disallowed tool names
···
@param settings Path to settings file
@param add_dirs Additional directories to allow access to
@param extra_args Additional CLI flags to pass through
-
@param debug_stderr Sink for debug output when debug-to-stderr is set *)
+
@param debug_stderr Sink for debug output when debug-to-stderr is set
+
@param hooks Hooks configuration for event interception
+
@param max_budget_usd Hard spending limit in USD (terminates on exceed)
+
@param fallback_model Automatic fallback on primary model unavailability
+
@param setting_sources Control which settings load (user/project/local)
+
@param max_buffer_size Control for stdout buffer size in bytes
+
@param user Unix user for subprocess execution
+
@param output_format Optional structured output format specification *)
(** {1 Accessors} *)
···
val permission_callback : t -> Permissions.callback option
(** [permission_callback t] returns the optional permission callback. *)
-
val model : t -> string option
+
val model : t -> Model.t option
(** [model t] returns the optional model override. *)
val cwd : t -> Eio.Fs.dir_ty Eio.Path.t option
···
val hooks : t -> Hooks.config option
(** [hooks t] returns the optional hooks configuration. *)
+
val max_budget_usd : t -> float option
+
(** [max_budget_usd t] returns the optional spending limit in USD. *)
+
+
val fallback_model : t -> Model.t option
+
(** [fallback_model t] returns the optional fallback model. *)
+
+
val setting_sources : t -> setting_source list option
+
(** [setting_sources t] returns the optional list of setting sources to load. *)
+
+
val max_buffer_size : t -> int option
+
(** [max_buffer_size t] returns the optional stdout buffer size in bytes. *)
+
+
val user : t -> string option
+
(** [user t] returns the optional Unix user for subprocess execution. *)
+
+
val output_format : t -> Structured_output.t option
+
(** [output_format t] returns the optional structured output format. *)
+
(** {1 Builders} *)
val with_allowed_tools : string list -> t -> t
···
val with_permission_callback : Permissions.callback -> t -> t
(** [with_permission_callback callback t] sets the permission callback. *)
-
val with_model : string -> t -> t
-
(** [with_model model t] sets the model override. *)
+
val with_model : Model.t -> t -> t
+
(** [with_model model t] sets the model override using a typed Model.t. *)
+
+
val with_model_string : string -> t -> t
+
(** [with_model_string model t] sets the model override from a string.
+
The string is parsed using {!Model.of_string}. *)
val with_cwd : Eio.Fs.dir_ty Eio.Path.t -> t -> t
(** [with_cwd cwd t] sets the working directory. *)
···
val with_hooks : Hooks.config -> t -> t
(** [with_hooks hooks t] sets the hooks configuration. *)
+
+
val with_max_budget_usd : float -> t -> t
+
(** [with_max_budget_usd budget t] sets the maximum spending limit in USD.
+
The session will terminate if this limit is exceeded. *)
+
+
val with_fallback_model : Model.t -> t -> t
+
(** [with_fallback_model model t] sets the fallback model using a typed Model.t. *)
+
+
val with_fallback_model_string : string -> t -> t
+
(** [with_fallback_model_string model t] sets the fallback model from a string.
+
The string is parsed using {!Model.of_string}. *)
+
+
val with_setting_sources : setting_source list -> t -> t
+
(** [with_setting_sources sources t] sets which configuration sources to load.
+
Use empty list for isolated environments (e.g., CI/CD). *)
+
+
val with_no_settings : t -> t
+
(** [with_no_settings t] disables all settings loading (user, project, local).
+
Useful for CI/CD environments where you want isolated, reproducible behavior. *)
+
+
val with_max_buffer_size : int -> t -> t
+
(** [with_max_buffer_size size t] sets the maximum stdout buffer size in bytes. *)
+
+
val with_user : string -> t -> t
+
(** [with_user user t] sets the Unix user for subprocess execution. *)
+
+
val with_output_format : Structured_output.t -> t -> t
+
(** [with_output_format format t] sets the structured output format. *)
(** {1 Serialization} *)
+91 -4
claudeio/lib/sdk_control.ml
···
server_name : string;
message : value;
}
-
+
+
type set_model = {
+
subtype : [`Set_model];
+
model : string;
+
}
+
+
type get_server_info = {
+
subtype : [`Get_server_info];
+
}
+
type t =
| Interrupt of interrupt
| Permission of permission
···
| Set_permission_mode of set_permission_mode
| Hook_callback of hook_callback
| Mcp_message of mcp_message
+
| Set_model of set_model
+
| Get_server_info of get_server_info
let interrupt () = Interrupt { subtype = `Interrupt }
···
server_name;
message;
}
-
+
+
let set_model ~model =
+
Set_model { subtype = `Set_model; model }
+
+
let get_server_info () =
+
Get_server_info { subtype = `Get_server_info }
+
let to_json = function
| Interrupt _ ->
`O [("subtype", `String "interrupt")]
···
("server_name", `String m.server_name);
("message", m.message);
]
-
+
| Set_model s ->
+
`O [
+
("subtype", `String "set_model");
+
("model", `String s.model);
+
]
+
| Get_server_info _ ->
+
`O [("subtype", `String "get_server_info")]
+
let of_json = function
| `O fields ->
let subtype = JU.assoc_string "subtype" fields in
···
server_name;
message;
}
+
| "set_model" ->
+
let model = JU.assoc_string "model" fields in
+
Set_model { subtype = `Set_model; model }
+
| "get_server_info" ->
+
Get_server_info { subtype = `Get_server_info }
| _ -> raise (Invalid_argument ("Unknown request subtype: " ^ subtype)))
| _ -> raise (Invalid_argument "Request.of_json: expected object")
···
| Mcp_message m ->
Fmt.pf fmt "@[<2>McpMessage@ { server = %S }@]"
m.server_name
+
| Set_model s ->
+
Fmt.pf fmt "@[<2>SetModel@ { model = %S }@]" s.model
+
| Get_server_info _ ->
+
Fmt.pf fmt "@[<2>GetServerInfo@]"
end
module Response = struct
···
Log.debug (fun m -> m "SDK control request: %a" Request.pp req)
let log_response resp =
-
Log.debug (fun m -> m "SDK control response: %a" Response.pp resp)
+
Log.debug (fun m -> m "SDK control response: %a" Response.pp resp)
+
+
(** Server information *)
+
module Server_info = struct
+
type t = {
+
version : string;
+
capabilities : string list;
+
commands : string list;
+
output_styles : string list;
+
}
+
+
let create ~version ~capabilities ~commands ~output_styles =
+
{ version; capabilities; commands; output_styles }
+
+
let version t = t.version
+
let capabilities t = t.capabilities
+
let commands t = t.commands
+
let output_styles t = t.output_styles
+
+
let of_json = function
+
| `O fields ->
+
let version = JU.assoc_string "version" fields in
+
let capabilities =
+
match List.assoc_opt "capabilities" fields with
+
| Some (`A lst) -> List.map Ezjsonm.get_string lst
+
| _ -> []
+
in
+
let commands =
+
match List.assoc_opt "commands" fields with
+
| Some (`A lst) -> List.map Ezjsonm.get_string lst
+
| _ -> []
+
in
+
let output_styles =
+
match List.assoc_opt "outputStyles" fields with
+
| Some (`A lst) -> List.map Ezjsonm.get_string lst
+
| _ -> []
+
in
+
{ version; capabilities; commands; output_styles }
+
| _ -> raise (Invalid_argument "Server_info.of_json: expected object")
+
+
let to_json t =
+
`O [
+
("version", `String t.version);
+
("capabilities", `A (List.map (fun s -> `String s) t.capabilities));
+
("commands", `A (List.map (fun s -> `String s) t.commands));
+
("outputStyles", `A (List.map (fun s -> `String s) t.output_styles));
+
]
+
+
let pp fmt t =
+
Fmt.pf fmt "@[<2>ServerInfo@ { version = %S;@ capabilities = [%a];@ commands = [%a];@ output_styles = [%a] }@]"
+
t.version
+
Fmt.(list ~sep:(any ", ") (quote string)) t.capabilities
+
Fmt.(list ~sep:(any ", ") (quote string)) t.commands
+
Fmt.(list ~sep:(any ", ") (quote string)) t.output_styles
+
end
+149 -6
claudeio/lib/sdk_control.mli
···
(** SDK Control Protocol for Claude.
-
-
This module defines the typed SDK control protocol for communication
-
between the SDK and Claude, including request and response types. *)
+
+
This module defines the typed SDK control protocol for bidirectional
+
communication between the SDK and the Claude CLI. It handles:
+
+
- Permission requests (tool usage authorization)
+
- Hook callbacks (intercepting and modifying tool execution)
+
- Dynamic control (changing settings mid-conversation)
+
- Server introspection (querying capabilities)
+
+
{2 Protocol Overview}
+
+
The SDK control protocol is a JSON-based request/response protocol that
+
runs alongside the main message stream. It enables:
+
+
1. {b Callbacks}: Claude asks the SDK for permission or hook execution
+
2. {b Control}: SDK changes Claude's behavior dynamically
+
3. {b Introspection}: SDK queries server metadata
+
+
{2 Request/Response Flow}
+
+
{v
+
SDK Claude CLI
+
| |
+
|-- Initialize (with hooks) --> |
+
|<-- Permission Request --------| (for tool usage)
+
|-- Allow/Deny Response ------> |
+
| |
+
|<-- Hook Callback -------------| (pre/post tool)
+
|-- Hook Result -------------> |
+
| |
+
|-- Set Model ---------------> | (dynamic control)
+
|<-- Success Response ----------|
+
| |
+
|-- Get Server Info ----------> |
+
|<-- Server Info Response ------|
+
v}
+
+
{2 Usage}
+
+
Most users won't interact with this module directly. The {!Client} module
+
handles the protocol automatically. However, this module is exposed for:
+
+
- Understanding the control protocol
+
- Implementing custom control logic
+
- Debugging control message flow
+
- Advanced SDK extensions
+
+
{2 Dynamic Control Examples}
+
+
See {!Client.set_permission_mode}, {!Client.set_model}, and
+
{!Client.get_server_info} for high-level APIs that use this protocol. *)
open Ezjsonm
···
message : value;
}
(** MCP server message request. *)
-
+
+
type set_model = {
+
subtype : [`Set_model];
+
model : string;
+
}
+
(** Request to change the AI model. *)
+
+
type get_server_info = {
+
subtype : [`Get_server_info];
+
}
+
(** Request to get server information. *)
+
type t =
| Interrupt of interrupt
| Permission of permission
···
| Set_permission_mode of set_permission_mode
| Hook_callback of hook_callback
| Mcp_message of mcp_message
+
| Set_model of set_model
+
| Get_server_info of get_server_info
(** The type of SDK control requests. *)
val interrupt : unit -> t
···
val mcp_message : server_name:string -> message:value -> t
(** [mcp_message ~server_name ~message] creates an MCP message request. *)
-
+
+
val set_model : model:string -> t
+
(** [set_model ~model] creates a model change request. *)
+
+
val get_server_info : unit -> t
+
(** [get_server_info ()] creates a server info request. *)
+
val to_json : t -> value
(** [to_json t] converts a request to JSON. *)
···
(** [log_request req] logs an SDK control request. *)
val log_response : Response.t -> unit
-
(** [log_response resp] logs an SDK control response. *)
+
(** [log_response resp] logs an SDK control response. *)
+
+
(** {1 Server Information}
+
+
Server information provides metadata about the Claude CLI server,
+
including version, capabilities, available commands, and output styles.
+
+
{2 Use Cases}
+
+
- Feature detection: Check if specific capabilities are available
+
- Version compatibility: Ensure minimum version requirements
+
- Debugging: Log server information for troubleshooting
+
- Dynamic adaptation: Adjust SDK behavior based on capabilities
+
+
{2 Example}
+
+
{[
+
let info = Client.get_server_info client in
+
Printf.printf "Claude CLI version: %s\n"
+
(Server_info.version info);
+
+
if List.mem "structured-output" (Server_info.capabilities info) then
+
Printf.printf "Structured output is supported\n"
+
else
+
Printf.printf "Structured output not available\n";
+
]} *)
+
+
module Server_info : sig
+
(** Server information and capabilities. *)
+
+
type t = {
+
version : string;
+
(** Server version string (e.g., "2.0.0") *)
+
+
capabilities : string list;
+
(** Available server capabilities (e.g., "hooks", "structured-output") *)
+
+
commands : string list;
+
(** Available CLI commands *)
+
+
output_styles : string list;
+
(** Supported output formats (e.g., "json", "stream-json") *)
+
}
+
(** Server metadata and capabilities.
+
+
This information is useful for feature detection and debugging. *)
+
+
val create :
+
version:string ->
+
capabilities:string list ->
+
commands:string list ->
+
output_styles:string list ->
+
t
+
(** [create ~version ~capabilities ~commands ~output_styles] creates server info. *)
+
+
val version : t -> string
+
(** [version t] returns the server version. *)
+
+
val capabilities : t -> string list
+
(** [capabilities t] returns the server capabilities. *)
+
+
val commands : t -> string list
+
(** [commands t] returns available commands. *)
+
+
val output_styles : t -> string list
+
(** [output_styles t] returns available output styles. *)
+
+
val of_json : value -> t
+
(** [of_json json] parses server info from JSON.
+
@raise Invalid_argument if the JSON is not valid server info. *)
+
+
val to_json : t -> value
+
(** [to_json t] converts server info to JSON. *)
+
+
val pp : Format.formatter -> t -> unit
+
(** [pp fmt t] pretty-prints the server info. *)
+
end
+45 -9
claudeio/lib/transport.ml
···
sw : Switch.t;
}
+
let setting_source_to_string = function
+
| Options.User -> "user"
+
| Options.Project -> "project"
+
| Options.Local -> "local"
+
let build_command ~claude_path ~options =
let cmd = [claude_path; "--output-format"; "stream-json"; "--verbose"] in
-
+
let cmd = match Options.system_prompt options with
| Some prompt -> cmd @ ["--system-prompt"; prompt]
| None -> cmd
in
-
+
let cmd = match Options.append_system_prompt options with
| Some prompt -> cmd @ ["--append-system-prompt"; prompt]
| None -> cmd
in
-
+
let cmd = match Options.allowed_tools options with
| [] -> cmd
| tools -> cmd @ ["--allowedTools"; String.concat "," tools]
in
-
+
let cmd = match Options.disallowed_tools options with
| [] -> cmd
| tools -> cmd @ ["--disallowedTools"; String.concat "," tools]
in
-
+
let cmd = match Options.model options with
-
| Some model -> cmd @ ["--model"; model]
+
| Some model -> cmd @ ["--model"; Model.to_string model]
| None -> cmd
in
-
+
let cmd = match Options.permission_mode options with
| Some mode ->
let mode_str = Permissions.Mode.to_string mode in
···
| None -> cmd
in
+
(* Advanced configuration options *)
+
let cmd = match Options.max_budget_usd options with
+
| Some budget -> cmd @ ["--max-budget-usd"; Float.to_string budget]
+
| None -> cmd
+
in
+
+
let cmd = match Options.fallback_model options with
+
| Some model -> cmd @ ["--fallback-model"; Model.to_string model]
+
| None -> cmd
+
in
+
+
let cmd = match Options.setting_sources options with
+
| Some sources ->
+
let sources_str = String.concat "," (List.map setting_source_to_string sources) in
+
cmd @ ["--setting-sources"; sources_str]
+
| None -> cmd
+
in
+
+
(* Add JSON Schema if specified *)
+
let cmd = match Options.output_format options with
+
| Some format ->
+
let schema = Structured_output.json_schema format in
+
let schema_str = Ezjsonm.value_to_string schema in
+
cmd @ ["--json-schema"; schema_str]
+
| None -> cmd
+
in
+
(* Use streaming input mode *)
cmd @ ["--input-format"; "stream-json"]
···
let stdin = (stdin_w :> Eio.Flow.sink_ty r) in
let stdin_close = (stdin_w :> [`Close | `Flow] r) in
-
let stdout = Eio.Buf_read.of_flow ~max_size:1_000_000 (stdout_r :> Eio.Flow.source_ty r) in
-
+
let max_size = match Options.max_buffer_size options with
+
| Some size -> size
+
| None -> 1_000_000 (* Default 1MB *)
+
in
+
let stdout = Eio.Buf_read.of_flow ~max_size (stdout_r :> Eio.Flow.source_ty r) in
+
{ process = P process; stdin; stdin_close; stdout; sw }
let send t json =
+1 -1
claudeio/test/camel_jokes.ml
···
let run_claude ~sw ~env name prompt =
Log.info (fun m -> m "🐪 Starting %s..." name);
-
let options = Claude.Options.create ~model:"sonnet" ~allowed_tools:[] () in
+
let options = Claude.Options.create ~model:(Claude.Model.of_string "sonnet") ~allowed_tools:[] () in
let client = Claude.Client.create ~options ~sw
~process_mgr:env#process_mgr
+1 -1
claudeio/test/discovery_demo.ml
···
Log.app (fun m -> m "This will discover what permissions Claude needs.\n");
(* Create client with discovery mode *)
-
let options = Claude.Options.create ~model:"sonnet" () in
+
let options = Claude.Options.create ~model:(Claude.Model.of_string "sonnet") () in
let client = Claude.Client.discover_permissions
(Claude.Client.create ~options ~sw ~process_mgr:env#process_mgr ()) in
+27 -1
claudeio/test/dune
···
(public_name hooks_example)
(name hooks_example)
(modules hooks_example)
-
(libraries claude eio_main cmdliner logs logs.fmt fmt.tty fmt.cli logs.cli))
+
(libraries claude eio_main cmdliner logs logs.fmt fmt.tty fmt.cli logs.cli))
+
+
(executable
+
(public_name dynamic_control_demo)
+
(name dynamic_control_demo)
+
(modules dynamic_control_demo)
+
(libraries claude eio_main cmdliner logs logs.fmt fmt.tty fmt.cli logs.cli))
+
+
(executable
+
(public_name advanced_config_demo)
+
(name advanced_config_demo)
+
(modules advanced_config_demo)
+
(libraries claude eio_main logs logs.fmt fmt.tty))
+
+
(executable
+
(public_name structured_output_demo)
+
(name structured_output_demo)
+
(modules structured_output_demo)
+
(flags (:standard -w -33))
+
(libraries claude eio_main logs logs.fmt fmt.tty))
+
+
(executable
+
(public_name structured_output_simple)
+
(name structured_output_simple)
+
(modules structured_output_simple)
+
(flags (:standard -w -33))
+
(libraries claude eio_main logs logs.fmt fmt.tty))
+1 -1
claudeio/test/hooks_example.ml
···
in
let options = Claude.Options.create
-
~model:"sonnet"
+
~model:(Claude.Model.of_string "sonnet")
~hooks
() in
+1 -1
claudeio/test/permission_demo.ml
···
(* DON'T specify allowed_tools - let the permission callback handle everything.
The Default permission mode with a callback should send requests for all tools. *)
let options = Claude.Options.create
-
~model:"sonnet"
+
~model:(Claude.Model.of_string "sonnet")
~permission_mode:Claude.Permissions.Mode.Default
~permission_callback:interactive_permission_callback
() in
+1 -1
claudeio/test/simple_permission_test.ml
···
(* Create options with permission callback *)
let options = Claude.Options.create
-
~model:"sonnet"
+
~model:(Claude.Model.of_string "sonnet")
~permission_callback:auto_allow_callback
() in
+1 -1
claudeio/test/test_permissions.ml
···
(* Create options with custom permission callback *)
let options = Claude.Options.create
-
~model:"sonnet"
+
~model:(Claude.Model.of_string "sonnet")
~permission_callback:auto_allow_callback
() in