refactor: apply repository naming contract

Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
Tianyi Cui
2026-08-13 00:36:22 +08:00
parent 101df7cf58
commit a2d0f7f411
3281 changed files with 21730 additions and 21592 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/web/tool-web/README.md
README.md: 6a7ca42773f4a62b75d6077c5cdd892b0ccfdbee
README.zh.md: d8dd51dcc8cf5146c3bc8ae28715ddf08ca7721a
README.md: 81dc4d3f3dbf9788e68b86513bfa09271fb99c84
README.zh.md: 8e976f23f72347e12c6aea35f9b99c5d8ad31d85

View File

@@ -2,7 +2,7 @@
English | [中文](README.zh.md)
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-timeout-policy`](../../guard/timeout-policy/README.md) (a `tools/execute` wrapper); each tool just forwards `exec.signal` to the seam.
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.
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.
@@ -11,7 +11,7 @@ Each tool is registered independently; a product that wants only one disables th
| Tool | Args | Behavior |
|---|---|---|
| `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. |
| `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-timeout-policy`), not a model argument. |
| `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. |
Both tools opt into concurrent scheduling because provider reads return content without mutating parent-agent state.
@@ -28,7 +28,7 @@ The normalized service results are also the canonical tool values: `WebSearchRes
| `searchTimeoutMs` | `30000` | Cooperative tool-call timeout budget (ms) for `web_search`. |
| `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. |
`fetchTimeoutMs`/`searchTimeoutMs` declare each tool's cooperative timeout budget (attached as `ToolDefinition.timeoutMs`), enforced by [`@deepseek-ai/dsh-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.
`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.
```yaml
- id: tool-web
@@ -37,7 +37,7 @@ The normalized service results are also the canonical tool values: `WebSearchRes
## Stable registration
Tool registration follows product **enablement**, not backend availability. A tool stays visible even when its selected provider is missing, misconfigured, ambiguous, or temporarily unavailable; the seam resolves the provider at execution time and execution fails with a structured `WebError` (e.g. `WEB_PROVIDER_UNAVAILABLE`, `WEB_PROVIDER_AMBIGUOUS`), which `ToolRegistry.execute()` turns into an error tool result the model can read and hooks/UI can route on. This keeps the model schema stable without making plugin load order, credential state, or HMR timing part of the model-facing contract. To remove a web tool entirely, disable it here in config.
Tool registration follows product **enablement**, not backend availability. A tool stays visible even when its selected provider is missing, misconfigured, ambiguous, or temporarily unavailable; the seam resolves the provider at execution time and execution fails with a structured `WebError` (e.g. `WEB_PROVIDER_UNAVAILABLE`, `WEB_PROVIDER_AMBIGUOUS`), which `ToolRuntime.execute()` turns into an error tool result the model can read and hooks/UI can route on. This keeps the model schema stable without making plugin load order, credential state, or HMR timing part of the model-facing contract. To remove a web tool entirely, disable it here in config.
The tool never calls a provider's `available()` and never enumerates providers — its only execution path is `ctx.web.search()` / `ctx.web.fetch()`, and provider unavailability reaches it as the structured `WebError` codes selection throws at execution time. Provider selection stays entirely inside the seam, with one owner.

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文
面向模型的 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-timeout-policy`](../../guard/timeout-policy/README.md)`tools/execute` 包装层)强制执行;每个工具只把 `exec.signal` 转发给 seam。
面向模型的 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。
每个工具独立注册;只需要其中一个工具的产品可以通过配置禁用另一个(`{ search: false }``{ fetch: false }`)。仅当抓取也通过配置启用时,搜索指引才会提及 `web_fetch`;仅启用搜索的组合则会要求模型使用返回的 snippet 并引用其 URL。
@@ -11,7 +11,7 @@
| 工具 | 参数 | 行为 |
|---|---|---|
| `web_search` | `query`string | 用于发现信息。返回可选答案与来源 URL。`max_results` **不**面向模型:工具设置上限(`searchMaxResults` 配置,默认 8并传给 seam。 |
| `web_fetch` | `url`string | 获取特定 URL。HTML 主体渲染为 markdownturndown带 GFM 表格/删除线);文本主体原样通过。非 2xx 状态会报告,而非报错。工具调用超时是部署策略(`dsh-timeout-policy`),不是模型参数。 |
| `web_fetch` | `url`string | 获取特定 URL。HTML 主体渲染为 markdownturndown带 GFM 表格/删除线);文本主体原样通过。非 2xx 状态会报告,而非报错。工具调用超时是部署策略(`dsh-tool-call-timeout-policy`),不是模型参数。 |
两个工具都选择并发调度,因为提供方读取会返回内容,不会修改父 agent智能体的状态。
@@ -28,7 +28,7 @@
| `searchTimeoutMs` | `30000` | `web_search` 的协作式工具调用超时预算ms。 |
| `fetchMaxOutputChars` | `200000` | 同步转换的源字符数与单次完整 `web_fetch` 输出的上限(状态头、渲染后的主体与页脚合并计算);主体被截断时,在能容纳的情况下附带截断提示。 |
`fetchTimeoutMs``searchTimeoutMs` 声明每个工具的协作式超时预算(附加为 `ToolDefinition.timeoutMs`),由 [`@deepseek-ai/dsh-timeout-policy`](../../guard/timeout-policy/README.md) 强制执行;面向模型的 schema 不公开超时参数。`fetchMaxOutputChars` 同时限制同步转换工作量和完整渲染结果:只转换至多该数量的源字符,随后对状态头、转换后的前缀和截断提示合并设限。默认值为本地提供方的 100,000 字符主体上限留出余量,但渲染膨胀仍可能使最终上限截断结果。
`fetchTimeoutMs``searchTimeoutMs` 声明每个工具的协作式超时预算(附加为 `ToolDefinition.timeoutMs`),由 [`@deepseek-ai/dsh-tool-call-timeout-policy`](../../guard/timeout-policy/README.md) 强制执行;面向模型的 schema 不公开超时参数。`fetchMaxOutputChars` 同时限制同步转换工作量和完整渲染结果:只转换至多该数量的源字符,随后对状态头、转换后的前缀和截断提示合并设限。默认值为本地提供方的 100,000 字符主体上限留出余量,但渲染膨胀仍可能使最终上限截断结果。
```yaml
- id: tool-web
@@ -37,7 +37,7 @@
## 稳定注册
工具注册遵循产品**启用状态**而非后端可用性。即使选中的提供方缺失、错误配置、存在歧义或暂时不可用工具仍保持可见seam 在执行时解析提供方,执行以结构化 `WebError`(例如 `WEB_PROVIDER_UNAVAILABLE``WEB_PROVIDER_AMBIGUOUS`)失败,`ToolRegistry.execute()` 会把它转为模型可读、钩子UI 可路由的错误工具结果。这样无需把插件加载顺序、凭据状态或 HMR热模块替换时机纳入面向模型约定也能保持模型 schema 稳定。要彻底移除 web 工具,请在此处通过配置将其禁用。
工具注册遵循产品**启用状态**而非后端可用性。即使选中的提供方缺失、错误配置、存在歧义或暂时不可用工具仍保持可见seam 在执行时解析提供方,执行以结构化 `WebError`(例如 `WEB_PROVIDER_UNAVAILABLE``WEB_PROVIDER_AMBIGUOUS`)失败,`ToolRuntime.execute()` 会把它转为模型可读、钩子UI 可路由的错误工具结果。这样无需把插件加载顺序、凭据状态或 HMR热模块替换时机纳入面向模型约定也能保持模型 schema 稳定。要彻底移除 web 工具,请在此处通过配置将其禁用。
工具绝不会调用提供方的 `available()`,也不会枚举提供方;唯一执行路径是 `ctx.web.search()``ctx.web.fetch()`,提供方不可用时,选择机制会在执行阶段抛出结构化 `WebError`,其错误码由工具接收。提供方选择完全留在 seam 内,由单一主体负责。

View File

@@ -53,10 +53,10 @@
"@deepseek-ai/dsh-spill-local": "workspace:^",
"@deepseek-ai/dsh-spill-policy": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-timeout-policy": "workspace:^",
"@deepseek-ai/dsh-tool-call-timeout-policy": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-web": "workspace:^",
"@deepseek-ai/dsh-web-fetch-local": "workspace:^",
"@deepseek-ai/dsh-web-fetch-http": "workspace:^",
"@deepseek-ai/dsh-web-search-exa": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
}

View File

@@ -80,7 +80,7 @@ turndown.addRule('tableRowWithoutSpanExpansion', {
* Validate value constraints the schema DSL can't express: a non-blank `url`.
* Throws a plain `Error` otherwise. No timeout parameter — the tool-call budget
* is deployment policy declared via `fetchTimeoutMs` config and enforced by
* `@deepseek-ai/dsh-timeout-policy`, not a model argument.
* `@deepseek-ai/dsh-tool-call-timeout-policy`, not a model argument.
*
* @param args - the schema-validated `web_fetch` arguments.
* @returns the arguments as the seam's request fields.
@@ -422,7 +422,7 @@ export function presentFetchResult(args: { url: string }, result: ToolResult): W
* @param ctx - context whose `tools` and `systemPrompt` registries receive the
* registrations; both are effect-scoped and unregister on plugin dispose.
* @param timeoutMs - the cooperative tool-call budget (ms) attached as the tool's
* `ToolDefinition.timeoutMs` for `@deepseek-ai/dsh-timeout-policy` to enforce.
* `ToolDefinition.timeoutMs` for `@deepseek-ai/dsh-tool-call-timeout-policy` to enforce.
* @param maxOutputChars - cap on the complete rendered tool output (see
* {@link formatFetchOutput}) and on source characters converted synchronously.
*/

View File

@@ -73,7 +73,7 @@ function assertPositiveInteger(name: string, value: number): void {
* that wants only one disables the other in config. Each tool's cooperative
* timeout budget (`fetchTimeoutMs`/`searchTimeoutMs`, default 30000) is resolved
* here and attached to the tool as `ToolDefinition.timeoutMs` for
* `@deepseek-ai/dsh-timeout-policy` to enforce. The tools' disposers are
* `@deepseek-ai/dsh-tool-call-timeout-policy` to enforce. The tools' disposers are
* fiber-scoped (the effect-based registries clean up on dispose), so no manual
* teardown is needed.
*/

View File

@@ -203,7 +203,7 @@ export function presentSearchResult(args: { query: string }, result: ToolResult)
* @param maxResults - the deployment's source cap, sent as every seam
* request's `maxResults`.
* @param timeoutMs - the cooperative tool-call budget (ms) attached as the tool's
* `ToolDefinition.timeoutMs` for `@deepseek-ai/dsh-timeout-policy` to enforce.
* `ToolDefinition.timeoutMs` for `@deepseek-ai/dsh-tool-call-timeout-policy` to enforce.
* @param fetchEnabled - whether the same composition exposes `web_fetch`, which
* controls whether search guidance may recommend that follow-up tool.
*/

View File

@@ -1,7 +1,7 @@
/**
* Integration: the real fetch backend (`dsh-web-fetch-local`) + a real search provider
* Integration: the real fetch backend (`dsh-web-fetch-http`) + a real search provider
* (`dsh-web-search-exa`) + the real seam (`dsh-web`) + the model tool (`dsh-tool-web`) + the
* tool-call timeout policy (`dsh-timeout-policy`), exercised through `ctx.tools.execute()` —
* tool-call timeout policy (`dsh-tool-call-timeout-policy`), exercised through `ctx.tools.execute()` —
* nothing bypasses the tool registry. Fetch verifies world effects against loopback HTTP; search
* uses the real Exa provider with only its network boundary stubbed.
*/
@@ -12,12 +12,12 @@ import { AddressInfo } from 'node:net'
import { Context } from '@deepseek-ai/cordis'
import { CallId } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { type ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import WebService from '@deepseek-ai/dsh-web'
import * as WebFetchLocal from '@deepseek-ai/dsh-web-fetch-local'
import ToolRuntime, { type ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import WebRuntime from '@deepseek-ai/dsh-web'
import * as WebFetchLocal from '@deepseek-ai/dsh-web-fetch-http'
import * as WebSearchExa from '@deepseek-ai/dsh-web-search-exa'
import * as ToolWeb from '@deepseek-ai/dsh-tool-web'
import * as TimeoutPolicy from '@deepseek-ai/dsh-timeout-policy'
import * as TimeoutPolicy from '@deepseek-ai/dsh-tool-call-timeout-policy'
const testToolSignal = new AbortController().signal
@@ -37,8 +37,8 @@ beforeEach(async () => {
ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(WebService, { searchProvider: WebSearchExa.EXA_PROVIDER_ID, fetchProvider: WebFetchLocal.LOCAL_FETCH_PROVIDER_ID })
await ctx.plugin(ToolRuntime)
await ctx.plugin(WebRuntime, { searchProvider: WebSearchExa.EXA_PROVIDER_ID, fetchProvider: WebFetchLocal.LOCAL_FETCH_PROVIDER_ID })
await ctx.plugin(WebFetchLocal, {})
await ctx.plugin(WebSearchExa, { apiKey: 'exa-key', baseURL: 'https://api.exa.test' })
// The shipped deployment shape: the tool-call budget is declared by tool-web
@@ -132,8 +132,8 @@ describe('tool-call timeout returns TOOL_TIMEOUT (deadline wins over a slow fetc
tctx = new Context()
await tctx.plugin(SystemPrompt)
await tctx.plugin(ToolRegistry)
await tctx.plugin(WebService, { fetchProvider: WebFetchLocal.LOCAL_FETCH_PROVIDER_ID })
await tctx.plugin(ToolRuntime)
await tctx.plugin(WebRuntime, { fetchProvider: WebFetchLocal.LOCAL_FETCH_PROVIDER_ID })
// Provider backstop well ABOVE the tool-call budget, so the policy wins.
await tctx.plugin(WebFetchLocal, { timeoutMs: 30_000 })
await tctx.plugin(TimeoutPolicy)
@@ -150,7 +150,7 @@ describe('tool-call timeout returns TOOL_TIMEOUT (deadline wins over a slow fetc
it('returns a structured TOOL_TIMEOUT (not the provider WEB_FETCH_TIMEOUT) when the tool-call budget wins', async () => {
const out = await tctx.tools.execute({ signal: testToolSignal, callId: CallId('slow-1'), name: 'web_fetch', arguments: { url: slowBase } })
expect(out.isError).toBe(true)
// The outer tool-call deadline won: TOOL_TIMEOUT, owned by dsh-timeout-policy,
// The outer tool-call deadline won: TOOL_TIMEOUT, owned by dsh-tool-call-timeout-policy,
// NOT the provider's own WEB_FETCH_TIMEOUT (its 30s backstop never fired).
expect(out.error?.info?.code).toBe('TOOL_TIMEOUT')
const text = out.content.map(b => (b.type === 'text' ? b.text : '')).join('')
@@ -160,7 +160,7 @@ describe('tool-call timeout returns TOOL_TIMEOUT (deadline wins over a slow fetc
it('the provider backstop still protects a direct provider call (no tool-call policy in that path)', async () => {
// A direct provider caller bypasses tools/execute, so a short configured backstop
// must produce provider-owned WEB_FETCH_TIMEOUT rather than TOOL_TIMEOUT.
const direct = new WebFetchLocal.LocalFetchProvider({
const direct = new WebFetchLocal.HttpFetchProvider({
maxUrlLength: 2048,
maxResponseBytes: 5_000_000,
maxBodyChars: 100_000,

View File

@@ -9,8 +9,8 @@ import { describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import WebService from '@deepseek-ai/dsh-web'
import ToolRuntime from '@deepseek-ai/dsh-tools'
import WebRuntime from '@deepseek-ai/dsh-web'
import * as toolWeb from '@deepseek-ai/dsh-tool-web'
describe('dsh-tool-web real-load-path guard', () => {
@@ -28,8 +28,8 @@ describe('dsh-tool-web real-load-path guard', () => {
it('boots over ctx.web through the unwrapped module without an inject error', async () => {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(WebService, {})
await ctx.plugin(ToolRuntime)
await ctx.plugin(WebRuntime, {})
const loader = Object.create(Loader.prototype) as Loader
const unwrapped = loader.unwrapExports(toolWeb) as Parameters<Context['plugin']>[0]

View File

@@ -17,12 +17,12 @@ import { Context } from '@deepseek-ai/cordis'
import { CallId } from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import ToolRuntime from '@deepseek-ai/dsh-tools'
import type { ToolExecution } from '@deepseek-ai/dsh-tools'
const testToolSignal = new AbortController().signal
import WebService from '@deepseek-ai/dsh-web'
import * as WebFetchLocal from '@deepseek-ai/dsh-web-fetch-local'
import WebRuntime from '@deepseek-ai/dsh-web'
import * as WebFetchLocal from '@deepseek-ai/dsh-web-fetch-http'
import LocalSpillStore from '@deepseek-ai/dsh-spill-local'
import * as SpillPolicy from '@deepseek-ai/dsh-spill-policy'
import * as ToolWeb from '@deepseek-ai/dsh-tool-web'
@@ -47,8 +47,8 @@ beforeEach(async () => {
ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(WebService, { fetchProvider: WebFetchLocal.LOCAL_FETCH_PROVIDER_ID })
await ctx.plugin(ToolRuntime)
await ctx.plugin(WebRuntime, { fetchProvider: WebFetchLocal.LOCAL_FETCH_PROVIDER_ID })
// Provider cap generous so the tool returns a large formatted result; the
// policy cap is what triggers the spill (the Agent Note's separation of concerns).
await ctx.plugin(WebFetchLocal, { maxBodyChars: 500_000 })

View File

@@ -3,8 +3,8 @@ import { Context } from '@deepseek-ai/cordis'
import TurndownService from 'turndown'
import { CallId } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { type ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import WebService from '@deepseek-ai/dsh-web'
import ToolRuntime, { type ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import WebRuntime from '@deepseek-ai/dsh-web'
import type { WebSearchProvider, WebSearchResult } from '@deepseek-ai/dsh-web'
import * as ToolWeb from '@deepseek-ai/dsh-tool-web'
import {
@@ -36,14 +36,14 @@ function searchProvider(result: WebSearchResult, isAvailable = available): WebSe
/** Mount the real registry, seam, and tool-web; return an executor helper. */
async function mountTools(opts: {
config?: ToolWeb.Config
webConfig?: ConstructorParameters<typeof WebService>[1]
webConfig?: ConstructorParameters<typeof WebRuntime>[1]
search?: WebSearchProvider
fetchProvider?: import('@deepseek-ai/dsh-web').WebFetchProvider
} = {}): Promise<{ ctx: Context; fiber: Awaited<ReturnType<Context['plugin']>>; call: (name: string, args: unknown) => Promise<ToolExecutionResult> }> {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(WebService, opts.webConfig ?? {})
await ctx.plugin(ToolRuntime)
await ctx.plugin(WebRuntime, opts.webConfig ?? {})
if (opts.search) ctx.web.registerSearchProvider(opts.search)
if (opts.fetchProvider) ctx.web.registerFetchProvider(opts.fetchProvider)
const fiber = await ctx.plugin(ToolWeb, opts.config ?? {})
@@ -595,7 +595,7 @@ describe('tool-web execution through the real registry', () => {
truncated: false,
})
// The model schema exposes no timeout: the tool forwards only the url; the
// tool-call budget is owned by dsh-timeout-policy over exec.signal.
// tool-call budget is owned by dsh-tool-call-timeout-policy over exec.signal.
expect(seen.request).toEqual({ url: 'https://a.test' })
expect(seen.signal).toBe(controller.signal)
await fiber.dispose()
@@ -679,8 +679,8 @@ describe('searchMaxResults is plugin config', () => {
])('rejects a %s searchMaxResults at load', async (_label, value) => {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(WebService, {})
await ctx.plugin(ToolRuntime)
await ctx.plugin(WebRuntime, {})
await expect(ctx.plugin(ToolWeb, { searchMaxResults: value }))
.rejects.toThrow(/tool-web: searchMaxResults must be a positive integer/)
})
@@ -707,8 +707,8 @@ describe('tool-call timeout budget is plugin config', () => {
])('rejects a non-positive-integer %s at load', async (key, config) => {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(WebService, {})
await ctx.plugin(ToolRuntime)
await ctx.plugin(WebRuntime, {})
await expect(ctx.plugin(ToolWeb, config))
.rejects.toThrow(new RegExp(`tool-web: ${key} must be a positive integer`))
})
@@ -739,8 +739,8 @@ describe('fetchMaxOutputChars is plugin config', () => {
it.each([0, -1, 1.5])('rejects an invalid fetchMaxOutputChars value %s at load', async (value) => {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(WebService, {})
await ctx.plugin(ToolRuntime)
await ctx.plugin(WebRuntime, {})
await expect(ctx.plugin(ToolWeb, { fetchMaxOutputChars: value }))
.rejects.toThrow(/tool-web: fetchMaxOutputChars must be a positive integer/)
})

View File

@@ -31,7 +31,7 @@
"path": "../web"
},
{
"path": "../../support/invariants"
"path": "../../runtime-diagnostics/invariants"
}
]
}