use std::sync::Arc; use std::sync::Weak; use codex_code_mode_protocol::CellId; use codex_code_mode_protocol::CodeModeNestedToolCall; use codex_code_mode_protocol::CodeModeSessionDelegate; use codex_code_mode_protocol::NotificationFuture; use codex_code_mode_protocol::ToolInvocationFuture; use codex_code_mode_protocol::grpc as proto; use tokio::sync::oneshot; use tokio_util::sync::CancellationToken; use uuid::Uuid; use super::session::GrpcSession; pub(super) struct GrpcDelegate { session: Weak, } impl GrpcDelegate { pub(super) fn new(session: Weak) -> Self { Self { session } } } impl CodeModeSessionDelegate for GrpcDelegate { fn invoke_tool<'a>( &'a self, invocation: CodeModeNestedToolCall, cancellation: CancellationToken, ) -> ToolInvocationFuture<'a> { Box::pin(async move { let session = self .session .upgrade() .ok_or_else(|| "code-mode session is closed".to_string())?; let _permit = session.delegate_permit()?; let execution_id = session .execution_id(invocation.cell_id.as_str(), &cancellation) .await?; let input_json = invocation .input .as_ref() .map(serde_json::to_vec) .transpose() .map_err(|error| format!("failed to encode code-mode tool input: {error}"))?; let invocation_id = Uuid::new_v4(); let (response, receiver) = oneshot::channel(); session .dispatch_tool( invocation, execution_id, invocation_id, input_json, response, &cancellation, ) .await?; let mut pending = PendingToolCall { session: Arc::clone(&session), id: Some(invocation_id), }; let result = tokio::select! { biased; result = receiver => result .map_err(|_| "code-mode client closed before returning tool output".to_string())?, _ = cancellation.cancelled() => { Err("code mode delegate request cancelled".to_string()) } _ = session.closed.cancelled() => { Err("code-mode session closed before returning tool output".to_string()) } }; if result.is_ok() { pending.id = None; } result }) } fn notify<'a>( &'a self, call_id: String, cell_id: CellId, text: String, cancellation: CancellationToken, ) -> NotificationFuture<'a> { Box::pin(async move { let session = self .session .upgrade() .ok_or_else(|| "code-mode session is closed".to_string())?; let _permit = session.delegate_permit()?; let execution_id = session .execution_id(cell_id.as_str(), &cancellation) .await?; let notification_id = Uuid::new_v4(); session .send_event( proto::session_event::Event::Notification(proto::Notification { notification_id: notification_id.to_string(), execution_id, cell_id: cell_id.to_string(), call_id, text, }), &cancellation, ) .await }) } fn cell_closed(&self, cell_id: &CellId) { if let Some(session) = self.session.upgrade() { session.close_cell(cell_id.as_str()); } } } struct PendingToolCall { session: Arc, id: Option, } impl Drop for PendingToolCall { fn drop(&mut self) { if let Some(id) = self.id.take() { self.session.cancel_invocation(id); } } }