@deepseek-ai/dsh-tool-web 0.1.0-rc.7 → 0.1.0-rc.8

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 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/tool-web/README.md
5
- README.md: 81dc4d3f3dbf9788e68b86513bfa09271fb99c84
6
- README.zh.md: 8e976f23f72347e12c6aea35f9b99c5d8ad31d85
5
+ README.md: 787b70a5070f48a3bac6435d5d7e8b64c01e0341
6
+ README.zh.md: bce95dbc0eea1bebe59b0b80cafae2a4eb948aa6
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- The model-facing web tool suite — `web_search` and `web_fetch` — over the [web capability seam](../web/README.md) (`ctx.web`). It owns model-facing concerns only: tool names, JSON schemas, snake_case argument names, prompt sections, the result-count bound, result formatting, HTML→markdown presentation, and the UI presentation projection — `presentCall`, `presentResult` (a `card: 'web'` result card discriminated by `kind: 'search' | 'fetch'`), and the `output.presentationMeta` that carries the structured search sources or the fetch summary the lossy render text cannot (see the [web-result-card Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card.md)). All web access goes through `ctx.web`; this package never imports a concrete provider. Neither tool exposes a model-facing timeout — each tool's cooperative tool-call budget is declared here via config (`fetchTimeoutMs`/`searchTimeoutMs`, attached as `ToolDefinition.timeoutMs`) and enforced by [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md) (a `tools/execute` wrapper); each tool just forwards `exec.signal` to the seam.
5
+ The model-facing web tool suite — `web_search` and `web_fetch` — over the [web capability seam](../web/README.md) (`ctx.web`). It owns model-facing concerns only: tool names, JSON schemas, snake_case argument names, prompt sections, the result-count bound, result formatting, HTML→markdown presentation, and the UI presentation projection — `presentCall`, `presentResult` (a `card: 'web'` result card discriminated by `kind: 'search' | 'fetch'`), and the `output.presentationMeta` that carries the structured search sources or the fetch summary the lossy render text cannot (see the [web-result-card Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card.md)). All web access goes through `ctx.web`; this package never imports a concrete provider. Neither tool exposes a model-facing timeout — each tool's cooperative tool-call budget is declared here via config (`fetchTimeoutMs`/`searchTimeoutMs`, attached as `ToolDefinition.timeoutMs`) and enforced by [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md) (a `tools/execute` wrapper). Single operations forward `exec.signal`; a multi-query search fuses it with batch cancellation so a failed query aborts its siblings.
6
6
 
7
7
  Each tool is registered independently; a product that wants only one disables the other via config (`{ search: false }` / `{ fetch: false }`). Search guidance mentions `web_fetch` only when fetch is also config-enabled; a search-only composition instead tells the model to use returned snippets and cite their URLs.
8
8
 
@@ -10,7 +10,7 @@ Each tool is registered independently; a product that wants only one disables th
10
10
 
11
11
  | Tool | Args | Behavior |
12
12
  |---|---|---|
13
- | `web_search` | `query` (string) | Discovery. Returns an optional answer plus source URLs. `max_results` is **not** model-facing — the tool sets the bound (the `searchMaxResults` config, default 8) and passes it to the seam. |
13
+ | `web_search` | `queries` (required string[]) | Discovery. Returns an optional answer plus source URLs. It runs one to `searchMaxQueries` distinct searches concurrently and merges their sources in round-robin order before applying the combined `searchMaxResults` cap. A one-item array performs one search. Exact duplicate queries run once. Any failed search aborts the remaining batch, which settles before the call returns an error. Neither bound is model-facing. |
14
14
  | `web_fetch` | `url` (string) | Retrieves a specific URL. HTML bodies are rendered to markdown (turndown with GFM tables/strikethrough); text bodies pass through. A non-2xx status is reported, not an error. The tool-call timeout is deployment policy (`dsh-tool-call-timeout-policy`), not a model argument. |
15
15
 
16
16
  Both tools opt into concurrent scheduling because provider reads return content without mutating parent-agent state.
@@ -23,12 +23,13 @@ The normalized service results are also the canonical tool values: `WebSearchRes
23
23
  |---|---|---|
24
24
  | `search` | `true` | Register `web_search`. |
25
25
  | `fetch` | `true` | Register `web_fetch`. |
26
- | `searchMaxResults` | `8` | Upper bound on sources returned by one `web_search` call (the seam truncates a longer provider list and flags it). |
26
+ | `searchMaxResults` | `8` | Upper bound on sources returned by one `web_search` call (the seam truncates each provider list; the tool also caps a combined multi-query list). |
27
+ | `searchMaxQueries` | `4` | Upper bound on queries accepted by one `web_search` call. The configured value appears in its prompt guidance and schema descriptions. |
27
28
  | `fetchTimeoutMs` | `30000` | Cooperative tool-call timeout budget (ms) for `web_fetch`. |
28
29
  | `searchTimeoutMs` | `30000` | Cooperative tool-call timeout budget (ms) for `web_search`. |
29
30
  | `fetchMaxOutputChars` | `200000` | Cap on source characters converted synchronously and on one complete `web_fetch` output (header, rendered body, and footer); a cut body gets the truncation notice when it fits. |
30
31
 
31
- `fetchTimeoutMs`/`searchTimeoutMs` declare each tool's cooperative timeout budget (attached as `ToolDefinition.timeoutMs`), enforced by [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md); the model-facing schema exposes no timeout argument. `fetchMaxOutputChars` bounds both synchronous conversion work and the complete rendered result: only that many source characters are converted, and the header, converted prefix, and truncation notice are then capped together. The default leaves headroom above the local provider's 100,000-character body cap, but rendered expansion can still make the final bound truncate the result.
32
+ `searchMaxQueries` bounds the accepted array before exact-string deduplication, provider fan-out, and combined provider-answer growth; validation rejects an oversized array before any search starts, then dispatch keeps the first occurrence of each query. Together with each provider's own controls such as `maxUses`, these independent settings are the product's search budgets; the generic seam does not expose provider-internal native-search accounting. `fetchTimeoutMs`/`searchTimeoutMs` declare each tool's cooperative timeout budget (attached as `ToolDefinition.timeoutMs`), enforced by [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md); the model-facing schema exposes no timeout argument. `fetchMaxOutputChars` bounds both synchronous conversion work and the complete rendered result: only that many source characters are converted, and the header, converted prefix, and truncation notice are then capped together. The default leaves headroom above the local provider's 100,000-character body cap, but rendered expansion can still make the final bound truncate the result.
32
33
 
33
34
  ```yaml
34
35
  - id: tool-web
@@ -52,13 +53,13 @@ Search and fetch contribute the web-search and web-fetch guidance below. Search
52
53
  ##### Web search guidance with fetch enabled
53
54
 
54
55
  ```markdown
55
- Use the web_search tool to discover current information on the web. It returns an optional answer plus a list of source URLs. Follow up with web_fetch when you need the full content of a specific result, and cite the relevant URLs as markdown links.
56
+ Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Follow up with web_fetch when you need the full content of a specific result, and cite the relevant URLs as markdown links.
56
57
  ```
57
58
 
58
59
  ##### Web search-only guidance
59
60
 
60
61
  ```markdown
61
- Use the web_search tool to discover current information on the web. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links.
62
+ Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links.
62
63
  ```
63
64
 
64
65
  ##### Web fetch guidance
@@ -69,11 +70,11 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
69
70
 
70
71
  #### Token effect
71
72
 
72
- Fixed guidance cost per request for each config-enabled tool, even when a restriction hides its schema. Toggling fetch changes the search guidance as well as registering or removing the fetch section.
73
+ Fixed guidance cost per request for each config-enabled tool, even when a restriction hides its schema. Toggling fetch or changing `searchMaxQueries` changes the search guidance; toggling fetch also registers or removes the fetch section.
73
74
 
74
75
  #### KV Cache effect
75
76
 
76
- Prefix-stable while enabled tools, scope, and guidance text are unchanged. Config enablement—including toggling fetch's search-guidance branch—or plugin lifecycle may invalidate reuse from the first changed prompt section; scoped schema restrictions do not remove it.
77
+ Prefix-stable while enabled tools, scope, and guidance text are unchanged. Config enablement—including toggling fetch's search-guidance branch—changing `searchMaxQueries`, or plugin lifecycle may invalidate reuse from the first changed prompt section; scoped schema restrictions do not remove it.
77
78
 
78
79
  ### Tool schemas
79
80
 
@@ -83,26 +84,40 @@ The model sees the generated [`web_search` and `web_fetch` schemas](../../../doc
83
84
 
84
85
  #### Token effect
85
86
 
86
- Fixed schema cost per request; config disablement removes both schema and guidance, while a scoped restriction removes only the schema.
87
+ Fixed schema cost per request for a resolved `searchMaxQueries`; config disablement removes both schema and guidance, while a scoped restriction removes only the schema.
87
88
 
88
89
  #### KV Cache effect
89
90
 
90
- Prefix-stable while definitions and visibility are unchanged. Config enablement, plugin lifecycle, or scoped restrictions may invalidate reuse from the first changed schema token.
91
+ Prefix-stable while definitions, resolved query cap, and visibility are unchanged. Config enablement, changing `searchMaxQueries`, plugin lifecycle, or scoped restrictions may invalidate reuse from the first changed schema token.
91
92
 
92
93
  ### Search result
93
94
 
94
95
  #### What the model sees
95
96
 
96
- The optional provider-owned answer is followed by `Sources:` and data-dependent lines shaped exactly `- [<title-or-url>](<url>)`, optionally suffixed ` — <snippet> (<publishedAt>)`. With neither answer nor sources the result says `No results found.` A capped list adds `(Showing the first <count> sources. Refine the query for more.)`; every result ends `Cite the relevant URLs above as markdown links in your answer.`
97
+ The optional provider-owned answer is followed by `Sources:` and data-dependent lines shaped exactly `- [<title-or-url>](<url>)`, optionally suffixed ` — <snippet> (<publishedAt>)`. A multi-query call runs each exact query string once, preserving its first position; it labels each provider answer with the originating query as a markdown heading, deduplicates sources by URL, and takes one source at each rank from every query before advancing to the next rank. With neither answer nor sources the result says `No results found.` A capped list adds `(Showing the first <count> sources. Refine the query for more.)`; every result ends `Cite the relevant URLs above as markdown links in your answer.`
97
98
 
98
99
  #### Token effect
99
100
 
100
- Data-dependent results are resent until compaction and sources are capped by `searchMaxResults`.
101
+ Data-dependent results are resent until compaction; query fan-out is capped by `searchMaxQueries`, and sources are capped by `searchMaxResults`.
101
102
 
102
103
  #### KV Cache effect
103
104
 
104
105
  Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
105
106
 
107
+ ### Search failure
108
+
109
+ #### What the model sees
110
+
111
+ If any query in a multi-query call fails, `web_search` aborts the other searches, waits for every started search to settle, discards successful results, and returns `Error: <message>` for the first failure.
112
+
113
+ #### Token effect
114
+
115
+ Only the retained error result adds tokens; discarded successful results do not enter model history.
116
+
117
+ #### KV Cache effect
118
+
119
+ Append-only; the error follows the reusable request prefix and does not invalidate existing KV-cache entries.
120
+
106
121
  ### Fetch result
107
122
 
108
123
  #### What the model sees
@@ -121,7 +136,7 @@ Append-only; newly visible content follows the reusable request prefix and does
121
136
 
122
137
  #### What the model sees
123
138
 
124
- Blank inputs become exactly `Error: query must be a non-empty string` or `Error: url must be a non-empty string`.
139
+ Schema validation rejects an absent or non-array `queries` field and non-string array elements before execution. Value errors become exactly `Error: queries must contain at least one query`, `Error: queries must contain at most 1 query` when the configured cap is one, `Error: queries must contain at most <count> queries` for larger caps, `Error: each query must be a non-empty string`, or `Error: url must be a non-empty string`.
125
140
 
126
141
  #### Token effect
127
142
 
@@ -133,6 +148,7 @@ Append-only; newly visible content follows the reusable request prefix and does
133
148
 
134
149
  ## Known Limitations and Deferred Work
135
150
 
151
+ - **There is no batch-wide native-search counter** — `searchMaxQueries` bounds `ctx.web.search` calls, but a provider may perform several native searches inside each call. For example, a model-backed provider configured with `maxUses` can permit up to `searchMaxQueries × maxUses` native searches; `searchMaxResults` limits only the combined sources returned to the caller. Deployments control cost through these independent consumer and provider settings because the generic seam does not know provider-internal search units.
136
152
  - **HTML→markdown conversion degrades on inputs GFM cannot safely represent** — [turndown](https://github.com/mixmark-io/turndown) (with GFM tables/strikethrough) converts at most `fetchMaxOutputChars` source characters through a real DOM. A conservative 512-level lexical guard passes deeply or ambiguously nested bodies through as raw HTML, conversion exceptions do the same, and table `colspan` is ignored because GFM has no spanning-cell representation; these bounds avoid blocking the event loop or expanding output from an untrusted numeric attribute ([archived dependency decision](../../../.agents/notes/archived/simplification/2026-07-26-turndown-for-tool-web-html-markdown.md)).
137
153
  - **The model-facing API is minimal by design, with promotions deferred** — `max_results` stays a config bound (not a model argument), and `web_fetch` takes only `url` (no `format`/`prompt`/LLM-summarization mode); both are named later steps in [the seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md).
138
154
  - **No web-specific permission policy** — both tools execute without requesting `ctx.approval`; a deployment that needs confirmation must add a `tools/pre-execute` policy, and the package does not define persistent URL/domain grants.
package/README.zh.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 面向模型的 web 工具套件 `web_search` 与 `web_fetch`,构建于 [web 能力 seam](../web/README.md)(`ctx.web`)之上。它只负责面向模型的事项:工具名称、JSON Schema、snake_case 参数名称、提示词区段、结果数量上限、结果格式、HTML→markdown 呈现,以及 UI 呈现投影——`presentCall`、`presentResult`(以 `kind: 'search' | 'fetch'` 区分的 `card: 'web'` 结果卡片),以及承载有损渲染文本无法携带的结构化搜索来源或抓取摘要的 `output.presentationMeta`(见 [web-result-card Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card.md))。所有 web 访问都通过 `ctx.web`;该包绝不导入具体提供方。两个工具都不公开面向模型的超时:每个工具的协作式工具调用超时预算通过配置在此声明(`fetchTimeoutMs`/`searchTimeoutMs`,附加为 `ToolDefinition.timeoutMs`),由 [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md)(`tools/execute` 包装层)强制执行;每个工具只把 `exec.signal` 转发给 seam。
5
+ 面向模型的 web 工具套件 `web_search` 与 `web_fetch`,构建于 [web 能力 seam](../web/README.md)(`ctx.web`)之上。它只负责面向模型的事项:工具名称、JSON Schema、snake_case 参数名称、提示词区段、结果数量上限、结果格式、HTML→markdown 呈现,以及 UI 呈现投影——`presentCall`、`presentResult`(以 `kind: 'search' | 'fetch'` 区分的 `card: 'web'` 结果卡片),以及承载有损渲染文本无法携带的结构化搜索来源或抓取摘要的 `output.presentationMeta`(见 [web-result-card Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card.md))。所有 web 访问都通过 `ctx.web`;该包绝不导入具体提供方。两个工具都不公开面向模型的超时:每个工具的协作式工具调用超时预算通过配置在此声明(`fetchTimeoutMs`/`searchTimeoutMs`,附加为 `ToolDefinition.timeoutMs`),由 [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md)(`tools/execute` 包装层)强制执行。单项操作会转发 `exec.signal`;多查询搜索会把它与批次取消信号融合,使失败查询能够中止其余查询。
6
6
 
7
7
  每个工具独立注册;只需要其中一个工具的产品可以通过配置禁用另一个(`{ search: false }`/`{ fetch: false }`)。仅当抓取也通过配置启用时,搜索指引才会提及 `web_fetch`;仅启用搜索的组合则会要求模型使用返回的 snippet 并引用其 URL。
8
8
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  | 工具 | 参数 | 行为 |
12
12
  |---|---|---|
13
- | `web_search` | `query`(string) | 用于发现信息。返回可选答案与来源 URL。`max_results` **不**面向模型:工具设置上限(`searchMaxResults` 配置,默认 8)并传给 seam。 |
13
+ | `web_search` | `queries`(必填 string[]) | 用于发现信息。返回可选答案与来源 URL。它会并发执行 1 至 `searchMaxQueries` 个不同搜索,按轮询顺序合并来源,再应用组合后的 `searchMaxResults` 上限。单元素数组执行一次搜索。完全相同的查询只执行一次。任何搜索失败都会中止批次中的其余搜索;批次结算完毕后调用才返回错误。两个上限都不面向模型。 |
14
14
  | `web_fetch` | `url`(string) | 获取特定 URL。HTML 主体渲染为 markdown(turndown,带 GFM 表格/删除线);文本主体原样通过。非 2xx 状态会报告,而非报错。工具调用超时是部署策略(`dsh-tool-call-timeout-policy`),不是模型参数。 |
15
15
 
16
16
  两个工具都选择并发调度,因为提供方读取会返回内容,不会修改父 agent(智能体)的状态。
@@ -23,12 +23,13 @@
23
23
  |---|---|---|
24
24
  | `search` | `true` | 注册 `web_search`。 |
25
25
  | `fetch` | `true` | 注册 `web_fetch`。 |
26
- | `searchMaxResults` | `8` | 一次 `web_search` 调用返回的来源数量上限(seam 截断更长的提供方列表并标记)。 |
26
+ | `searchMaxResults` | `8` | 一次 `web_search` 调用返回的来源数量上限(seam 截断各提供方列表;工具还会限制多查询组合列表)。 |
27
+ | `searchMaxQueries` | `4` | 一次 `web_search` 调用接受的查询数量上限。配置值会出现在提示词指引与 schema 描述中。 |
27
28
  | `fetchTimeoutMs` | `30000` | `web_fetch` 的协作式工具调用超时预算(ms)。 |
28
29
  | `searchTimeoutMs` | `30000` | `web_search` 的协作式工具调用超时预算(ms)。 |
29
30
  | `fetchMaxOutputChars` | `200000` | 同步转换的源字符数与单次完整 `web_fetch` 输出的上限(状态头、渲染后的主体与页脚合并计算);主体被截断时,在能容纳的情况下附带截断提示。 |
30
31
 
31
- `fetchTimeoutMs`/`searchTimeoutMs` 声明每个工具的协作式超时预算(附加为 `ToolDefinition.timeoutMs`),由 [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md) 强制执行;面向模型的 schema 不公开超时参数。`fetchMaxOutputChars` 同时限制同步转换工作量和完整渲染结果:只转换至多该数量的源字符,随后对状态头、转换后的前缀和截断提示合并设限。默认值为本地提供方的 100,000 字符主体上限留出余量,但渲染膨胀仍可能使最终上限截断结果。
32
+ `searchMaxQueries` 在完全相同的字符串去重前限制可接受数组、提供方请求扇出与组合后的提供方答案增长;校验会在任何搜索开始前拒绝超限数组,随后分发只保留每个查询第一次出现的位置。该设置与各提供方自己的 `maxUses` 等控制项共同构成产品的搜索预算;通用 seam 不公开提供方内部的原生搜索计数。`fetchTimeoutMs`/`searchTimeoutMs` 声明每个工具的协作式超时预算(附加为 `ToolDefinition.timeoutMs`),由 [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md) 强制执行;面向模型的 schema 不公开超时参数。`fetchMaxOutputChars` 同时限制同步转换工作量和完整渲染结果:只转换至多该数量的源字符,随后对状态头、转换后的前缀和截断提示合并设限。默认值为本地提供方的 100,000 字符主体上限留出余量,但渲染膨胀仍可能使最终上限截断结果。
32
33
 
33
34
  ```yaml
34
35
  - id: tool-web
@@ -52,13 +53,13 @@
52
53
  ##### 启用抓取时的 Web 搜索指引
53
54
 
54
55
  ```markdown
55
- Use the web_search tool to discover current information on the web. It returns an optional answer plus a list of source URLs. Follow up with web_fetch when you need the full content of a specific result, and cite the relevant URLs as markdown links.
56
+ Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Follow up with web_fetch when you need the full content of a specific result, and cite the relevant URLs as markdown links.
56
57
  ```
57
58
 
58
59
  ##### 仅搜索时的 Web 搜索指引
59
60
 
60
61
  ```markdown
61
- Use the web_search tool to discover current information on the web. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links.
62
+ Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links.
62
63
  ```
63
64
 
64
65
  ##### Web 抓取指引
@@ -69,11 +70,11 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
69
70
 
70
71
  #### Token 影响
71
72
 
72
- 每个通过配置启用的工具都会为每次请求增加固定的指引 token 开销,即使限制隐藏了其 schema。切换抓取状态不仅会注册或移除抓取区段,也会更改搜索指引。
73
+ 每个通过配置启用的工具都会为每次请求增加固定的指引 token 开销,即使限制隐藏了其 schema。切换抓取状态或更改 `searchMaxQueries` 会改变搜索指引;切换抓取状态还会注册或移除抓取区段。
73
74
 
74
75
  #### KV Cache 影响
75
76
 
76
- 只要启用工具、scope 与指引文本不变,前缀就保持稳定。配置启用状态(包括因切换抓取状态而改变搜索指引分支)或插件生命周期可能使从第一个变化的提示词区段起的复用失效;scope schema 限制不会移除该区段。
77
+ 只要启用工具、scope 与指引文本不变,前缀就保持稳定。配置启用状态(包括因切换抓取状态而改变搜索指引分支)、更改 `searchMaxQueries` 或插件生命周期可能使从第一个变化的提示词区段起的复用失效;scope schema 限制不会移除该区段。
77
78
 
78
79
  ### 工具 schema
79
80
 
@@ -83,26 +84,40 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
83
84
 
84
85
  #### Token 影响
85
86
 
86
- 每次请求都会产生固定的 schema token 开销;通过配置禁用会同时移除 schema 与指引,scope 限制只移除 schema。
87
+ 对于已解析的 `searchMaxQueries`,每次请求都会产生固定的 schema token 开销;通过配置禁用会同时移除 schema 与指引,scope 限制只移除 schema。
87
88
 
88
89
  #### KV Cache 影响
89
90
 
90
- 只要定义与可见性不变,前缀就保持稳定。配置启用状态、插件生命周期或 scope 限制可能使从第一个变化的 schema token 起的复用失效。
91
+ 只要定义、已解析查询上限与可见性不变,前缀就保持稳定。配置启用状态、更改 `searchMaxQueries`、插件生命周期或 scope 限制可能使从第一个变化的 schema token 起的复用失效。
91
92
 
92
93
  ### 搜索结果
93
94
 
94
95
  #### 模型看到的内容
95
96
 
96
- 可选的提供方答案之后是 `Sources:`,再跟随内容取决于数据且格式严格为 `- [<title-or-url>](<url>)` 的行,并可添加后缀 ` — <snippet> (<publishedAt>)`。既无答案也无来源时,结果显示 `No results found.`。列表被截断至上限时会添加 `(Showing the first <count> sources. Refine the query for more.)`;每个结果都以 `Cite the relevant URLs above as markdown links in your answer.` 结尾。
97
+ 可选的提供方答案之后是 `Sources:`,再跟随内容取决于数据且格式严格为 `- [<title-or-url>](<url>)` 的行,并可添加后缀 ` — <snippet> (<publishedAt>)`。多查询调用会让每个完全相同的查询字符串只执行一次,并保留它首次出现的位置;调用会用来源查询作为 markdown 标题标注每个提供方答案,按 URL 对来源去重,并从每个查询取得同一排名的一条来源后再推进至下一排名。既无答案也无来源时,结果显示 `No results found.`。列表被截断至上限时会添加 `(Showing the first <count> sources. Refine the query for more.)`;每个结果都以 `Cite the relevant URLs above as markdown links in your answer.` 结尾。
97
98
 
98
99
  #### Token 影响
99
100
 
100
- 数据相关结果会重复发送直到压缩(compaction),来源数量由 `searchMaxResults` 限制。
101
+ 数据相关结果会重复发送直到压缩(compaction);查询请求扇出由 `searchMaxQueries` 限制,来源数量由 `searchMaxResults` 限制。
101
102
 
102
103
  #### KV Cache 影响
103
104
 
104
105
  仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
105
106
 
107
+ ### 搜索失败
108
+
109
+ #### 模型看到的内容
110
+
111
+ 多查询调用中的任何查询失败时,`web_search` 会中止其余搜索,等待所有已启动搜索结算,丢弃成功结果,并针对首次失败返回 `Error: <message>`。
112
+
113
+ #### Token 影响
114
+
115
+ 只有保留的错误结果会增加 token;被丢弃的成功结果不会进入模型历史。
116
+
117
+ #### KV Cache 影响
118
+
119
+ 仅追加;错误位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
120
+
106
121
  ### 抓取结果
107
122
 
108
123
  #### 模型看到的内容
@@ -121,7 +136,7 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
121
136
 
122
137
  #### 模型看到的内容
123
138
 
124
- 空输入精确地变为 `Error: query must be a non-empty string` 或 `Error: url must be a non-empty string`。
139
+ schema 校验会在执行前拒绝缺失或非数组的 `queries` 字段以及非字符串数组元素。值错误精确地变为 `Error: queries must contain at least one query`、配置上限为 1 时的 `Error: queries must contain at most 1 query`、上限更大时的 `Error: queries must contain at most <count> queries`、`Error: each query must be a non-empty string` 或 `Error: url must be a non-empty string`。
125
140
 
126
141
  #### Token 影响
127
142
 
@@ -133,6 +148,7 @@ Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for ex
133
148
 
134
149
  ## 已知限制与暂缓事项
135
150
 
151
+ - **没有覆盖整个批次的原生搜索计数器**:`searchMaxQueries` 限制 `ctx.web.search` 调用数,但提供方可以在每次调用内执行多次原生搜索。例如,配置了 `maxUses` 的模型型提供方最多可以执行 `searchMaxQueries × maxUses` 次原生搜索;`searchMaxResults` 只限制返回给调用方的组合来源。部署通过这些独立的消费方与提供方设置控制成本,因为通用 seam 不知道提供方内部的搜索计量单位。
136
152
  - **HTML→markdown 转换会在 GFM 无法安全表示的输入上降级**:[turndown](https://github.com/mixmark-io/turndown)(带 GFM 表格/删除线)通过真实 DOM 转换至多 `fetchMaxOutputChars` 个源字符。保守的 512 层词法守卫会将深层或嵌套有歧义的主体作为原始 HTML 直接透传,转换异常也会如此处理;表格的 `colspan` 会被忽略,因为 GFM 无法表示跨列单元格。这些限制可避免阻塞事件循环,也避免不受信任的数值属性使输出膨胀([已归档的依赖决策](../../../.agents/notes/archived/simplification/2026-07-26-turndown-for-tool-web-html-markdown.md))。
137
153
  - **面向模型的接口有意保持精简,后续扩展暂缓**:`max_results` 保持为配置上限(不是模型参数),`web_fetch` 只接受 `url`(没有 `format`/`prompt`/LLM(大语言模型)摘要模式);两项都列为 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) 中的后续步骤。
138
154
  - **没有 web 专用权限策略**:两个工具都不会请求 `ctx.approval` 就直接执行;需要确认的部署必须添加 `tools/pre-execute` 策略,该包不定义持久化的 URL/域名授权。
package/lib/index.js CHANGED
@@ -17,16 +17,24 @@ import { assertNever } from "@deepseek-ai/dsh-llm";
17
17
  * context returns. The default `8` aligns with OpenCode's Exa default.
18
18
  */
19
19
  const WEB_SEARCH_MAX_RESULTS = 8;
20
+ /** Default upper bound on concurrent searches in one tool call. */
21
+ const WEB_SEARCH_MAX_QUERIES = 4;
20
22
  /**
21
- * Validate value constraints the schema DSL can't express: a non-blank
22
- * `query`. Throws a plain `Error` otherwise.
23
+ * Validate value constraints the schema DSL can't express: `queries` is
24
+ * non-empty, contains only non-blank strings, and fits the deployment's
25
+ * query-count bound. Exact duplicate strings are collapsed after the bound
26
+ * check. Throws a plain `Error` otherwise.
23
27
  *
24
28
  * @param args - the schema-validated `web_search` arguments.
25
- * @returns the accepted arguments, passed through unchanged.
29
+ * @param maxQueries - the deployment's upper bound on queries in one call.
30
+ * @returns the accepted queries in their first-occurrence order.
26
31
  */
27
- function parseSearchArgs(args) {
28
- if (args.query.trim().length === 0) throw new Error("query must be a non-empty string");
29
- return { query: args.query };
32
+ function parseSearchArgs(args, maxQueries) {
33
+ const queries = args.queries;
34
+ if (queries.length === 0) throw new Error("queries must contain at least one query");
35
+ if (queries.length > maxQueries) throw new Error(`queries must contain at most ${maxQueries} ${maxQueries === 1 ? "query" : "queries"}`);
36
+ if (queries.some((query) => query.trim().length === 0)) throw new Error("each query must be a non-empty string");
37
+ return [...new Set(queries)];
30
38
  }
31
39
  /** Display label for a source: its title, else its hostname. */
32
40
  function sourceLabel(url, title) {
@@ -64,17 +72,18 @@ function formatSearchOutput(result) {
64
72
  return parts.join("\n\n");
65
73
  }
66
74
  /**
67
- * Pending-call presentation: a search card titled by the query.
75
+ * Pending-call presentation: a search card titled by the query list.
68
76
  *
69
- * @param args - the raw tool arguments; only `query` feeds the view.
77
+ * @param args - the raw tool arguments; only the query text feeds the view.
70
78
  * @returns the generic card view (`kind: 'search'`) shown while the call runs.
71
79
  */
72
80
  function presentSearchCall(args) {
81
+ const title = args.queries.join(", ");
73
82
  return {
74
83
  card: "generic",
75
- title: args.query,
84
+ title,
76
85
  kind: "search",
77
- rawInput: args.query
86
+ rawInput: title
78
87
  };
79
88
  }
80
89
  /**
@@ -139,8 +148,8 @@ function searchMetaFromResult(meta) {
139
148
  * `web` capability falls back to the raw `tool/result` content, which is the
140
149
  * same text (see the web-result-card Agent Note).
141
150
  *
142
- * @param args - the raw tool arguments; `query` becomes the result-state title so
143
- * a window-truncated replay that dropped the call head still has one.
151
+ * @param args - the raw tool arguments; the queries become the result-state
152
+ * title so a window-truncated replay that dropped the call head still has one.
144
153
  * @param result - the final model-facing tool result; `meta` carries the sources.
145
154
  * @returns the search result view, or `undefined` (generic card) on failure or
146
155
  * malformed meta.
@@ -152,37 +161,105 @@ function presentSearchResult(args, result) {
152
161
  return {
153
162
  card: "web",
154
163
  kind: "search",
155
- title: args.query,
164
+ title: args.queries.join(", "),
156
165
  sources: meta.sources,
157
166
  truncated: meta.truncated,
158
167
  ...meta.answer !== void 0 ? { answer: meta.answer } : {}
159
168
  };
160
169
  }
161
170
  /**
171
+ * Run one or more searches through the web seam. A single query keeps the
172
+ * provider's exact result; multiple queries run concurrently and are merged
173
+ * into one normalized result capped at `maxResults`. A failed search aborts
174
+ * its siblings, and this function waits for every search to settle before
175
+ * rethrowing the first failure.
176
+ *
177
+ * @param ctx - context whose `web` service performs the searches.
178
+ * @param queries - validated non-empty queries.
179
+ * @param maxResults - the deployment's source cap for the combined result.
180
+ * @param signal - cancellation signal forwarded to every search.
181
+ * @returns the combined search result.
182
+ */
183
+ async function runSearchQueries(ctx, queries, maxResults, signal) {
184
+ if (queries.length === 1) return ctx.web.search({
185
+ query: queries[0],
186
+ maxResults
187
+ }, signal);
188
+ const controller = new AbortController();
189
+ const batchSignal = AbortSignal.any([signal, controller.signal]);
190
+ let firstFailure;
191
+ const results = [];
192
+ const searches = queries.map(async (query, index) => {
193
+ try {
194
+ results[index] = await ctx.web.search({
195
+ query,
196
+ maxResults
197
+ }, batchSignal);
198
+ } catch (error) {
199
+ if (firstFailure === void 0) firstFailure = { error };
200
+ controller.abort(error);
201
+ throw error;
202
+ }
203
+ });
204
+ await Promise.allSettled(searches);
205
+ if (firstFailure !== void 0) throw firstFailure.error;
206
+ return mergeSearchResults(queries, results, maxResults);
207
+ }
208
+ /** Merge per-query results into one deduplicated, round-robin, capped result. */
209
+ function mergeSearchResults(queries, results, maxResults) {
210
+ const seen = /* @__PURE__ */ new Set();
211
+ const sources = [];
212
+ let sourceRanks = 0;
213
+ for (const result of results) sourceRanks = Math.max(sourceRanks, result.sources.length);
214
+ let droppedSource = false;
215
+ merge: for (let rank = 0; rank < sourceRanks; rank++) for (const result of results) {
216
+ const source = result.sources[rank];
217
+ if (source !== void 0 && !seen.has(source.url)) {
218
+ seen.add(source.url);
219
+ if (sources.length === maxResults) {
220
+ droppedSource = true;
221
+ break merge;
222
+ }
223
+ sources.push(source);
224
+ }
225
+ }
226
+ const contents = results.flatMap((result, index) => {
227
+ if (result.content === void 0 || result.content.length === 0) return [];
228
+ return [`### ${queries[index]}\n\n${result.content}`];
229
+ });
230
+ return {
231
+ ...contents.length > 0 ? { content: contents.join("\n\n") } : {},
232
+ sources,
233
+ truncated: results.some((result) => result.truncated) || droppedSource
234
+ };
235
+ }
236
+ /**
162
237
  * Register the `web_search` tool and its system-prompt guidance.
163
238
  *
164
239
  * @param ctx - context whose `tools` and `systemPrompt` registries receive the
165
240
  * registrations; both are effect-scoped and unregister on plugin dispose.
166
241
  * @param maxResults - the deployment's source cap, sent as every seam
167
242
  * request's `maxResults`.
243
+ * @param maxQueries - the deployment's query cap enforced before provider calls.
168
244
  * @param timeoutMs - the cooperative tool-call budget (ms) attached as the tool's
169
245
  * `ToolDefinition.timeoutMs` for `@deepseek-ai/dsh-tool-call-timeout-policy` to enforce.
170
246
  * @param fetchEnabled - whether the same composition exposes `web_fetch`, which
171
247
  * controls whether search guidance may recommend that follow-up tool.
172
248
  */
173
- function applyWebSearchTool(ctx, maxResults, timeoutMs, fetchEnabled) {
249
+ function applyWebSearchTool(ctx, maxResults, maxQueries, timeoutMs, fetchEnabled) {
174
250
  ctx.systemPrompt.section({
175
251
  name: "tool:web_search",
176
252
  order: 110,
177
- text: fetchEnabled ? "Use the web_search tool to discover current information on the web. It returns an optional answer plus a list of source URLs. Follow up with web_fetch when you need the full content of a specific result, and cite the relevant URLs as markdown links." : "Use the web_search tool to discover current information on the web. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links."
253
+ text: fetchEnabled ? `Use the web_search tool to discover current information on the web. The required queries array accepts 1–${maxQueries} non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Follow up with web_fetch when you need the full content of a specific result, and cite the relevant URLs as markdown links.` : `Use the web_search tool to discover current information on the web. The required queries array accepts 1–${maxQueries} non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links.`
178
254
  });
179
255
  ctx.tools.register(defineTool({
180
256
  name: "web_search",
181
- description: "Search the web for current information. Returns an optional summary answer and a list of source URLs.",
182
- parameters: { query: {
183
- type: "string",
257
+ description: `Search the web for current information. Provide 1–${maxQueries} queries in the required queries array. Returns an optional summary answer and a list of source URLs.`,
258
+ parameters: { queries: {
259
+ type: "array",
184
260
  required: true,
185
- description: "The search query."
261
+ items: { type: "string" },
262
+ description: `Required search queries; accepts 1–${maxQueries} items and merges their results.`
186
263
  } },
187
264
  output: {
188
265
  schema: {
@@ -222,11 +299,7 @@ function applyWebSearchTool(ctx, maxResults, timeoutMs, fetchEnabled) {
222
299
  timeoutMs,
223
300
  isConcurrencySafe: () => true,
224
301
  async execute(args, exec) {
225
- const input = parseSearchArgs(args);
226
- const result = await ctx.web.search({
227
- query: input.query,
228
- maxResults
229
- }, exec.signal);
302
+ const result = await runSearchQueries(ctx, parseSearchArgs(args, maxQueries), maxResults, exec.signal);
230
303
  return {
231
304
  ...result.content !== void 0 ? { content: result.content } : {},
232
305
  sources: result.sources.map(projectSource),
@@ -747,6 +820,7 @@ const Config = z.object({
747
820
  search: z.boolean().default(true),
748
821
  fetch: z.boolean().default(true),
749
822
  searchMaxResults: z.number().default(8),
823
+ searchMaxQueries: z.number().default(4),
750
824
  fetchTimeoutMs: z.number().default(DEFAULT_WEB_TOOL_TIMEOUT_MS),
751
825
  searchTimeoutMs: z.number().default(DEFAULT_WEB_TOOL_TIMEOUT_MS),
752
826
  fetchMaxOutputChars: z.number().default(DEFAULT_FETCH_MAX_OUTPUT_CHARS)
@@ -767,11 +841,12 @@ function assertPositiveInteger(name, value) {
767
841
  function apply(ctx, config) {
768
842
  const resolved = config;
769
843
  assertPositiveInteger("searchMaxResults", resolved.searchMaxResults);
844
+ assertPositiveInteger("searchMaxQueries", resolved.searchMaxQueries);
770
845
  assertPositiveInteger("fetchTimeoutMs", resolved.fetchTimeoutMs);
771
846
  assertPositiveInteger("searchTimeoutMs", resolved.searchTimeoutMs);
772
847
  assertPositiveInteger("fetchMaxOutputChars", resolved.fetchMaxOutputChars);
773
- if (resolved.search) applyWebSearchTool(ctx, resolved.searchMaxResults, resolved.searchTimeoutMs, resolved.fetch);
848
+ if (resolved.search) applyWebSearchTool(ctx, resolved.searchMaxResults, resolved.searchMaxQueries, resolved.searchTimeoutMs, resolved.fetch);
774
849
  if (resolved.fetch) applyWebFetchTool(ctx, resolved.fetchTimeoutMs, resolved.fetchMaxOutputChars);
775
850
  }
776
851
  //#endregion
777
- export { Config, DEFAULT_FETCH_MAX_OUTPUT_CHARS, DEFAULT_WEB_TOOL_TIMEOUT_MS, WEB_SEARCH_MAX_RESULTS, apply, applyWebFetchTool, applyWebSearchTool, fetchMetaFromResult, fetchMetaFromValue, formatFetchOutput, formatSearchOutput, inject, name, parseFetchArgs, parseSearchArgs, presentFetchCall, presentFetchResult, presentSearchCall, presentSearchResult, searchMetaFromResult, searchMetaFromValue };
852
+ export { Config, DEFAULT_FETCH_MAX_OUTPUT_CHARS, DEFAULT_WEB_TOOL_TIMEOUT_MS, WEB_SEARCH_MAX_QUERIES, WEB_SEARCH_MAX_RESULTS, apply, applyWebFetchTool, applyWebSearchTool, fetchMetaFromResult, fetchMetaFromValue, formatFetchOutput, formatSearchOutput, inject, name, parseFetchArgs, presentFetchCall, presentFetchResult, presentSearchCall, presentSearchResult, searchMetaFromResult, searchMetaFromValue };
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import type { Context } from '@deepseek-ai/cordis';
9
9
  import z from '@deepseek-ai/schemastery';
10
- export { WEB_SEARCH_MAX_RESULTS, applyWebSearchTool, formatSearchOutput, parseSearchArgs, presentSearchCall, presentSearchResult, searchMetaFromValue, searchMetaFromResult } from './search.ts';
10
+ export { WEB_SEARCH_MAX_QUERIES, WEB_SEARCH_MAX_RESULTS, applyWebSearchTool, formatSearchOutput, presentSearchCall, presentSearchResult, searchMetaFromValue, searchMetaFromResult } from './search.ts';
11
11
  export type { WebSearchMeta } from './search.ts';
12
12
  export { applyWebFetchTool, formatFetchOutput, parseFetchArgs, presentFetchCall, presentFetchResult, fetchMetaFromValue, fetchMetaFromResult } from './fetch.ts';
13
13
  export type { WebFetchMeta } from './fetch.ts';
@@ -23,7 +23,7 @@ export declare const DEFAULT_WEB_TOOL_TIMEOUT_MS = 30000;
23
23
  * 100,000-character body cap while bounding custom providers and rendered output.
24
24
  */
25
25
  export declare const DEFAULT_FETCH_MAX_OUTPUT_CHARS = 200000;
26
- /** Plugin config: which web tools to register, the source cap, per-tool budgets, and the fetch output cap. */
26
+ /** Plugin config: which web tools to register, search bounds, per-tool budgets, and the fetch output cap. */
27
27
  export interface Config {
28
28
  /** Register `web_search`. Defaults to true. */
29
29
  search?: boolean;
@@ -31,6 +31,8 @@ export interface Config {
31
31
  fetch?: boolean;
32
32
  /** Upper bound on sources returned by one `web_search` call. */
33
33
  searchMaxResults?: number;
34
+ /** Upper bound on queries accepted by one `web_search` call. */
35
+ searchMaxQueries?: number;
34
36
  /** Cooperative timeout budget (ms) for `web_fetch`. Defaults to 30000. */
35
37
  fetchTimeoutMs?: number;
36
38
  /** Cooperative timeout budget (ms) for `web_search`. Defaults to 30000. */
@@ -14,18 +14,23 @@ import type { WebSearchResult } from '@deepseek-ai/dsh-web';
14
14
  * context returns. The default `8` aligns with OpenCode's Exa default.
15
15
  */
16
16
  export declare const WEB_SEARCH_MAX_RESULTS = 8;
17
+ /** Default upper bound on concurrent searches in one tool call. */
18
+ export declare const WEB_SEARCH_MAX_QUERIES = 4;
19
+ /** Model-facing `web_search` arguments. */
20
+ interface WebSearchArgs {
21
+ queries: string[];
22
+ }
17
23
  /**
18
- * Validate value constraints the schema DSL can't express: a non-blank
19
- * `query`. Throws a plain `Error` otherwise.
24
+ * Validate value constraints the schema DSL can't express: `queries` is
25
+ * non-empty, contains only non-blank strings, and fits the deployment's
26
+ * query-count bound. Exact duplicate strings are collapsed after the bound
27
+ * check. Throws a plain `Error` otherwise.
20
28
  *
21
29
  * @param args - the schema-validated `web_search` arguments.
22
- * @returns the accepted arguments, passed through unchanged.
30
+ * @param maxQueries - the deployment's upper bound on queries in one call.
31
+ * @returns the accepted queries in their first-occurrence order.
23
32
  */
24
- export declare function parseSearchArgs(args: {
25
- query: string;
26
- }): {
27
- query: string;
28
- };
33
+ export declare function parseSearchArgs(args: WebSearchArgs, maxQueries: number): string[];
29
34
  /**
30
35
  * Format a search result as one model-facing text block.
31
36
  *
@@ -36,14 +41,12 @@ export declare function parseSearchArgs(args: {
36
41
  */
37
42
  export declare function formatSearchOutput(result: WebSearchResult): string;
38
43
  /**
39
- * Pending-call presentation: a search card titled by the query.
44
+ * Pending-call presentation: a search card titled by the query list.
40
45
  *
41
- * @param args - the raw tool arguments; only `query` feeds the view.
46
+ * @param args - the raw tool arguments; only the query text feeds the view.
42
47
  * @returns the generic card view (`kind: 'search'`) shown while the call runs.
43
48
  */
44
- export declare function presentSearchCall(args: {
45
- query: string;
46
- }): GenericCallView;
49
+ export declare function presentSearchCall(args: WebSearchArgs): GenericCallView;
47
50
  /**
48
51
  * The `web_search` tool's private `tool/result` `meta` payload: the structured
49
52
  * sources, the optional provider answer, and the truncation flag. Attached
@@ -55,7 +58,7 @@ export declare function presentSearchCall(args: {
55
58
  export interface WebSearchMeta {
56
59
  /** The faithful structured sources, in result order. */
57
60
  sources: WebSource[];
58
- /** True when the seam cut the source list to honor the result cap. */
61
+ /** True when the seam or multi-query merge cut the source list to honor the result cap. */
59
62
  truncated: boolean;
60
63
  /** The provider-generated answer text, when any. */
61
64
  answer?: string;
@@ -83,15 +86,13 @@ export declare function searchMetaFromResult(meta: unknown): WebSearchMeta | und
83
86
  * `web` capability falls back to the raw `tool/result` content, which is the
84
87
  * same text (see the web-result-card Agent Note).
85
88
  *
86
- * @param args - the raw tool arguments; `query` becomes the result-state title so
87
- * a window-truncated replay that dropped the call head still has one.
89
+ * @param args - the raw tool arguments; the queries become the result-state
90
+ * title so a window-truncated replay that dropped the call head still has one.
88
91
  * @param result - the final model-facing tool result; `meta` carries the sources.
89
92
  * @returns the search result view, or `undefined` (generic card) on failure or
90
93
  * malformed meta.
91
94
  */
92
- export declare function presentSearchResult(args: {
93
- query: string;
94
- }, result: ToolResult): WebSearchResultView | undefined;
95
+ export declare function presentSearchResult(args: WebSearchArgs, result: ToolResult): WebSearchResultView | undefined;
95
96
  /**
96
97
  * Register the `web_search` tool and its system-prompt guidance.
97
98
  *
@@ -99,10 +100,12 @@ export declare function presentSearchResult(args: {
99
100
  * registrations; both are effect-scoped and unregister on plugin dispose.
100
101
  * @param maxResults - the deployment's source cap, sent as every seam
101
102
  * request's `maxResults`.
103
+ * @param maxQueries - the deployment's query cap enforced before provider calls.
102
104
  * @param timeoutMs - the cooperative tool-call budget (ms) attached as the tool's
103
105
  * `ToolDefinition.timeoutMs` for `@deepseek-ai/dsh-tool-call-timeout-policy` to enforce.
104
106
  * @param fetchEnabled - whether the same composition exposes `web_fetch`, which
105
107
  * controls whether search guidance may recommend that follow-up tool.
106
108
  */
107
- export declare function applyWebSearchTool(ctx: Context, maxResults: number, timeoutMs: number, fetchEnabled: boolean): void;
109
+ export declare function applyWebSearchTool(ctx: Context, maxResults: number, maxQueries: number, timeoutMs: number, fetchEnabled: boolean): void;
110
+ export {};
108
111
  //# sourceMappingURL=search.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-tool-web",
3
3
  "description": "Model-facing web tools (web_search, web_fetch) over the DeepSeek Harness web capability seam (ctx.web)",
4
- "version": "0.1.0-rc.7",
4
+ "version": "0.1.0-rc.8",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,12 +32,12 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
36
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.7",
37
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.7",
38
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.7",
35
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
36
+ "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
37
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
38
+ "@deepseek-ai/dsh-web": "^0.1.0-rc.8",
39
39
  "@deepseek-ai/cordis": "^4.0.1",
40
- "@deepseek-ai/dsh-web": "^0.1.0-rc.7"
40
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.8"
41
41
  },
42
42
  "dependencies": {
43
43
  "@joplin/turndown-plugin-gfm": "^1.0.67",
@@ -46,18 +46,18 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/turndown": "^5.0.6",
49
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.7",
50
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
51
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.7",
52
- "@deepseek-ai/dsh-session": "^0.1.0-rc.7",
53
- "@deepseek-ai/dsh-spill-local": "^0.1.0-rc.7",
54
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.7",
55
- "@deepseek-ai/dsh-tool-call-timeout-policy": "^0.1.0-rc.7",
56
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.7",
57
- "@deepseek-ai/dsh-web": "^0.1.0-rc.7",
58
- "@deepseek-ai/dsh-web-fetch-http": "^0.1.0-rc.7",
59
- "@deepseek-ai/dsh-web-search-exa": "^0.1.0-rc.7",
60
- "@deepseek-ai/cordis": "^4.0.1",
61
- "@deepseek-ai/dsh-spill-policy": "^0.1.0-rc.7"
49
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
50
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
51
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.8",
52
+ "@deepseek-ai/dsh-spill-local": "^0.1.0-rc.8",
53
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
54
+ "@deepseek-ai/dsh-spill-policy": "^0.1.0-rc.8",
55
+ "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
56
+ "@deepseek-ai/dsh-tool-call-timeout-policy": "^0.1.0-rc.8",
57
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
58
+ "@deepseek-ai/dsh-web": "^0.1.0-rc.8",
59
+ "@deepseek-ai/dsh-web-fetch-http": "^0.1.0-rc.8",
60
+ "@deepseek-ai/dsh-web-search-exa": "^0.1.0-rc.8",
61
+ "@deepseek-ai/cordis": "^4.0.1"
62
62
  }
63
63
  }