pi-web-providers 1.1.0 → 2.1.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/README.md CHANGED
@@ -12,11 +12,11 @@ off entirely.
12
12
 
13
13
  ## ✨ Features
14
14
 
15
- - **Multiple providers** Claude, Codex, Custom CLI, Exa, Gemini,
16
- Perplexity, Parallel, Valyu
17
- - **Batched search and answers** run several related queries in a single
15
+ - **Multiple providers**: Claude, Cloudflare, Codex, Exa, Firecrawl,
16
+ Gemini, Linkup, Perplexity, Parallel, [Tavily](https://tavily.com), Valyu
17
+ - **Batched search and answers**: run several related queries in a single
18
18
  `web_search` or `web_answer` call and get grouped results back in one response
19
- - **Async contents prefetch** optionally start background `web_contents`
19
+ - **Async contents prefetch**: optionally start background `web_contents`
20
20
  extraction from `web_search` results and reuse the cached pages later
21
21
 
22
22
  ## 📦 Install
@@ -34,39 +34,46 @@ Run:
34
34
  ```
35
35
 
36
36
  This edits the global config file `~/.pi/agent/web-providers.json`. The
37
- settings UI mirrors the three sections below: tools, providers, and generic
38
- settings.
37
+ settings UI mirrors the three sections below: tools, providers, and settings.
39
38
 
40
39
  Each tool can be routed to any compatible provider:
41
40
 
42
- | Provider | search | contents | answer | research | Auth |
43
- | -------------- | :----: | :------: | :----: | :------: | ---------------------- |
44
- | **Claude** | ✔ | | ✔ | | Local Claude Code auth |
45
- | **Codex** | | | | | Local Codex CLI auth |
46
- | **Exa** | ✔ |||| `EXA_API_KEY` |
47
- | **Gemini** | ✔ | | ✔ | ✔ | `GOOGLE_API_KEY` |
48
- | **Perplexity** | ✔ | | ✔ | ✔ | `PERPLEXITY_API_KEY` |
49
- | **Parallel** | ✔ | ✔ | | | `PARALLEL_API_KEY` |
50
- | **Valyu** | ✔ | ✔ ||| `VALYU_API_KEY` |
51
-
52
- Advanced option: `custom-cli` is a configurable adapter provider that can route
41
+ | Provider | search | contents | answer | research | Auth |
42
+ | -------------- | :----: | :------: | :----: | :------: | ------------------------------------------------ |
43
+ | **Claude** | ✔ | | ✔ | | Local Claude Code auth |
44
+ | **Cloudflare** | | | | | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` |
45
+ | **Codex** | ✔ | | | | Local Codex CLI auth |
46
+ | **Exa** | ✔ || ✔ | ✔ | `EXA_API_KEY` |
47
+ | **Firecrawl** | ✔ | ✔ | | | `FIRECRAWL_API_KEY` |
48
+ | **Gemini** | | | ✔ | ✔ | `GOOGLE_API_KEY` |
49
+ | **Linkup** | ✔ | ✔ | | | `LINKUP_API_KEY` |
50
+ | **Perplexity** | ✔ | | ✔ | ✔ | `PERPLEXITY_API_KEY` |
51
+ | **Parallel** | ✔ | ✔ | | | `PARALLEL_API_KEY` |
52
+ | **Tavily** | ✔ | ✔ | | | `TAVILY_API_KEY` |
53
+ | **Valyu** | ✔ | ✔ | ✔ | ✔ | `VALYU_API_KEY` |
54
+
55
+ Advanced option: `custom` is a configurable adapter provider that can route
53
56
  any managed tool through a local wrapper command using a JSON stdin/stdout
54
57
  contract.
55
58
 
56
- See [`example-config.json`](example-config.json) for a full default
59
+ See [`example-config.json`](example-config.json) for the minimal default
57
60
  configuration.
58
61
 
59
62
  ### Tools
60
63
 
61
- Each managed tool maps to one provider id or `null` for off under the top-level
62
- `tools` key. A tool is only exposed when it is mapped to a compatible provider
63
- and that provider is currently available. Tool-specific settings live under
64
- `toolSettings`; today this covers `toolSettings.search.prefetch`.
64
+ Each managed tool maps to one provider id under the top-level `tools` key.
65
+ Removing a tool mapping turns that tool off. A tool is only exposed when it is
66
+ mapped to a compatible provider and that provider is currently available.
67
+ Shared defaults and tool-specific settings live under `settings`; search-specific
68
+ settings live under `settings.search`, and async research uses
69
+ `settings.researchTimeoutMs`.
65
70
 
66
71
  #### `web_search`
67
72
 
68
73
  Search the public web for up to 10 queries in one call. It returns grouped
69
- titles, URLs, and snippets for each query.
74
+ titles, URLs, and snippets for each query. Batch related queries when grouped
75
+ comparison matters; use separate sibling `web_search` calls when independent
76
+ results should arrive as soon as they are ready.
70
77
 
71
78
  <details>
72
79
  <summary><strong>Parameters and behavior</strong></summary>
@@ -81,14 +88,16 @@ titles, URLs, and snippets for each query.
81
88
  SDK. It accepts `provider`, `maxUrls`, `ttlMs`, and `contentsOptions`, and
82
89
  starts a background page-extraction workflow only when `prefetch.provider` is
83
90
  set. `/web-providers` can also persist default search prefetch settings under
84
- `toolSettings.search.prefetch`.
91
+ `settings.search`.
85
92
 
86
93
  </details>
87
94
 
88
95
  #### `web_contents`
89
96
 
90
97
  Read the main text from one or more web pages. It reuses cached pages when they
91
- match and fetches only missing or stale URLs.
98
+ match and fetches only missing or stale URLs. Batch related pages when they are
99
+ meant to be read as one bundle; use separate sibling `web_contents` calls when
100
+ each page can be acted on independently.
92
101
 
93
102
  <details>
94
103
  <summary><strong>Parameters and behavior</strong></summary>
@@ -99,7 +108,7 @@ match and fetches only missing or stale URLs.
99
108
  | `options` | object | — | Provider-specific extraction options |
100
109
 
101
110
  `web_contents` reuses any matching cached pages already present in the local
102
- content store—whether they came from prefetch or an earlier read—and only
111
+ in-memory cache—whether they came from prefetch or an earlier read—and only
103
112
  fetches missing or stale URLs.
104
113
 
105
114
  </details>
@@ -107,7 +116,9 @@ fetches missing or stale URLs.
107
116
  #### `web_answer`
108
117
 
109
118
  Answer one or more questions using web-grounded evidence. When you ask more
110
- than one question, the response is grouped into per-question sections.
119
+ than one question, the response is grouped into per-question sections. Batch
120
+ related questions when the answers belong together; split them into sibling
121
+ calls when earlier independent answers can unblock the next step.
111
122
 
112
123
  <details>
113
124
  <summary><strong>Parameters and behavior</strong></summary>
@@ -124,9 +135,10 @@ provided.
124
135
 
125
136
  #### `web_research`
126
137
 
127
- Investigate a topic across web sources and produce a longer report. The
128
- provider-specific `options` stay native to each SDK, and runtime options
129
- override provider configuration when both are set.
138
+ Investigate a topic across web sources and produce a longer report.
139
+ `web_research` is always asynchronous: it starts a background run, returns a
140
+ short dispatch notice immediately, and later posts a completion message with a
141
+ saved report path.
130
142
 
131
143
  <details>
132
144
  <summary><strong>Parameters and behavior</strong></summary>
@@ -136,34 +148,15 @@ override provider configuration when both are set.
136
148
  | `input` | string | required | Research brief or question |
137
149
  | `options` | object | — | Provider-specific options |
138
150
 
139
- `options` are provider-native and provider-specific. Equivalent concepts can use
151
+ `options` are provider-specific. Equivalent concepts can use
140
152
  different field names across SDKs—for example Perplexity uses `country`, Exa
141
153
  uses `userLocation`, and Valyu uses `countryCode`. Runtime `options` override
142
- provider-native config, but managed tool inputs and tool wiring stay fixed.
154
+ provider config, but managed tool inputs and tool wiring stay fixed.
143
155
 
144
- </details>
145
-
146
- <details>
147
- <summary><strong>Timeout, retry, and delivery modes</strong></summary>
148
-
149
- The extension accepts local control fields for robustness: `requestTimeoutMs`,
150
- `retryCount`, and `retryDelayMs` on request/response tools, plus
151
- `pollIntervalMs`, `timeoutMs`, `maxConsecutivePollErrors`, and `resumeId` on
152
- `web_research` for lifecycle-based research providers. These fields are handled
153
- by the extension and are not forwarded into the provider SDK call.
154
-
155
- - Exa and Valyu research support polling, overall deadlines, and resume IDs
156
- but reject `requestTimeoutMs` and do not retry non-idempotent job creation.
157
- - Perplexity research runs in streaming foreground mode and only supports
158
- `requestTimeoutMs`, `retryCount`, and `retryDelayMs`.
159
-
160
- Providers deliver results in one of three modes:
161
-
162
- - **Silent foreground** — no intermediate output; result returned when done.
163
- - **Streaming foreground** — progress updates while running, but the result is
164
- still only usable after the tool finishes.
165
- - **Background research** — the provider runs in the background; if
166
- interrupted, the run can be resumed later via `resumeId`.
156
+ Unlike the other managed tools, `web_research` does not accept local timeout,
157
+ retry, polling, or resume controls. Research has one opinionated execution
158
+ style: pi starts it asynchronously, tracks it locally, and saves the final
159
+ report under `.pi/artifacts/research/`.
167
160
 
168
161
  </details>
169
162
 
@@ -177,19 +170,56 @@ The built-in providers below are thin adapters around official SDKs.
177
170
  - SDK: `@anthropic-ai/claude-agent-sdk`
178
171
  - Uses Claude Code's built-in `WebSearch` and `WebFetch` tools behind a
179
172
  structured JSON adapter
180
- - Runs in **silent foreground** mode
181
173
  - Supports request-shaping `options` such as `model`, `thinking`, `effort`, and
182
174
  `maxTurns`
183
175
  - Great for search plus grounded answers if you already use Claude Code locally
184
176
 
185
177
  </details>
186
178
 
179
+ <details>
180
+ <summary><strong>Cloudflare</strong></summary>
181
+
182
+ - SDK: `cloudflare`
183
+ - Supports `web_contents` via Cloudflare Browser Rendering's `/markdown`
184
+ endpoint
185
+ - Good for JavaScript-heavy pages that need a real browser render before
186
+ extraction
187
+ - Supports provider-specific markdown options such as `gotoOptions`,
188
+ `waitForSelector`, `waitForTimeout`, `cacheTTL`, and request filtering
189
+
190
+ **Setup**
191
+
192
+ 1. In the Cloudflare dashboard, create an API token.
193
+ 2. Grant it this permission:
194
+ - `Account | Browser Rendering | Edit`
195
+ 3. Scope it to the account you want to use.
196
+ 4. Copy that account's **Account ID** from the Cloudflare dashboard.
197
+ 5. Configure pi with both values:
198
+
199
+ ```json
200
+ {
201
+ "tools": {
202
+ "contents": "cloudflare"
203
+ },
204
+ "providers": {
205
+ "cloudflare": {
206
+ "apiToken": "CLOUDFLARE_API_TOKEN",
207
+ "accountId": "CLOUDFLARE_ACCOUNT_ID"
208
+ }
209
+ }
210
+ }
211
+ ```
212
+
213
+ If Cloudflare returns `401 Authentication error`, the token permission, token
214
+ scope, or account ID is usually wrong.
215
+
216
+ </details>
217
+
187
218
  <details>
188
219
  <summary><strong>Codex</strong></summary>
189
220
 
190
221
  - SDK: `@openai/codex-sdk`
191
222
  - Runs in read-only mode with web search enabled
192
- - Runs in **silent foreground** mode
193
223
  - Supports request-shaping `web_search.options` such as `model`,
194
224
  `modelReasoningEffort`, and `webSearchMode`
195
225
  - Best if you already use the local Codex CLI and auth flow
@@ -200,32 +230,61 @@ The built-in providers below are thin adapters around official SDKs.
200
230
  <summary><strong>Exa</strong></summary>
201
231
 
202
232
  - SDK: `exa-js`
203
- - Search, contents, and answer run in **silent foreground** mode
204
- - Research runs in **background research** mode and supports `resumeId`
233
+ - Supports `web_search`, `web_contents`, `web_answer`, and `web_research`
234
+ - `web_research` is exposed through pi's async research workflow
205
235
  - Neural, keyword, hybrid, and deep-research search modes
206
236
  - Inline text-content extraction on search results
207
237
 
208
238
  </details>
209
239
 
240
+ <details>
241
+ <summary><strong>Firecrawl</strong></summary>
242
+
243
+ - SDK: `@mendable/firecrawl-js`
244
+ - Supports `web_search` and `web_contents`
245
+ - Search can optionally include Firecrawl scrape-backed result enrichment
246
+ - Contents extraction uses Firecrawl scrape with markdown-first defaults
247
+ - Supports provider-specific `options.search` such as `sources`, `categories`,
248
+ `location`, `timeout`, and `scrapeOptions`
249
+ - Supports provider-specific `options.scrape` such as `formats`,
250
+ `onlyMainContent`, `waitFor`, `headers`, `location`, `mobile`, `proxy`, and
251
+ cache controls
252
+
253
+ </details>
254
+
210
255
  <details>
211
256
  <summary><strong>Gemini</strong></summary>
212
257
 
213
258
  - SDK: `@google/genai`
214
- - Search and answer run in **silent foreground** mode
215
- - Research runs in **background research** mode and supports `resumeId`
259
+ - Supports `web_search`, `web_answer`, and `web_research`
260
+ - `web_research` is exposed through pi's async research workflow
216
261
  - Google Search grounding for answers
217
262
  - Deep-research agents via Google's Gemini API
218
- - Supports provider-native request options such as `model`, `config`,
263
+ - Supports provider-specific request options such as `model`, `config`,
219
264
  `generation_config`, and `agent_config` depending on the tool
220
265
 
221
266
  </details>
222
267
 
268
+ <details>
269
+ <summary><strong>Linkup</strong></summary>
270
+
271
+ - SDK: `linkup-sdk`
272
+ - Supports `web_search` via Linkup Search with fixed `searchResults` output
273
+ - Supports `web_contents` via Linkup Fetch and always returns markdown
274
+ - Supports provider-specific `options.search` such as `depth`, domain filters,
275
+ image inclusion, and date filters
276
+ - Supports provider-specific `options.fetch` such as `renderJs`,
277
+ `includeRawHtml`, and `extractImages`
278
+ - Good fit for a simple search-plus-markdown setup without extra provider wiring
279
+
280
+ </details>
281
+
223
282
  <details>
224
283
  <summary><strong>Perplexity</strong></summary>
225
284
 
226
285
  - SDK: `@perplexity-ai/perplexity_ai`
227
- - `web_search` and `web_answer` run in **silent foreground** mode
228
- - `web_research` runs in **streaming foreground** mode (no `resumeId` support)
286
+ - Supports `web_search`, `web_answer`, and `web_research`
287
+ - `web_research` is exposed through pi's async research workflow
229
288
  - Uses Perplexity Search for `web_search`
230
289
  - Uses Sonar for `web_answer` and `sonar-deep-research` for `web_research`
231
290
  - Supports provider-specific `web_search.options` such as `country`,
@@ -237,10 +296,25 @@ The built-in providers below are thin adapters around official SDKs.
237
296
  <summary><strong>Parallel</strong></summary>
238
297
 
239
298
  - SDK: `parallel-web`
240
- - Runs in **silent foreground** mode
241
299
  - Agentic and one-shot search modes
242
300
  - Page content extraction with excerpt and full-content toggles
243
- - Supports provider-native search and extraction options from the Parallel SDK
301
+ - Supports provider-specific search and extraction options from the Parallel SDK
302
+
303
+ </details>
304
+
305
+ <details>
306
+ <summary><strong>Tavily</strong></summary>
307
+
308
+ - SDK: `@tavily/core`
309
+ - Supports `web_search` via Tavily Search
310
+ - Supports `web_contents` via Tavily Extract
311
+ - Good for pairing LLM-oriented web search with lightweight page extraction
312
+ - Supports provider-specific `options.search` such as `searchDepth`, `topic`,
313
+ `timeRange`, `includeRawContent`, `includeDomains`, `excludeDomains`,
314
+ `country`, `exactMatch`, and `includeFavicon`
315
+ - Supports provider-specific `options.extract` such as `extractDepth`,
316
+ `format`, `includeImages`, `query`, `chunksPerSource`, and
317
+ `includeFavicon`
244
318
 
245
319
  </details>
246
320
 
@@ -248,23 +322,23 @@ The built-in providers below are thin adapters around official SDKs.
248
322
  <summary><strong>Valyu</strong></summary>
249
323
 
250
324
  - SDK: `valyu-js`
251
- - Search, contents, and answer run in **silent foreground** mode
252
- - Research runs in **background research** mode and supports `resumeId`
325
+ - Supports `web_search`, `web_contents`, `web_answer`, and `web_research`
326
+ - `web_research` is exposed through pi's async research workflow
253
327
  - Web, proprietary, and news search types
254
- - Supports provider-native options such as `countryCode`, `responseLength`, and
328
+ - Supports provider-specific options such as `countryCode`, `responseLength`, and
255
329
  search/source filters
256
330
  - Configurable response length for answers and research
257
331
 
258
332
  </details>
259
333
 
260
- ### Custom CLI provider
334
+ ### Custom provider
261
335
 
262
- The `custom-cli` provider lets you bring your own wrapper command for any
336
+ The `custom` provider lets you bring your own wrapper command for any
263
337
  managed tool. Each capability can point at a different local command under
264
- `providers["custom-cli"].native`.
338
+ `providers["custom"].options`.
265
339
 
266
340
  The repo includes actual wrapper examples under
267
- [`examples/custom-cli/wrappers/`](examples/custom-cli/wrappers/). They are
341
+ [`examples/custom/wrappers/`](examples/custom/wrappers/). They are
268
342
  small bash scripts that use `jq` for JSON handling. Each one uses a different
269
343
  backend pattern:
270
344
 
@@ -281,15 +355,14 @@ Copy the example wrappers into a local `./wrappers/` directory, then configure:
281
355
  ```json
282
356
  {
283
357
  "tools": {
284
- "search": "custom-cli",
285
- "contents": "custom-cli",
286
- "answer": "custom-cli",
287
- "research": "custom-cli"
358
+ "search": "custom",
359
+ "contents": "custom",
360
+ "answer": "custom",
361
+ "research": "custom"
288
362
  },
289
363
  "providers": {
290
- "custom-cli": {
291
- "enabled": true,
292
- "native": {
364
+ "custom": {
365
+ "options": {
293
366
  "search": {
294
367
  "argv": ["bash", "./wrappers/codex-search.sh"]
295
368
  },
@@ -316,9 +389,9 @@ one wrapper must run from a specific directory. Use `env` for per-command
316
389
  variables; each value can be a literal string, an environment variable name, or
317
390
  `!command`.
318
391
 
319
- `web_research` runs as a foreground wrapper command, so local polling controls
320
- (`pollIntervalMs`, `timeoutMs`, `maxConsecutivePollErrors`) and `resumeId` do
321
- not apply to `custom-cli`.
392
+ `web_research` uses the same async workflow as every other research provider:
393
+ pi starts the wrapper in the background, tracks the job locally, and writes the
394
+ final report to a file when it finishes.
322
395
 
323
396
  Wrapper contract:
324
397
 
@@ -326,31 +399,51 @@ Wrapper contract:
326
399
  inputs (`query`, `urls`, `input`, `maxResults`, `options`, `cwd`)
327
400
  - `stdout`: one JSON response object
328
401
  - `search`: `{ "results": [{ "title", "url", "snippet" }] }`
329
- - `contents` / `answer` / `research`: `{ "text": "...", "summary"?: "...", "itemCount"?: 1, "metadata"?: {} }`
402
+ - `contents`: `{ "answers": [{ "url", "content"?: "...", "summary"?: unknown, "metadata"?: {}, "error"?: "..." }] }`
403
+ - `answer` / `research`: `{ "text": "...", "summary"?: "...", "itemCount"?: 1, "metadata"?: {} }`
330
404
  - `stderr`: optional progress lines
331
405
  - exit code `0`: success
332
406
  - non-zero exit code: failure
333
407
 
334
408
  </details>
335
409
 
336
- See [`examples/custom-cli/README.md`](examples/custom-cli/README.md) for a
410
+ See [`examples/custom/README.md`](examples/custom/README.md) for a
337
411
  copy-and-pasteable setup, and see
338
- [`examples/custom-cli/wrappers/`](examples/custom-cli/wrappers/) for the actual
412
+ [`examples/custom/wrappers/`](examples/custom/wrappers/) for the actual
339
413
  wrapper files.
340
414
 
341
- ### Generic settings
415
+ ### Settings
416
+
417
+ The `settings` block holds shared execution defaults that apply to all
418
+ providers unless overridden in a provider's own `settings` block:
419
+
420
+ | Field | Default | Description |
421
+ | ------------------- | --------- | ----------------------------------------------------------- |
422
+ | `requestTimeoutMs` | `30000` | Maximum time for a single provider request |
423
+ | `retryCount` | `3` | Retries for transient failures |
424
+ | `retryDelayMs` | `2000` | Initial delay before retrying |
425
+ | `researchTimeoutMs` | `1800000` | Maximum total time for an async `web_research` job (30 min) |
426
+
427
+ ## 🔎 Live smoke tests
428
+
429
+ Use the opt-in live smoke runner to validate the configured providers with the
430
+ same config-resolution and execution path the extension uses at runtime:
342
431
 
343
- The `genericSettings` block sets shared execution defaults that apply to all
344
- providers unless overridden in a provider's `policy` block:
432
+ ```bash
433
+ npm run smoke:live
434
+ ```
435
+
436
+ Optional filters:
437
+
438
+ ```bash
439
+ npm run smoke:live -- --provider gemini
440
+ npm run smoke:live -- --tool contents
441
+ npm run smoke:live -- --include-research
442
+ ```
345
443
 
346
- | Field | Default | Description |
347
- | ---------------------------------- | ---------- | ---------------------------------------------- |
348
- | `requestTimeoutMs` | `30000` | Maximum time for a single provider request |
349
- | `retryCount` | `3` | Retries for transient failures |
350
- | `retryDelayMs` | `2000` | Initial delay before retrying |
351
- | `researchPollIntervalMs` | `3000` | How often to poll long-running research jobs |
352
- | `researchTimeoutMs` | `21600000` | Overall deadline for research before returning |
353
- | `researchMaxConsecutivePollErrors` | `3` | Consecutive poll failures before stopping |
444
+ The default run exercises `search`, `contents`, and `answer`. Research probes
445
+ are excluded unless you pass `--include-research`, because they are slower and
446
+ may incur higher provider cost.
354
447
 
355
448
  ## 📄 License
356
449