Codex Handbook
core/src/plugins/mentions.rs 120 lines
use std::collections::HashMap;use std::collections::HashSet;use codex_connectors::metadata::connector_mention_slug;use codex_protocol::user_input::UserInput;use crate::connectors;use crate::injection::ToolMentionKind;use crate::injection::app_id_from_path;use crate::injection::extract_tool_mentions_with_sigil;use crate::injection::plugin_config_name_from_path;use crate::injection::tool_kind_for_path;use crate::mention_syntax::PLUGIN_TEXT_MENTION_SIGIL;use crate::mention_syntax::TOOL_MENTION_SIGIL;use super::PluginCapabilitySummary;pub(crate) struct CollectedToolMentions {    pub(crate) plain_names: HashSet<String>,    pub(crate) paths: HashSet<String>,}pub(crate) fn collect_tool_mentions_from_messages(messages: &[String]) -> CollectedToolMentions {    collect_tool_mentions_from_messages_with_sigil(messages, TOOL_MENTION_SIGIL)}fn collect_tool_mentions_from_messages_with_sigil(    messages: &[String],    sigil: char,) -> CollectedToolMentions {    let mut plain_names = HashSet::new();    let mut paths = HashSet::new();    for message in messages {        let mentions = extract_tool_mentions_with_sigil(message, sigil);        plain_names.extend(mentions.plain_names().map(str::to_string));        paths.extend(mentions.paths().map(str::to_string));    }    CollectedToolMentions { plain_names, paths }}pub(crate) fn collect_explicit_app_ids(input: &[UserInput]) -> HashSet<String> {    let messages = input        .iter()        .filter_map(|item| match item {            UserInput::Text { text, .. } => Some(text.clone()),            _ => None,        })        .collect::<Vec<String>>();    input        .iter()        .filter_map(|item| match item {            UserInput::Mention { path, .. } => Some(path.clone()),            _ => None,        })        .chain(collect_tool_mentions_from_messages(&messages).paths)        .filter(|path| tool_kind_for_path(path.as_str()) == ToolMentionKind::App)        .filter_map(|path| app_id_from_path(path.as_str()).map(str::to_string))        .collect()}/// Collect explicit structured or linked `plugin://...` mentions.pub(crate) fn collect_explicit_plugin_mentions(    input: &[UserInput],    plugins: &[PluginCapabilitySummary],) -> Vec<PluginCapabilitySummary> {    if plugins.is_empty() {        return Vec::new();    }    let messages = input        .iter()        .filter_map(|item| match item {            UserInput::Text { text, .. } => Some(text.clone()),            _ => None,        })        .collect::<Vec<String>>();    let mentioned_config_names: HashSet<String> = input        .iter()        .filter_map(|item| match item {            UserInput::Mention { path, .. } => Some(path.clone()),            _ => None,        })        .chain(            // Plugin plaintext links use `@`, not the default `$` tool sigil.            collect_tool_mentions_from_messages_with_sigil(&messages, PLUGIN_TEXT_MENTION_SIGIL)                .paths,        )        .filter(|path| tool_kind_for_path(path.as_str()) == ToolMentionKind::Plugin)        .filter_map(|path| plugin_config_name_from_path(path.as_str()).map(str::to_string))        .collect();    if mentioned_config_names.is_empty() {        return Vec::new();    }    plugins        .iter()        .filter(|plugin| mentioned_config_names.contains(plugin.config_name.as_str()))        .cloned()        .collect()}pub(crate) use crate::build_skill_name_counts;pub(crate) fn build_connector_slug_counts(    connectors: &[connectors::AppInfo],) -> HashMap<String, usize> {    let mut counts: HashMap<String, usize> = HashMap::new();    for connector in connectors {        let slug = connector_mention_slug(connector);        *counts.entry(slug).or_insert(0) += 1;    }    counts}#[cfg(test)]#[path = "mentions_tests.rs"]mod tests;