From 744130725110d4efe535094652ac2c2b87bdc088 Mon Sep 17 00:00:00 2001 From: Dudu-0223 Date: Fri, 3 Jul 2026 15:52:05 +0800 Subject: [PATCH] fix(web): open WebError.code to string, aligning with other seams The closed WebErrorCode union leaked fetch-transport details (redirect, too-large, content-type) into the seam's shared vocabulary and made web the only seam with a closed error-code union. Drop it and let WebError carry an open code: string like LlmError/SubagentError; document the codes grouped by owner (seam-neutral vs dsh-web-fetch-local transport). Addresses tianyicui's leaky-abstraction review comment on WebErrorCode. --- docs/cordis-catalog/events-and-services.md | 4 +- docs/core-data-structures/core.md | 2 +- docs/core-data-structures/web.md | 19 +------- packages/web/web/src/index.ts | 1 - packages/web/web/src/types.ts | 55 ++++++++-------------- scripts/type-equiv.manifest.json | 3 +- 6 files changed, 25 insertions(+), 59 deletions(-) diff --git a/docs/cordis-catalog/events-and-services.md b/docs/cordis-catalog/events-and-services.md index c3843f944d..1bb243afc8 100644 --- a/docs/cordis-catalog/events-and-services.md +++ b/docs/cordis-catalog/events-and-services.md @@ -309,7 +309,7 @@ Fired after the provider registry changes — a search or fetch provider was reg 'web/providers-change'(this: WebService): void ``` -Source: [`packages/web/web/src/index.ts:66`](../../packages/web/web/src/index.ts) +Source: [`packages/web/web/src/index.ts:65`](../../packages/web/web/src/index.ts) ## Services @@ -506,7 +506,7 @@ async search(request: WebSearchRequest, exec?: WebExecContext): Promise ``` -Source: [`packages/web/web/src/index.ts:106`](../../packages/web/web/src/index.ts) +Source: [`packages/web/web/src/index.ts:105`](../../packages/web/web/src/index.ts) ## Inherited tier (cordis core + loader/hmr/timer) diff --git a/docs/core-data-structures/core.md b/docs/core-data-structures/core.md index fdfbcfbbf4..b6ce3d060f 100644 --- a/docs/core-data-structures/core.md +++ b/docs/core-data-structures/core.md @@ -22,7 +22,7 @@ Everything else is documented on a **sub-page**, not here. The rule that draws t | [bash.md](bash.md) | the bash executor seam: `BashExecRequest`/`Spec`, `BashRunResult`, background `BashTask`s | | [compaction.md](compaction.md) | the compaction seam: the `compact/*` session events, `CompactionResult`, the `CompactService` interface | | [subagent.md](subagent.md) | the subagent seam: the named-provider registry, `SubagentStartRequest`/`Result`/`Run`, the start-time-vs-runtime capability split | -| [web.md](web.md) | the web access seam: `WebSearchRequest`/`Result`, `WebFetchRequest`/`Result`, `WebFetchBody`, provider/capability status, `WebErrorCode` | +| [web.md](web.md) | the web access seam: `WebSearchRequest`/`Result`, `WebFetchRequest`/`Result`, `WebFetchBody`, provider/capability status, `WebError` | > Type definitions on this page are pasted **verbatim** from source and drift-checked by `pnpm run verify-type-equiv` (see [development.md](../development.md#documenting-types-verbatim-ts-type-equiv)). Inline JSDoc is omitted for readability; follow the source link for the full contracts. diff --git a/docs/core-data-structures/web.md b/docs/core-data-structures/web.md index 43ed4e7aeb..1adde3bd75 100644 --- a/docs/core-data-structures/web.md +++ b/docs/core-data-structures/web.md @@ -95,24 +95,7 @@ Selection never depends on registration, config, or HMR order: a capability has ## Errors -`WebError extends HarnessError` ([core.md](core.md) error taxonomy) with a stable `WebErrorCode`. `WEB_DUPLICATE_PROVIDER` is a registration-time programming error (the analogue of `LlmService`'s `DUPLICATE_ADAPTER`); the `WEB_PROVIDER_*` selection codes and the fetch transport codes are execution outcomes. `WEB_PROVIDER_ERROR` is the catch-all for a provider's own failure surfaced through the seam, including network/transport failure (DNS, connection refused, TLS). - -```ts type-equiv -type WebErrorCode = - | 'WEB_PROVIDER_UNAVAILABLE' - | 'WEB_PROVIDER_CONFIGURED_MISSING' - | 'WEB_PROVIDER_CONFIGURED_UNAVAILABLE' - | 'WEB_PROVIDER_AMBIGUOUS' - | 'WEB_DUPLICATE_PROVIDER' - | 'WEB_INVALID_URL' - | 'WEB_BLOCKED_URL' - | 'WEB_REDIRECT_BLOCKED' - | 'WEB_FETCH_TOO_LARGE' - | 'WEB_FETCH_TIMEOUT' - | 'WEB_ABORTED' - | 'WEB_UNSUPPORTED_CONTENT_TYPE' - | 'WEB_PROVIDER_ERROR' -``` +`WebError extends HarnessError` ([core.md](core.md) error taxonomy) with a `code: string` (open, like every other seam's error — `LlmError`, `SubagentError`), not a closed union: a provider may raise its own codes without editing `dsh-web`, and consumers must tolerate an unknown code. The codes split by owner. Seam-neutral codes are raised by `WebService` selection and the shared contract: `WEB_PROVIDER_UNAVAILABLE`, `WEB_PROVIDER_CONFIGURED_MISSING`, `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`, `WEB_PROVIDER_AMBIGUOUS`, `WEB_DUPLICATE_PROVIDER` (a registration-time programming error, the analogue of `LlmService`'s `DUPLICATE_ADAPTER`), `WEB_ABORTED`, and `WEB_PROVIDER_ERROR` (the catch-all for a provider's own failure surfaced through the seam, including network/transport failure — DNS, connection refused, TLS). Fetch-transport codes are owned by the `dsh-web-fetch-local` implementation and a different fetch backend need not raise them: `WEB_INVALID_URL`, `WEB_BLOCKED_URL`, `WEB_REDIRECT_BLOCKED`, `WEB_FETCH_TOO_LARGE`, `WEB_FETCH_TIMEOUT`, `WEB_UNSUPPORTED_CONTENT_TYPE`. ## The service diff --git a/packages/web/web/src/index.ts b/packages/web/web/src/index.ts index 172c1a0ecb..50150f6961 100644 --- a/packages/web/web/src/index.ts +++ b/packages/web/web/src/index.ts @@ -36,7 +36,6 @@ export { } from './types.ts' export type { WebCapabilityStatus, - WebErrorCode, WebExecContext, WebFetchBody, WebFetchProvider, diff --git a/packages/web/web/src/types.ts b/packages/web/web/src/types.ts index ec97101ae2..6f85787d1b 100644 --- a/packages/web/web/src/types.ts +++ b/packages/web/web/src/types.ts @@ -172,8 +172,19 @@ export interface WebFetchProvider { } /** - * Stable codes for {@link WebError}. Callers (hooks, tests, UI) route on these. + * Typed web error. Extends {@link HarnessError} so it carries a stable, + * machine-routable `code` (a `string`, like every other seam's error) and + * chains `cause`. `ToolRegistry.execute()` converts a thrown `WebError` into an + * error tool result whose structured metadata exposes the code, so callers + * (hooks, tests, UI) route on it. * + * The `code` is an open `string`, NOT a closed union: a provider may raise its + * own codes without editing this package, and a consumer must tolerate an + * unknown code (a future provider will introduce ones this file never named). + * The codes split by who owns them — seam-neutral codes any provider may see, + * versus codes specific to a single implementation: + * + * Seam-neutral (raised by `WebService` selection and the shared contract): * - `WEB_PROVIDER_UNAVAILABLE`: no provider configured and none usable. * - `WEB_PROVIDER_CONFIGURED_MISSING`: a configured id is not registered. * - `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`: a configured id is registered but its @@ -182,44 +193,18 @@ export interface WebFetchProvider { * exist (selection refuses to pick by registration order). * - `WEB_DUPLICATE_PROVIDER`: a registration-time programming error — an id is * already registered for that capability kind. + * - `WEB_ABORTED`: the operation was aborted via `WebExecContext.signal`. + * - `WEB_PROVIDER_ERROR`: catch-all for a provider's own failure surfaced + * through the seam, including network/transport failure (DNS, connection + * refused, TLS). + * + * Fetch-transport codes (owned by the `dsh-web-fetch-local` implementation; a + * different fetch backend need not raise these and may raise its own): * - `WEB_INVALID_URL`: the fetch URL is malformed or not http(s). * - `WEB_BLOCKED_URL`: the fetch URL is rejected by policy (credentials in URL). * - `WEB_REDIRECT_BLOCKED`: a cross-origin redirect was refused. * - `WEB_FETCH_TOO_LARGE`: the response exceeded the byte/character cap. * - `WEB_FETCH_TIMEOUT`: the fetch exceeded its timeout. - * - `WEB_ABORTED`: the operation was aborted via `WebExecContext.signal`. * - `WEB_UNSUPPORTED_CONTENT_TYPE`: the response content type cannot be decoded. - * - `WEB_PROVIDER_ERROR`: catch-all for a provider's own failure surfaced through - * the seam, including network/transport failure (DNS, connection refused, TLS). */ -export type WebErrorCode = - | 'WEB_PROVIDER_UNAVAILABLE' - | 'WEB_PROVIDER_CONFIGURED_MISSING' - | 'WEB_PROVIDER_CONFIGURED_UNAVAILABLE' - | 'WEB_PROVIDER_AMBIGUOUS' - | 'WEB_DUPLICATE_PROVIDER' - | 'WEB_INVALID_URL' - | 'WEB_BLOCKED_URL' - | 'WEB_REDIRECT_BLOCKED' - | 'WEB_FETCH_TOO_LARGE' - | 'WEB_FETCH_TIMEOUT' - | 'WEB_ABORTED' - | 'WEB_UNSUPPORTED_CONTENT_TYPE' - | 'WEB_PROVIDER_ERROR' - -/** - * Typed web error. Extends {@link HarnessError} so it carries a stable - * {@link WebErrorCode} and chains `cause`. `dsh-web` owns this vocabulary so - * providers, the seam, and the tool layer raise the same codes instead of each - * inventing message strings. `ToolRegistry.execute()` converts a thrown - * `WebError` into an error tool result whose structured metadata exposes the - * code. - */ -export class WebError extends HarnessError { - override readonly code: WebErrorCode - - constructor(message: string, code: WebErrorCode, options?: ErrorOptions) { - super(message, code, options) - this.code = code - } -} +export class WebError extends HarnessError {} diff --git a/scripts/type-equiv.manifest.json b/scripts/type-equiv.manifest.json index 2a499580a7..ed8d342e28 100644 --- a/scripts/type-equiv.manifest.json +++ b/scripts/type-equiv.manifest.json @@ -58,7 +58,6 @@ { "doc": "docs/core-data-structures/web.md", "symbol": "WebFetchResult", "source": "packages/web/web/src/types.ts" }, { "doc": "docs/core-data-structures/web.md", "symbol": "WebFetchBody", "source": "packages/web/web/src/types.ts" }, { "doc": "docs/core-data-structures/web.md", "symbol": "WebProviderStatus", "source": "packages/web/web/src/types.ts" }, - { "doc": "docs/core-data-structures/web.md", "symbol": "WebCapabilityStatus", "source": "packages/web/web/src/types.ts" }, - { "doc": "docs/core-data-structures/web.md", "symbol": "WebErrorCode", "source": "packages/web/web/src/types.ts" } + { "doc": "docs/core-data-structures/web.md", "symbol": "WebCapabilityStatus", "source": "packages/web/web/src/types.ts" } ] }