polyc-tools 2026.9.0

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 `web_fetch` tool.
//!
//! `web_fetch` has no in-process implementation: the conversation sandbox has no
//! outbound network, so the GET is performed on the trusted side (the control
//! plane). The harness advertises this spec via a proxy tool that forwards the
//! call up the harness stream, exactly like `paid_fetch` — but non-paying. It is
//! read-only and ungated.

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

/// The `web_fetch` tool name.
pub const WEB_FETCH: &str = "web_fetch";

/// Scoping/audit name for the LLM provider's native web-search-grounding
/// primitive (issue `#1226`).
///
/// E.g. a request-level flag the provider turns into its own built-in search
/// tool entry mid-generation. Unlike every other name in this crate, this is
/// NEVER advertised as a [`ToolSpec`]: the model doesn't emit a `tool_use`
/// call to invoke it (the provider grounds transparently when the flag is
/// set), so there is nothing for the harness to intercept and no schema to
/// show the model. It exists purely so this name can appear in an agent's
/// `builtinTools` grant list and be checked by
/// [`crate::capability::native_search_grounding_requirements`] — the same
/// `required ⊆ granted` comparison every real tool call goes through, applied
/// once per step instead of per call.
///
/// Re-exported from [`polyc_capability`] rather than defined here: the
/// per-step gate lives in `polyc_agent`, which cannot depend on `polyc_tools`
/// (the reverse dependency already exists), so the ONE literal lives in the
/// shared foundation crate both sides import.
pub const NATIVE_SEARCH_GROUNDING: &str = polyc_capability::NATIVE_SEARCH_GROUNDING;

/// `web_fetch` spec — a plain HTTP GET, performed trusted-side, read-only.
#[must_use]
pub fn fetch_spec() -> ToolSpec {
    ToolSpec::new(
        WEB_FETCH,
        "HTTP GET a public URL and return its status and body. The fetch runs on \
         the trusted side (the sandbox has no direct network); the destination is \
         SSRF-guarded and redirects are not followed. Use for reading public web \
         pages or APIs. Does not pay — for 402-gated resources use paid_fetch.",
        json!({
            "type": "object",
            "properties": {
                "url": { "type": "string", "description": "Public URL to GET." }
            },
            "required": ["url"],
            "additionalProperties": false
        }),
    )
    .titled("Fetch a web page")
    .read_only()
    // Fetches an arbitrary public URL — the result is attacker-authorable
    // external content, so it seeds the untrusted-content (trifecta) leg.
    .open_world()
}