deepseek-web-retrieval 0.5.1 → 0.7.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 (46) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/README.md +167 -45
  3. package/dist/cli.js +2303 -1836
  4. package/docs/adr/0002-shared-config-base-layer.md +43 -0
  5. package/docs/agents/codex.md +60 -32
  6. package/docs/agents/opencode-v2.md +69 -39
  7. package/docs/agents/pi.md +60 -31
  8. package/docs/design/config-layering-design.md +180 -0
  9. package/docs/design/installer-design.md +33 -20
  10. package/docs/integration.md +41 -7
  11. package/docs/research/control-matrix.md +17 -4
  12. package/docs/research/mcp-compatibility.md +9 -2
  13. package/docs/research/ori-question-sourcing.md +336 -0
  14. package/docs/research/result-quality-passthrough.md +159 -0
  15. package/docs/research/search-api-defaults.md +18 -2
  16. package/docs/research/search-inline-content.md +244 -0
  17. package/examples/opencode.global.jsonc +3 -2
  18. package/package.json +2 -1
  19. package/src/core/auth.ts +3 -0
  20. package/src/core/client.ts +26 -4
  21. package/src/core/config-file.ts +168 -0
  22. package/src/core/config-summary.ts +82 -0
  23. package/src/core/constants.ts +15 -1
  24. package/src/core/descriptions.ts +6 -2
  25. package/src/core/env.ts +99 -32
  26. package/src/core/format.ts +19 -1
  27. package/src/core/options.ts +22 -2
  28. package/src/core/search.ts +6 -0
  29. package/src/core/types.ts +11 -0
  30. package/src/installer/apply.ts +5 -3
  31. package/src/installer/cli.ts +92 -25
  32. package/src/installer/config-command.ts +116 -0
  33. package/src/installer/hosts/codex.ts +5 -3
  34. package/src/installer/hosts/opencode.ts +9 -5
  35. package/src/installer/hosts/pi.ts +10 -6
  36. package/src/installer/ownership.ts +54 -1
  37. package/src/installer/paths.ts +7 -1
  38. package/src/installer/state.ts +73 -19
  39. package/src/installer/types.ts +12 -0
  40. package/src/mcp/auth.ts +2 -3
  41. package/src/mcp/cli.ts +9 -51
  42. package/src/mcp/serve.ts +60 -0
  43. package/src/mcp/server.ts +7 -2
  44. package/src/opencode/plugin.ts +22 -2
  45. package/src/pi/extension.ts +78 -9
  46. package/src/version.ts +12 -0
@@ -0,0 +1,43 @@
1
+ # Share one config base layer across hosts
2
+
3
+ The option pipeline was single-sourced, but the channels feeding it were per
4
+ host: `DEEPSEEK_*` shell variables on Pi, only `ctx.options` on OpenCode, and
5
+ on Codex only the names the installer's `env_vars` entry forwards — just
6
+ `DEEPSEEK_API_KEY`, so every other knob set in the shell silently never
7
+ reached the server. One intent ("20 results, block pinterest.com") took up to
8
+ three configuration acts in two vocabularies. We decided that a shared config
9
+ file becomes the base layer under every existing channel, with the precedence
10
+ `built-in defaults < shared config file < DEEPSEEK_* environment < host-native
11
+ config`, a three-class parameter taxonomy that isolates host integration in
12
+ the host-native layer and rejects it from the file, and no API key in the
13
+ file — auth keeps its existing channels.
14
+
15
+ ## Considered Options
16
+
17
+ - **Unify on the file alone** (retire the environment and host-option
18
+ vocabularies so there is one configuration act). But host integration —
19
+ `searchEnabled`/`fetchEnabled`, `setDefaultWebsearch`,
20
+ `fetchOverrideWebfetch`, `fetchRegisterTool`, `apiKeySource` — describes the
21
+ host's tool landscape, not the user↔DeepSeek relationship, so the host layer
22
+ must keep owning it, and retiring `DEEPSEEK_*` would break every existing
23
+ shell profile.
24
+ - **Keep the per-host channels as the only surface** (previous status quo).
25
+ The Codex `env_vars` trap stays — knobs set in the shell reach the server
26
+ only by hand-editing `config.toml` — and the same knob keeps two
27
+ vocabularies across hosts.
28
+
29
+ ## Consequences
30
+
31
+ One file — `$XDG_CONFIG_HOME/deepseek-web-retrieval/config.json`, default
32
+ `~/.config/deepseek-web-retrieval/config.json`, camelCase keys — configures
33
+ every host, and host layers keep winning: installer-written intent and
34
+ explicit per-host overrides always outrank the base, bounding the blast
35
+ radius of a bad shared value. Unknown keys and host-integration keys in the
36
+ file draw a one-line stderr warning and are otherwise ignored. Because a bad
37
+ shared value now fails everywhere at once, every adapter prints a
38
+ deviations-from-defaults summary of its effective configuration.
39
+
40
+ The installer's Codex `env_vars` entry forwards every `DEEPSEEK_*` name, so
41
+ environment overrides work on Codex without hand-editing `config.toml`.
42
+ Design detail (per-adapter composition, validation, visibility, rollout)
43
+ lives in the [layering design note](../design/config-layering-design.md).
@@ -27,7 +27,8 @@ For shared search/fetch limits, retries, and privacy behavior, see
27
27
 
28
28
  ## Requirements
29
29
 
30
- - Codex 0.163.0 or newer
30
+ - Codex at the tested version in [`docs/hosts.jsonc`](../hosts.jsonc); init reports
31
+ version drift, and newer versions may require rechecking routing behavior
31
32
  - Node.js 20 or newer (`npx` runs the server)
32
33
  - A DeepSeek account with access to `/v1/tools/search` and `/v1/tools/fetch`
33
34
  - A funded DeepSeek account; Search and Fetch are billed per successful request
@@ -43,9 +44,11 @@ npx deepseek-web-retrieval init --host codex --scope global \
43
44
  ```
44
45
 
45
46
  It prints the full plan — every file, old value → new value, and notes about existing
46
- MCP servers — and writes nothing until you pass `--apply`. `uninstall` restores exactly
47
- what the plan applied (snapshots, merged entries, created files). Codex's config lives in
48
- `~/.codex/config.toml` at the user level only, so the server registration and kill switch
47
+ MCP servers — and writes nothing until you pass `--apply`. Add `--pin` to register the
48
+ exact version of the CLI you are running instead of the always-latest package name.
49
+ `uninstall` removes recorded
50
+ additions and restores snapshotted values that have not since been edited. Codex's config
51
+ lives in `~/.codex/config.toml` at the user level only, so the server registration and kill switch
49
52
  are written there even for `--scope project`; the rules file follows the scope. An `off`
50
53
  capability is written as the tool's name in the server entry's `disabled_tools` array —
51
54
  Codex's own mechanism — merging with any entries you put there yourself. When `codex` is
@@ -54,6 +57,10 @@ release differs from the tested lock (see
54
57
  [hosts.jsonc](../hosts.jsonc)) — version-anchored behavior such as the kill switch may
55
58
  have drifted between the two.
56
59
 
60
+ For an existing installer-managed setup, follow the
61
+ [upgrade instructions](../integration.md#upgrading-existing-installs) from the original
62
+ project directory and scope.
63
+
57
64
  ### Manual install
58
65
 
59
66
  Add the server to `~/.codex/config.toml`:
@@ -137,45 +144,64 @@ Fetch https://learn.chatgpt.com/docs/extend/mcp and summarize the config keys.
137
144
 
138
145
  The model calls `mcp__deepseek__deepseek_web_search` or `mcp__deepseek__deepseek_fetch`.
139
146
 
147
+ The search tool accepts an optional `original_question` argument — the user's question in
148
+ their own words — forwarded as DeepSeek's `ori_question` to sharpen search intent; omit it
149
+ when the query already stands alone.
150
+
140
151
  ## Configuration
141
152
 
142
153
  Set these in the server's `env` table. Values map onto the shared option pipeline, so
143
154
  defaults and clamping match the other adapters. `DEEPSEEK_API_KEY` goes in `env_vars`.
144
155
 
156
+ > [!NOTE]
157
+ > A shared config file — `~/.config/deepseek-web-retrieval/config.json`
158
+ > (Windows: `%APPDATA%\deepseek-web-retrieval\config.json`), camelCase keys —
159
+ > sits under the environment layer and works with no env forwarding at all: the server
160
+ > reads the file itself, making it the way to set knobs without editing `env_vars` (the
161
+ > installer's `env_vars` entry also forwards every `DEEPSEEK_*` name). The file carries
162
+ > neither the API key nor the host-integration switches. Run
163
+ > `npx deepseek-web-retrieval config` to see every knob's effective value and source
164
+ > (it replays file warnings visibly and exits 1 on file problems); `config --search "<query>"`
165
+ > verifies key, config, and network with one billed search.
166
+
145
167
  ### Search
146
168
 
147
- | Env variable | Default | Description |
148
- | --- | --- | --- |
149
- | `DEEPSEEK_ENABLE_SEARCH` | `true` | Register the search tool. Only an explicit `false`/`0`/`no`/`off` disables it; at least one tool must stay enabled. |
150
- | `DEEPSEEK_SEARCH_COUNT` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
151
- | `DEEPSEEK_SEARCH_MAX_RESULTS` | `10` | Results returned to the model after filtering, 0–50. `0` returns no results. |
152
- | `DEEPSEEK_SEARCH_MIN_RERANK` | `0` | Drop results below this relevance score. DeepSeek scores are 0–3. |
153
- | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `0` | Drop known authorities below this tier. Authority is 0–4. |
154
- | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `true` | Keep results whose authority is missing or unknown. Set `false`/`0` to drop them. |
155
- | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | unset | Comma-separated; adds `-site:` operators to the query. |
156
- | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | unset | Comma-separated; restricts results with `site:` operators. |
157
- | `DEEPSEEK_SEARCH_LANGUAGE` | unset | Adds a `lang:` operator, for example `zh,en`. |
158
- | `DEEPSEEK_SEARCH_MAX_SUMMARY_CHARS` | `1200` | Per-result summary cap, 0–1,000,000. |
159
- | `DEEPSEEK_SEARCH_TIMEOUT_MS` | `30000` | Search timeout, 1,000–120,000 ms. |
169
+ The "Config file" column names each key's camelCase twin in the shared config file —
170
+ one vocabulary pair, one pipeline. `—` marks host-integration entries the file rejects.
171
+
172
+ | Env variable | Config file | Default | Description |
173
+ | --- | --- | --- | --- |
174
+ | `DEEPSEEK_ENABLE_SEARCH` | — | `true` | Register the search tool. Only an explicit `false`/`0`/`no`/`off` disables it; at least one tool must stay enabled. |
175
+ | `DEEPSEEK_SEARCH_COUNT` | `count` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
176
+ | `DEEPSEEK_SEARCH_MAX_RESULTS` | `maxResults` | `10` | Results returned to the model after filtering, 0–50; defaults to `DEEPSEEK_SEARCH_COUNT` when unset. `0` returns no results. |
177
+ | `DEEPSEEK_SEARCH_MIN_RERANK` | `minRerank` | `0` | Drop results below this relevance score (0–3); results without a finite score pass at zero and are dropped at a positive threshold. |
178
+ | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `minAuthority` | `0` | Drop known authorities below this tier. Authority is 0–4. |
179
+ | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `allowUnknownAuthority` | `true` | Keep results whose authority is missing or unknown. Set `false`/`0`/`no`/`off` to drop them. |
180
+ | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | `blockedDomains` | unset | Comma-separated; adds `-site:` operators to the query. |
181
+ | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | `includeDomains` | unset | Comma-separated; restricts results with `site:` operators. |
182
+ | `DEEPSEEK_SEARCH_LANGUAGE` | `language` | unset | Adds a `lang:` operator, for example `zh,en`. |
183
+ | `DEEPSEEK_SEARCH_LOCALIZATION` | `searchLocalization` | unset | Valid IPv4 or IPv6 address sent as DeepSeek's `localization` to prefer geo-localized results; invalid values are ignored. Deployment-level only, never a per-call parameter. |
184
+ | `DEEPSEEK_SEARCH_MAX_SUMMARY_CHARS` | `maxSummaryChars` | `1200` | Per-result summary cap, 0–1,000,000. |
185
+ | `DEEPSEEK_SEARCH_TIMEOUT_MS` | `searchTimeoutMs` | `30000` | Search timeout, 1,000–120,000 ms. |
160
186
 
161
187
  ### Fetch
162
188
 
163
- | Env variable | Default | Description |
164
- | --- | --- | --- |
165
- | `DEEPSEEK_ENABLE_FETCH` | `true` | Register the fetch tool. Same false-only semantics as `DEEPSEEK_ENABLE_SEARCH`. |
166
- | `DEEPSEEK_FETCH_MAX_CHARS` | `16000` | Maximum characters returned, including the title, 0–1,000,000. |
167
- | `DEEPSEEK_FETCH_TIMEOUT_MS` | `30000` | Fetch timeout, 1,000–120,000 ms. |
168
- | `DEEPSEEK_FETCH_ALLOWED_DOMAINS` | unset | If set, only these hosts and subdomains may be fetched. |
169
- | `DEEPSEEK_FETCH_BLOCKED_DOMAINS` | unset | Always block these hosts and subdomains. Blocked wins over allowed. |
170
- | `DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS` | `true` | Reject localhost, private IPs, `.local`, `.internal`, and similar hosts. |
189
+ | Env variable | Config file | Default | Description |
190
+ | --- | --- | --- | --- |
191
+ | `DEEPSEEK_ENABLE_FETCH` | — | `true` | Register the fetch tool. Same false-only semantics as `DEEPSEEK_ENABLE_SEARCH`. |
192
+ | `DEEPSEEK_FETCH_MAX_CHARS` | `fetchMaxChars` | `16000` | Maximum characters returned, including the title, 0–1,000,000. |
193
+ | `DEEPSEEK_FETCH_TIMEOUT_MS` | `fetchTimeoutMs` | `30000` | Fetch timeout, 1,000–120,000 ms. |
194
+ | `DEEPSEEK_FETCH_ALLOWED_DOMAINS` | `fetchAllowedDomains` | unset | If set, only these hosts and subdomains may be fetched. |
195
+ | `DEEPSEEK_FETCH_BLOCKED_DOMAINS` | `fetchBlockedDomains` | unset | Always block these hosts and subdomains. Blocked wins over allowed. |
196
+ | `DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS` | `fetchBlockPrivateHosts` | `true` | Reject localhost, private IPs, `.local`, `.internal`, and similar hosts. |
171
197
 
172
198
  ### Credentials and retries
173
199
 
174
- | Env variable | Default | Description |
175
- | --- | --- | --- |
176
- | `DEEPSEEK_API_KEY` | required | DeepSeek API key, forwarded via `env_vars`. The server refuses to start without it. |
177
- | `DEEPSEEK_MAX_RETRIES` | `0` | Retries for 408, 425, 429, and 5xx responses, 0–5. |
178
- | `DEEPSEEK_RETRY_BASE_DELAY_MS` | `250` | Base delay for exponential backoff. `Retry-After` is honored up to 10 seconds. |
200
+ | Env variable | Config file | Default | Description |
201
+ | --- | --- | --- | --- |
202
+ | `DEEPSEEK_API_KEY` | — | required | DeepSeek API key, forwarded via `env_vars`. The server refuses to start without it. |
203
+ | `DEEPSEEK_MAX_RETRIES` | `maxRetries` | `2` | Retries for 408, 425, 429, and 5xx responses, 0–5. |
204
+ | `DEEPSEEK_RETRY_BASE_DELAY_MS` | `retryBaseDelayMs` | `250` | Base delay for exponential backoff. `Retry-After` is honored up to 10 seconds. |
179
205
 
180
206
  ## Troubleshooting
181
207
 
@@ -208,7 +234,9 @@ Tested with Codex 0.163.0 on Linux over stdio.
208
234
  npx deepseek-web-retrieval uninstall --host codex --scope global
209
235
  ```
210
236
 
211
- This restores the previous `web_search` value, removes the `mcp_servers.deepseek` table
212
- and the rules block, and deletes files the installer created. Manually, remove the
237
+ This restores snapshotted `web_search` and `mcp_servers.deepseek` values only when their
238
+ current values still match what the installer wrote, removes the managed rules block,
239
+ and deletes installer-created config files only when left empty. Later user edits to
240
+ configuration values are kept and reported. For a manual installation, remove the
213
241
  `[mcp_servers.deepseek]` table, any `deepseek-web-retrieval` rules block in your
214
242
  `AGENTS.md`, and the top-level `web_search` key if it was only set for the forced tier.
@@ -2,7 +2,7 @@
2
2
 
3
3
  OpenCode v2 adapter for [DeepSeek Web Retrieval](../../README.md).
4
4
 
5
- Integration mechanism: OpenCode's native plugin API (`@opencode/plugin` 2.0.32+), not MCP.
5
+ Integration mechanism: OpenCode's native plugin API (`@opencode/plugin`), not MCP.
6
6
 
7
7
  When loaded, it adds:
8
8
 
@@ -18,7 +18,9 @@ For shared search/fetch limits, retries, and privacy behavior, see
18
18
 
19
19
  ## Requirements
20
20
 
21
- - OpenCode v2 with `@opencode/plugin` 2.0.32 or newer
21
+ - OpenCode v2 with `@opencode/plugin` in the `^2.0.32` peer range; the tested version
22
+ is recorded in [`docs/hosts.jsonc`](../hosts.jsonc)
23
+ - Node.js 20+ and npm when using the installer CLI
22
24
  - A DeepSeek account with access to `/v1/tools/search` and `/v1/tools/fetch`
23
25
  - A funded DeepSeek account; Search and Fetch are billed per successful request
24
26
  - A DeepSeek credential stored in OpenCode, or `DEEPSEEK_API_KEY`
@@ -36,10 +38,12 @@ npx deepseek-web-retrieval init --host opencode --scope <project|global> \
36
38
  ```
37
39
 
38
40
  It prints the full plan — every file, old value → new value — and writes nothing until
39
- you pass `--apply`. It snapshots the current `websearch.provider` before the forced tier
41
+ you pass `--apply`. Add `--pin` to register the exact version of the CLI you are running
42
+ instead of the always-latest package name. It snapshots the current `websearch.provider` before the forced tier
40
43
  replaces it, merges `permissions` without touching yours, preserves comments in
41
- `opencode.jsonc`, and `uninstall` restores everything. Search has no `preferred` tier on
42
- OpenCode (the provider is host-level config, never a model choice); the CLI rejects it.
44
+ `opencode.jsonc`, and `uninstall` restores recorded values that have not since been edited.
45
+ Search has no `preferred` tier on OpenCode (the provider is host-level config, never a
46
+ model choice); the CLI rejects it.
43
47
 
44
48
  ### OpenCode CLI install
45
49
 
@@ -100,17 +104,23 @@ A complete global config:
100
104
  Use the bare package name (optionally pinned) as the `package` value:
101
105
 
102
106
  ```jsonc
103
- "plugins": [{ "package": "deepseek-web-retrieval@0.4.0" }]
107
+ "plugins": [{ "package": "deepseek-web-retrieval" }]
104
108
  ```
105
109
 
110
+ Append `@X.Y.Z` to pin a published version. For an existing installer-managed setup,
111
+ follow the [upgrade instructions](../integration.md#upgrading-existing-installs).
112
+
106
113
  **Global checkout**
107
114
 
108
115
  ```bash
109
- git clone https://github.com/gwokhou/deepseek-web-retrieval.git \
116
+ git clone https://github.com/guokhou/deepseek-web-retrieval.git \
110
117
  ~/.config/opencode/vendor/deepseek-web-retrieval
111
118
  ```
112
119
 
113
- Use `"package": "./vendor/deepseek-web-retrieval"`.
120
+ Use `"package": "./vendor/deepseek-web-retrieval"`. On Windows the global config root is
121
+ `%APPDATA%\opencode\` (unless `XDG_CONFIG_HOME` is explicitly set), so clone into
122
+ `%APPDATA%\opencode\vendor\deepseek-web-retrieval` there — the same root the installer
123
+ writes to.
114
124
 
115
125
  **Project checkout**
116
126
 
@@ -125,7 +135,8 @@ Use `"package": "./.opencode/vendor/deepseek-web-retrieval"`.
125
135
  **Auto-discovery**
126
136
 
127
137
  Clone or copy the repository into `~/.config/opencode/plugins/deepseek-web-retrieval` or
128
- `.opencode/plugins/deepseek-web-retrieval`. Auto-discovered plugins do not receive options,
138
+ `.opencode/plugins/deepseek-web-retrieval` (on Windows, `%APPDATA%\opencode\plugins\…`).
139
+ Auto-discovered plugins do not receive options,
129
140
  so `webfetch` stays in place; the `deepseek` search provider and `deepseek_fetch` are still
130
141
  added.
131
142
 
@@ -163,47 +174,66 @@ Fetch https://opencode.ai/v2/docs/build/plugins and summarize the plugin lifecyc
163
174
  The model calls `websearch` (provider `deepseek`) or `deepseek_fetch`. When
164
175
  `fetchOverrideWebfetch` is enabled, Markdown fetches go through `webfetch` instead.
165
176
 
177
+ DeepSeek's optional `ori_question` is not sent on this path: OpenCode's `websearch` tool
178
+ input is fixed to `query` at 2.0.32, so the provider has no channel for it (see the
179
+ [research note](../research/ori-question-sourcing.md)).
180
+
166
181
  ## Configuration
167
182
 
168
183
  Set these in the plugin's `options` object.
169
184
 
185
+ > [!NOTE]
186
+ > These options use the same camelCase vocabulary as the shared config file
187
+ > (`~/.config/deepseek-web-retrieval/config.json`; on Windows,
188
+ > `%APPDATA%\deepseek-web-retrieval\config.json`), with the `DEEPSEEK_*` environment in
189
+ > between: `shared file < environment < options`. Configure once in the file — it works on
190
+ > every host — and override per host here in `options`; existing `options` keep winning
191
+ > exactly as before. `npx deepseek-web-retrieval config` shows the file/env/defaults view
192
+ > of every knob (these `options` sit above it, so check them here when it differs).
193
+
170
194
  ### Search
171
195
 
172
- | Option | Default | Description |
173
- | --- | --- | --- |
174
- | `searchEnabled` | `true` | Register the DeepSeek search provider at all. Only an explicit `false` disables it. |
175
- | `count` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
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. A result without a finite score passes while the threshold is `0` and is dropped once it is positive. |
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
- | `blockedDomains` | `[]` | Add `-site:` operators to the search query. |
181
- | `includeDomains` | `[]` | Restrict results with `site:` operators. |
182
- | `language` | unset | Add a `lang:` operator, for example `zh,en`. |
183
- | `maxSummaryChars` | `1200` | Per-result summary cap, 0–1,000,000. |
184
- | `searchTimeoutMs` | `30000` | Search timeout, 1,000–120,000 ms. |
185
- | `setDefaultWebsearch` | `false` | Make `deepseek` the default websearch provider when the plugin loads. |
196
+ The "Environment" column names each option's `DEEPSEEK_*` twin — the plugin reads the
197
+ environment as the layer beneath these options, and the same camelCase keys also work
198
+ in the shared config file beneath both. `—` marks host-integration options with no
199
+ environment or file channel.
200
+
201
+ | Option | Environment | Default | Description |
202
+ | --- | --- | --- | --- |
203
+ | `searchEnabled` | `DEEPSEEK_ENABLE_SEARCH` | `true` | Register the DeepSeek search provider at all. Only an explicit `false` disables it. |
204
+ | `count` | `DEEPSEEK_SEARCH_COUNT` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
205
+ | `maxResults` | `DEEPSEEK_SEARCH_MAX_RESULTS` | `10` | Results returned to the model after filtering, 0–50; defaults to `count` when unset. `0` returns no results. |
206
+ | `minRerank` | `DEEPSEEK_SEARCH_MIN_RERANK` | `0` | Drop results below this relevance score. DeepSeek scores are 0–3. A result without a finite score passes while the threshold is `0` and is dropped once it is positive. |
207
+ | `minAuthority` | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `0` | Drop known authorities below this tier. Authority is 0–4. |
208
+ | `allowUnknownAuthority` | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `true` | Keep results whose authority is missing or unknown. Only an explicit `false` disables it. |
209
+ | `blockedDomains` | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | `[]` | Add `-site:` operators to the search query. |
210
+ | `includeDomains` | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | `[]` | Restrict results with `site:` operators. |
211
+ | `language` | `DEEPSEEK_SEARCH_LANGUAGE` | unset | Add a `lang:` operator, for example `zh,en`. |
212
+ | `searchLocalization` | `DEEPSEEK_SEARCH_LOCALIZATION` | unset | Valid IPv4 or IPv6 address sent as DeepSeek's `localization` to prefer geo-localized results; invalid values are ignored. Deployment-level only, never a per-call parameter. |
213
+ | `maxSummaryChars` | `DEEPSEEK_SEARCH_MAX_SUMMARY_CHARS` | `1200` | Per-result summary cap, 0–1,000,000. |
214
+ | `searchTimeoutMs` | `DEEPSEEK_SEARCH_TIMEOUT_MS` | `30000` | Search timeout, 1,000–120,000 ms. |
215
+ | `setDefaultWebsearch` | — | `false` | Make `deepseek` the default websearch provider when the plugin loads. |
186
216
 
187
217
  ### Fetch
188
218
 
189
- | Option | Default | Description |
190
- | --- | --- | --- |
191
- | `fetchEnabled` | `true` | Register any DeepSeek fetch tooling (the separate tool and the webfetch override). |
192
- | `fetchMaxChars` | `16000` | Maximum characters returned, including the title, 0–1,000,000. |
193
- | `fetchTimeoutMs` | `30000` | Fetch timeout, 1,000–120,000 ms. |
194
- | `fetchOverrideWebfetch` | `false` | Replace the built-in `webfetch`. |
195
- | `fetchRegisterTool` | `true` | Register the separate `deepseek_fetch` tool. |
196
- | `fetchAllowedDomains` | `[]` | If non-empty, only these hosts and subdomains may be fetched. |
197
- | `fetchBlockedDomains` | `[]` | Always block these hosts and subdomains. Blocked wins over allowed. |
198
- | `fetchBlockPrivateHosts` | `true` | Reject localhost, private IPs, `.local`, `.internal`, and similar hosts. |
219
+ | Option | Environment | Default | Description |
220
+ | --- | --- | --- | --- |
221
+ | `fetchEnabled` | `DEEPSEEK_ENABLE_FETCH` | `true` | Register any DeepSeek fetch tooling (the separate tool and the webfetch override). |
222
+ | `fetchMaxChars` | `DEEPSEEK_FETCH_MAX_CHARS` | `16000` | Maximum characters returned, including the title, 0–1,000,000. |
223
+ | `fetchTimeoutMs` | `DEEPSEEK_FETCH_TIMEOUT_MS` | `30000` | Fetch timeout, 1,000–120,000 ms. |
224
+ | `fetchOverrideWebfetch` | — | `false` | Replace the built-in `webfetch`. |
225
+ | `fetchRegisterTool` | — | `true` | Register the separate `deepseek_fetch` tool. |
226
+ | `fetchAllowedDomains` | `DEEPSEEK_FETCH_ALLOWED_DOMAINS` | `[]` | If non-empty, only these hosts and subdomains may be fetched. |
227
+ | `fetchBlockedDomains` | `DEEPSEEK_FETCH_BLOCKED_DOMAINS` | `[]` | Always block these hosts and subdomains. Blocked wins over allowed. |
228
+ | `fetchBlockPrivateHosts` | `DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS` | `true` | Reject localhost, private IPs, `.local`, `.internal`, and similar hosts. |
199
229
 
200
230
  ### Credentials and retries
201
231
 
202
- | Option | Default | Description |
203
- | --- | --- | --- |
204
- | `apiKeySource` | `"auto"` | Credential strategy: `"auto"`, `"opencode"`, or `"env"`. |
205
- | `maxRetries` | `0` | Retries for 408, 425, 429, and 5xx responses, 0–5. |
206
- | `retryBaseDelayMs` | `250` | Base delay for exponential backoff. `Retry-After` is honored up to 10 seconds. |
232
+ | Option | Environment | Default | Description |
233
+ | --- | --- | --- | --- |
234
+ | `apiKeySource` | — | `"auto"` | Credential strategy: `"auto"`, `"opencode"`, or `"env"`. |
235
+ | `maxRetries` | `DEEPSEEK_MAX_RETRIES` | `2` | Retries for 408, 425, 429, and 5xx responses, 0–5. |
236
+ | `retryBaseDelayMs` | `DEEPSEEK_RETRY_BASE_DELAY_MS` | `250` | Base delay for exponential backoff. `Retry-After` is honored up to 10 seconds. |
207
237
 
208
238
  ## Troubleshooting
209
239
 
@@ -234,7 +264,7 @@ using both an OpenCode-stored DeepSeek credential and `DEEPSEEK_API_KEY`.
234
264
  ## Uninstall
235
265
 
236
266
  With the installer CLI (restores the snapshotted `websearch.provider`, removes only the
237
- appended permissions, and deletes files it created):
267
+ appended permissions, and deletes installer-created files only when they are left empty):
238
268
 
239
269
  ```bash
240
270
  npx deepseek-web-retrieval uninstall --host opencode --scope <project|global>
package/docs/agents/pi.md CHANGED
@@ -25,7 +25,9 @@ shared search/fetch limits, retries, and privacy behavior, see
25
25
 
26
26
  ## Requirements
27
27
 
28
- - Pi 1.3.0 or newer
28
+ - Pi with host SDKs in the `^1.3.0` peer range (`>=1.3.0 <2.0.0`); the tested
29
+ version is recorded in [`docs/hosts.jsonc`](../hosts.jsonc)
30
+ - Node.js 20+ and npm when using the installer CLI or MCP fallback
29
31
  - A DeepSeek account with access to `/v1/tools/search` and `/v1/tools/fetch`
30
32
  - A funded DeepSeek account; Search and Fetch are billed per successful request
31
33
  - `DEEPSEEK_API_KEY` set in the shell that launches Pi
@@ -40,14 +42,20 @@ npx deepseek-web-retrieval init --host pi --scope <project|global> \
40
42
  ```
41
43
 
42
44
  It prints the full plan — every file, old value → new value, and notes about existing
43
- packages and MCP servers — and writes nothing until you pass `--apply`. The default path is
45
+ packages and MCP servers — and writes nothing until you pass `--apply`. Add `--pin` to
46
+ register the exact version of the CLI you are running instead of the always-latest
47
+ package name. The default path is
44
48
  the extension: the plan appends `npm:deepseek-web-retrieval` to the `packages` array in
45
49
  `~/.pi/agent/settings.json` (global) or `.pi/settings.json` (project), and Pi resolves the
46
50
  source at the next session start (the first launch fetches the package from npm;
47
51
  `pi update --extensions` refreshes it later). An off capability is written into the
48
52
  `deepseekWebRetrieval` settings namespace the extension reads at load. Pass `--path mcp`
49
- to install the [MCP fallback path](#the-mcp-fallback-path) instead. `uninstall` restores
50
- exactly what the plan applied.
53
+ to install the [MCP fallback path](#the-mcp-fallback-path) instead. `uninstall` removes
54
+ recorded additions and restores unchanged configuration values from their snapshots.
55
+
56
+ For upgrades from 0.4.0, select `--path mcp` explicitly to retain the old path, or use
57
+ the extension default to migrate; follow the shared
58
+ [upgrade instructions](../integration.md#upgrading-existing-installs).
51
59
 
52
60
  ### Manual install
53
61
 
@@ -96,6 +104,12 @@ Fetch https://pi.dev/docs/latest/extensions and summarize the extension lifecycl
96
104
  The model calls `deepseek_web_search` or `deepseek_fetch` — the same names over both paths;
97
105
  only the MCP fallback prefixes them with `mcp__deepseek__`.
98
106
 
107
+ The search tool accepts an optional `original_question` input — the user's question in their
108
+ own words — forwarded as DeepSeek's `ori_question` to sharpen search intent. When the model
109
+ leaves it blank, the extension falls back to the most recent user message in the session
110
+ (best-effort; truncated to the API's 128-character limit). The MCP fallback path has no
111
+ session access, so there a blank input simply omits the parameter.
112
+
99
113
  ## Configuration
100
114
 
101
115
  The runtime config surface is the same `DEEPSEEK_*` environment the MCP server reads
@@ -103,40 +117,53 @@ The runtime config surface is the same `DEEPSEEK_*` environment the MCP server r
103
117
  `deepseekWebRetrieval` settings namespace for the off switches (written by the installer;
104
118
  it outranks the environment and is preserved by Pi's own settings rewrites).
105
119
 
120
+ The shared config file (`~/.config/deepseek-web-retrieval/config.json`, camelCase keys —
121
+ on Windows, `%APPDATA%\deepseek-web-retrieval\config.json`)
122
+ also sits in this surface, layered under the environment, and needs no forwarding on any
123
+ path — the extension and the MCP server both read the file themselves. One file therefore
124
+ configures every host running this package, with this host's environment and settings
125
+ still outranking it. `npx deepseek-web-retrieval config` prints what actually resolves —
126
+ each knob's value and source, with file warnings surfaced and `--search "<query>"` running
127
+ one billed search through it all.
128
+
106
129
  ### Search
107
130
 
108
- | Env variable | Default | Description |
109
- | --- | --- | --- |
110
- | `DEEPSEEK_ENABLE_SEARCH` | `true` | Register the search tool. Only an explicit `false`/`0`/`no`/`off` disables it; at least one tool must stay enabled. |
111
- | `DEEPSEEK_SEARCH_COUNT` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
112
- | `DEEPSEEK_SEARCH_MAX_RESULTS` | `10` | Results returned to the model after filtering, 0–50. `0` returns no results. |
113
- | `DEEPSEEK_SEARCH_MIN_RERANK` | `0` | Drop results below this relevance score. DeepSeek scores are 0–3. |
114
- | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `0` | Drop known authorities below this tier. Authority is 0–4. |
115
- | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `true` | Keep results whose authority is missing or unknown. Set `false`/`0` to drop them. |
116
- | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | unset | Comma-separated; adds `-site:` operators to the query. |
117
- | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | unset | Comma-separated; restricts results with `site:` operators. |
118
- | `DEEPSEEK_SEARCH_LANGUAGE` | unset | Adds a `lang:` operator, for example `zh,en`. |
119
- | `DEEPSEEK_SEARCH_MAX_SUMMARY_CHARS` | `1200` | Per-result summary cap, 0–1,000,000. |
120
- | `DEEPSEEK_SEARCH_TIMEOUT_MS` | `30000` | Search timeout, 1,000–120,000 ms. No host-side cap applies on the extension path. |
131
+ The "Config file" column names each key's camelCase twin in the shared config file —
132
+ one vocabulary pair, one pipeline. `—` marks host-integration entries the file rejects.
133
+
134
+ | Env variable | Config file | Default | Description |
135
+ | --- | --- | --- | --- |
136
+ | `DEEPSEEK_ENABLE_SEARCH` | — | `true` | Register the search tool. Only an explicit `false`/`0`/`no`/`off` disables it; at least one tool must stay enabled. |
137
+ | `DEEPSEEK_SEARCH_COUNT` | `count` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
138
+ | `DEEPSEEK_SEARCH_MAX_RESULTS` | `maxResults` | `10` | Results returned to the model after filtering, 0–50; defaults to `DEEPSEEK_SEARCH_COUNT` when unset. `0` returns no results. |
139
+ | `DEEPSEEK_SEARCH_MIN_RERANK` | `minRerank` | `0` | Drop results below this relevance score (0–3); results without a finite score pass at zero and are dropped at a positive threshold. |
140
+ | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `minAuthority` | `0` | Drop known authorities below this tier. Authority is 0–4. |
141
+ | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `allowUnknownAuthority` | `true` | Keep results whose authority is missing or unknown. Set `false`/`0`/`no`/`off` to drop them. |
142
+ | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | `blockedDomains` | unset | Comma-separated; adds `-site:` operators to the query. |
143
+ | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | `includeDomains` | unset | Comma-separated; restricts results with `site:` operators. |
144
+ | `DEEPSEEK_SEARCH_LANGUAGE` | `language` | unset | Adds a `lang:` operator, for example `zh,en`. |
145
+ | `DEEPSEEK_SEARCH_LOCALIZATION` | `searchLocalization` | unset | Valid IPv4 or IPv6 address sent as DeepSeek's `localization` to prefer geo-localized results; invalid values are ignored. Deployment-level only, never a per-call parameter. |
146
+ | `DEEPSEEK_SEARCH_MAX_SUMMARY_CHARS` | `maxSummaryChars` | `1200` | Per-result summary cap, 0–1,000,000. |
147
+ | `DEEPSEEK_SEARCH_TIMEOUT_MS` | `searchTimeoutMs` | `30000` | Search timeout, 1,000–120,000 ms. No host-side cap applies on the extension path. |
121
148
 
122
149
  ### Fetch
123
150
 
124
- | Env variable | Default | Description |
125
- | --- | --- | --- |
126
- | `DEEPSEEK_ENABLE_FETCH` | `true` | Register the fetch tool. Same false-only semantics as `DEEPSEEK_ENABLE_SEARCH`. |
127
- | `DEEPSEEK_FETCH_MAX_CHARS` | `16000` | Maximum characters returned, including the title, 0–1,000,000. The only output cap on this path. |
128
- | `DEEPSEEK_FETCH_TIMEOUT_MS` | `30000` | Fetch timeout, 1,000–120,000 ms. No host-side cap applies on the extension path. |
129
- | `DEEPSEEK_FETCH_ALLOWED_DOMAINS` | unset | If set, only these hosts and subdomains may be fetched. |
130
- | `DEEPSEEK_FETCH_BLOCKED_DOMAINS` | unset | Always block these hosts and subdomains. Blocked wins over allowed. |
131
- | `DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS` | `true` | Reject localhost, private IPs, `.local`, `.internal`, and similar hosts. Set `false`/`0` to disable. |
151
+ | Env variable | Config file | Default | Description |
152
+ | --- | --- | --- | --- |
153
+ | `DEEPSEEK_ENABLE_FETCH` | — | `true` | Register the fetch tool. Same false-only semantics as `DEEPSEEK_ENABLE_SEARCH`. |
154
+ | `DEEPSEEK_FETCH_MAX_CHARS` | `fetchMaxChars` | `16000` | Maximum characters returned, including the title, 0–1,000,000. The only output cap on this path. |
155
+ | `DEEPSEEK_FETCH_TIMEOUT_MS` | `fetchTimeoutMs` | `30000` | Fetch timeout, 1,000–120,000 ms. No host-side cap applies on the extension path. |
156
+ | `DEEPSEEK_FETCH_ALLOWED_DOMAINS` | `fetchAllowedDomains` | unset | If set, only these hosts and subdomains may be fetched. |
157
+ | `DEEPSEEK_FETCH_BLOCKED_DOMAINS` | `fetchBlockedDomains` | unset | Always block these hosts and subdomains. Blocked wins over allowed. |
158
+ | `DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS` | `fetchBlockPrivateHosts` | `true` | Reject localhost, private IPs, `.local`, `.internal`, and similar hosts. Set `false`/`0` to disable. |
132
159
 
133
160
  ### Credentials and retries
134
161
 
135
- | Env variable | Default | Description |
136
- | --- | --- | --- |
137
- | `DEEPSEEK_API_KEY` | required | DeepSeek API key, read from the Pi process environment. |
138
- | `DEEPSEEK_MAX_RETRIES` | `0` | Retries for 408, 425, 429, and 5xx responses, 0–5. |
139
- | `DEEPSEEK_RETRY_BASE_DELAY_MS` | `250` | Base delay for exponential backoff. `Retry-After` is honored up to 10 seconds. |
162
+ | Env variable | Config file | Default | Description |
163
+ | --- | --- | --- | --- |
164
+ | `DEEPSEEK_API_KEY` | — | required | DeepSeek API key, read from the Pi process environment. |
165
+ | `DEEPSEEK_MAX_RETRIES` | `maxRetries` | `2` | Retries for 408, 425, 429, and 5xx responses, 0–5. |
166
+ | `DEEPSEEK_RETRY_BASE_DELAY_MS` | `retryBaseDelayMs` | `250` | Base delay for exponential backoff. `Retry-After` is honored up to 10 seconds. |
140
167
 
141
168
  Invalid values fall back to the shared default.
142
169
 
@@ -175,7 +202,9 @@ With the installer CLI:
175
202
  npx deepseek-web-retrieval uninstall --host pi --scope <project|global>
176
203
  ```
177
204
 
178
- Or remove the source with Pi itself:
205
+ For a manual installation, remove the source with Pi itself; use `--local` for a
206
+ project-local install. Installer-managed installs should use `uninstall` above so
207
+ rules, snapshots, and install records are cleaned up as well:
179
208
 
180
209
  ```bash
181
210
  pi remove npm:deepseek-web-retrieval