pub const FORBIDDEN_TOKENS: &[&str] = &[
"dictation",
"dictate",
"dictating",
"paste",
"pasting",
"pasteboard",
"clipboard",
"history",
"transcripts",
"deliver",
"delivery",
"detokenize",
"detokenization",
"ptt",
];
pub const FORBIDDEN_PHRASES: &[&[&str]] = &[
&["recent", "transcript"],
&["last", "transcript"],
&["start", "capture"],
&["stop", "capture"],
&["push", "to", "talk"],
];
fn tokens(name: &str) -> Vec<String> {
name.split(|c: char| !c.is_ascii_alphanumeric())
.filter(|t| !t.is_empty())
.map(|t| t.to_ascii_lowercase())
.collect()
}
pub fn forbidden(name: &str) -> Option<String> {
let toks = tokens(name);
for t in &toks {
if FORBIDDEN_TOKENS.contains(&t.as_str()) {
return Some(format!(
"`{t}` is never exposed to agents (no dictation, paste, pasteboard or history)"
));
}
}
for p in FORBIDDEN_PHRASES {
let hit = toks.windows(p.len()).any(|w| {
w.iter()
.zip(p.iter())
.all(|(a, b)| a.trim_end_matches('s') == *b)
});
if hit {
return Some(format!(
"`{}` is never exposed to agents (dictation rule)",
p.join("_")
));
}
}
None
}
pub const GENERIC_READ_METHODS: &[&str] = &["eval", "dom", "ax", "screenshot"];