agents
Version:
A home for your AI agents
302 lines (301 loc) • 12.2 kB
TypeScript
//#region src/web/fetch/contract.d.ts
/**
* The `web_fetch` tool contract: its name, what it tells the model, what the
* model passes in, what comes back, and how a page is rendered for the
* model. Shared by every executor of the tool — the harness-executed
* adapters in `agents/webfetch/*`, and a gateway-executed variant once AI
* Gateway server tools ship — so a `web_fetch` call looks the same in a
* transcript whoever ran it.
*/
/**
* The tool's name, as the model calls it and as it appears in transcripts.
* `web_fetch` is the name models know from Anthropic's built-in fetch tool.
*/
declare const WEB_FETCH_TOOL_NAME = "web_fetch";
/** How many characters of content the model sees per call. */
declare const DEFAULT_WEB_FETCH_PAGE_CHARS = 20000;
/** The most response body bytes one fetch reads, streamed. */
declare const DEFAULT_WEB_FETCH_MAX_BYTES = 5000000;
/** How long one fetch may take, in milliseconds, redirects and conversion included. */
declare const DEFAULT_WEB_FETCH_TIMEOUT_MS = 30000;
/** How many redirects one fetch follows unless the host sets `maxRedirects`. */
declare const DEFAULT_WEB_FETCH_MAX_REDIRECTS = 5;
/** The longest URL accepted, in characters. */
declare const MAX_WEB_FETCH_URL_LENGTH = 2000;
/**
* How to represent the body. `auto` converts to the most readable text
* (Markdown for HTML and documents, pretty-printed JSON); `raw` returns
* textual bodies exactly as served.
*/
type WebFetchFormat = "auto" | "raw";
/** What the model says when it wants a page. */
type WebFetchToolInput = {
/** The http(s) URL to read. */ url: string /** Character offset into the content for continuing a long page. Default 0. */;
offset?: number /** "auto" (default) converts to the most readable form; "raw" returns the body as served. */;
format?: WebFetchFormat;
};
/** How the content of a {@link WebFetchPage} was produced. */
type WebFetchVia =
| /** The server answered `text/markdown` (Markdown for Agents). */ "markdown-negotiated" /** Converted to Markdown by Workers AI (`env.AI.toMarkdown`). */
| "converted" /** JSON, pretty-printed. */
| "json" /** Text passed through as served. */
| "text" /** `format: "raw"`: the body as served. */
| "raw"
/**
* Rendered in a browser first (Browser Run). Reserved: nothing produces
* it yet. It is in the union now so adding a browser source later doesn't
* break hosts' exhaustive switches.
*/
| "rendered";
/**
* One fetched page, whole: what a `WebFetchSource` and `fetchWeb()`
* return. The model never sees this directly; the tool stores one window of
* it as a {@link WebFetchToolOutput}.
*/
type WebFetchPage = {
/** The URL as requested, trimmed. */ url: string /** The URL after redirects. */;
finalUrl: string /** The final response's HTTP status. */;
status: number /** The media type only, lowercased, without parameters. */;
contentType: string;
via: WebFetchVia /** The full converted content. */;
content: string /** `content.length`. */;
totalChars: number /** The page title, when the page has one. */;
title?: string;
/**
* Selected response headers, lowercased: content-type, content-length,
* last-modified, etag, x-markdown-tokens, cf-mitigated.
*/
headers: Record<string, string> /** Body bytes read. */;
bytes: number /** The URLs that redirected, in order; `finalUrl` is not included. */;
redirects: string[];
};
/**
* What one `web_fetch` run stores for the host, and what
* {@link renderWebFetchPage} formats for the model: the page's metadata and
* the one window the model read. Bounded by the host's `pageChars`, however
* large the page, so it is safe to persist in chat history.
*/
type WebFetchToolOutput = Omit<WebFetchPage, "content"> & {
/** The window of content the model read: at most `pageChars` characters. */ content: string /** Where the window starts in the page; may be past the end. */;
offset: number /** Where the next window starts, or `null` when the window reached the end. */;
nextOffset: number | null;
};
declare const WEB_FETCH_TOOL_DESCRIPTION: string;
interface WindowWebFetchPageOptions {
/** Where the window starts. Defaults to 0. */
offset?: number;
/** Window size in characters. Defaults to {@link DEFAULT_WEB_FETCH_PAGE_CHARS}. */
pageChars?: number;
}
/**
* Cut one window out of a page: the {@link WebFetchToolOutput} the tool
* stores for a call at `offset`. Use it to show a page fetched with
* `fetchWeb()` to a model one window at a time, exactly as the tool would.
*
* The window is `pageChars` characters from `offset`, ending at the last
* newline in it unless that would drop more than a fifth of it, and never
* between the halves of a surrogate pair. An offset past the end is kept
* as given, with empty content, so rendering can say so.
*/
declare function windowWebFetchPage(
page: WebFetchPage,
options?: WindowWebFetchPageOptions
): WebFetchToolOutput;
/**
* Render a stored window as the text the model reads: a header line, then
* the window inside `<untrusted_web_content>`. Formats what's stored; it
* never re-slices, so a re-rendered history entry reads as it did.
*
* ```
* web_fetch: https://final.url · 200 · text/html → markdown · chars 0–20000 of 81200 · continue with offset=20000
* <untrusted_web_content>
* …window…
* </untrusted_web_content>
* ```
*/
declare function renderWebFetchPage(output: WebFetchToolOutput): string;
//#endregion
//#region src/web/fetch/source.d.ts
/** One fetch request as a source receives it. */
interface WebFetchRequest {
/** The http(s) URL to read. */
url: string;
/** Defaults to `"auto"`. */
format?: WebFetchFormat;
}
/** Per-call options a source receives alongside the request. */
interface WebFetchCallOptions {
/** Abort the fetch. A source should reject with the signal's reason. */
signal?: AbortSignal;
}
/**
* Runs fetches. The tool core calls `fetch` and stores one window of the
* page that comes back. A source validates the request (`url`, `format`),
* throwing `invalid_web_fetch_input` for a malformed one, and returns the
* whole page.
*/
interface WebFetchSource {
fetch(
request: WebFetchRequest,
options?: WebFetchCallOptions
): Promise<WebFetchPage>;
}
/** Options for {@link createDirectWebFetch}. */
interface DirectWebFetchOptions {
/**
* The Workers AI binding, for converting HTML and documents to Markdown.
* Requires `"ai": { "binding": "AI" }` in wrangler.jsonc; pass `env.AI`.
*/
binding: Ai;
/** Override for tests. Defaults to the global `fetch`. */
fetch?: typeof fetch;
/** Defaults to `Cloudflare-Agents (+https://github.com/cloudflare/agents)`. */
userAgent?: string;
/** Stop reading the body past this many bytes. A positive integer; defaults to 5 MB. */
maxBytes?: number;
/** Follow at most this many redirects. An integer from 0 to 20; defaults to 5. */
maxRedirects?: number;
/**
* Host patterns URLs must match, when non-empty: `example.com` or
* `*.example.com`. Empty or omitted: any public host.
*/
allowedHosts?: string[];
/** Host patterns never fetched, in the same syntax. */
blockedHosts?: string[];
/**
* Allow loopback, private, link-local, and local-only hosts. Off by
* default; turn on for local development against `localhost`.
*/
allowPrivateHosts?: boolean;
}
/**
* Fetch pages directly with `fetch`: GET only, redirects followed by hand
* and every hop re-checked against the URL policy (an https → http
* downgrade is refused), the body streamed up to
* `maxBytes`, and the content converted per the content-type table. A
* 4xx/5xx response with a textual body is returned like any page, with its
* status; one without becomes a `web_fetch_http_error`.
*/
declare function createDirectWebFetch(
options: DirectWebFetchOptions
): WebFetchSource;
/**
* Error codes a {@link WebFetchError} carries, so every failure has a code
* to branch on.
*/
type WebFetchErrorCode =
| /** The URL or format was malformed or too long. */ "invalid_web_fetch_input" /** The URL's scheme, credentials, or host isn't allowed here. */
| "web_fetch_disallowed_url" /** A redirect pointed at a URL that isn't allowed here, or from https to http. */
| "web_fetch_disallowed_redirect" /** More redirects than allowed, or a redirect loop. */
| "web_fetch_too_many_redirects" /** The fetch took longer than the tool's `timeoutMs` (on the error as `timeoutMs`). */
| "web_fetch_timeout" /** The body is larger than `maxBytes`. */
| "web_fetch_too_large" /** The body is binary, or a document requested with `format: "raw"`. */
| "web_fetch_unsupported_content_type" /** A 4xx/5xx response with an empty or non-textual body. */
| "web_fetch_http_error" /** The site answered with a bot challenge instead of the page. */
| "web_fetch_blocked" /** Workers AI could not convert the body to Markdown. */
| "web_fetch_conversion_failed" /** The network failed: before a response (DNS, TLS, connection) or while reading the body. */
| "web_fetch_unavailable";
/** Why a fetch failed. */
declare class WebFetchError extends Error {
readonly name = "WebFetchError";
/**
* The HTTP status of the response the failure is about, when there was
* one; otherwise 400 (bad input), 403 (disallowed URL), 502 (unavailable),
* or 504 (timeout).
*/
readonly status: number;
readonly code: WebFetchErrorCode;
/** Whether retrying the same fetch might succeed. */
readonly retryable: boolean;
/** The URL the failure is about: the requested URL, or the redirecting hop. */
readonly url?: string;
/** The response's media type, when a response was read. */
readonly contentType?: string;
/** The deadline that expired, in milliseconds, for `web_fetch_timeout`. */
readonly timeoutMs?: number;
constructor(
message: string,
details: {
status: number;
code: WebFetchErrorCode;
retryable?: boolean;
url?: string;
contentType?: string;
timeoutMs?: number;
cause?: unknown;
}
);
}
//#endregion
//#region src/web/fetch/tool.d.ts
/**
* Fetch directly with `fetch`, converting through the Workers AI binding;
* the tool builds the source with {@link createDirectWebFetch}.
*/
type WebFetchToolDirectOptions = DirectWebFetchOptions & {
source?: never;
};
/**
* Fetch through a source you built. Limits, URL policy, and the user agent
* belong to the source, so the direct-fetch options are not accepted here.
*/
type WebFetchToolSourceOptions = {
/** Where fetches run, instead of a direct `fetch`. */ source: WebFetchSource;
binding?: never;
fetch?: never;
userAgent?: never;
maxBytes?: never;
maxRedirects?: never;
allowedHosts?: never;
blockedHosts?: never;
allowPrivateHosts?: never;
};
/**
* Options every `web_fetch` tool adapter accepts. Invalid values throw a
* `RangeError` when the tool is created.
*/
type WebFetchToolOptions = (
| WebFetchToolDirectOptions
| WebFetchToolSourceOptions
) & {
/**
* How many characters of content the model reads per call; it continues
* with `offset`. A positive integer; defaults to 20,000.
*/
pageChars?: number;
/**
* Give up on a fetch after this many milliseconds, as a retryable
* `web_fetch_timeout` failure. A positive integer; defaults to 30 seconds.
*/
timeoutMs?: number /** Replaces the default tool description. */;
description?: string;
};
//#endregion
export {
WindowWebFetchPageOptions as C,
WebFetchVia as S,
windowWebFetchPage as T,
WEB_FETCH_TOOL_NAME as _,
WebFetchCallOptions as a,
WebFetchToolInput as b,
WebFetchRequest as c,
DEFAULT_WEB_FETCH_MAX_BYTES as d,
DEFAULT_WEB_FETCH_MAX_REDIRECTS as f,
WEB_FETCH_TOOL_DESCRIPTION as g,
MAX_WEB_FETCH_URL_LENGTH as h,
DirectWebFetchOptions as i,
WebFetchSource as l,
DEFAULT_WEB_FETCH_TIMEOUT_MS as m,
WebFetchToolOptions as n,
WebFetchError as o,
DEFAULT_WEB_FETCH_PAGE_CHARS as p,
WebFetchToolSourceOptions as r,
WebFetchErrorCode as s,
WebFetchToolDirectOptions as t,
createDirectWebFetch as u,
WebFetchFormat as v,
renderWebFetchPage as w,
WebFetchToolOutput as x,
WebFetchPage as y
};
//# sourceMappingURL=tool-DoVberpN.d.ts.map