# Conflicts: # .agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.i18n.yaml # .agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.zh.md # .agents/notes/implemented/architecture/2026-06-18-shared-persistence-write-coordinator.i18n.yaml # .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.i18n.yaml # .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.zh.md # .agents/notes/implemented/bug-fix/2026-07-29-pnpm-setup-runner-isolation.i18n.yaml # .agents/notes/implemented/bug-fix/2026-07-29-pnpm-setup-runner-isolation.zh.md # .agents/notes/implemented/bug-fix/2026-08-18-request-image-payload-bound.i18n.yaml # .agents/notes/implemented/bug-fix/2026-08-18-request-image-payload-bound.zh.md # .agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml # .agents/notes/implemented/feature/2026-07-06-sandbox.zh.md # .agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.i18n.yaml # .agents/notes/implemented/feature/2026-07-21-cross-session-references.i18n.yaml # .agents/notes/implemented/feature/2026-07-21-cross-session-references.zh.md # .agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.i18n.yaml # .agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.zh.md # .agents/notes/implemented/feature/2026-07-27-web-subagent-conversations.i18n.yaml # .agents/notes/implemented/feature/2026-07-31-permission-default-for-new-sessions.i18n.yaml # .agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.i18n.yaml # .agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.zh.md # .agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml # .agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md # .agents/notes/implemented/feature/2026-08-05-agent-teams.i18n.yaml # .agents/notes/implemented/feature/2026-08-05-agent-teams.zh.md # .agents/notes/implemented/feature/2026-08-11-workspace-sidebar-order-and-folding.i18n.yaml # .agents/notes/implemented/feature/2026-08-15-product-subagent-noninteractive-permissions.i18n.yaml # .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.i18n.yaml # .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.zh.md # .agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.i18n.yaml # .agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.zh.md # .agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.i18n.yaml # .agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.zh.md # .agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.i18n.yaml # .agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.zh.md # .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml # .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md # .agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.i18n.yaml # README.i18n.yaml # README.zh.md # docs/architecture.i18n.yaml # docs/architecture.zh.md # docs/development.i18n.yaml # docs/development.zh.md # docs/persistence-catalog.i18n.yaml # docs/persistence-catalog.zh.md # docs/subsystems/README.i18n.yaml # docs/subsystems/README.zh.md # docs/subsystems/agent-team.i18n.yaml # docs/subsystems/agent-team.zh.md # docs/subsystems/client-modules.i18n.yaml # docs/subsystems/client-modules.zh.md # docs/subsystems/commands.i18n.yaml # docs/subsystems/commands.zh.md # docs/subsystems/persistence.i18n.yaml # docs/subsystems/persistence.zh.md # docs/subsystems/session-reference.i18n.yaml # docs/tool-catalog.i18n.yaml # docs/tool-catalog.zh.md # docs/user/guide/providers.i18n.yaml # docs/user/guide/providers.zh.md # packages/README.i18n.yaml # packages/README.zh.md # packages/bundle/web-app/README.i18n.yaml # packages/bundle/web-app/README.zh.md # packages/client/README.i18n.yaml # packages/client/README.zh.md # packages/client/connection/README.i18n.yaml # packages/client/connection/README.zh.md # packages/client/ui-conversation/README.i18n.yaml # packages/client/ui-conversation/README.zh.md # packages/client/ui-primitives/README.i18n.yaml # packages/client/ui-primitives/README.zh.md # packages/client/ui-sidebar/README.i18n.yaml # packages/client/ui-sidebar/README.zh.md # packages/client/ui-workspace/README.i18n.yaml # packages/client/ui-workspace/README.zh.md # packages/context/README.i18n.yaml # packages/context/README.zh.md # packages/core/agent-loop/README.i18n.yaml # packages/credentials/README.i18n.yaml # packages/credentials/README.zh.md # packages/experimental/agent-team/README.i18n.yaml # packages/experimental/agent-team/README.zh.md # packages/experimental/tool-agent-team/README.i18n.yaml # packages/experimental/tool-agent-team/README.zh.md # packages/host/frontend-static/README.i18n.yaml # packages/host/frontend-static/README.zh.md # packages/host/webserver/README.i18n.yaml # packages/host/webserver/README.zh.md # packages/interaction/commands/README.i18n.yaml # packages/interaction/commands/README.zh.md # packages/plan/plan-mode/README.i18n.yaml # packages/plan/plan-mode/README.zh.md # packages/sandbox/sandbox-local/README.i18n.yaml # packages/sandbox/sandbox-local/README.zh.md # packages/session/README.i18n.yaml # packages/session/README.zh.md # packages/session/session-persistence-sqlite/README.i18n.yaml # packages/session/session-persistence-sqlite/README.zh.md # packages/session/session-projection-cache/README.i18n.yaml # packages/session/session-projection-cache/README.zh.md # packages/shell/tool-pwsh/README.i18n.yaml # packages/shell/tool-pwsh/README.zh.md # packages/subagent/subagent-codex/README.i18n.yaml # packages/subagent/subagent-codex/README.zh.md # packages/subagent/subagent/README.i18n.yaml # packages/subagent/subagent/README.zh.md # packages/web/tool-web/README.i18n.yaml # packages/web/tool-web/README.zh.md # scripts/snapshots/translation-prompt-v4/request-response.expected.json |
||
|---|---|---|
| .. | ||
| src | ||
| tests | ||
| package.json | ||
| README.i18n.yaml | ||
| README.md | ||
| README.zh.md | ||
| tsconfig.json | ||
@deepseek-ai/dsh-web
English | 中文
The WebRuntime (ctx.web) defines WHAT web access the harness has — search the web, fetch a URL — over multiple providers, without binding the model contract to one vendor's API shape.
This package owns the Service Definition role of the web capability. Unlike shell/fs it spans two operations (search and fetch) on one seam, with potentially multiple providers each:
| Package | Role |
|---|---|
@deepseek-ai/dsh-web (this) |
Service Definition: the service, provider registries, selection policy, request/result vocabulary, the WebError taxonomy |
@deepseek-ai/dsh-web-search-exa |
Search provider: Exa |
@deepseek-ai/dsh-web-search-perplexity |
Search provider: Perplexity |
@deepseek-ai/dsh-web-fetch-http |
Fetch provider: anonymous public HTTP(S) |
@deepseek-ai/dsh-tool-web |
Consumer: the model-facing web_search / web_fetch tool schemas over ctx.web |
Search and fetch share no request schema and no business logic, but they are deliberately one seam: ctx.web is a single web-access middle layer with one provider-selection policy owner, one abort/error vocabulary, and one product-facing "how this harness reaches the web" config surface. The Search/Fetch method pairs are deliberately parallel.
Service API (ctx.web)
| Member | Semantics |
|---|---|
registerSearchProvider(provider) / registerFetchProvider(provider) |
Register a backend. Throws WebError WEB_DUPLICATE_PROVIDER on a duplicate id within that capability kind. Returns a disposer. Disposed with the calling fiber. |
search(request, signal?) |
Resolve the search provider and run one search. Enforces request.maxResults on the result (truncates sources[], sets truncated). Throws WebError when the capability cannot run. |
fetch(request, signal?) |
Resolve the fetch provider and retrieve one URL. A non-2xx response is a result, not a throw. Throws WebError for failures to safely retrieve or represent the resource. |
Providers register capabilities, not tools. dsh-tool-web is the only owner of model-facing names, descriptions, prompt guidance, JSON schemas, and presentation.
Selection
Selection never depends on registration, config, or HMR order. A capability has an explicit provider id (config searchProvider/fetchProvider, or env $DSH_WEB_SEARCH_PROVIDER/$DSH_WEB_FETCH_PROVIDER feeding the same fields), or auto-selects when exactly one usable provider is registered. search()/fetch() resolve the provider at execution time:
| Situation | Execution |
|---|---|
configured id registered and available() |
runs that provider |
| configured id not registered | WEB_PROVIDER_CONFIGURED_MISSING |
| configured id registered but unavailable | WEB_PROVIDER_CONFIGURED_UNAVAILABLE |
| no id, exactly one registered usable provider | runs it |
| no id, no usable provider | WEB_PROVIDER_UNAVAILABLE |
| no id, multiple usable providers | WEB_PROVIDER_AMBIGUOUS |
The failure branches throw WebError, whose structured code (plus message detail — the missing id, the ambiguous candidate set) is the direct callers route on. A provider's own available() is a cheap local check (credential presence, parseable config) that feeds this execution-time selection and must not make network calls; dsh-tool-web never calls it — the tool executes through ctx.web.search()/fetch() and routes on the thrown codes, so provider selection has one owner.
Vocabulary
WebSearchRequest (query, maxResults?) → WebSearchResult (content?, sources[], truncated); each WebSearchSource has a required url and optional title/snippet/publishedAt (Perplexity citations may be URL-only). WebFetchRequest (url) → WebFetchResult (final url, statusCode, body, truncated); cancellation is a direct optional AbortSignal argument to search()/fetch(). WebFetchBody is a CLOSED discriminated union (html | text) owned here — consumers switch to exhaustiveness so a new kind breaks their compilation until handled. See src/types.ts for the full contracts and the WebError code taxonomy.
Model Experience
Indirectly, through dsh-tool-web, which retains bounded normalized provider data or the exact configured-provider, unavailable-provider, no-provider, multiple-provider, and Error: <message> failures while this registry contributes no prompt or schema itself.
KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
Known Limitations and Deferred Work
- No observation surface — no provider-change event and no capability-status query; availability is observed only by executing
search()/fetch()and routing the thrownWebErrorcodes, and the no-provider failure is the genericWEB_PROVIDER_UNAVAILABLEwith no per-provider reason enumeration (Agent Note). WebSearchRequestcarries onlyquery+maxResults— provider-neutral controls (recency, domain filters, regional hints, search depth) are deferred until Exa and Perplexity can both honor them honestly (seam Agent Note).WebFetchBodyhas nopdfarm — text-extractable PDF support is named deferred work; the closed union makes adding it a compile-enforced change across the three web packages.- Provider-backed page extraction is out of scope of
fetch()— a Firecrawl/Tavily-styleweb_extractcapability is deferred rather than widening the fetch operation.