use polyc_llm::ToolSpec;
use serde_json::json;
pub const TOOL_NAME: &str = "revoke";
pub const ALL: &[&str] = &[TOOL_NAME];
pub const ARG_TARGET_USER_ID: &str = "target_user_id";
#[must_use]
pub fn all_specs() -> Vec<ToolSpec> {
vec![revoke_spec()]
}
#[must_use]
pub fn revoke_spec() -> ToolSpec {
ToolSpec::new(
TOOL_NAME,
"For an admin only: remove a specific person's access to Polychrome. Use \
it when an admin asks to remove, revoke, or uninvite someone's access — \
for example \"remove @sam's access\" or \"revoke @Vitor\". Pass the \
target's user id EXACTLY as it appears in the mention markup in the \
message (the id inside `<@...>`), never a typed-out name. This cuts off \
that person's access to Polychrome across every account they've linked, \
and cancels any invite still waiting for them — an admin can always \
invite them again later. This is for removing a PERSON's system access, \
which is different from unlink_self (disconnecting someone's OWN \
spending wallet or email) and different from someone simply leaving this \
conversation (no tool needed for that). If the person asking isn't an \
admin, it returns a refusal rather than removing anyone.",
json!({
"type": "object",
"properties": {
ARG_TARGET_USER_ID: {
"type": "string",
"description": "The target's provider-native user id, taken \
verbatim from the mention markup (`<@U...>`) in the \
message — not a display name or handle."
}
},
"required": [ARG_TARGET_USER_ID],
"additionalProperties": false
}),
)
.titled("Remove someone's access to Polychrome (admin)")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn revoke_spec_names_mention_markup_id() {
let spec = revoke_spec();
assert_eq!(spec.name, TOOL_NAME);
assert!(
spec.description
.contains("EXACTLY as it appears in the mention markup"),
"the description must demand the markup id verbatim: {}",
spec.description
);
assert!(
spec.description.contains("`<@...>`"),
"the description must name the markup shape: {}",
spec.description
);
assert!(
spec.description.contains("never a typed-out name"),
"the description must forbid a typed name: {}",
spec.description
);
let schema = &spec.schema_json;
assert_eq!(schema["required"], json!([ARG_TARGET_USER_ID]));
assert_eq!(schema["additionalProperties"], json!(false));
let argument = schema["properties"][ARG_TARGET_USER_ID]["description"]
.as_str()
.expect("the target argument carries a description");
assert!(
argument.contains("verbatim from the mention markup"),
"the argument description must repeat the rule: {argument}"
);
assert!(
argument.contains("not a display name"),
"the argument description must refuse a display name: {argument}"
);
assert_eq!(
schema["properties"].as_object().map(serde_json::Map::len),
Some(1),
"the target id is the only argument"
);
}
}