/** * The per-run fetch cache: a URL a model tool call fetched once is not * fetched again in the same conversation. The second call gets the * stored result, with no request and no interrupt. * See docs/dev/stdlib/fetch-cache.md. */ import type { Run } from "./asyncContext.js"; import type { StateStack } from "./state/stateStack.js"; /** One fetched URL: how the model spelled it, which tool fetched it, the * thread whose conversation it was fetched for, the fetch's number in * the run (the oldest is dropped first), the result, and the result's * size in characters. */ export type FetchCacheEntry = { url: string; tool: string; threadId: string | null; ordinal: number; value: unknown; size: number; }; /** The most result text the cache keeps, in characters. The cache rides * in every checkpoint beside the spill store, whose cap of 1,000,000 is * set by the hosted resume limit (spill.md), so the two together stay * under it with room for two copies. */ export declare const MAX_CACHED_CHARS = 500000; export type FetchCache = { entries: Record; count: number; }; /** Where the cache is kept: a module of its own in the run's globals. The * globals of a run are shared by its tool calls, copied into a fork * branch, and serialized with every checkpoint, which is what the cache * needs (globalstore.md). Not under `__internal`: a trace stores each * module's globals as one chunk, and `__internal` changes on every model * call (the token stats), which would store every cached page again each * time. In its own module the pages are stored again only when a fetch * changes the cache. */ export declare const FETCH_CACHE_MODULE = "__fetchCache"; /** What a fetch looks like to the cache. */ export type FetchRequest = { url: string; method: string; body: unknown; headers?: Record; tool: string; }; /** * The key two spellings of one page share: scheme and host lowercased, a * leading `www.` dropped, the fragment dropped, and a trailing slash * dropped. The query stays, since `?page=2` is another page. `null` when * the string is not a URL. */ export declare function normalizeFetchUrl(url: string): string | null; /** What the loop records for one tool call: the threads whose messages * the call's result will sit among (the conversation's thread and its * ancestors, nearest first, from `ThreadStore.lineageOf`), and which * function the model called. */ export type ToolCallRecord = { conversation: string[]; tool: { module: string; registeredName: string; }; }; /** Record a tool call on its branch stack: the function the model called * and the conversation its result goes to. The tool loop calls this * before it invokes the tool. */ export declare function recordToolCall(branchStack: StateStack, tool: { module: string; registeredName: string; }, conversation: string[]): void; /** * The stored result a tool call gets when this conversation already * fetched the URL, or `null` when the fetch should run: the fetch function * is not the tool the model called, the request is not a plain GET, the * call asked for a refresh, or the URL was not fetched in this * conversation before (or its result was dropped to make room). The * result is wrapped because a JSON body can itself be `null`. */ export declare function cachedFetch(run: Run, request: FetchRequest, refresh: boolean): { value: unknown; } | null; /** Record a fetch that completed and its result, so the next call for * the same URL in this conversation gets the result back. Call it after * the fetch succeeded: a failed fetch is not recorded, so the model may * try the URL again. A result larger than the whole cap is not kept, and * that URL is fetched again next time. */ export declare function recordFetch(run: Run, request: FetchRequest, value: unknown): void;