From 214ddcd7af89f9c55c7dc09556ffe3842cec3473 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 23 Feb 2026 16:01:25 +0000 Subject: [PATCH] story-kit: merge 62_story_allow_frontend_ui_to_accept_permissions_requests --- server/src/llm/providers/claude_code.rs | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/server/src/llm/providers/claude_code.rs b/server/src/llm/providers/claude_code.rs index 0b63fbc..63a210b 100644 --- a/server/src/llm/providers/claude_code.rs +++ b/server/src/llm/providers/claude_code.rs @@ -322,6 +322,53 @@ fn run_pty_session( let _ = writeln!(pty_writer, "{}", response); } } + // Claude Code is requesting user approval before executing a tool. + // Forward the request to the async context via permission_tx and + // block until the user responds (or a 5-minute timeout elapses). + "permission_request" => { + let request_id = json + .get("id") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + let tool_name = json + .get("tool_name") + .and_then(|v| v.as_str()) + .unwrap_or("unknown") + .to_string(); + let tool_input = json + .get("input") + .cloned() + .unwrap_or(serde_json::Value::Object(serde_json::Map::new())); + + if let Some(ref ptx) = permission_tx { + let (resp_tx, resp_rx) = std::sync::mpsc::sync_channel(1); + let _ = ptx.send(PermissionReqMsg { + request_id: request_id.clone(), + tool_name, + tool_input, + response_tx: resp_tx, + }); + // Block until the user responds or a 5-minute timeout elapses. + let approved = resp_rx + .recv_timeout(std::time::Duration::from_secs(300)) + .unwrap_or(false); + let response = serde_json::json!({ + "type": "permission_response", + "id": request_id, + "approved": approved, + }); + let _ = writeln!(pty_writer, "{}", response); + } else { + // No handler configured — deny by default. + let response = serde_json::json!({ + "type": "permission_response", + "id": request_id, + "approved": false, + }); + let _ = writeln!(pty_writer, "{}", response); + } + } _ => {} } }