Files
deepseek-harness/packages/web/web-fetch-local

@deepseek-ai/dsh-web-fetch-local

An anonymous public HTTP(S) WebFetchProvider for the harness web capability seam (ctx.web). It retrieves a concrete URL and returns a status code plus bounded decoded content.

This is an implementation package: it registers a provider into ctx.web, it does not own the key and it does not register a model-facing tool. It is a function/namespace plugin (inject: ['web']).

Responsibility split

The provider owns safe resource retrieval: URL validation, HTTP transport, redirect policy, a resource-backstop timeout, abort propagation, byte caps, charset decoding, content-type classification, and binary rejection. @deepseek-ai/dsh-tool-web owns presentation (HTML→markdown, truncation formatting). A non-2xx HTTP response is a result (status code + decoded body), not an error; WebError is reserved for failures to safely retrieve or represent the resource.

The provider's timeoutMs/maxTimeoutMs is a resource backstop for direct ctx.web.fetch() callers and misconfigured deployments — it is NOT the model-facing tool-call budget. The tool-call budget for web_fetch is deployment policy owned by @deepseek-ai/dsh-timeout-policy, which arms a per-call deadline on exec.signal. A shipped web-tool deployment sets the provider backstop above the tool-timeout budget, so the tool-call policy normally wins for model calls (returning TOOL_TIMEOUT); when the outer deadline signal reaches this provider first, it classifies as WEB_ABORTED and the outer wrapper replaces the result with TOOL_TIMEOUT. The provider's own WEB_FETCH_TIMEOUT only fires for a direct seam caller whose own budget elapsed.

Transport hygiene

  • Accepts only http: and https: URLs; rejects credentials in URLs (WEB_BLOCKED_URL) and over-long/malformed URLs (WEB_INVALID_URL).
  • Enforces a max URL length, response byte cap (WEB_FETCH_TOO_LARGE), decoded body character cap, timeout (WEB_FETCH_TIMEOUT), and redirect hop cap.
  • Propagates the caller's abort signal (WEB_ABORTED) into the network request and the streaming read.
  • Follows only same-origin redirects; a cross-origin redirect fails with WEB_REDIRECT_BLOCKED, requiring a fresh tool call (the model of Claude Code's WebFetch).
  • Sends an explicit product User-Agent, never a browser disguise.
  • Rejects unsupported (e.g. binary) content types with WEB_UNSUPPORTED_CONTENT_TYPE.

Config

Key Default Meaning
maxUrlLength 2048 Maximum accepted request URL length.
maxResponseBytes 5_000_000 Maximum response body size in bytes.
maxBodyChars 100_000 Maximum decoded body length in characters.
timeoutMs 30_000 Default fetch timeout — a resource backstop for direct ctx.web.fetch() callers, not the model-facing tool-call budget (that is dsh-timeout-policy).
maxTimeoutMs 120_000 Upper bound for a per-request timeout override (direct callers).
maxRedirects 5 Maximum same-origin redirect hops (0 follows none).
userAgent deepseek-harness/… User-Agent header.

The numeric limits are validated at plugin construction: every cap except maxRedirects must be a positive finite number, and maxRedirects must be a non-negative integer. An invalid value throws rather than silently constructing a provider with nonsensical limits.

Model Experience

Web fetch result, indirectly

What the model sees: Through dsh-tool-web, the conversation model sees that consumer's exact fetch header plus the data-dependent decoded text or markdown-shaped HTML. This provider's stable failures become Error: <message>; messages include web fetch aborted, web fetch timed out, web fetch failed: <error>, exceeded the maximum of <count> redirects, redirect response (HTTP <status>) without a Location header, invalid redirect Location "<location>", cross-origin redirect to <origin> is not followed automatically; retry against that URL directly, unsupported content type "<type>", response exceeds the maximum of <bytes> bytes, URL exceeds the maximum length of <chars>, invalid URL: <url>, unsupported URL scheme "<scheme>" (only http and https are allowed), credentials in URLs are not allowed, and unsupported charset "<charset>". Redirects, headers, and transport mechanics are otherwise absent.

Token effect: Zero direct tokens. maxBodyChars bounds decoded result length before the tool records it; the retained result or error is resent until compaction.

Known Limitations and Deferred Work

  • SSRF / private-network protection is deferred — no blocking of private, loopback, link-local, multicast, or otherwise non-public destinations, no DNS-resolve-then-validate, no per-hop re-validation (see the web capability seam RFC). Until it lands, this provider is an SSRF primitive and must not be enabled in a deployment that can reach sensitive internal network targets.
  • Only textual content decodes — html/xhtml and text/*-plus-JSON/XML families; a missing Content-Type or any binary type throws WEB_UNSUPPORTED_CONTENT_TYPE, and text-extractable PDF decoding is named deferred work.
  • Charset comes only from the Content-Type header (UTF-8 default) — an HTML <meta charset> declaration is ignored, and a declared-but-unrecognized charset label throws rather than falling back.