deepseek-web-retrieval 0.3.0 → 0.5.0

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +96 -230
  2. package/README.md +53 -25
  3. package/dist/cli.js +1358 -617
  4. package/docs/agents/codex.md +32 -11
  5. package/docs/agents/opencode-v2.md +5 -5
  6. package/docs/agents/pi.md +92 -90
  7. package/docs/design/installer-design.md +72 -9
  8. package/docs/hosts.jsonc +2 -2
  9. package/docs/integration.md +14 -9
  10. package/docs/research/control-matrix.md +48 -13
  11. package/docs/research/mcp-compatibility.md +9 -4
  12. package/docs/research/search-api-defaults.md +309 -0
  13. package/examples/pi.settings.jsonc +17 -0
  14. package/index.ts +2 -2
  15. package/package.json +35 -4
  16. package/src/core/auth.ts +4 -6
  17. package/src/core/client.ts +65 -53
  18. package/src/core/constants.ts +30 -24
  19. package/src/core/descriptions.ts +30 -6
  20. package/src/core/domains.ts +56 -67
  21. package/src/core/env.ts +112 -0
  22. package/src/core/format.ts +53 -50
  23. package/src/core/index.ts +9 -9
  24. package/src/core/jsonc.ts +6 -6
  25. package/src/core/object.ts +4 -0
  26. package/src/core/options.ts +21 -26
  27. package/src/core/search.ts +45 -44
  28. package/src/core/transport.ts +109 -113
  29. package/src/core/types.ts +47 -47
  30. package/src/core/url.ts +20 -20
  31. package/src/installer/apply.ts +256 -81
  32. package/src/installer/cli.ts +135 -104
  33. package/src/installer/detect.ts +46 -31
  34. package/src/installer/formats/index.ts +26 -28
  35. package/src/installer/formats/json.ts +81 -77
  36. package/src/installer/formats/rules.ts +55 -28
  37. package/src/installer/formats/toml.ts +88 -81
  38. package/src/installer/formats/value.ts +34 -30
  39. package/src/installer/hosts/codex.ts +100 -54
  40. package/src/installer/hosts/index.ts +126 -41
  41. package/src/installer/hosts/opencode.ts +70 -51
  42. package/src/installer/hosts/pi.ts +269 -75
  43. package/src/installer/hosts/shared.ts +19 -14
  44. package/src/installer/operation-lock.ts +54 -0
  45. package/src/installer/ownership.ts +129 -0
  46. package/src/installer/paths.ts +7 -5
  47. package/src/installer/persistence.ts +66 -0
  48. package/src/installer/render.ts +29 -29
  49. package/src/installer/revert.ts +33 -29
  50. package/src/installer/rulesContent.ts +24 -13
  51. package/src/installer/state.ts +116 -31
  52. package/src/installer/types.ts +74 -56
  53. package/src/installer/uninstall.ts +40 -17
  54. package/src/mcp/auth.ts +7 -8
  55. package/src/mcp/cli.ts +38 -20
  56. package/src/mcp/client.ts +35 -0
  57. package/src/mcp/server.ts +68 -97
  58. package/src/opencode/auth.ts +24 -26
  59. package/src/opencode/plugin.ts +99 -87
  60. package/src/pi/extension.ts +164 -0
  61. package/src/pi/settings.ts +49 -0
  62. package/src/mcp/config.ts +0 -124
@@ -79,14 +79,19 @@ paths imply the available scopes; keys locked to a single scope are enumerated i
79
79
 
80
80
  - **Positioning** — VSCode-extension agent with an experimental lightweight CLI (`clite`,
81
81
  built on the Cline SDKs). Apache-2.0. MCP plus CLI/SDK custom tools; neither replaces the
82
- built-in `fetch_web_content`. Current docs.cline.bot describes the newer 3.x `cline`
83
- binary — the lock is 0.0.13 `clite`, and everything here is verified against that binary.
82
+ built-in `fetch_web_content`. `@cline/cli` is frozen at 0.0.13 (last publish 2026-05-11);
83
+ the next day the unscoped `cline` package superseded it (3.0.0 on 2026-05-12; 3.0.73 at
84
+ 2026-10-21; binary renamed `clite` → `cline`), and docs.cline.bot now describes only the
85
+ `cline` binary (re-verified 2026-10-21). The lock stays 0.0.13 `clite` and everything
86
+ here is verified against that binary; adopting 3.x means re-verifying this whole row.
84
87
  - **Built-in web tools** — `fetch_web_content` (fetch a URL and analyze it with a prompt);
85
88
  no built-in web search (the binary's `web_search` strings are Bedrock provider tool
86
89
  versions). Kill switch: `"disabledTools": ["fetch_web_content"]` in
87
90
  `~/.cline/data/settings/global-settings.json` (path overridable via
88
91
  `CLINE_GLOBAL_SETTINGS_PATH`); a disabled tool is dropped from the available set.
89
- `--yolo` mode also deactivates web fetch.
92
+ `--yolo` mode also deactivates web fetch. Current docs.cline.bot documents the SDK's
93
+ `toolPolicies: { enabled: false }` as the disable mechanism and no longer mentions the
94
+ `disabledTools` key, which still exists in the settings file (re-verified 2026-10-21).
90
95
  - **MCP registration** — `~/.cline/settings/cline_mcp_settings.json`
91
96
  (`CLINE_MCP_SETTINGS_PATH`), written by the `clite mcp` wizard in a nested form:
92
97
  `mcpServers.<name>.transport` with `type` (`stdio` | `sse` | `streamableHttp`) plus
@@ -108,11 +113,25 @@ paths imply the available scopes; keys locked to a single scope are enumerated i
108
113
  <bool>` form parses but discards the value at 0.163.0 — a silent no-op
109
114
  ([config source at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/config/src/config_toml.rs)).
110
115
  - **MCP registration** — `~/.codex/config.toml` `[mcp_servers.<name>]`: stdio
111
- `command`/`args`/`env`/`env_vars`; `startup_timeout_sec` (10 s), `tool_timeout_sec`
112
- (60 s); `enabled_tools`/`disabled_tools`, per-tool approval modes, and output limits.
116
+ `command`/`args`/`env`/`env_vars`; `startup_timeout_sec` (30 s) and `tool_timeout_sec`
117
+ (300 s) defaults — the `DEFAULT_STARTUP_TIMEOUT`/`DEFAULT_TOOL_TIMEOUT` constants
118
+ ([rmcp_client.rs at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/codex-mcp/src/rmcp_client.rs));
119
+ `enabled_tools`/`disabled_tools` (verbatim raw tool-name match, filtered before
120
+ registration so the model never sees a disabled tool; an empty `enabled_tools` allow-list
121
+ exposes nothing), per-tool approval modes, and output limits.
113
122
  stdio children do not inherit the parent environment (only `HOME`, `PATH`, and explicit
114
123
  `env`), so `DEEPSEEK_API_KEY` must be passed explicitly. When a tool result has
115
124
  `structuredContent`, Codex replaces the content blocks with the serialized JSON. No SSE.
125
+ Codex identifies itself in the initialize handshake as clientInfo name `codex-mcp-client`
126
+ with title `Codex`
127
+ ([rmcp_client.rs at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/codex-mcp/src/rmcp_client.rs)).
128
+ The official docs moved to learn.chatgpt.com
129
+ ([MCP](https://learn.chatgpt.com/docs/extend/mcp),
130
+ [config reference](https://learn.chatgpt.com/docs/config-file/config-reference); the old
131
+ developers.openai.com URLs 308-redirect there) and now document every key above — but
132
+ state 10 s/60 s timeout defaults, which describe an older release line; the 30 s/300 s
133
+ constants above are the facts at the locked 0.163.0 (docs discrepancy re-verified
134
+ 2026-10-21).
116
135
  - **Rules** — `~/.codex/AGENTS.md` (with `AGENTS.override.md` preferred) at user scope;
117
136
  every `AGENTS.md` from the project root down to the cwd, concatenated root→cwd.
118
137
  Extensible via `project_doc_fallback_filenames` and `developer_instructions`
@@ -136,12 +155,25 @@ paths imply the available scopes; keys locked to a single scope are enumerated i
136
155
 
137
156
  ### Pi
138
157
 
139
- - **Positioning** — minimal, fully customizable agent (earendil-works). MIT. MCP server
140
- plus a TypeScript extension API. See [Pi](../agents/pi.md).
158
+ - **Positioning** — minimal, fully customizable agent (earendil-works). MIT. A TypeScript
159
+ extension API plus MCP. See [Pi](../agents/pi.md); the shipped adapter is the native
160
+ extension (`src/pi/`), with the MCP server as the fallback path.
141
161
  - **Built-in web tools** — none at 1.3.0. The built-in set is exactly `bash, edit, find,
142
162
  grep, ls, powershell, read, write` (default-active: `read, bash, edit, write`); web
143
163
  search/fetch arrive via MCP or extensions
144
164
  ([tools index at v1.3.0](https://github.com/earendil-works/pi/blob/v1.3.0/packages/coding-agent/src/core/tools/index.ts)).
165
+ - **Extension registration** — `packages` in settings.json (`~/.pi/agent/settings.json` or
166
+ `.pi/settings.json`): source strings (`npm:<pkg>`, `git:…`, local paths) resolved at
167
+ session start; `pi install <source>` appends them, and a package's `pi` manifest field
168
+ names its entry files (loaded via jiti, TypeScript directly). Extension tools default to
169
+ `exposure: "direct"` — no codemode trap — and run in-process: the key is read from
170
+ `process.env` (no forwarding), and results bypass the MCP layer entirely, so neither the
171
+ 20 KB truncation nor the 60 s request timeout applies. There is no per-extension options
172
+ channel: the runtime config surface is the `DEEPSEEK_*` environment (the same pipeline as
173
+ the MCP server) plus a `deepseekWebRetrieval` settings namespace the installer writes for
174
+ the off tier — Pi preserves unknown settings keys on read and on its own rewrites
175
+ (`pi install`), verified at 1.3.0. Action methods such as `getSettings()` cannot be
176
+ called during extension loading, so the adapter reads the settings files itself.
145
177
  - **MCP registration** — `~/.pi/agent/mcp.json` or `.pi/mcp.json`: stdio
146
178
  `command`/`args`/`env`/`cwd`, HTTP `url`/`headers`, plus `timeout`, `enabled`,
147
179
  `exposure`. MCP tools are hidden from the model by default
@@ -237,7 +269,9 @@ paths imply the available scopes; keys locked to a single scope are enumerated i
237
269
  - **Rules** — `~/.copilot/copilot-instructions.md` (user),
238
270
  `.github/copilot-instructions.md` (repo) and `.github/instructions/**/*.instructions.md`
239
271
  (path-scoped via `applyTo` globs); `AGENTS.md`/`CLAUDE.md` discovery (changelog-dated
240
- ≤1.0.86; `GEMINI.md` could not be dated to the lock);
272
+ ≤1.0.86); `GEMINI.md` is never named in the changelog but the official docs have listed
273
+ it since the page's creation on 2026-02-16 (docs-history-dated, 0.0.41x era — comfortably
274
+ ≤1.0.98; re-verified 2026-10-21); `.claude/rules` support is changelog-dated 1.0.89;
241
275
  `COPILOT_CUSTOM_INSTRUCTIONS_DIRS` adds directories
242
276
  ([custom instructions](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-custom-instructions)).
243
277
 
@@ -250,8 +284,9 @@ paths imply the available scopes; keys locked to a single scope are enumerated i
250
284
  - **Replacement is exclusive to native extension points** — only OpenCode. Exclusion is
251
285
  the general forced path on MCP hosts. The two structural limits: OpenCode search has no
252
286
  middle tier, and Cursor search cannot be forced at all.
253
- - **Version drift to re-verify on the next baseline pass:** Cline's docs describe the 3.x
254
- `cline` binary while the lock is 0.0.13 `clite`; Codex's config docs have moved and
255
- describe a newer release; Copilot's `GEMINI.md` support could not be changelog-dated to
256
- ≤1.0.98. Protocol-revision evidence for every host lives in
257
- [mcp-compatibility.md](mcp-compatibility.md).
287
+ - **Version drift to re-verify on the next baseline pass:** Cline's `cline` 3.x package
288
+ (3.0.73 at 2026-10-21) has superseded the locked 0.0.13 `clite`; adopting it means
289
+ re-verifying that host's whole row (the documented kill switch already differs: SDK
290
+ `toolPolicies`). Codex's docs location and Copilot's `GEMINI.md` dating were re-verified
291
+ 2026-10-21 and are settled in their sections above. Protocol-revision evidence for every
292
+ host lives in [mcp-compatibility.md](mcp-compatibility.md).
@@ -107,12 +107,17 @@ differences:
107
107
  capability; do not rely on resources, prompts, sampling, elicitation, or roots — the
108
108
  hosts' client capabilities are inconsistent (Codex has elicitation only; Pi serves
109
109
  roots). Always return readable
110
- text in `content` blocks, and mirror the same data in `structuredContent` for
111
- `2025-06-18`+ clients — the spec's own backwards-compat SHOULD. This split matters in
112
- practice: Codex *replaces* content with `structuredContent` when present; Cline reads
110
+ text in `content` blocks, and mirror the same data in `structuredContent` — the
111
+ spec's own backwards-compat SHOULD — except for clients that would replace the text
112
+ with its serialized JSON: Codex *replaces* content with `structuredContent` when
113
+ present, so the shipped server detects its `codex-mcp-client` identity (the
114
+ initialize `clientInfo` on handshake-era connections, the per-request envelope on
115
+ `2026-07-28`) and omits the mirror for it. Cline reads
113
116
  text content only; Pi is
114
117
  content-first, falling back to `structuredContent` only when a result has no content
115
- blocks (verified in `tools.ts` at v1.3.0).
118
+ blocks (verified in `tools.ts` at v1.3.0). No `outputSchema` is declared: the SDK
119
+ rejects text-only results for a tool with a declared schema, and no host in the
120
+ matrix validates the schema anyway.
116
121
  2. **stdio is the universal transport.** Every host in the matrix launches stdio servers
117
122
  with the same conceptual JSON (`mcpServers` → `command`/`args`/`env`), differing only in
118
123
  file location and cosmetic field names. stdio needs no OAuth and no URL config, and the
@@ -0,0 +1,309 @@
1
+ # Search and fetch API defaults across vendors
2
+
3
+ This note answers one question: what are the **factory defaults** — the values applied when a
4
+ caller passes nothing — of the mature AI-native web-search and web-fetch APIs? It exists
5
+ because this repo layers its own client-side defaults on top of the DeepSeek Search/Fetch API,
6
+ and the original values (`maxResults=5` on an API `count` of 10, `minRerank=1.5`,
7
+ `minAuthority=2`, `allowUnknownAuthority=false`, `maxRetries=0`, `timeout=30s`, fetch
8
+ truncation at 16,000 characters, summary truncation at 1,200, response-body cap 5 MiB) were
9
+ challenged as arbitrary. The pages below are the benchmark against which the repo's defaults
10
+ are grounded; based on this survey the search-side defaults were changed on 2026-10-21 to
11
+ `maxResults=10`, `minRerank=0`, `minAuthority=0`, `allowUnknownAuthority=true` (filters stay
12
+ available as opt-ins). It records facts only; the closing section lines the repo's values up
13
+ against them without recommending changes.
14
+
15
+ **Research baseline:** 2026-10-21. Claims are cited to primary sources only: official vendor
16
+ documentation and official SDK source (GitHub repos and published npm/PyPI packages). Secondhand
17
+ blogs and tutorials are excluded. "Default" means the value in force when the parameter is
18
+ omitted — not the allowed range, and not an example value. Where no default is published, that
19
+ is stated as such rather than guessed.
20
+
21
+ The five questions asked of every product:
22
+
23
+ 1. Search-result count default (the analogue of this repo's `maxResults`/API `count`).
24
+ 2. Any **client-side relevance/authority filtering** default (the analogue of
25
+ `minRerank`/`minAuthority`/`allowUnknownAuthority`) — in particular, does anyone drop
26
+ results by authority in the client?
27
+ 3. HTTP retry default at the SDK layer.
28
+ 4. Request timeout default (SDK and API layer).
29
+ 5. Fetched-content truncation default (character limits, byte caps).
30
+
31
+ ## Tavily
32
+
33
+ API defaults from the official OpenAPI reference for `POST /search`
34
+ ([docs](https://docs.tavily.com/documentation/api-reference/endpoint/search)):
35
+
36
+ | Parameter | Default when omitted | Source |
37
+ | --- | --- | --- |
38
+ | `max_results` | **10** (min 0, max 20) | [API reference](https://docs.tavily.com/documentation/api-reference/endpoint/search) |
39
+ | `search_depth` | **`basic`** (vs `advanced`/`fast`/`ultra-fast`) | same |
40
+ | `chunks_per_source` | **3** (chunks of ~500 chars `basic`/`fast`, ~800 `advanced`) | same |
41
+ | `topic` | **`general`** | same |
42
+ | `include_answer` / `include_raw_content` / `include_images` | **false** | same |
43
+ | `auto_parameters` | **false** | same |
44
+ | `include_domains` / `exclude_domains` | **empty** | same |
45
+ | `include_domains_mode` | **`restrict`** ("`include_domains` acts as a hard filter unless set to `prefer`") | same |
46
+
47
+ No relevance score, authority tier, or any other per-result quality signal is returned or
48
+ filterable in the client — ranking is server-side and opaque.
49
+
50
+ SDK layer ([tavily-python, master](https://github.com/tavily-ai/tavily-python/blob/master/tavily/tavily.py)):
51
+ `max_results` and `search_depth` default to `None` (so the API defaults above apply); `search`
52
+ has `timeout: float = 60` clamped to `min(timeout, 120)` seconds; `extract` defaults to 30 s and
53
+ `crawl` to 150 s. The client posts through a plain `requests.Session` with no retry adapter —
54
+ the only retry-related code parses a `retry_after_seconds` field out of error payloads.
55
+
56
+ ## Exa
57
+
58
+ API defaults from the official OpenAPI spec
59
+ ([reference](https://exa.ai/docs/reference/search)):
60
+
61
+ | Parameter | Default when omitted | Source |
62
+ | --- | --- | --- |
63
+ | `numResults` | **10** (max 100) | [reference](https://exa.ai/docs/reference/search); the [search quickstart](https://exa.ai/docs/search/quickstart) states it in prose: "Search returns up to 10 results by default" |
64
+ | `type` | **`auto`** | [reference](https://exa.ai/docs/reference/search) |
65
+ | `contents.text` (boolean form) | **false** — "If true, returns full page text with default settings. If false, disables text return." | same |
66
+ | `contents.text.verbosity` | **`compact`** | same |
67
+ | `contents.highlights` (boolean form) | **false** | same |
68
+ | `moderation` | **false** | same |
69
+ | `contents.livecrawlTimeout` | **10000 ms** | same |
70
+ | `contents.text.maxCharacters` | **no published default** (max 1,000,000); the [contents quickstart](https://exa.ai/docs/contents/quickstart) frames it as opt-in: "Full pages can be large, so use `maxCharacters` when you need a limit" | both |
71
+
72
+ Like Tavily, Exa exposes no per-result authority/score field and no client filter for one;
73
+ `moderation` (off by default) is content-moderation, not authority.
74
+
75
+ ## Perplexity
76
+
77
+ The Sonar chat-completions API was retired on 2026-09-27; the live products are the Search API
78
+ and the Agent API ([migration guide](https://docs.perplexity.ai/docs/agent-api/migrate-from-sonar/overview)).
79
+
80
+ Search API (`POST /search`,
81
+ [reference](https://docs.perplexity.ai/api-reference/search-post); the
82
+ [quickstart](https://docs.perplexity.ai/docs/search/quickstart) restates it):
83
+
84
+ | Parameter | Default when omitted | Source |
85
+ | --- | --- | --- |
86
+ | `max_results` | **10** (1–20) | [quickstart](https://docs.perplexity.ai/docs/search/quickstart): "`max_results` accepts values from 1 to 20 and defaults to 10" |
87
+ | `search_type` | **`web`** | [reference](https://docs.perplexity.ai/api-reference/search-post) |
88
+
89
+ Agent API `web_search` tool ([tool guide](https://docs.perplexity.ai/docs/agent-api/tools/web-search),
90
+ [schema](https://docs.perplexity.ai/api-reference/agent-post)): `max_results` accepts 1–50 and
91
+ `max_tokens`/`max_tokens_per_page` cap search context, but none of the three publishes a numeric
92
+ default — "When omitted, the default budget applies" is all the docs say. The one published
93
+ tool-count default in the same schema is `image_search.max_results`: **5** (1–30)
94
+ ([reference](https://docs.perplexity.ai/api-reference/agent-post)). The retired Sonar
95
+ `web_search_options.search_context_size` has no named-size successor; the
96
+ [migration table](https://docs.perplexity.ai/docs/agent-api/migrate-from-sonar/how-to) maps it
97
+ to the token budgets above, and the current docs no longer state its old default. No
98
+ authority/score filtering exists on any of these surfaces.
99
+
100
+ ## Firecrawl
101
+
102
+ API defaults from the official OpenAPI references
103
+ ([search](https://docs.firecrawl.dev/api-reference/endpoint/search),
104
+ [scrape](https://docs.firecrawl.dev/api-reference/endpoint/scrape)):
105
+
106
+ | Parameter | Default when omitted | Source |
107
+ | --- | --- | --- |
108
+ | search `limit` | **10** (1–100; per source type when multiple `sources`) | [search reference](https://docs.firecrawl.dev/api-reference/endpoint/search) |
109
+ | search `sources` | **`["web"]`** | same |
110
+ | search `timeout` | **60000 ms** | same |
111
+ | search `highlights` | **true** | same |
112
+ | scrape `timeout` | **60000 ms** (min 1 s, max 300 s) | [scrape reference](https://docs.firecrawl.dev/api-reference/endpoint/scrape) |
113
+ | scrape `onlyMainContent` | **true** | same |
114
+ | scrape `maxAge` (cache freshness window) | **172800000 ms (2 days)** | same |
115
+
116
+ No character or byte truncation default is published for scraped content. No authority/score
117
+ filter exists.
118
+
119
+ SDK layer (`@mendable/firecrawl-js` 4.50.0, npm tarball with registry provenance;
120
+ [source](https://www.npmjs.com/package/@mendable/firecrawl-js)): `maxRetries` defaults to
121
+ **3** (`options.maxRetries ?? 3`), retrying **only HTTP 502** with exponential backoff
122
+ (`backoffFactor` 0.5 s × 2^attempt); `timeoutMs` defaults to **300000 ms (5 min)**.
123
+
124
+ ## Jina Reader (r.jina.ai / s.jina.ai)
125
+
126
+ Official product page ([jina.ai/reader](https://jina.ai/reader/)) and official repo
127
+ ([jina-ai/reader](https://github.com/jina-ai/reader)):
128
+
129
+ | Behavior | Default | Source |
130
+ | --- | --- | --- |
131
+ | Search-mode result count (`s.jina.ai`) | **fixed 5** — "Reader searches the web, fetches the top 5 results, visits each URL, and applies `r.jina.ai` to it"; no count parameter is exposed | [repo README](https://github.com/jina-ai/reader/blob/main/README.md); the [product page](https://jina.ai/reader/) likewise: "it returns a list of five entries" |
132
+ | `x-timeout` | **no default set** — optional header, integer seconds, max 180; when absent the option stays `null` (no client-side deadline) | [README](https://github.com/jina-ai/reader/blob/main/README.md) and [`crawler-options.ts`](https://github.com/jina-ai/reader/blob/main/src/dto/crawler-options.ts) (`instance.timeout ??= null`) |
133
+ | `x-token-budget` | **no default** (parsed only when sent) | [`crawler-options.ts`](https://github.com/jina-ai/reader/blob/main/src/dto/crawler-options.ts) |
134
+ | Content truncation | none published | — |
135
+
136
+ No relevance/authority filter; no retry logic documented.
137
+
138
+ ## Anthropic web_search / web_fetch server tools
139
+
140
+ Current docs live at
141
+ [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool),
142
+ [web fetch tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool),
143
+ and the [server tools guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools).
144
+
145
+ | Parameter | Default when omitted | Source |
146
+ | --- | --- | --- |
147
+ | `web_search.max_uses` | optional; no numeric default published ("limits the number of searches performed"; guidance: "Simple factual queries typically use 1–3 searches") | [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) |
148
+ | `web_fetch.max_uses` | optional; **"There is currently no default limit."** | [web fetch tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool) |
149
+ | `web_fetch.max_content_tokens` | optional token cap; **no default value published** (doc example: 100000) — "If the fetched content exceeds this limit, the tool truncates it … applies to text content, not to binary content such as PDFs" | [web fetch tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool) |
150
+ | `web_fetch` URL length | hard max **250 characters** | same (error `url_too_long`) |
151
+ | `web_fetch` content types | text, HTML, PDF only; `robots.txt` and private addresses respected server-side | same |
152
+ | `web_fetch.use_cache` | **true** | same |
153
+ | `web_fetch.citations` | **disabled by default** | same |
154
+ | `response_inclusion` (both tools) | **`"full"`** | both |
155
+ | Result count / authority filter | **neither exists** — the model decides how many searches to run; no score or authority field is returned | both |
156
+
157
+ A widely repeated pair of numbers — "truncate to 20,000 characters" and "5 MiB" — is **not in
158
+ the current official docs** (checked 2026-10-21), nor in the November 2025 snapshot of the same
159
+ page on the Internet Archive; the published truncation mechanism is the token-based
160
+ `max_content_tokens` parameter above, with no published default.
161
+
162
+ SDK layer (official repos, default branch):
163
+
164
+ | SDK | Default timeout | Default max retries | Source |
165
+ | --- | --- | --- | --- |
166
+ | anthropic-sdk-typescript | **10 minutes** (`options.timeout ?? BaseAnthropic.DEFAULT_TIMEOUT`) | **2** (`options.maxRetries ?? 2`) | [`client.ts`](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/src/client.ts) |
167
+ | anthropic-sdk-python | **600 s total, 5 s connect** (`httpx.Timeout(timeout=10 * 60, connect=5.0)`) | **2** (`DEFAULT_MAX_RETRIES = 2`) | [`_constants.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/_constants.py) |
168
+
169
+ ## OpenAI Responses web_search
170
+
171
+ From the official guide
172
+ ([Web search tool](https://platform.openai.com/docs/guides/tools-web-search)):
173
+
174
+ | Parameter | Default when omitted | Source |
175
+ | --- | --- | --- |
176
+ | Result count | **not exposed** — no count parameter; the docs note the size setting "does not set an exact token count or guarantee a specific number of sources or citations" | [guide](https://platform.openai.com/docs/guides/tools-web-search) |
177
+ | `search_context_size` | **`medium`** — "Use `low` for simple lookups, `medium` for a balanced default, and `high` …" | [guide](https://platform.openai.com/docs/guides/tools-web-search) |
178
+ | `external_web_access` | **true** (live access) | [guide](https://platform.openai.com/docs/guides/tools-web-search) |
179
+ | `return_token_budget` | **`default`** — "Uses the standard returned-token budget … the same behavior as omitting `return_token_budget`" (numeric value unpublished) | [guide](https://platform.openai.com/docs/guides/tools-web-search) |
180
+
181
+ No relevance/authority score or filter is exposed on any web-search surface.
182
+
183
+ SDK layer (official repos, default branch):
184
+
185
+ | SDK | Default timeout | Default max retries | Source |
186
+ | --- | --- | --- | --- |
187
+ | openai-node | **600000 ms (10 min)** (`OpenAI.DEFAULT_TIMEOUT = 600000`) | **2** (`@param {number} [opts.maxRetries=2]`) | [`client.ts`](https://github.com/openai/openai-node/blob/master/src/client.ts) |
188
+ | openai-python | **600 s total, 5 s connect** (`httpx.Timeout(timeout=600, connect=5.0)`) | **2** (`DEFAULT_MAX_RETRIES = 2`); initial retry delay 0.5 s, max 8 s | [`_constants.py`](https://github.com/openai/openai-python/blob/main/src/openai/_constants.py) |
189
+
190
+ ## Brave Search API
191
+
192
+ From the official Web Search API query documentation
193
+ ([docs](https://api-dashboard.search.brave.com/app/documentation/web-search/query)):
194
+
195
+ | Parameter | Default when omitted | Source |
196
+ | --- | --- | --- |
197
+ | `count` | **20** — "Maximum number of results per page (max 20, default 20). The actual number of results returned may be less than count." | [docs](https://api-dashboard.search.brave.com/app/documentation/web-search/query) |
198
+ | `offset` | 0 (0-based, max 9) | same |
199
+
200
+ No relevance/authority filter beyond `safesearch`-style content controls; ranking is
201
+ server-side.
202
+
203
+ ## Linkup
204
+
205
+ From the official OpenAPI reference
206
+ ([search reference](https://docs.linkup.so/pages/documentation/endpoints/search/reference))
207
+ and the official Python SDK source
208
+ ([linkup-python-sdk](https://github.com/LinkupPlatform/linkup-python-sdk/blob/main/src/linkup/_client.py)):
209
+
210
+ | Parameter | Default when omitted | Source |
211
+ | --- | --- | --- |
212
+ | `depth` | **no published default** — enum `flash`/`fast`/`standard`/`deep`; the spec's prose calls `fast` "the recommended default for most agentic applications" without declaring an omission default. The SDK makes `depth` a required argument | [reference](https://docs.linkup.so/pages/documentation/endpoints/search/reference), [`_client.py`](https://github.com/LinkupPlatform/linkup-python-sdk/blob/main/src/linkup/_client.py) |
213
+ | `maxResults` | **no published default** (min 1; schema example: 5) | [reference](https://docs.linkup.so/pages/documentation/endpoints/search/reference) |
214
+ | `includeImages` / `includeInlineCitations` / `includeSources` | **false** | same |
215
+ | SDK `timeout` | **None — "the request will have no timeout"** | [`_client.py`](https://github.com/LinkupPlatform/linkup-python-sdk/blob/main/src/linkup/_client.py) |
216
+
217
+ The SDK has no retry loop for transient failures (its one retry path is the x402 payment
218
+ handshake). No relevance/authority filter exists.
219
+
220
+ ## You.com
221
+
222
+ The API documentation is still public at
223
+ [you.com/docs](https://you.com/docs/api-reference/search/v1-search). Defaults for
224
+ `POST /v1/search` ([reference](https://you.com/docs/api-reference/search/v1-search)):
225
+
226
+ | Parameter | Default when omitted | Source |
227
+ | --- | --- | --- |
228
+ | `count` | **10** — per section (`web` and `news`) | [reference](https://you.com/docs/api-reference/search/v1-search) |
229
+ | `safesearch` | **`moderate`** | same |
230
+ | `crawl_timeout` | **10 s** (1–60) | same |
231
+ | `livecrawl_formats` (deprecated) | **`["html"]`** | same |
232
+ | `extraction.highlights.total_max_tokens` | **8192 tokens** (range 1024–8192; "Omit it to get the default budget of 8192") | same |
233
+ | `extraction.full_page.extraction_formats` | **`["markdown"]`** | same |
234
+ | Result content | **snippets by default**; highlights/full-page are opt-in via `extraction` | same |
235
+
236
+ `moderate` safesearch is a content-moderation filter, not an authority filter; no
237
+ relevance/authority score is exposed.
238
+
239
+ ## What DeepSeek itself claims
240
+
241
+ The challenge quote attributed to the DeepSeek docs — an "AI 原生的通用搜索引擎" that
242
+ "通过严格的识别机制进行过滤,确保向用户与 LLM 呈现权威可信的信息" — is **not present** on
243
+ <https://api-docs.deepseek.com/guides/search> (checked 2026-10-21, English and 中文 pages).
244
+ The closest positioning sentences actually on the page:
245
+
246
+ - EN: "It is powered by DeepSeek's in-house search engine."
247
+ ([guide](https://api-docs.deepseek.com/guides/search))
248
+ - ZH: "该接口由 DeepSeek 自研搜索引擎提供支持。"
249
+ ([指南](https://api-docs.deepseek.com/zh-cn/guides/search))
250
+
251
+ So the official docs claim an in-house engine but make **no published claim of server-side
252
+ quality filtering**. What they do publish, verbatim
253
+ ([指南](https://api-docs.deepseek.com/zh-cn/guides/search)):
254
+
255
+ - `rerank_score`: "排序分数,0~3 分,综合考虑相关性、权威性、时效性等维度,**大于等于 1.5 分为优质结果**。"
256
+ - `authority`: "网页权威性,0~4(**-1 表示未知**),**大于等于 3 为极权威结果**。"
257
+ - Request defaults: `count` "否 / 默认值 10 / 返回条数,1~50"; `need_content` "否 / false";
258
+ `localization` "否 / 空"; `ori_question` optional.
259
+ - Response sizing: `snippet` "短摘要 … 通常为 100~200 字符"; `summary` "长摘要 … 通常为 600~800 字符".
260
+
261
+ ## What this means for this repo's defaults
262
+
263
+ Fact-by-fact alignment of the repo's client-side defaults
264
+ (`src/core/constants.ts`) against the surveyed factory defaults. No recommendation is made
265
+ here; the table only lines up what the industry ships.
266
+
267
+ | Repo default | Industry factory defaults observed above |
268
+ | --- | --- |
269
+ | API `count=10`, client `maxResults=10` (post-dedup cap; `5` before 2026-10-21) | API-level count defaults: Tavily 10, Exa 10, Perplexity Search API 10, Firecrawl 10, You.com 10 (per section), Brave 20. Post-filter caps are uncommon; a fixed 5 exists at Jina (`s.jina.ai` top 5, not configurable) and Perplexity's `image_search` tool (default 5). Linkup's schema example is 5 but no default is published. OpenAI and Anthropic expose no count at all |
270
+ | `minRerank=0`, `minAuthority=0`, `allowUnknownAuthority=true` — no client-side filtering (previously `1.5`/`2`/`false`) | **No surveyed product filters search results client-side by relevance score or authority.** The only published thresholds for DeepSeek's own fields are the docs' quality labels (≥1.5 "优质", ≥3 "极权威") — labels, not filters. The closest shipped analogues are domain allow/block lists (Tavily, Exa, Perplexity, You.com, Anthropic, Brave) and You.com's `safesearch: moderate` default, which is content moderation, not authority |
271
+ | `maxRetries=0` | Official model-API SDKs: OpenAI 2, Anthropic 2 (both languages). Search-API SDKs: firecrawl-js 3 (502 only), tavily-python 0, Linkup SDK 0 |
272
+ | `timeout=30s` (search and fetch, 1–120 s range, covers body read) | Search/scrape API timeouts: Firecrawl 60 s (scrape max 300 s), Tavily SDK search 60 s (cap 120 s) / extract 30 s, You.com `crawl_timeout` 10 s, Linkup SDK no timeout, Jina `x-timeout` unset by default (max 180 s). Model-API SDKs: OpenAI and Anthropic 10 minutes |
273
+ | Fetch truncation 16,000 chars | Exa: page text **off** by default (`contents.text: false`), opt-in `maxCharacters` with no published default; DeepSeek itself: `need_content: false` by default; Anthropic `web_fetch`: token-based `max_content_tokens`, no published default (example 100000), no char/MiB figure in current docs; You.com highlights budget 8192 tokens; Firecrawl and Jina publish no truncation default |
274
+ | Summary truncation 1,200 chars | DeepSeek's own field doc says `summary` is "通常为 600~800 字符"; no surveyed vendor ships a client-side summary cap (they return snippets/highlights sized server-side: Exa verbosity `compact`, You.com highlights budget) |
275
+ | Response body cap 5 MiB | No surveyed product publishes a response-body byte cap; the only published byte-adjacent limits are Anthropic's URL length (250 chars) and content-type whitelist, and Exa's `maxCharacters` ceiling of 1,000,000 characters |
276
+ | Filter markers (`minRerank`/`minAuthority` matching) | Nothing to compare: no surveyed vendor returns a per-result authority tier at all — DeepSeek's `rerank_score`/`authority` response fields are the outlier, which is what makes client-side thresholds possible here and nowhere else in this survey |
277
+
278
+ ## Sources
279
+
280
+ - DeepSeek: [Search & Fetch guide (EN)](https://api-docs.deepseek.com/guides/search) ·
281
+ [网页搜索与抓取 (ZH)](https://api-docs.deepseek.com/zh-cn/guides/search)
282
+ - Tavily: [search API reference](https://docs.tavily.com/documentation/api-reference/endpoint/search) ·
283
+ [tavily-python source](https://github.com/tavily-ai/tavily-python/blob/master/tavily/tavily.py)
284
+ - Exa: [search reference (OpenAPI)](https://exa.ai/docs/reference/search) ·
285
+ [search quickstart](https://exa.ai/docs/search/quickstart) ·
286
+ [contents quickstart](https://exa.ai/docs/contents/quickstart)
287
+ - Perplexity: [Search API quickstart](https://docs.perplexity.ai/docs/search/quickstart) ·
288
+ [Search API reference](https://docs.perplexity.ai/api-reference/search-post) ·
289
+ [Agent API web_search tool](https://docs.perplexity.ai/docs/agent-api/tools/web-search) ·
290
+ [Agent API schema](https://docs.perplexity.ai/api-reference/agent-post) ·
291
+ [Sonar migration guide](https://docs.perplexity.ai/docs/agent-api/migrate-from-sonar/how-to)
292
+ - Firecrawl: [search reference](https://docs.firecrawl.dev/api-reference/endpoint/search) ·
293
+ [scrape reference](https://docs.firecrawl.dev/api-reference/endpoint/scrape) ·
294
+ [npm @mendable/firecrawl-js](https://www.npmjs.com/package/@mendable/firecrawl-js)
295
+ - Jina: [reader product page](https://jina.ai/reader/) ·
296
+ [jina-ai/reader README](https://github.com/jina-ai/reader/blob/main/README.md) ·
297
+ [crawler-options.ts](https://github.com/jina-ai/reader/blob/main/src/dto/crawler-options.ts)
298
+ - Anthropic: [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) ·
299
+ [web fetch tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool) ·
300
+ [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools) ·
301
+ [anthropic-sdk-typescript client.ts](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/src/client.ts) ·
302
+ [anthropic-sdk-python _constants.py](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/_constants.py)
303
+ - OpenAI: [web search tool guide](https://platform.openai.com/docs/guides/tools-web-search) ·
304
+ [openai-node client.ts](https://github.com/openai/openai-node/blob/master/src/client.ts) ·
305
+ [openai-python _constants.py](https://github.com/openai/openai-python/blob/main/src/openai/_constants.py)
306
+ - Brave: [Web Search API query docs](https://api-dashboard.search.brave.com/app/documentation/web-search/query)
307
+ - Linkup: [search reference (OpenAPI)](https://docs.linkup.so/pages/documentation/endpoints/search/reference) ·
308
+ [linkup-python-sdk _client.py](https://github.com/LinkupPlatform/linkup-python-sdk/blob/main/src/linkup/_client.py)
309
+ - You.com: [POST /v1/search reference](https://you.com/docs/api-reference/search/v1-search)
@@ -0,0 +1,17 @@
1
+ {
2
+ // Pi extension config: ~/.pi/agent/settings.json (user) or .pi/settings.json (project).
3
+ // "packages" sources resolve at session start; the first launch fetches
4
+ // npm:deepseek-web-retrieval from the registry (pi update --extensions refreshes it).
5
+ // The key never enters this file: the extension reads DEEPSEEK_API_KEY from the
6
+ // environment of the Pi process itself.
7
+ "packages": [
8
+ "npm:deepseek-web-retrieval"
9
+ ],
10
+ // Optional: the off switches the installer writes; they outrank the
11
+ // DEEPSEEK_ENABLE_* environment variables. Pi preserves unknown keys like
12
+ // this namespace on read and on its own rewrites.
13
+ "deepseekWebRetrieval": {
14
+ // "enableSearch": false,
15
+ // "enableFetch": false
16
+ }
17
+ }
package/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { default } from "./src/opencode/plugin"
2
- export * from "./src/core/index"
1
+ export * from "./src/core/index";
2
+ export { default } from "./src/opencode/plugin";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "deepseek-web-retrieval",
4
- "version": "0.3.0",
4
+ "version": "0.5.0",
5
5
  "description": "Token-efficient DeepSeek Search and Fetch for coding agents, starting with OpenCode v2.",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -14,6 +14,8 @@
14
14
  "keywords": [
15
15
  "opencode",
16
16
  "opencode-plugin",
17
+ "pi",
18
+ "pi-extension",
17
19
  "deepseek",
18
20
  "websearch",
19
21
  "webfetch",
@@ -23,7 +25,13 @@
23
25
  "exports": {
24
26
  ".": "./index.ts",
25
27
  "./core": "./src/core/index.ts",
26
- "./mcp": "./src/mcp/server.ts"
28
+ "./mcp": "./src/mcp/server.ts",
29
+ "./pi": "./src/pi/extension.ts"
30
+ },
31
+ "pi": {
32
+ "extensions": [
33
+ "./src/pi/extension.ts"
34
+ ]
27
35
  },
28
36
  "bin": {
29
37
  "deepseek-web-retrieval": "dist/cli.js"
@@ -39,19 +47,42 @@
39
47
  "examples"
40
48
  ],
41
49
  "scripts": {
50
+ "lint": "biome check --error-on-warnings .",
51
+ "lint:ci": "biome ci --error-on-warnings .",
52
+ "lint:fix": "biome check --write --error-on-warnings .",
53
+ "format": "biome format --write .",
42
54
  "typecheck": "tsc --noEmit",
43
55
  "build": "bun build src/mcp/cli.ts --target=node --format=esm --outdir=dist --external @modelcontextprotocol/server --external @modelcontextprotocol/server/stdio --external zod --external zod/v4",
44
- "prepublishOnly": "bun run typecheck && bun run build",
56
+ "prepublishOnly": "bun run check:release",
45
57
  "test": "bun test",
58
+ "test:package": "node scripts/test-package.mjs",
59
+ "test:e2e:keyless": "bun run test:e2e pi pi-extension installer reinit upgrade pkg capability-off",
60
+ "check:release": "bun run check && bun run test:package && bun run test:e2e:keyless",
46
61
  "test:e2e": "bun run test/e2e/run.ts",
47
62
  "docs:matrix": "bun run scripts/render-matrix.ts",
48
63
  "test:coverage": "bun test --coverage",
49
- "check": "bun run typecheck && bun run docs:matrix --check && bun run build && bun test"
64
+ "check": "bun run lint && bun run typecheck && bun run docs:matrix --check && bun run build && bun test"
50
65
  },
51
66
  "peerDependencies": {
67
+ "@earendil-works/pi-ai": "^1.3.0",
68
+ "@earendil-works/pi-coding-agent": "^1.3.0",
52
69
  "@opencode/plugin": "^2.0.32"
53
70
  },
71
+ "peerDependenciesMeta": {
72
+ "@earendil-works/pi-ai": {
73
+ "optional": true
74
+ },
75
+ "@earendil-works/pi-coding-agent": {
76
+ "optional": true
77
+ },
78
+ "@opencode/plugin": {
79
+ "optional": true
80
+ }
81
+ },
54
82
  "devDependencies": {
83
+ "@biomejs/biome": "2.5.15",
84
+ "@earendil-works/pi-ai": "1.3.0",
85
+ "@earendil-works/pi-coding-agent": "1.3.0",
55
86
  "@modelcontextprotocol/client": "^2.3.1",
56
87
  "@opencode/plugin": "2.0.32",
57
88
  "@types/bun": "1.4.0",
package/src/core/auth.ts CHANGED
@@ -1,8 +1,6 @@
1
- import { API_KEY_ENV } from "./constants"
1
+ import { API_KEY_ENV } from "./constants";
2
2
 
3
- export function apiKeyFromEnv(
4
- env: Readonly<Record<string, string | undefined>> = process.env,
5
- ): string | undefined {
6
- const key = env[API_KEY_ENV]?.trim()
7
- return key ? key : undefined
3
+ export function apiKeyFromEnv(env: Readonly<Record<string, string | undefined>> = process.env): string | undefined {
4
+ const key = env[API_KEY_ENV]?.trim();
5
+ return key ? key : undefined;
8
6
  }