ai-agent 0.88.0

Idiomatic agent sdk inspired by the claude code source leak
Documentation
// Source: ~/claudecode/openclaudecode/src/utils/permissions/permissionExplainer.ts
#![allow(dead_code)]

//! Permission explainer — generates human-readable explanations for permission requests.

use serde::{Deserialize, Serialize};

/// Risk level for permission explanations.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum RiskLevel {
    Low,
    Medium,
    High,
}

/// Map risk levels to numeric values for analytics.
pub fn risk_level_to_numeric(level: &RiskLevel) -> u8 {
    match level {
        RiskLevel::Low => 1,
        RiskLevel::Medium => 2,
        RiskLevel::High => 3,
    }
}

/// Error type codes for analytics.
pub const ERROR_TYPE_PARSE: u8 = 1;
pub const ERROR_TYPE_NETWORK: u8 = 2;
pub const ERROR_TYPE_UNKNOWN: u8 = 3;

/// A permission explanation with risk assessment.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionExplanation {
    pub risk_level: RiskLevel,
    pub explanation: String,
    pub reasoning: String,
    pub risk: String,
}

/// Parameters for generating a permission explanation.
pub struct GenerateExplanationParams<'a> {
    pub tool_name: &'a str,
    pub tool_input: serde_json::Value,
    pub tool_description: Option<&'a str>,
    pub messages: Option<&'a [serde_json::Value]>,
    pub signal: Option<&'a tokio::sync::oneshot::Receiver<()>>,
}

const SYSTEM_PROMPT: &str = "Analyze shell commands and explain what they do, why you're running them, and potential risks.";

/// Formats tool input for display.
fn format_tool_input(input: &serde_json::Value) -> String {
    if let Some(s) = input.as_str() {
        return s.to_string();
    }
    serde_json::to_string_pretty(input).unwrap_or_else(|_| input.to_string())
}

/// Extracts recent conversation context from messages.
fn extract_conversation_context(messages: &[serde_json::Value], max_chars: usize) -> String {
    // Simplified implementation
    let _ = (messages, max_chars);
    String::new()
}

/// Checks if the permission explainer feature is enabled.
pub fn is_permission_explainer_enabled() -> bool {
    // Enabled by default; users can opt out via config
    true
}

/// Generates a permission explanation.
/// Returns None if the feature is disabled, request is aborted, or an error occurs.
pub async fn generate_permission_explanation(
    params: GenerateExplanationParams<'_>,
) -> Option<PermissionExplanation> {
    if !is_permission_explainer_enabled() {
        return None;
    }

    let start_time = std::time::Instant::now();

    let formatted_input = format_tool_input(&params.tool_input);
    let conversation_context = params.messages.map_or(String::new(), |msgs| {
        extract_conversation_context(msgs, 1000)
    });

    let user_prompt = format!(
        "Tool: {}\n{}Input:\n{}\n{}",
        params.tool_name,
        params
            .tool_description
            .map_or(String::new(), |d| format!("Description: {}\n", d)),
        formatted_input,
        if conversation_context.is_empty() {
            String::new()
        } else {
            format!("\nRecent conversation context:\n{}", conversation_context)
        },
    );

    // In a full implementation, this would call the LLM API
    let _ = (user_prompt, start_time);

    // Placeholder — full implementation requires API integration
    None
}