fix: resolve merge conflict in claude_code.rs
Keep master's quiet system/rate_limit_event handlers while preserving the story-62 permission_request handler (the core feature). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ use poem::handler;
|
||||
use poem::web::Data;
|
||||
use poem::web::websocket::{Message as WsMessage, WebSocket};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -17,12 +18,17 @@ use tokio::sync::mpsc;
|
||||
///
|
||||
/// - `chat` starts a streaming chat session.
|
||||
/// - `cancel` stops the active session.
|
||||
/// - `permission_response` approves or denies a pending permission request.
|
||||
enum WsRequest {
|
||||
Chat {
|
||||
messages: Vec<Message>,
|
||||
config: chat::ProviderConfig,
|
||||
},
|
||||
Cancel,
|
||||
PermissionResponse {
|
||||
request_id: String,
|
||||
approved: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -63,6 +69,12 @@ enum WsResponse {
|
||||
qa: Vec<crate::http::workflow::UpcomingStory>,
|
||||
merge: Vec<crate::http::workflow::UpcomingStory>,
|
||||
},
|
||||
/// Claude Code is requesting user approval before executing a tool.
|
||||
PermissionRequest {
|
||||
request_id: String,
|
||||
tool_name: String,
|
||||
tool_input: serde_json::Value,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<WatcherEvent> for WsResponse {
|
||||
@@ -139,52 +151,105 @@ pub async fn ws_handler(ws: WebSocket, ctx: Data<&Arc<AppContext>>) -> impl poem
|
||||
}
|
||||
});
|
||||
|
||||
while let Some(Ok(msg)) = stream.next().await {
|
||||
if let WsMessage::Text(text) = msg {
|
||||
let parsed: Result<WsRequest, _> = serde_json::from_str(&text);
|
||||
match parsed {
|
||||
Ok(WsRequest::Chat { messages, config }) => {
|
||||
let tx_updates = tx.clone();
|
||||
let tx_tokens = tx.clone();
|
||||
let ctx_clone = ctx.clone();
|
||||
// Channel for permission requests flowing from the PTY thread to this handler.
|
||||
let (perm_req_tx, mut perm_req_rx) =
|
||||
mpsc::unbounded_channel::<crate::llm::providers::claude_code::PermissionReqMsg>();
|
||||
// Map of pending permission request_id → one-shot responder.
|
||||
let mut pending_perms: HashMap<String, std::sync::mpsc::SyncSender<bool>> = HashMap::new();
|
||||
|
||||
let result = chat::chat(
|
||||
messages,
|
||||
config,
|
||||
&ctx_clone.state,
|
||||
ctx_clone.store.as_ref(),
|
||||
|history| {
|
||||
let _ = tx_updates.send(WsResponse::Update {
|
||||
messages: history.to_vec(),
|
||||
});
|
||||
},
|
||||
|token| {
|
||||
let _ = tx_tokens.send(WsResponse::Token {
|
||||
content: token.to_string(),
|
||||
});
|
||||
},
|
||||
)
|
||||
.await;
|
||||
loop {
|
||||
// Outer loop: wait for the next WebSocket message.
|
||||
let Some(Ok(WsMessage::Text(text))) = stream.next().await else {
|
||||
break;
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(chat_result) => {
|
||||
if let Some(sid) = chat_result.session_id {
|
||||
let _ = tx.send(WsResponse::SessionId { session_id: sid });
|
||||
let parsed: Result<WsRequest, _> = serde_json::from_str(&text);
|
||||
match parsed {
|
||||
Ok(WsRequest::Chat { messages, config }) => {
|
||||
let tx_updates = tx.clone();
|
||||
let tx_tokens = tx.clone();
|
||||
let ctx_clone = ctx.clone();
|
||||
let perm_tx = perm_req_tx.clone();
|
||||
|
||||
// Build the chat future without driving it yet so we can
|
||||
// interleave it with permission-request forwarding.
|
||||
let chat_fut = chat::chat(
|
||||
messages,
|
||||
config,
|
||||
&ctx_clone.state,
|
||||
ctx_clone.store.as_ref(),
|
||||
move |history| {
|
||||
let _ = tx_updates.send(WsResponse::Update {
|
||||
messages: history.to_vec(),
|
||||
});
|
||||
},
|
||||
move |token| {
|
||||
let _ = tx_tokens.send(WsResponse::Token {
|
||||
content: token.to_string(),
|
||||
});
|
||||
},
|
||||
Some(perm_tx),
|
||||
);
|
||||
tokio::pin!(chat_fut);
|
||||
|
||||
// Inner loop: drive the chat while concurrently handling
|
||||
// permission requests and WebSocket messages.
|
||||
let chat_result = loop {
|
||||
tokio::select! {
|
||||
result = &mut chat_fut => break result,
|
||||
|
||||
// Forward permission requests from PTY to the client.
|
||||
Some(perm_req) = perm_req_rx.recv() => {
|
||||
let _ = tx.send(WsResponse::PermissionRequest {
|
||||
request_id: perm_req.request_id.clone(),
|
||||
tool_name: perm_req.tool_name.clone(),
|
||||
tool_input: perm_req.tool_input.clone(),
|
||||
});
|
||||
pending_perms.insert(
|
||||
perm_req.request_id,
|
||||
perm_req.response_tx,
|
||||
);
|
||||
}
|
||||
|
||||
// Handle WebSocket messages during an active chat
|
||||
// (permission responses and cancellations).
|
||||
Some(Ok(WsMessage::Text(inner_text))) = stream.next() => {
|
||||
match serde_json::from_str::<WsRequest>(&inner_text) {
|
||||
Ok(WsRequest::PermissionResponse { request_id, approved }) => {
|
||||
if let Some(resp_tx) = pending_perms.remove(&request_id) {
|
||||
let _ = resp_tx.send(approved);
|
||||
}
|
||||
}
|
||||
Ok(WsRequest::Cancel) => {
|
||||
let _ = chat::cancel_chat(&ctx.state);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = tx.send(WsResponse::Error { message: err });
|
||||
}
|
||||
};
|
||||
|
||||
match chat_result {
|
||||
Ok(chat_result) => {
|
||||
if let Some(sid) = chat_result.session_id {
|
||||
let _ = tx.send(WsResponse::SessionId { session_id: sid });
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = tx.send(WsResponse::Error { message: err });
|
||||
}
|
||||
}
|
||||
Ok(WsRequest::Cancel) => {
|
||||
let _ = chat::cancel_chat(&ctx.state);
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = tx.send(WsResponse::Error {
|
||||
message: format!("Invalid request: {err}"),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(WsRequest::Cancel) => {
|
||||
let _ = chat::cancel_chat(&ctx.state);
|
||||
}
|
||||
Ok(WsRequest::PermissionResponse { .. }) => {
|
||||
// Permission responses outside an active chat are ignored.
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = tx.send(WsResponse::Error {
|
||||
message: format!("Invalid request: {err}"),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user