/**
* Pluggable `web_search` backends + HTML→markdown extraction (T1742 · epic
* T11456 · SG-TOOLS).
*
* The `web` toolset's network backend. TWO concerns live here, both built over
* an INJECTABLE {@link HttpFetch} so unit tests mock the network and CI never
* makes a real request (AC9):
*
* - **web_search backends** — a {@link WebSearchBackend} interface with two
* keyless builtin implementations ({@link duckDuckGoBackend},
* {@link searxngBackend}). NO hard paid-API dependency: backends are resolved
* at call time and the tool degrades gracefully to an `unavailable` result
* when none answer (AC1). New backends register by satisfying the interface —
* the search tool is backend-agnostic.
* - **web_extract** — {@link htmlToMarkdown}, a dependency-free HTML→markdown
* converter (AC2), plus {@link extractTitle}.
*
* The pure helpers (parsers, the markdown converter) are exported for direct unit
* testing. Import-time side-effect-free.
*
* @epic T11456
* @task T1742
*/
import type { WebExtractResult, WebSearchBackendId, WebSearchHit, WebSearchInput } from '@cleocode/contracts/tools/web-tools';
/**
* A minimal HTTP-fetch surface the web tools depend on by INJECTION. Structurally
* a subset of the global `fetch`, declared here so the executables can be driven
* by a mock in tests (no real network in CI · AC9) and so a future routing of
* web egress through a guarded `net` primitive is a one-line swap.
*/
export type HttpFetch = (url: string, init?: {
readonly headers?: Record;
readonly signal?: AbortSignal;
}) => Promise<{
readonly status: number;
text(): Promise;
readonly headers: {
get(name: string): string | null;
};
}>;
/** The default {@link HttpFetch} — the platform global `fetch`. */
export declare const defaultHttpFetch: HttpFetch;
/**
* One pluggable web-search backend. A backend resolves a query into ranked
* {@link WebSearchHit}s, or throws (the resolver moves on to the next backend).
* `isConfigured()` lets a backend that needs a URL/instance opt OUT of the `auto`
* rotation when it has nothing to talk to — keeping the "graceful when
* unconfigured" contract (AC1) without a network round-trip.
*/
export interface WebSearchBackend {
/** Canonical backend id. */
readonly id: WebSearchBackendId;
/** Whether this backend has everything it needs to run (e.g. an instance URL). */
isConfigured(): boolean;
/**
* Run the search.
*
* @param query - The search query.
* @param maxResults - Cap on returned hits.
* @param http - The injected fetch surface.
* @returns Ranked hits.
*/
search(query: string, maxResults: number, http: HttpFetch): Promise;
}
/**
* Parse DuckDuckGo's keyless HTML endpoint (`html.duckduckgo.com/html/`) into
* structured hits. Pure helper (AC9) — exported for direct unit testing against
* a captured fixture, so no network is needed.
*
* @param html - The DuckDuckGo HTML response body.
* @param maxResults - Cap on returned hits.
* @returns Parsed hits.
*/
export declare function parseDuckDuckGoHtml(html: string, maxResults: number): WebSearchHit[];
/**
* The DuckDuckGo backend — keyless HTML endpoint, always "configured" (no
* instance URL needed). The canonical no-API-key default (AC1).
*/
export declare const duckDuckGoBackend: WebSearchBackend;
/**
* Parse a SearXNG JSON response (`{ results: [{ title, url, content }] }`) into
* hits. Pure helper (AC9).
*
* @param json - The parsed SearXNG JSON body.
* @param maxResults - Cap on returned hits.
* @returns Parsed hits.
*/
export declare function parseSearxngJson(json: unknown, maxResults: number): WebSearchHit[];
/**
* Build a SearXNG backend bound to an instance URL (from `SEARXNG_URL` env or
* config). When no instance URL is supplied it reports `isConfigured() === false`
* so the `auto` resolver skips it — preserving the "graceful when unconfigured"
* contract (AC1) with no paid dependency.
*
* @param instanceUrl - The SearXNG instance base URL (or `undefined`/empty).
* @returns A {@link WebSearchBackend}.
*/
export declare function makeSearxngBackend(instanceUrl: string | undefined): WebSearchBackend;
/**
* Resolve the candidate backends for a {@link WebSearchInput}, in priority
* order. A SearXNG instance (when configured) ranks before DuckDuckGo; an
* explicit `input.backend` pins a single backend. The DuckDuckGo backend is
* always present (keyless), so the list is never empty — but a pinned backend
* that is not configured yields an empty list, which the executable surfaces as
* `unavailable` (AC1).
*
* @param input - The search input.
* @param searxngUrl - Optional SearXNG instance URL (config/env).
* @returns The ordered candidate backends.
*/
export declare function resolveSearchBackends(input: WebSearchInput, searxngUrl: string | undefined): WebSearchBackend[];
/** Extract the document `` text, or `''` when absent. Pure (AC9). */
export declare function extractTitle(html: string): string;
/**
* Convert an HTML document to markdown. Dependency-free (AC2) — a focused,
* deterministic transform of the structural tags an agent cares about
* (headings, links, lists, code, emphasis, paragraphs); `