polyc-tools 2026.8.3

The in-process tool core for polychrome agents: local executors (coding, web fetch, wallet, ...), the tool registry, and MCP composition. The networked connectors live in polyc-connectors.
//! Spec for the agent-evaluable admin `revoke` tool (`#713`).
//!
//! The offboarding sibling of `invite` (`#698`/`#700`): an admin asking in
//! natural language — "remove @someone's access", "revoke @someone", "uninvite
//! @someone" — should be handled as an admin removal, not mis-answered by the
//! model. This tool lets the agent recognize that intent and hand it to the
//! control plane, which enforces the admin gate, severs the target's linked
//! identities, and cancels any outstanding invite to them.
//!
//! Like `invite` it has no in-process implementation: the conversation sandbox
//! can't reach the persona store. The harness advertises it via the same
//! control-plane proxy; the control plane runs it (admin-gated). The agent only
//! ever sees a codeless confirmation — there is no secret here, unlike
//! `invite`'s minted code, but the removal itself always requires an explicit
//! human approval naming the exact target, same as a grant.
//!
//! This is de-admitting a PERSON from Polychrome — distinct from `unlink_self`
//! (a person's own spending wallet or email) and from a person simply leaving
//! a conversation. Only the admin REMOVAL is agent-evaluable.

use polyc_llm::ToolSpec;
use serde_json::json;

/// The `revoke` tool name.
pub const TOOL_NAME: &str = "revoke";

/// Every revoke tool name, for allowlist checks and dispatch (one, today).
pub const ALL: &[&str] = &[TOOL_NAME];

/// The required argument: the target's provider-native user id, taken from the
/// mention markup already in the agent's input.
pub const ARG_TARGET_USER_ID: &str = "target_user_id";

/// Every revoke tool spec.
#[must_use]
pub fn all_specs() -> Vec<ToolSpec> {
    vec![revoke_spec()]
}

/// `revoke` spec — an admin removes a person's access to Polychrome.
///
/// Admin-only: the control plane refuses a non-admin caller and removes
/// nothing. Not read-only (it severs the target's linked identities) and not
/// egress. No secret is ever produced — the tool result is a plain,
/// codeless confirmation.
#[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)")
}