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

claude

Changed files
+63 -14
claudeio
+39 -10
claudeio/lib/client.ml
···
| "can_use_tool" ->
let tool_name = Json_utils.find_string data ["request"; "tool_name"] in
let input = find data ["request"; "input"] in
-
Log.info (fun m -> m "Permission request for tool '%s' with input: %s"
+
Log.info (fun m -> m "Permission request for tool '%s' with input: %s"
tool_name (value_to_string input));
-
let suggestions =
+
let suggestions =
try
let sugg_json = find data ["request"; "permission_suggestions"] in
match sugg_json with
-
| `A _ ->
+
| `A _ ->
(* TODO: Parse permission suggestions *)
[]
| _ -> []
with Not_found -> []
in
let context = Permissions.Context.create ~suggestions () in
-
+
Log.info (fun m -> m "Invoking permission callback for tool: %s" tool_name);
let result = match t.permission_callback with
-
| Some callback ->
+
| Some callback ->
Log.info (fun m -> m "Using custom permission callback");
callback ~tool_name ~input ~context
-
| None ->
+
| None ->
Log.info (fun m -> m "Using default allow callback");
Permissions.default_allow_callback ~tool_name ~input ~context
in
-
Log.info (fun m -> m "Permission callback returned: %s"
-
(match result with
+
Log.info (fun m -> m "Permission callback returned: %s"
+
(match result with
| Permissions.Result.Allow _ -> "ALLOW"
| Permissions.Result.Deny _ -> "DENY"));
-
+
+
(* Convert permission result to CLI format: {"behavior": "allow", "updatedInput": ...} or {"behavior": "deny", "message": ...} *)
+
let response_data = match result with
+
| Permissions.Result.Allow { updated_input; updated_permissions = _ } ->
+
(* updatedInput is required when allowing - use original input if not modified *)
+
let updated_input = match updated_input with
+
| Some inp -> inp
+
| None -> input (* Use original input *)
+
in
+
dict [
+
("behavior", string "allow");
+
("updatedInput", updated_input);
+
]
+
| Permissions.Result.Deny { message; interrupt = _ } ->
+
dict [
+
("behavior", string "deny");
+
("message", string message);
+
]
+
in
+
let response = dict [
"type", string "control_response";
"response", dict [
"subtype", string "success";
"request_id", string (Control.request_id control_msg);
-
"response", Permissions.Result.to_json result
+
"response", response_data
]
] in
+
Log.info (fun m -> m "Sending control response: %s" (value_to_string response));
Transport.send t.transport response
| subtype ->
···
loop
let create ?(options = Options.default) ~sw ~process_mgr () =
+
(* Automatically enable permission prompt tool when callback is configured
+
(matching Python SDK behavior in client.py:104-121) *)
+
let options =
+
match Options.permission_callback options with
+
| Some _ when Options.permission_prompt_tool_name options = None ->
+
(* Set permission_prompt_tool_name to "stdio" to enable control protocol *)
+
Options.with_permission_prompt_tool_name "stdio" options
+
| _ -> options
+
in
let transport = Transport.create ~sw ~process_mgr ~options () in
{
transport;
+7 -2
claudeio/lib/transport.ml
···
in
let cmd = match Options.permission_mode options with
-
| Some mode ->
+
| Some mode ->
let mode_str = Permissions.Mode.to_string mode in
cmd @ ["--permission-mode"; mode_str]
| None -> cmd
in
-
+
+
let cmd = match Options.permission_prompt_tool_name options with
+
| Some tool_name -> cmd @ ["--permission-prompt-tool"; tool_name]
+
| None -> cmd
+
in
+
(* Use streaming input mode *)
cmd @ ["--input-format"; "stream-json"]
+12
claudeio/test/dune
···
(public_name simulated_permissions)
(name simulated_permissions)
(modules simulated_permissions)
+
(libraries claude eio_main cmdliner logs logs.fmt fmt.tty fmt.cli logs.cli))
+
+
(executable
+
(public_name test_permissions)
+
(name test_permissions)
+
(modules test_permissions)
+
(libraries claude eio_main cmdliner logs logs.fmt fmt.tty fmt.cli logs.cli))
+
+
(executable
+
(public_name simple_permission_test)
+
(name simple_permission_test)
+
(modules simple_permission_test)
(libraries claude eio_main cmdliner logs logs.fmt fmt.tty fmt.cli logs.cli))
+5 -2
claudeio/test/permission_demo.ml
···
Log.info (fun m -> m "Returning allow result for %s" tool_name);
Claude.Permissions.Result.allow ()
end else begin
-
(* Ask user *)
+
(* Ask user - read from /dev/tty since stdin is connected to Claude process *)
Printf.printf "Allow? [y/N/always]: %!";
-
match read_line () |> String.lowercase_ascii with
+
let tty = open_in "/dev/tty" in
+
let response = input_line tty |> String.lowercase_ascii in
+
close_in tty;
+
match response with
| "y" | "yes" ->
Log.app (fun m -> m "→ Allowed (this time only)");
Log.info (fun m -> m "User approved %s for this request only" tool_name);