vtcode 0.99.1

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use serde_json::Value;

use crate::agent::runloop::unified::tool_summary::{describe_tool_action, humanize_tool_name};

use super::permission_prompt::{
    extract_shell_approval_command_words, extract_shell_command_text,
    extract_shell_permission_scope_signature, extract_shell_persistent_approval_prefix_rule,
    render_shell_approval_command_words, render_shell_persistent_approval_prefix_entry,
};

#[derive(Debug, Clone)]
pub(super) struct ApprovalLearningTarget {
    pub approval_key: String,
    pub display_label: String,
}

#[derive(Debug, Clone)]
pub(super) struct ToolDisplayLabels {
    pub prompt_label: String,
    pub learning_label: String,
}

#[derive(Debug, Clone)]
pub(super) enum PersistentApprovalTarget {
    ToolLevel,
    ExactInvocation {
        display_label: String,
    },
    PrefixRule {
        prefix_rule: Vec<String>,
        display_label: String,
    },
}

fn exact_shell_learning_target(
    tool_name: &str,
    tool_args: Option<&Value>,
    default_learning_label: &str,
) -> Option<ApprovalLearningTarget> {
    let scope_signature = extract_shell_permission_scope_signature(tool_name, tool_args)?;

    if let Some(command_words) = extract_shell_approval_command_words(tool_name, tool_args) {
        let rendered_command = render_shell_approval_command_words(&command_words);
        return Some(ApprovalLearningTarget {
            approval_key: format!("{rendered_command}|{scope_signature}"),
            display_label: format!("command `{rendered_command}`"),
        });
    }

    if let Some(command_text) = extract_shell_command_text(tool_name, tool_args) {
        return Some(ApprovalLearningTarget {
            approval_key: format!("{command_text}|{scope_signature}"),
            display_label: format!("command `{command_text}`"),
        });
    }

    let fallback_key = tool_args
        .map(Value::to_string)
        .unwrap_or_else(|| tool_name.to_string());
    Some(ApprovalLearningTarget {
        approval_key: format!("{fallback_key}|{scope_signature}"),
        display_label: default_learning_label.to_string(),
    })
}

pub(super) fn approval_learning_target(
    tool_name: &str,
    tool_args: Option<&Value>,
    default_learning_label: &str,
) -> ApprovalLearningTarget {
    if let Some(scope_signature) = extract_shell_permission_scope_signature(tool_name, tool_args) {
        if let Some(prefix_rule) =
            extract_shell_persistent_approval_prefix_rule(tool_name, tool_args)
            && let Some(rendered_rule) =
                render_shell_persistent_approval_prefix_entry(tool_name, tool_args, &prefix_rule)
        {
            let rendered_prefix = render_shell_approval_command_words(&prefix_rule);
            return ApprovalLearningTarget {
                approval_key: rendered_rule,
                display_label: format!("commands starting with `{rendered_prefix}`"),
            };
        }

        return exact_shell_learning_target(tool_name, tool_args, default_learning_label)
            .unwrap_or_else(|| ApprovalLearningTarget {
                approval_key: format!("{tool_name}|{scope_signature}"),
                display_label: default_learning_label.to_string(),
            });
    }

    ApprovalLearningTarget {
        approval_key: vtcode_core::tools::names::canonical_tool_name(tool_name).into_owned(),
        display_label: default_learning_label.to_string(),
    }
}

pub(super) fn exact_shell_approval_target(
    tool_name: &str,
    tool_args: Option<&Value>,
    default_learning_label: &str,
) -> Option<ApprovalLearningTarget> {
    exact_shell_learning_target(tool_name, tool_args, default_learning_label)
}

pub(super) fn persistent_approval_target(
    tool_name: &str,
    tool_args: Option<&Value>,
    default_learning_label: &str,
) -> PersistentApprovalTarget {
    if let Some(prefix_rule) = extract_shell_persistent_approval_prefix_rule(tool_name, tool_args) {
        let rendered_prefix = render_shell_approval_command_words(&prefix_rule);
        return PersistentApprovalTarget::PrefixRule {
            prefix_rule,
            display_label: format!("commands starting with `{rendered_prefix}`"),
        };
    }

    if extract_shell_permission_scope_signature(tool_name, tool_args).is_some() {
        let learning = approval_learning_target(tool_name, tool_args, default_learning_label);
        return PersistentApprovalTarget::ExactInvocation {
            display_label: learning.display_label,
        };
    }

    PersistentApprovalTarget::ToolLevel
}

pub(super) fn tool_display_labels(tool_name: &str, tool_args: Option<&Value>) -> ToolDisplayLabels {
    let learning_label = humanize_tool_name(tool_name);
    let prompt_label = tool_args
        .map(|args| describe_tool_action(tool_name, args).0)
        .filter(|headline| !headline.is_empty())
        .unwrap_or_else(|| learning_label.clone());

    ToolDisplayLabels {
        prompt_label,
        learning_label,
    }
}