plugin_web/lib.rs
1//! Web access for prompts: a Plugin that gives a run two tools, one
2//! that fetches a page and one that searches the web.
3//!
4//! A Host installs [`PACKAGE`], by default under the name `web`, and every
5//! run then receives both tools: `web/fetch`, which fetches a URL and
6//! returns its content as text, and `web/search`, which runs a search
7//! through the Host's [`SearchProvider`]. The two tools always come
8//! together, named under whatever name the Host installed the Plugin
9//! under.
10//!
11//! The Plugin has no prelude, so declaring it, as in `plugins: [web]`,
12//! only makes it required. A prompt reaches the tools by id from Lua,
13//! offering one to the model with `tools.offer("web/fetch")` or calling it
14//! with `tools.call("web/search", ...)`, whether or not it declares the
15//! Plugin.
16//!
17//! The Host provides two Host-wide services beside the Plugin. Its
18//! [`SearchProvider`] goes under the key [`SEARCH_PROVIDER`], and the
19//! tokio runtime handle that every fetch is spawned onto goes under the
20//! Plugin contract's [`runvil_plugin::TOKIO_RUNTIME`] service.
21//! Install reads both; when either is missing, the Plugin is installed as
22//! unavailable, no run receives its tools, and a prompt that declares it
23//! is refused naming the missing service.
24//!
25//! The fetch tool is security-critical. The model supplies the URL, so the
26//! tool is the server-side request forgery (SSRF) boundary between an
27//! untrusted argument and the network. It enforces the built-in default
28//! fetch policy.
29//!
30//! The fetch tool sends a GET request and chooses how to render the
31//! response from its `Content-Type`. For an HTML page, it extracts the
32//! main article with `readabilityrs` and renders it to markdown. When a
33//! page yields too little article text, `htmd` converts the whole page to
34//! markdown. Any other text body, such as JSON, XML, or plain text, is
35//! decoded and returned verbatim. The tool refuses every other type.
36//!
37//! The search tool validates the model's arguments into a [`SearchQuery`]
38//! and hands it to the provider. It returns the provider's
39//! [`SearchResults`] as compact JSON, marked untrusted. The JSON matches
40//! the Gateway's search output: an object with the `query` and a `results`
41//! array. Each result has a `title`, `url`, and `description`, plus `age`,
42//! `site_name`, and `extra_snippets` when present. The provider owns the
43//! transport and its deadline, so a search vendor's credential stays
44//! wherever the provider keeps it.
45//!
46//! ## Invariants
47//!
48//! - May depend on: `runvil-plugin`, `shared-*` crates, and outside
49//! libraries. `cargo test -p build-xtask` enforces the Plugin family's
50//! allow-list.
51//! - Every URL selected by the model or a tool, and every resolved
52//! address, is validated again on each redirect hop. A non-global
53//! address is denied unless the fetch policy grants an exact
54//! host-and-address exception.
55//! - No fetch includes an ambient identity on any hop. The client has no
56//! proxy, no cookie store, no automatic `Referer` header, and no default
57//! credentials.
58//! - Every fetch runs on the Host's runtime handle, and dropping the call
59//! aborts it.
60//! - Install accepts only a `null` or `{}` configuration; any other value
61//! leaves the Plugin unavailable.
62
63mod address;
64#[cfg_attr(
65 not(test),
66 expect(
67 dead_code,
68 reason = "web's configuration accepts no settings yet, so only the crate's tests build a custom fetch policy"
69 )
70)]
71mod config;
72mod error;
73mod fetch;
74mod provider;
75mod redirect;
76mod resolver;
77mod response;
78mod search;
79mod url_policy;
80mod web;
81
82pub use crate::provider::{
83 Freshness, SafeSearch, SearchError, SearchErrorKind, SearchProvider, SearchQuery, SearchResult,
84 SearchResults,
85};
86pub use crate::web::{PACKAGE, SEARCH_PROVIDER};