Codex Handbook
tui/src/chatwidget/protocol_requests.rs 168 lines
//! App-server request and notification dispatch for `ChatWidget`.//!//! This module translates protocol requests into the focused chat-widget flows//! that render approvals, permissions, tool input, and guardian reviews.use super::*;impl ChatWidget {    pub(crate) fn handle_server_request(        &mut self,        request: ServerRequest,        replay_kind: Option<ReplayKind>,    ) {        let id = request.id().to_string();        match request {            ServerRequest::CommandExecutionRequestApproval { params, .. } => {                let fallback_cwd = self.config.cwd.clone();                self.on_exec_approval_request(                    id,                    exec_approval_request_from_params(params, &fallback_cwd),                );            }            ServerRequest::FileChangeRequestApproval { params, .. } => {                self.on_apply_patch_approval_request(                    id,                    patch_approval_request_from_params(params),                );            }            ServerRequest::McpServerElicitationRequest { request_id, params } => {                self.on_elicitation_request(request_id, params);            }            ServerRequest::PermissionsRequestApproval { params, .. } => {                // TODO(anp): Remove this native-path localization error path once core permission                // paths remain PathUri after crossing the app-server boundary.                match request_permissions_from_params(params) {                    Ok(event) => self.on_request_permissions(event),                    Err(err) => {                        self.add_error_message(format!(                            "failed to localize requested filesystem paths: {err}"                        ));                    }                }            }            ServerRequest::ToolRequestUserInput { params, .. } => {                self.on_request_user_input(params);            }            ServerRequest::DynamicToolCall { .. }            | ServerRequest::AttestationGenerate { .. }            | ServerRequest::ChatgptAuthTokensRefresh { .. }            | ServerRequest::ApplyPatchApproval { .. }            | ServerRequest::ExecCommandApproval { .. } => {                if replay_kind.is_none() {                    self.add_error_message(TUI_STUB_MESSAGE.to_string());                }            }        }    }    pub(crate) fn handle_skills_list_response(&mut self, response: SkillsListResponse) {        self.on_list_skills(response);    }    pub(super) fn on_patch_apply_output_delta(&mut self, _item_id: String, _delta: String) {}    pub(super) fn on_guardian_review_notification(        &mut self,        id: String,        turn_id: String,        started_at_ms: i64,        review: codex_app_server_protocol::GuardianApprovalReview,        completion: Option<(i64, codex_app_server_protocol::AutoReviewDecisionSource)>,        action: GuardianApprovalReviewAction,    ) {        // TODO(anp): Remove this native-path localization error path once core permission paths        // remain PathUri after crossing the app-server boundary.        let action = match action.try_into() {            Ok(action) => action,            Err(err) => {                self.add_error_message(format!(                    "failed to localize guardian filesystem paths: {err}"                ));                return;            }        };        let (completed_at_ms, decision_source) = match completion {            Some((completed_at_ms, decision_source)) => {                (Some(completed_at_ms), Some(decision_source))            }            None => (None, None),        };        self.on_guardian_assessment(GuardianAssessmentEvent {            id,            target_item_id: None,            turn_id,            started_at_ms,            completed_at_ms,            status: match review.status {                codex_app_server_protocol::GuardianApprovalReviewStatus::InProgress => {                    GuardianAssessmentStatus::InProgress                }                codex_app_server_protocol::GuardianApprovalReviewStatus::Approved => {                    GuardianAssessmentStatus::Approved                }                codex_app_server_protocol::GuardianApprovalReviewStatus::Denied => {                    GuardianAssessmentStatus::Denied                }                codex_app_server_protocol::GuardianApprovalReviewStatus::TimedOut => {                    GuardianAssessmentStatus::TimedOut                }                codex_app_server_protocol::GuardianApprovalReviewStatus::Aborted => {                    GuardianAssessmentStatus::Aborted                }            },            risk_level: review.risk_level.map(|risk_level| match risk_level {                codex_app_server_protocol::GuardianRiskLevel::Low => {                    codex_protocol::approvals::GuardianRiskLevel::Low                }                codex_app_server_protocol::GuardianRiskLevel::Medium => {                    codex_protocol::approvals::GuardianRiskLevel::Medium                }                codex_app_server_protocol::GuardianRiskLevel::High => {                    codex_protocol::approvals::GuardianRiskLevel::High                }                codex_app_server_protocol::GuardianRiskLevel::Critical => {                    codex_protocol::approvals::GuardianRiskLevel::Critical                }            }),            user_authorization: review.user_authorization.map(|user_authorization| {                match user_authorization {                    codex_app_server_protocol::GuardianUserAuthorization::Unknown => {                        codex_protocol::approvals::GuardianUserAuthorization::Unknown                    }                    codex_app_server_protocol::GuardianUserAuthorization::Low => {                        codex_protocol::approvals::GuardianUserAuthorization::Low                    }                    codex_app_server_protocol::GuardianUserAuthorization::Medium => {                        codex_protocol::approvals::GuardianUserAuthorization::Medium                    }                    codex_app_server_protocol::GuardianUserAuthorization::High => {                        codex_protocol::approvals::GuardianUserAuthorization::High                    }                }            }),            rationale: review.rationale,            decision_source: decision_source.map(|source| match source {                codex_app_server_protocol::AutoReviewDecisionSource::Agent => {                    GuardianAssessmentDecisionSource::Agent                }            }),            action,        });    }    pub(super) fn on_shutdown_complete(&mut self) {        self.request_immediate_exit();    }    pub(super) fn on_turn_diff(&mut self, unified_diff: String) {        debug!("TurnDiffEvent: {unified_diff}");        self.refresh_status_line();    }    pub(super) fn on_deprecation_notice(&mut self, summary: String, details: Option<String>) {        self.add_to_history(history_cell::new_deprecation_notice(summary, details));        self.request_redraw();    }}