@xneog/dsh-web 0.1.0 → 0.1.2-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +136 -28
- package/README.zh.md +139 -31
- package/lib/types/types.d.ts +4 -3
- package/package.json +6 -13
- package/lib/invariant.js +0 -23
- package/lib/types/invariant.d.ts +0 -16
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/web/web/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 305509b3f4666611e25abe582a5dc59f43852f03
|
|
6
|
+
README.zh.md: db7a1e32ba3d4c72e55254bd0278c05c90fb657b
|
package/README.md
CHANGED
|
@@ -1,53 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "The web access service (ctx.web): how deployments and plugin authors search the web and fetch URLs through interchangeable providers, with one selection policy and error vocabulary."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @xneog/dsh-web
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
Any plugin or tool can search the web or fetch a URL through `dsh-web` (`ctx.web`) without binding to any vendor's API. Search and fetch providers plug in as backends, and the service picks one usable provider per operation, so callers never track which vendor runs behind a call. Choose it when building web tooling or another backend; the shipped model-facing tools (`dsh-tool-web`) mount it automatically. The service itself makes no network calls and registers no model-facing tool: a provider must be mounted before search or fetch can run. Search and fetch share one selection policy, one cancellation and error vocabulary, and one configuration surface, so "how this harness reaches the web" has a single owner.
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|---|---|
|
|
11
|
-
| `@xneog/dsh-web` (this) | Service Definition: the service, provider registries, selection policy, request/result vocabulary, the `WebError` taxonomy |
|
|
12
|
-
| `@xneog/dsh-web-search-exa` | Search provider: Exa |
|
|
13
|
-
| `@xneog/dsh-web-search-perplexity` | Search provider: Perplexity |
|
|
14
|
-
| `@xneog/dsh-web-fetch-http` | Fetch provider: anonymous public HTTP(S) |
|
|
15
|
-
| `@xneog/dsh-tool-web` | Consumer: the model-facing `web_search` / `web_fetch` tool schemas over `ctx.web` |
|
|
14
|
+
## Table of Contents
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
-----
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
A composition that needs web access loads the `dsh-web` service and mounts at least one backend — a search provider and/or a fetch provider — and plugin or tool authors then call `ctx.web.search()` and `ctx.web.fetch()` directly. The service resolves the backend for each call, so callers never see provider ids unless they configured one.
|
|
29
|
+
|
|
30
|
+
### When to choose it
|
|
31
|
+
|
|
32
|
+
Choose the service when a plugin or tool must search or fetch without hard-coding a vendor; a deployment that only uses the shipped `web_search`/`web_fetch` tools gets it for free through `dsh-tool-web`. You do not need it when the composition never reaches the web. The service adds no network access of its own: without at least one usable provider, every call fails with a structured `WebError`.
|
|
33
|
+
|
|
34
|
+
### Minimal configuration
|
|
35
|
+
|
|
36
|
+
Load the service and let a single mounted backend auto-select, or pin a provider id with `searchProvider`/`fetchProvider`. The environment variables `$DSH_WEB_SEARCH_PROVIDER` and `$DSH_WEB_FETCH_PROVIDER` feed the same fields and are not a separate priority chain.
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- name: '@xneog/dsh-web'
|
|
40
|
+
- name: '@xneog/dsh-web-search-exa'
|
|
41
|
+
- name: '@xneog/dsh-web-fetch-http'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Field | Default | Meaning |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `searchProvider` | (unset) | Pinned search provider id; unset auto-selects when exactly one is usable |
|
|
47
|
+
| `fetchProvider` | (unset) | Pinned fetch provider id; unset auto-selects when exactly one is usable |
|
|
48
|
+
|
|
49
|
+
The generated [configuration catalog](../../../docs/config-catalog.md#xneogdsh-web) is the exhaustive source for every accepted field and its JSDoc.
|
|
26
50
|
|
|
27
|
-
|
|
51
|
+
### Searching and fetching
|
|
28
52
|
|
|
29
|
-
|
|
53
|
+
`search()` runs one query and returns an optional provider answer plus a list of citeable sources; the service enforces `request.maxResults` by truncating `sources[]` and setting `truncated`. `fetch()` retrieves one URL and returns its final URL, status code, decoded body, and a truncation flag; a non-2xx response is a result, not an error.
|
|
30
54
|
|
|
31
|
-
|
|
55
|
+
```text
|
|
56
|
+
// Search the web; sources[] is capped to maxResults:
|
|
57
|
+
const result = await ctx.web.search({ query: 'deepseek harness', maxResults: 8 })
|
|
32
58
|
|
|
33
|
-
|
|
59
|
+
// Fetch one URL; a non-2xx response is a result, not an error:
|
|
60
|
+
const page = await ctx.web.fetch({ url: 'https://example.com' })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Both calls accept an optional `AbortSignal` that is forwarded to the provider for cancellation. The normalized request and result shapes are the contract callers build on; the vocabulary section of the [web subsystem](../../../docs/subsystems/web.md) reference describes them exhaustively.
|
|
64
|
+
|
|
65
|
+
### Provider selection
|
|
66
|
+
|
|
67
|
+
Each call resolves its provider at execution time, and registration or load order never matters. A configured provider id wins when it is registered and usable; without a configured id, the service runs the single usable provider or fails clearly:
|
|
68
|
+
|
|
69
|
+
| Situation | Outcome |
|
|
34
70
|
|---|---|
|
|
35
|
-
| configured id registered and
|
|
71
|
+
| configured id registered and usable | runs that provider |
|
|
36
72
|
| configured id not registered | `WEB_PROVIDER_CONFIGURED_MISSING` |
|
|
37
73
|
| configured id registered but unavailable | `WEB_PROVIDER_CONFIGURED_UNAVAILABLE` |
|
|
38
74
|
| no id, exactly one registered usable provider | runs it |
|
|
39
75
|
| no id, no usable provider | `WEB_PROVIDER_UNAVAILABLE` |
|
|
40
76
|
| no id, multiple usable providers | `WEB_PROVIDER_AMBIGUOUS` |
|
|
41
77
|
|
|
42
|
-
|
|
78
|
+
A provider's availability is a cheap local check — for example whether its API key is present — and never makes network calls, so selection stays fast and deterministic.
|
|
79
|
+
|
|
80
|
+
### Failures and recovery
|
|
81
|
+
|
|
82
|
+
Failures throw `WebError` with a stable, machine-routable code; the message adds detail such as the missing provider id or the ambiguous candidate set. Callers route on the code and decide how to degrade. To change which backend a call uses, reconfigure the pinned id, mount or unmount providers, or fix the provider's configuration so its availability check passes.
|
|
83
|
+
|
|
84
|
+
-----
|
|
85
|
+
|
|
86
|
+
<a id="understand-the-implementation"></a>
|
|
87
|
+
## Understand the implementation
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary>Implementation internals — click to expand</summary>
|
|
91
|
+
|
|
92
|
+
This section explains the design decisions behind the service; the observable behavior is fully covered in [Use this package](#use-this-package).
|
|
93
|
+
|
|
94
|
+
### Design philosophy
|
|
43
95
|
|
|
44
|
-
|
|
96
|
+
The package is built on one deliberate separation:
|
|
45
97
|
|
|
46
|
-
|
|
98
|
+
- **One seam, two independent operations.** Search and fetch share no request schema and no business logic, but they share one service so provider selection, cancellation, errors, and product configuration have a single owner. The parallel `Search`/`Fetch` method pairs are intentional.
|
|
99
|
+
- **Selection is never order-dependent.** A capability either pins a provider id or auto-selects when exactly one usable provider is registered; `search()`/`fetch()` resolve the provider at execution time.
|
|
100
|
+
- **The service owns the result bound.** `maxResults` is enforced by the seam after the provider returns, so an over-returning provider can never leak more sources than the caller asked for.
|
|
47
101
|
|
|
102
|
+
### Source map
|
|
103
|
+
|
|
104
|
+
| File | Role |
|
|
105
|
+
|---|---|
|
|
106
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: the `WebRuntime` service, both provider registries, and execution-time selection |
|
|
107
|
+
| [`src/types.ts`](src/types.ts) | Vocabulary: request/result types, the closed `WebFetchBody` union, and the `WebError` taxonomy |
|
|
108
|
+
| — | No runtime invariant companion is published; provider maps are private and selection/result caps are enforced on each call; the seam publishes no independent registry or request/result observation stream. |
|
|
109
|
+
|
|
110
|
+
### Data model
|
|
111
|
+
|
|
112
|
+
The request and result types define the normalized vocabulary callers build on — one `Search` pair and one `Fetch` pair — and the exhaustive fields and JSDoc live in [`src/types.ts`](src/types.ts) and the [web subsystem](../../../docs/subsystems/web.md) reference. Two deliberate choices shape them: `WebFetchBody` is a closed union (`html` | `text`) owned here, so adding a kind breaks compilation until every consumer handles it; `WebError` extends `HarnessError` with an open-string `code`, so consumers must tolerate provider-specific values. Source fields stay optional because not every provider returns all of them.
|
|
113
|
+
|
|
114
|
+
### Selection flow
|
|
115
|
+
|
|
116
|
+
At call time the service resolves the provider — configured id first, then the unique usable provider — and throws the matching `WebError` when no clear winner exists. A search result then passes through `capSources`, which truncates `sources[]` to `maxResults` and flags `truncated`. Registration is effect-based: providers register with the calling fiber and unregister when it disposes, and a duplicate id within a capability kind is rejected at registration.
|
|
117
|
+
|
|
118
|
+
</details>
|
|
119
|
+
|
|
120
|
+
-----
|
|
121
|
+
|
|
122
|
+
<a id="further-exploration"></a>
|
|
123
|
+
## Further Exploration
|
|
124
|
+
|
|
125
|
+
Read these pages when the package-level contract is not enough. They move from the shared vocabulary to the shipped backends, the model-facing tools, and the design rationale.
|
|
126
|
+
|
|
127
|
+
- [Web subsystem](../../../docs/subsystems/web.md) — the exhaustive search/fetch requests and results, provider availability, and error codes.
|
|
128
|
+
- [Web package map](../README.md) — the six-package family and each role.
|
|
129
|
+
- [dsh-tool-web](../tool-web/README.md) — the model-facing `web_search` and `web_fetch` tools over this service.
|
|
130
|
+
- [dsh-web-fetch-http](../web-fetch-http/README.md) — the shipped anonymous HTTP(S) fetch backend.
|
|
131
|
+
- [Generated configuration catalog](../../../docs/config-catalog.md#xneogdsh-web) — every accepted config field and its source declaration.
|
|
132
|
+
- [Web capability seam decision](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) — why search and fetch share one provider-selection service.
|
|
133
|
+
|
|
134
|
+
-----
|
|
135
|
+
|
|
136
|
+
<a id="model-experience"></a>
|
|
48
137
|
## Model Experience
|
|
49
138
|
|
|
50
|
-
Indirectly, through `dsh-tool-web`, which
|
|
139
|
+
Indirectly, through `dsh-tool-web`, which renders the seam's normalized search results and fetch bodies to the model while this service contributes no prompt or schema.
|
|
51
140
|
|
|
52
141
|
#### KV Cache effect
|
|
53
142
|
|
|
@@ -55,7 +144,26 @@ No direct invalidation; the named consumer owns any request-prefix changes.
|
|
|
55
144
|
|
|
56
145
|
## Known Limitations and Deferred Work
|
|
57
146
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
147
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
These limits define when the service is incomplete on its own. They are current package constraints.
|
|
151
|
+
|
|
152
|
+
- **No observation surface** — there is no provider-change event and no capability-status query; availability is observable only by running a search or fetch and routing the thrown code, and the no-provider failure is the generic `WEB_PROVIDER_UNAVAILABLE` with no per-provider reason enumeration ([Agent Note](../../../.agents/notes/archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md)).
|
|
153
|
+
- **Search requests carry only `query` and `maxResults`** — provider-neutral controls (recency, domain filters, regional hints, search depth) are deferred until the backends can honor them ([seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md)).
|
|
154
|
+
- **`WebFetchBody` has no `pdf` arm** — text-extractable PDF support is named deferred work; the closed union makes adding it a compile-enforced change across the web packages.
|
|
61
155
|
- **Provider-backed page extraction is out of scope of `fetch()`** — a Firecrawl/Tavily-style `web_extract` capability is deferred rather than widening the fetch operation.
|
|
156
|
+
|
|
157
|
+
<a id="dev-note"></a>
|
|
158
|
+
### Dev Note
|
|
159
|
+
|
|
160
|
+
<details>
|
|
161
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
162
|
+
|
|
163
|
+
This Dev Note is working context for maintainers: open questions and undecided directions. It is explicitly non-authoritative — shipped behavior, limits, and rationale live in the sections above and the linked Agent Notes.
|
|
164
|
+
|
|
165
|
+
#### Future: observing provider state
|
|
166
|
+
|
|
167
|
+
No provider-change event or capability-status query exists; consumers observe availability only by executing a call and routing the thrown code. Restoring a small observation surface is possible if a consumer needs per-provider reasons, but the archived simplification note records why the earlier one was dropped.
|
|
168
|
+
|
|
169
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,61 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "web 访问服务(ctx.web):部署方与插件作者如何通过可互换的提供方搜索 web 与抓取 URL,以及统一的选择策略与错误词汇。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @xneog/dsh-web
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
任何插件或工具都可以通过 `dsh-web`(`ctx.web`)搜索 web 或抓取 URL,而无需绑定任何厂商的 API。搜索与抓取提供方以后端形式接入,服务按操作挑选一个可用的提供方,调用方无需追踪每次调用背后是哪家厂商。在构建 web 工具或其他后端时选择它;已交付的面向模型工具(`dsh-tool-web`)会自动挂载它。服务本身不发起网络调用、不注册面向模型的工具:搜索或抓取执行前必须已挂载提供方。搜索与抓取共用同一套选择策略、取消与错误词汇以及配置接口,因此「这个 harness 如何访问 web」只有一个归属方。
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|---|---|
|
|
11
|
-
| `@xneog/dsh-web`(本包) | Service Definition:服务、提供方注册表、选择策略、请求/结果词汇、`WebError` 分类体系 |
|
|
12
|
-
| `@xneog/dsh-web-search-exa` | 搜索提供方:Exa |
|
|
13
|
-
| `@xneog/dsh-web-search-perplexity` | 搜索提供方:Perplexity |
|
|
14
|
-
| `@xneog/dsh-web-fetch-http` | 抓取提供方:匿名公共 HTTP(S) |
|
|
15
|
-
| `@xneog/dsh-tool-web` | Consumer:面向模型的 `web_search`/`web_fetch` 工具 schema,构建于 `ctx.web` 之上 |
|
|
14
|
+
## 目录
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
-----
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
需要 web 访问的组合会加载 `dsh-web` 服务并挂载至少一个后端——搜索提供方和/或抓取提供方——插件或工具作者随后直接调用 `ctx.web.search()` 与 `ctx.web.fetch()`。服务会为每次调用解析后端,因此除非调用方配置了提供方 id,否则它们看不到提供方 id。
|
|
29
|
+
|
|
30
|
+
### 何时选择
|
|
31
|
+
|
|
32
|
+
当插件或工具必须搜索或抓取、又不希望硬编码厂商时选择本服务;只使用已交付的 `web_search`/`web_fetch` 工具的组合会通过 `dsh-tool-web` 免费获得它。当组合从不访问 web 时,你不需要它。服务本身不增加任何网络访问能力:没有至少一个可用提供方时,每次调用都会以结构化 `WebError` 失败。
|
|
33
|
+
|
|
34
|
+
### 最小配置
|
|
35
|
+
|
|
36
|
+
加载服务并让唯一挂载的后端自动选择,或用 `searchProvider`/`fetchProvider` 固定提供方 id。环境变量 `$DSH_WEB_SEARCH_PROVIDER` 与 `$DSH_WEB_FETCH_PROVIDER` 提供相同字段,不是另一条优先级链。
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- name: '@xneog/dsh-web'
|
|
40
|
+
- name: '@xneog/dsh-web-search-exa'
|
|
41
|
+
- name: '@xneog/dsh-web-fetch-http'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| 字段 | 默认值 | 含义 |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `searchProvider` | (未设置) | 固定的搜索提供方 id;未设置时仅在恰好一个可用时自动选择 |
|
|
47
|
+
| `fetchProvider` | (未设置) | 固定的抓取提供方 id;未设置时仅在恰好一个可用时自动选择 |
|
|
48
|
+
|
|
49
|
+
生成的[配置目录](../../../docs/config-catalog.zh.md#xneogdsh-web)是每个受支持字段及其 JSDoc 的穷尽式真源。
|
|
26
50
|
|
|
27
|
-
|
|
51
|
+
### 搜索与抓取
|
|
28
52
|
|
|
29
|
-
|
|
53
|
+
`search()` 执行一次查询,返回可选的提供方答案与可引用的来源列表;服务强制执行 `request.maxResults`:截断 `sources[]` 并设置 `truncated`。`fetch()` 获取一个 URL,返回其最终 URL、状态码、解码后的正文与截断标志;非 2xx 响应是结果,不是错误。
|
|
30
54
|
|
|
31
|
-
|
|
55
|
+
```text
|
|
56
|
+
// Search the web; sources[] is capped to maxResults:
|
|
57
|
+
const result = await ctx.web.search({ query: 'deepseek harness', maxResults: 8 })
|
|
32
58
|
|
|
33
|
-
|
|
59
|
+
// Fetch one URL; a non-2xx response is a result, not an error:
|
|
60
|
+
const page = await ctx.web.fetch({ url: 'https://example.com' })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
两个调用都接受可选的 `AbortSignal`,用于把取消转发给提供方。规范化的请求与结果形状是调用方赖以构建的约定;[web 子系统](../../../docs/subsystems/web.zh.md) 参考中的词汇章节对其有穷尽式描述。
|
|
64
|
+
|
|
65
|
+
### 提供方选择
|
|
66
|
+
|
|
67
|
+
每次调用都在执行时解析提供方,注册或加载顺序从不影响结果。已配置的提供方 id 在已注册且可用时优先;没有配置 id 时,服务运行唯一可用的提供方,或在情况不明时明确失败:
|
|
68
|
+
|
|
69
|
+
| 情况 | 结果 |
|
|
34
70
|
|---|---|
|
|
35
|
-
| 已配置 id
|
|
71
|
+
| 已配置 id 已注册且可用 | 运行该提供方 |
|
|
36
72
|
| 已配置 id 未注册 | `WEB_PROVIDER_CONFIGURED_MISSING` |
|
|
37
73
|
| 已配置 id 已注册但不可用 | `WEB_PROVIDER_CONFIGURED_UNAVAILABLE` |
|
|
38
|
-
| 无 id,恰好一个已注册的可用提供方 |
|
|
74
|
+
| 无 id,恰好一个已注册的可用提供方 | 运行它 |
|
|
39
75
|
| 无 id,没有可用提供方 | `WEB_PROVIDER_UNAVAILABLE` |
|
|
40
76
|
| 无 id,多个可用提供方 | `WEB_PROVIDER_AMBIGUOUS` |
|
|
41
77
|
|
|
42
|
-
|
|
78
|
+
提供方的可用性是一项廉价的局部检查——例如其 API 密钥是否存在——并且从不发起网络调用,因此选择保持快速且确定。
|
|
79
|
+
|
|
80
|
+
### 失败与恢复
|
|
81
|
+
|
|
82
|
+
失败抛出 `WebError`,携带稳定、可按机器路由的 code;消息补充细节,例如缺失的提供方 id 或歧义候选集合。调用方按 code 路由并决定如何降级。要改变一次调用使用的后端,请重新配置固定的 id、挂载或卸载提供方,或修正提供方配置使其可用性检查通过。
|
|
83
|
+
|
|
84
|
+
-----
|
|
85
|
+
|
|
86
|
+
<a id="understand-the-implementation"></a>
|
|
87
|
+
## 理解实现
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary>实现细节——点击展开</summary>
|
|
91
|
+
|
|
92
|
+
本节解释服务背后的设计决策;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
93
|
+
|
|
94
|
+
### 设计理念
|
|
43
95
|
|
|
44
|
-
|
|
96
|
+
本包建立在一个刻意的分离之上:
|
|
45
97
|
|
|
46
|
-
|
|
98
|
+
- **一个 seam,两个独立操作。** 搜索与抓取没有共享请求 schema 或业务逻辑,但它们共用一个服务,使提供方选择、取消、错误与产品配置只有一个归属方。并行的 `Search`/`Fetch` 方法对是有意为之。
|
|
99
|
+
- **选择绝不依赖顺序。** 能力要么固定提供方 id,要么在恰好注册一个可用提供方时自动选择;`search()`/`fetch()` 在执行时解析提供方。
|
|
100
|
+
- **服务拥有结果上限。** `maxResults` 由 seam 在提供方返回后强制执行,因此超量返回的提供方绝不可能泄漏超出调用方要求的来源。
|
|
47
101
|
|
|
102
|
+
### 源码地图
|
|
103
|
+
|
|
104
|
+
| 文件 | 职责 |
|
|
105
|
+
|---|---|
|
|
106
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:`WebRuntime` 服务、两个提供方注册表与执行时选择 |
|
|
107
|
+
| [`src/types.ts`](src/types.ts) | 词汇:请求/结果类型、封闭的 `WebFetchBody` 联合与 `WebError` 分类体系 |
|
|
108
|
+
| — | 不发布运行时不变式伴生入口;约定在服务处强制执行。 |
|
|
109
|
+
|
|
110
|
+
### 数据模型
|
|
111
|
+
|
|
112
|
+
请求与结果类型定义了调用方赖以构建的规范化词汇——一组 `Search` 对与一组 `Fetch` 对——穷尽式字段与 JSDoc 见 [`src/types.ts`](src/types.ts) 与 [web 子系统](../../../docs/subsystems/web.zh.md) 参考。两个刻意的选择塑造了它们:`WebFetchBody` 是这里拥有的封闭联合(`html` | `text`),因此新增类型会破坏编译,直到每个消费方都处理它;`WebError` 继承 `HarnessError`,携带开放的字符串 `code`,因此消费方必须容忍提供方专有的取值。来源字段保持可选,因为并非每个提供方都返回全部字段。
|
|
113
|
+
|
|
114
|
+
### 选择流程
|
|
115
|
+
|
|
116
|
+
执行时,服务先按配置 id、再按唯一可用提供方解析提供方,没有明确赢家时抛出对应的 `WebError`。搜索结果随后经过 `capSources`:把 `sources[]` 截断到 `maxResults` 并标记 `truncated`。注册基于 effect:提供方随调用 fiber 注册,fiber 释放时注销;同一能力类型下重复的 id 会在注册时被拒绝。
|
|
117
|
+
|
|
118
|
+
</details>
|
|
119
|
+
|
|
120
|
+
-----
|
|
121
|
+
|
|
122
|
+
<a id="further-exploration"></a>
|
|
123
|
+
## 进一步探索
|
|
124
|
+
|
|
125
|
+
当包级约定不够用时阅读以下页面。它们从共享词汇逐步进入已交付后端、面向模型的工具与设计依据。
|
|
126
|
+
|
|
127
|
+
- [web 子系统](../../../docs/subsystems/web.zh.md)——穷尽式的搜索/抓取请求与结果、提供方可用性与错误码。
|
|
128
|
+
- [web 包映射](../README.zh.md)——六包家族与各角色。
|
|
129
|
+
- [dsh-tool-web](../tool-web/README.zh.md)——构建于本服务之上的面向模型 `web_search` 与 `web_fetch` 工具。
|
|
130
|
+
- [dsh-web-fetch-http](../web-fetch-http/README.zh.md)——已交付的匿名 HTTP(S) 抓取后端。
|
|
131
|
+
- [生成配置目录](../../../docs/config-catalog.zh.md#xneogdsh-web)——每个受支持配置字段及其源声明。
|
|
132
|
+
- [web 能力 seam 决策](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.zh.md)——搜索与抓取为何共用一项提供方选择服务。
|
|
133
|
+
|
|
134
|
+
-----
|
|
135
|
+
|
|
136
|
+
<a id="model-experience"></a>
|
|
48
137
|
## 模型体验
|
|
49
138
|
|
|
50
|
-
|
|
139
|
+
间接地,通过 `dsh-tool-web`:该工具把 seam 规范化的搜索结果与抓取正文渲染给模型,而本服务不贡献任何提示词或 schema。
|
|
51
140
|
|
|
52
141
|
#### KV Cache 影响
|
|
53
142
|
|
|
54
143
|
不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
|
|
55
144
|
|
|
56
|
-
##
|
|
145
|
+
## 已知限制与延期工作
|
|
146
|
+
|
|
147
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
这些限制说明本服务单独使用时在哪些方面不完整。它们是当前包约束。
|
|
151
|
+
|
|
152
|
+
- **没有观测接口**:没有提供方变更事件或能力状态查询;可用性只能通过执行搜索或抓取并按抛出的 code 路由来观测,无提供方失败是通用的 `WEB_PROVIDER_UNAVAILABLE`,不枚举逐提供方原因(见 [Agent Note](../../../.agents/notes/archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md))。
|
|
153
|
+
- **搜索请求只携带 `query` 与 `maxResults`**:提供方无关的控制项(新近程度、域名过滤条件、区域提示、搜索深度)暂缓至后端都能诚实支持时(见 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.zh.md))。
|
|
154
|
+
- **`WebFetchBody` 没有 `pdf` 分支**:可提取文本的 PDF 支持属于明确的延期工作;封闭联合会使新增该分支成为跨 web 包、由编译强制执行的变更。
|
|
155
|
+
- **提供方支持的页面提取不属于 `fetch()` 范围**:Firecrawl/Tavily 风格的 `web_extract` 能力延期,而不会扩展抓取操作。
|
|
156
|
+
|
|
157
|
+
<a id="dev-note"></a>
|
|
158
|
+
### 开发备注
|
|
159
|
+
|
|
160
|
+
<details>
|
|
161
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
162
|
+
|
|
163
|
+
本开发备注是维护者的工作上下文:开放问题与尚未决定的探索方向。它明确不具权威性——已交付的行为、限制与既定理由以上文和相关 Agent Note 为准。
|
|
164
|
+
|
|
165
|
+
#### 未来:观测提供方状态
|
|
166
|
+
|
|
167
|
+
没有提供方变更事件或能力状态查询;消费方只能通过执行调用并按抛出的 code 路由来观测可用性。如果消费方需要逐提供方原因,恢复一个小的观测接口是可行的,但已归档的简化笔记记录了为何放弃此前的那个接口。
|
|
57
168
|
|
|
58
|
-
|
|
59
|
-
- **`WebSearchRequest` 只携带 `query` + `maxResults`**:提供方无关的控制项(新近程度、域名过滤条件、区域提示、搜索深度)暂缓至 Exa 与 Perplexity 都能诚实支持时(见 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md))。
|
|
60
|
-
- **`WebFetchBody` 没有 `pdf` 分支**:可提取文本的 PDF 支持属于明确的暂缓工作;封闭联合会使新增该分支成为三个 web 包中由编译强制执行的变更。
|
|
61
|
-
- **提供方支持的页面提取不属于 `fetch()` 范围**:Firecrawl/Tavily 风格的 `web_extract` 能力暂缓,而不会扩展抓取操作。
|
|
169
|
+
</details>
|
package/lib/types/types.d.ts
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { HarnessError } from '@xneog/dsh-llm';
|
|
8
8
|
/**
|
|
9
|
-
* What one search-capable backend
|
|
10
|
-
*
|
|
11
|
-
* and enforced on the way
|
|
9
|
+
* What one search-capable backend is asked to search. Each request carries one
|
|
10
|
+
* query; a consumer may issue several requests. `maxResults` is a
|
|
11
|
+
* `dsh-tool-web`-layer bound passed through unchanged and enforced on the way
|
|
12
|
+
* back by the seam (see {@link WebSearchResult}).
|
|
12
13
|
*/
|
|
13
14
|
export interface WebSearchRequest {
|
|
14
15
|
readonly query: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xneog/dsh-web",
|
|
3
3
|
"description": "Abstract web access capability seam (ctx.web) for the xneog — search/fetch provider registry, registration-order-independent selection, request/result vocabulary, and the WebError taxonomy",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2-rc.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -18,30 +18,23 @@
|
|
|
18
18
|
"types": "./lib/types/index.d.ts",
|
|
19
19
|
"default": "./lib/index.js"
|
|
20
20
|
},
|
|
21
|
-
"./invariant": {
|
|
22
|
-
"types": "./lib/types/invariant.d.ts",
|
|
23
|
-
"default": "./lib/invariant.js"
|
|
24
|
-
},
|
|
25
21
|
"./src/*": "./src/*",
|
|
26
22
|
"./package.json": "./package.json"
|
|
27
23
|
},
|
|
28
24
|
"files": [
|
|
29
25
|
"lib/index.js",
|
|
30
|
-
"lib/invariant.js",
|
|
31
26
|
"lib/types/**/*.d.ts"
|
|
32
27
|
],
|
|
33
28
|
"license": "MIT",
|
|
34
29
|
"peerDependencies": {
|
|
35
|
-
"@xneog/dsh-
|
|
36
|
-
"@xneog/
|
|
37
|
-
"@xneog/cordis": "0.1.0"
|
|
30
|
+
"@xneog/dsh-llm": "^0.1.2-rc.1",
|
|
31
|
+
"@xneog/cordis": "^4.0.2"
|
|
38
32
|
},
|
|
39
33
|
"dependencies": {
|
|
40
|
-
"@xneog/schemastery": "
|
|
34
|
+
"@xneog/schemastery": "^3.18.2"
|
|
41
35
|
},
|
|
42
36
|
"devDependencies": {
|
|
43
|
-
"@xneog/
|
|
44
|
-
"@xneog/dsh-llm": "0.1.
|
|
45
|
-
"@xneog/cordis": "0.1.0"
|
|
37
|
+
"@xneog/cordis": "^4.0.2",
|
|
38
|
+
"@xneog/dsh-llm": "^0.1.2-rc.1"
|
|
46
39
|
}
|
|
47
40
|
}
|
package/lib/invariant.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
//#region lib/types/invariant.js
|
|
2
|
-
/**
|
|
3
|
-
* Package-owned invariant companion for `@xneog/dsh-web`.
|
|
4
|
-
* @module @xneog/dsh-web/invariant
|
|
5
|
-
*/
|
|
6
|
-
const PACKAGE_NAME = "@xneog/dsh-web";
|
|
7
|
-
/** Cordis companion plugin name. */
|
|
8
|
-
const name = "web-invariant";
|
|
9
|
-
/** Service required before the companion can reserve package ownership. */
|
|
10
|
-
const inject = ["invariants"];
|
|
11
|
-
/**
|
|
12
|
-
* No runtime invariant: provider maps are private and selection/result caps are enforced on each
|
|
13
|
-
* call; the seam publishes no independent registry or request/result observation stream.
|
|
14
|
-
*/
|
|
15
|
-
const install = () => {};
|
|
16
|
-
/**
|
|
17
|
-
* Register this package's invariant companion.
|
|
18
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
19
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
20
|
-
*/
|
|
21
|
-
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
22
|
-
//#endregion
|
|
23
|
-
export { apply, inject, name };
|
package/lib/types/invariant.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package-owned invariant companion for `@xneog/dsh-web`.
|
|
3
|
-
* @module @xneog/dsh-web/invariant
|
|
4
|
-
*/
|
|
5
|
-
import type { Context } from '@xneog/cordis';
|
|
6
|
-
/** Cordis companion plugin name. */
|
|
7
|
-
export declare const name = "web-invariant";
|
|
8
|
-
/** Service required before the companion can reserve package ownership. */
|
|
9
|
-
export declare const inject: string[];
|
|
10
|
-
/**
|
|
11
|
-
* Register this package's invariant companion.
|
|
12
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
-
*/
|
|
15
|
-
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
-
//# sourceMappingURL=invariant.d.ts.map
|