deepseek-web-retrieval 0.3.0 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to this project are documented here.
4
4
 
5
+ ## 0.4.0
6
+
7
+ - Ground the search defaults in surveyed industry factory defaults
8
+ ([research](docs/research/search-api-defaults.md)): every surveyed AI-native search API
9
+ returns ten results by default and none filters results client-side by score or
10
+ authority, so `maxResults` moves 5 → 10 (matching the API `count` default) and the
11
+ `minRerank`/`minAuthority` thresholds move to 0 with `allowUnknownAuthority` flipping
12
+ to `true` — the pass-through default returns what DeepSeek ranked, and the thresholds
13
+ remain available as opt-ins. `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` becomes a
14
+ false-only switch (`false`/`0`/`no`/`off` disables) since the default is now on.
15
+ Unchanged and now evidence-backed: `maxRetries=0` (search-API SDK cohort: tavily-python
16
+ and Linkup ship no retries; the 2-retry defaults belong to chat-completion SDKs), the
17
+ 30 s timeout, and the 1,200/16,000-character truncations
18
+ - Drop the "Token- and cost-efficient" bullet from the README's Features section —
19
+ the default parameters are no longer endorsed as a selling point, and their behavior
20
+ is stated neutrally in General behavior
21
+
5
22
  ## 0.3.0
6
23
 
7
24
  - Close the lifecycle gaps in the keyless e2e coverage with a `reinit` scenario: tier
package/README.md CHANGED
@@ -9,6 +9,24 @@ coding agents are in progress.
9
9
 
10
10
  This is an unofficial community project. It is not affiliated with OpenCode or DeepSeek.
11
11
 
12
+ ## Features
13
+
14
+ - **One core, every host** — a native OpenCode v2 plugin plus a stdio MCP
15
+ server for everything else; identical retrieval behavior on each
16
+ - **Tiered routing** — optional, preferred, forced, or off, per capability;
17
+ forced refuses when exclusivity is impossible
18
+ - **Plan-then-apply installer** — every write previewed as old → new before
19
+ touching disk; arrays merge instead of replacing; uninstall reverses
20
+ exactly what was applied
21
+ - **Trap-aware detection** — catches what the agents' own docs don't warn
22
+ about: Codex's silent `[tools] web_search` no-op, Pi's codemode hiding MCP
23
+ tools; conflicting rules and web-capable servers surface in the plan
24
+ - **Git-safe installs** — the key never enters config files
25
+ (`${DEEPSEEK_API_KEY}` indirection or `env_vars`), so a project-scope
26
+ install commits clean
27
+ - **Fails loudly, never falls back** — no silent provider switches;
28
+ localhost and private URLs blocked before they leave the machine
29
+
12
30
  ## Usage
13
31
 
14
32
  Shipped adapters:
@@ -31,28 +49,34 @@ For shared search, fetch, retry, and privacy behavior, see below.
31
49
 
32
50
  ## General behavior
33
51
 
52
+ What this adapter layer adds on top of the
53
+ [DeepSeek Search and Fetch APIs](https://api-docs.deepseek.com/guides/search) — the APIs'
54
+ own capabilities (Markdown-only output, no JavaScript rendering, no private networks or
55
+ binary files, per-request billing) are documented there.
56
+
34
57
  ### Search
35
58
 
36
- - Sends `need_content: false` and returns DeepSeek's `summary`, falling back to `snippet`.
37
- - Filters by `rerank_score` and `authority`, deduplicates URLs, and returns at most five
38
- results by default.
39
- - The final query, including `blockedDomains`, `includeDomains`, and `language` operators,
40
- must be 128 characters or fewer. Longer queries are rejected locally.
59
+ - Requests summaries only (`need_content: false`, `summary` falling back to `snippet`);
60
+ full page content never enters the results.
61
+ - Returns at most ten results by default (matching the API's `count` default and the
62
+ industry norm for AI-native search APIs), deduplicating URLs. Optional
63
+ `rerank_score` and `authority` thresholds are off by default; when enabled they
64
+ drop results below the configured score or tier.
65
+ - Builds `blockedDomains`, `includeDomains`, and `language` into query operators and
66
+ rejects the final query locally when it exceeds the API's 128-character limit.
41
67
  - An empty result set is normal and is not an error.
42
68
 
43
69
  ### Fetch
44
70
 
45
- - DeepSeek Fetch returns Markdown only. `format: "text"` and `format: "html"` are rejected.
46
- - Output is capped at 16,000 characters by default, including the title. A
71
+ - Output is capped at 16,000 characters by default, including the title; a
47
72
  `[content truncated]` marker is added when the limit leaves room for it.
48
- - If the page already starts with the same title as a top-level heading, no duplicate title
49
- is added.
50
- - Responses larger than 5 MiB are rejected before parsing.
51
- - URLs must be absolute `http://` or `https://` URLs with a host, no embedded credentials,
52
- and at most 8,192 characters.
53
- - The core supports allowed/blocked domain lists and private-host blocking. DeepSeek does
54
- not render JavaScript and cannot reach private networks; PDFs, images, and other binary
55
- files are unsupported.
73
+ - A page that already opens with its title as a top-level heading does not get a
74
+ duplicate title.
75
+ - Responses larger than 5 MiB are rejected before parsing; URLs must be absolute
76
+ `http://` or `https://`, credential-free, and at most 8,192 characters.
77
+ - `format: "text"` and `format: "html"` are rejected rather than misrepresented.
78
+ - Allowed/blocked domain lists and private-host blocking are enforced on the client,
79
+ before a request leaves the machine.
56
80
 
57
81
  ### Timeouts and retries
58
82
 
@@ -68,10 +92,9 @@ provider or fetch implementation.
68
92
 
69
93
  ### Privacy and cost
70
94
 
71
- Queries and URLs are sent to `https://api.deepseek.com`. The API key is sent only as a
72
- Bearer token to DeepSeek. Search and Fetch are billed per successful request; check current
73
- DeepSeek pricing and rate limits before enabling a team deployment. A 429 means you should
74
- wait, reduce concurrency, or increase retries.
95
+ Queries and URLs are sent to `https://api.deepseek.com`; the API key is sent only as a
96
+ Bearer token to DeepSeek and to nothing else. Retrieval is billed per successful request —
97
+ see the official docs for pricing and rate limits before enabling a team deployment.
75
98
 
76
99
  ## Development
77
100
 
package/dist/cli.js CHANGED
@@ -16,7 +16,7 @@ var __export = (target, all) => {
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
18
  // src/core/constants.ts
19
- var SEARCH_ENDPOINT = "https://api.deepseek.com/v1/tools/search", FETCH_ENDPOINT = "https://api.deepseek.com/v1/tools/fetch", DEFAULT_SEARCH_COUNT = 10, DEFAULT_MAX_RESULTS = 5, DEFAULT_MIN_RERANK = 1.5, DEFAULT_MIN_AUTHORITY = 2, DEFAULT_MAX_SUMMARY_CHARS = 1200, DEFAULT_FETCH_MAX_CHARS = 16000, DEFAULT_TIMEOUT_MS = 30000, MIN_TIMEOUT_MS = 1000, MAX_TIMEOUT_MS = 120000, MAX_QUERY_CHARS = 128, MAX_URL_CHARS = 8192, MAX_ERROR_BODY_CHARS = 2000, MAX_RESPONSE_BYTES, FETCH_TRUNCATION_MARKER = `
19
+ var SEARCH_ENDPOINT = "https://api.deepseek.com/v1/tools/search", FETCH_ENDPOINT = "https://api.deepseek.com/v1/tools/fetch", DEFAULT_SEARCH_COUNT = 10, DEFAULT_MAX_RESULTS = 10, DEFAULT_MIN_RERANK = 0, DEFAULT_MIN_AUTHORITY = 0, DEFAULT_MAX_SUMMARY_CHARS = 1200, DEFAULT_FETCH_MAX_CHARS = 16000, DEFAULT_TIMEOUT_MS = 30000, MIN_TIMEOUT_MS = 1000, MAX_TIMEOUT_MS = 120000, MAX_QUERY_CHARS = 128, MAX_URL_CHARS = 8192, MAX_ERROR_BODY_CHARS = 2000, MAX_RESPONSE_BYTES, FETCH_TRUNCATION_MARKER = `
20
20
 
21
21
  [content truncated]`, DEFAULT_API_KEY_SOURCE = "auto", DEFAULT_MAX_RETRIES = 0, MAX_RETRIES = 5, DEFAULT_RETRY_BASE_DELAY_MS = 250, MAX_RETRY_DELAY_MS = 1e4, API_KEY_ENV = "DEEPSEEK_API_KEY", ENABLE_SEARCH_ENV = "DEEPSEEK_ENABLE_SEARCH", ENABLE_FETCH_ENV = "DEEPSEEK_ENABLE_FETCH";
22
22
  var init_constants = __esm(() => {
@@ -4128,7 +4128,7 @@ function resolveSearchOptions(input) {
4128
4128
  maxResults: clampInt(input?.maxResults, DEFAULT_MAX_RESULTS, 0, 50),
4129
4129
  minRerank: finiteNumber(input?.minRerank, DEFAULT_MIN_RERANK),
4130
4130
  minAuthority: finiteNumber(input?.minAuthority, DEFAULT_MIN_AUTHORITY),
4131
- allowUnknownAuthority: input?.allowUnknownAuthority === true,
4131
+ allowUnknownAuthority: input?.allowUnknownAuthority !== false,
4132
4132
  blockedDomains: normalizeDomains(input?.blockedDomains),
4133
4133
  includeDomains: normalizeDomains(input?.includeDomains),
4134
4134
  language: optionalText(input?.language),
@@ -4195,10 +4195,8 @@ var DOMAINS_ENV = {
4195
4195
  fetchAllowedDomains: "DEEPSEEK_FETCH_ALLOWED_DOMAINS",
4196
4196
  fetchBlockedDomains: "DEEPSEEK_FETCH_BLOCKED_DOMAINS"
4197
4197
  };
4198
- var FLAG_ENV = {
4199
- allowUnknownAuthority: "DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY"
4200
- };
4201
4198
  var FALSE_ONLY_ENV = {
4199
+ allowUnknownAuthority: "DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY",
4202
4200
  fetchBlockPrivateHosts: "DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS",
4203
4201
  searchEnabled: ENABLE_SEARCH_ENV,
4204
4202
  fetchEnabled: ENABLE_FETCH_ENV
@@ -4212,7 +4210,7 @@ function pluginOptionsFromEnv(env) {
4212
4210
  putNumber(options, "maxResults", env);
4213
4211
  putNumber(options, "minRerank", env);
4214
4212
  putNumber(options, "minAuthority", env);
4215
- putBoolean(options, "allowUnknownAuthority", env);
4213
+ putFalseOnly(options, "allowUnknownAuthority", env);
4216
4214
  putDomains(options, "blockedDomains", env);
4217
4215
  putDomains(options, "includeDomains", env);
4218
4216
  putText(options, "language", env);
@@ -4248,10 +4246,6 @@ function putText(target, key, env) {
4248
4246
  if (trimmed)
4249
4247
  target[key] = trimmed;
4250
4248
  }
4251
- function putBoolean(target, key, env) {
4252
- if (isTrueValue(env[FLAG_ENV[key]]))
4253
- target[key] = true;
4254
- }
4255
4249
  function putFalseOnly(target, key, env) {
4256
4250
  if (isFalseValue(env[FALSE_ONLY_ENV[key]]))
4257
4251
  target[key] = false;
@@ -4264,10 +4258,6 @@ function putDomains(target, key, env) {
4264
4258
  if (domains.length > 0)
4265
4259
  target[key] = domains;
4266
4260
  }
4267
- function isTrueValue(value) {
4268
- const trimmed = value?.trim().toLowerCase();
4269
- return trimmed === "true" || trimmed === "1" || trimmed === "yes" || trimmed === "on";
4270
- }
4271
4261
  function isFalseValue(value) {
4272
4262
  const trimmed = value?.trim().toLowerCase();
4273
4263
  return trimmed === "false" || trimmed === "0" || trimmed === "no" || trimmed === "off";
@@ -4725,7 +4715,7 @@ var SEARCH_QUERY_OPERATORS = "Use site:/-site: operators in the query to include
4725
4715
  var package_default = {
4726
4716
  $schema: "https://json.schemastore.org/package.json",
4727
4717
  name: "deepseek-web-retrieval",
4728
- version: "0.3.0",
4718
+ version: "0.4.0",
4729
4719
  description: "Token-efficient DeepSeek Search and Fetch for coding agents, starting with OpenCode v2.",
4730
4720
  type: "module",
4731
4721
  license: "MIT",
@@ -127,10 +127,10 @@ defaults and clamping match the other adapters. `DEEPSEEK_API_KEY` goes in `env_
127
127
  | --- | --- | --- |
128
128
  | `DEEPSEEK_ENABLE_SEARCH` | `true` | Register the search tool. Only an explicit `false`/`0`/`no`/`off` disables it; at least one tool must stay enabled. |
129
129
  | `DEEPSEEK_SEARCH_COUNT` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
130
- | `DEEPSEEK_SEARCH_MAX_RESULTS` | `5` | Results returned to the model after filtering, 0–50. `0` returns no results. |
131
- | `DEEPSEEK_SEARCH_MIN_RERANK` | `1.5` | Drop results below this relevance score. DeepSeek scores are 0–3. |
132
- | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `2` | Drop known authorities below this tier. Authority is 0–4. |
133
- | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `false` | Keep results whose authority is missing or unknown. |
130
+ | `DEEPSEEK_SEARCH_MAX_RESULTS` | `10` | Results returned to the model after filtering, 0–50. `0` returns no results. |
131
+ | `DEEPSEEK_SEARCH_MIN_RERANK` | `0` | Drop results below this relevance score. DeepSeek scores are 0–3. |
132
+ | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `0` | Drop known authorities below this tier. Authority is 0–4. |
133
+ | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `true` | Keep results whose authority is missing or unknown. Set `false`/`0` to drop them. |
134
134
  | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | unset | Comma-separated; adds `-site:` operators to the query. |
135
135
  | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | unset | Comma-separated; restricts results with `site:` operators. |
136
136
  | `DEEPSEEK_SEARCH_LANGUAGE` | unset | Adds a `lang:` operator, for example `zh,en`. |
@@ -100,7 +100,7 @@ A complete global config:
100
100
  Use the bare package name (optionally pinned) as the `package` value:
101
101
 
102
102
  ```jsonc
103
- "plugins": [{ "package": "deepseek-web-retrieval@0.3.0" }]
103
+ "plugins": [{ "package": "deepseek-web-retrieval@0.4.0" }]
104
104
  ```
105
105
 
106
106
  **Global checkout**
@@ -173,10 +173,10 @@ Set these in the plugin's `options` object.
173
173
  | --- | --- | --- |
174
174
  | `searchEnabled` | `true` | Register the DeepSeek search provider at all. Only an explicit `false` disables it. |
175
175
  | `count` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
176
- | `maxResults` | `5` | Results returned to the model after filtering, 0–50. `0` returns no results. |
177
- | `minRerank` | `1.5` | Drop results below this relevance score. DeepSeek scores are 0–3. |
178
- | `minAuthority` | `2` | Drop known authorities below this tier. Authority is 0–4. |
179
- | `allowUnknownAuthority` | `false` | Keep results whose authority is missing or unknown. |
176
+ | `maxResults` | `10` | Results returned to the model after filtering, 0–50. `0` returns no results. |
177
+ | `minRerank` | `0` | Drop results below this relevance score. DeepSeek scores are 0–3. |
178
+ | `minAuthority` | `0` | Drop known authorities below this tier. Authority is 0–4. |
179
+ | `allowUnknownAuthority` | `true` | Keep results whose authority is missing or unknown. Only an explicit `false` disables it. |
180
180
  | `blockedDomains` | `[]` | Add `-site:` operators to the search query. |
181
181
  | `includeDomains` | `[]` | Restrict results with `site:` operators. |
182
182
  | `language` | unset | Add a `lang:` operator, for example `zh,en`. |
package/docs/agents/pi.md CHANGED
@@ -132,10 +132,10 @@ defaults and clamping match the OpenCode adapter.
132
132
  | --- | --- | --- |
133
133
  | `DEEPSEEK_ENABLE_SEARCH` | `true` | Register the search tool. Only an explicit `false`/`0`/`no`/`off` disables it; at least one tool must stay enabled. |
134
134
  | `DEEPSEEK_SEARCH_COUNT` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
135
- | `DEEPSEEK_SEARCH_MAX_RESULTS` | `5` | Results returned to the model after filtering, 0–50. `0` returns no results. |
136
- | `DEEPSEEK_SEARCH_MIN_RERANK` | `1.5` | Drop results below this relevance score. DeepSeek scores are 0–3. |
137
- | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `2` | Drop known authorities below this tier. Authority is 0–4. |
138
- | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `false` | Keep results whose authority is missing or unknown. Set `true`/`1`. |
135
+ | `DEEPSEEK_SEARCH_MAX_RESULTS` | `10` | Results returned to the model after filtering, 0–50. `0` returns no results. |
136
+ | `DEEPSEEK_SEARCH_MIN_RERANK` | `0` | Drop results below this relevance score. DeepSeek scores are 0–3. |
137
+ | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `0` | Drop known authorities below this tier. Authority is 0–4. |
138
+ | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `true` | Keep results whose authority is missing or unknown. Set `false`/`0` to drop them. |
139
139
  | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | unset | Comma-separated; adds `-site:` operators to the query. |
140
140
  | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | unset | Comma-separated; restricts results with `site:` operators. |
141
141
  | `DEEPSEEK_SEARCH_LANGUAGE` | unset | Adds a `lang:` operator, for example `zh,en`. |
@@ -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)
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.4.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",
@@ -1,10 +1,16 @@
1
1
  export const SEARCH_ENDPOINT = "https://api.deepseek.com/v1/tools/search"
2
2
  export const FETCH_ENDPOINT = "https://api.deepseek.com/v1/tools/fetch"
3
3
 
4
+ /**
5
+ * Search defaults are benchmarked against surveyed AI-native search APIs and
6
+ * DeepSeek's own factory defaults; see docs/research/search-api-defaults.md.
7
+ * Result caps cluster at 10 across vendors, and no surveyed vendor filters
8
+ * results client-side by score or authority — thresholds stay opt-in.
9
+ */
4
10
  export const DEFAULT_SEARCH_COUNT = 10
5
- export const DEFAULT_MAX_RESULTS = 5
6
- export const DEFAULT_MIN_RERANK = 1.5
7
- export const DEFAULT_MIN_AUTHORITY = 2
11
+ export const DEFAULT_MAX_RESULTS = 10
12
+ export const DEFAULT_MIN_RERANK = 0
13
+ export const DEFAULT_MIN_AUTHORITY = 0
8
14
  export const DEFAULT_MAX_SUMMARY_CHARS = 1200
9
15
 
10
16
  export const DEFAULT_FETCH_MAX_CHARS = 16000
@@ -40,7 +40,7 @@ export function resolveSearchOptions(input?: PluginOptions): SearchOptions {
40
40
  maxResults: clampInt(input?.maxResults, DEFAULT_MAX_RESULTS, 0, 50),
41
41
  minRerank: finiteNumber(input?.minRerank, DEFAULT_MIN_RERANK),
42
42
  minAuthority: finiteNumber(input?.minAuthority, DEFAULT_MIN_AUTHORITY),
43
- allowUnknownAuthority: input?.allowUnknownAuthority === true,
43
+ allowUnknownAuthority: input?.allowUnknownAuthority !== false,
44
44
  blockedDomains: normalizeDomains(input?.blockedDomains),
45
45
  includeDomains: normalizeDomains(input?.includeDomains),
46
46
  language: optionalText(input?.language),
package/src/mcp/config.ts CHANGED
@@ -30,12 +30,9 @@ const DOMAINS_ENV = {
30
30
  fetchBlockedDomains: "DEEPSEEK_FETCH_BLOCKED_DOMAINS",
31
31
  } as const
32
32
 
33
- const FLAG_ENV = {
34
- allowUnknownAuthority: "DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY",
35
- } as const
36
-
37
33
  /** Shared defaults are true; only an explicit false value disables them. */
38
34
  const FALSE_ONLY_ENV = {
35
+ allowUnknownAuthority: "DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY",
39
36
  fetchBlockPrivateHosts: "DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS",
40
37
  searchEnabled: ENABLE_SEARCH_ENV,
41
38
  fetchEnabled: ENABLE_FETCH_ENV,
@@ -58,7 +55,7 @@ export function pluginOptionsFromEnv(env: Env): PluginOptions {
58
55
  putNumber(options, "maxResults", env)
59
56
  putNumber(options, "minRerank", env)
60
57
  putNumber(options, "minAuthority", env)
61
- putBoolean(options, "allowUnknownAuthority", env)
58
+ putFalseOnly(options, "allowUnknownAuthority", env)
62
59
  putDomains(options, "blockedDomains", env)
63
60
  putDomains(options, "includeDomains", env)
64
61
  putText(options, "language", env)
@@ -95,10 +92,6 @@ function putText(target: Record<string, unknown>, key: keyof typeof TEXT_ENV, en
95
92
  if (trimmed) target[key] = trimmed
96
93
  }
97
94
 
98
- function putBoolean(target: Record<string, unknown>, key: keyof typeof FLAG_ENV, env: Env): void {
99
- if (isTrueValue(env[FLAG_ENV[key]])) target[key] = true
100
- }
101
-
102
95
  function putFalseOnly(target: Record<string, unknown>, key: keyof typeof FALSE_ONLY_ENV, env: Env): void {
103
96
  if (isFalseValue(env[FALSE_ONLY_ENV[key]])) target[key] = false
104
97
  }
@@ -113,11 +106,6 @@ function putDomains(target: Record<string, unknown>, key: keyof typeof DOMAINS_E
113
106
  if (domains.length > 0) target[key] = domains
114
107
  }
115
108
 
116
- function isTrueValue(value: string | undefined): boolean {
117
- const trimmed = value?.trim().toLowerCase()
118
- return trimmed === "true" || trimmed === "1" || trimmed === "yes" || trimmed === "on"
119
- }
120
-
121
109
  function isFalseValue(value: string | undefined): boolean {
122
110
  const trimmed = value?.trim().toLowerCase()
123
111
  return trimmed === "false" || trimmed === "0" || trimmed === "no" || trimmed === "off"