pi-web-providers 2.0.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.
Files changed (3) hide show
  1. package/README.md +16 -1
  2. package/dist/index.js +252 -27
  3. package/package.json +20 -18
package/README.md CHANGED
@@ -13,7 +13,7 @@ off entirely.
13
13
  ## ✨ Features
14
14
 
15
15
  - **Multiple providers**: Claude, Cloudflare, Codex, Exa, Firecrawl,
16
- Gemini, Perplexity, Parallel, [Tavily](https://tavily.com), Valyu
16
+ Gemini, Linkup, Perplexity, Parallel, [Tavily](https://tavily.com), Valyu
17
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
19
  - **Async contents prefetch**: optionally start background `web_contents`
@@ -46,6 +46,7 @@ Each tool can be routed to any compatible provider:
46
46
  | **Exa** | ✔ | ✔ | ✔ | ✔ | `EXA_API_KEY` |
47
47
  | **Firecrawl** | ✔ | ✔ | | | `FIRECRAWL_API_KEY` |
48
48
  | **Gemini** | ✔ | | ✔ | ✔ | `GOOGLE_API_KEY` |
49
+ | **Linkup** | ✔ | ✔ | | | `LINKUP_API_KEY` |
49
50
  | **Perplexity** | ✔ | | ✔ | ✔ | `PERPLEXITY_API_KEY` |
50
51
  | **Parallel** | ✔ | ✔ | | | `PARALLEL_API_KEY` |
51
52
  | **Tavily** | ✔ | ✔ | | | `TAVILY_API_KEY` |
@@ -264,6 +265,20 @@ scope, or account ID is usually wrong.
264
265
 
265
266
  </details>
266
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
+
267
282
  <details>
268
283
  <summary><strong>Perplexity</strong></summary>
269
284
 
package/dist/index.js CHANGED
@@ -612,7 +612,7 @@ function $constructor(name, initializer3, params) {
612
612
  Object.defineProperty(_, "name", { value: name });
613
613
  return _;
614
614
  }
615
- var $brand = Symbol("zod_brand");
615
+ var $brand = /* @__PURE__ */ Symbol("zod_brand");
616
616
  var $ZodAsyncError = class extends Error {
617
617
  constructor() {
618
618
  super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);
@@ -759,7 +759,7 @@ function floatSafeRemainder(val, step) {
759
759
  const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
760
760
  return valInt % stepInt / 10 ** decCount;
761
761
  }
762
- var EVALUATING = Symbol("evaluating");
762
+ var EVALUATING = /* @__PURE__ */ Symbol("evaluating");
763
763
  function defineLazy(object2, key, getter) {
764
764
  let value = void 0;
765
765
  Object.defineProperty(object2, key, {
@@ -9832,8 +9832,8 @@ function yo_default() {
9832
9832
 
9833
9833
  // node_modules/zod/v4/core/registries.js
9834
9834
  var _a;
9835
- var $output = Symbol("ZodOutput");
9836
- var $input = Symbol("ZodInput");
9835
+ var $output = /* @__PURE__ */ Symbol("ZodOutput");
9836
+ var $input = /* @__PURE__ */ Symbol("ZodInput");
9837
9837
  var $ZodRegistry = class {
9838
9838
  constructor() {
9839
9839
  this._map = /* @__PURE__ */ new WeakMap();
@@ -13814,6 +13814,7 @@ var PROVIDER_TOOLS_BY_ID = {
13814
13814
  exa: ["search", "contents", "answer", "research"],
13815
13815
  firecrawl: ["search", "contents"],
13816
13816
  gemini: ["search", "answer", "research"],
13817
+ linkup: ["search", "contents"],
13817
13818
  perplexity: ["search", "answer", "research"],
13818
13819
  parallel: ["search", "contents"],
13819
13820
  tavily: ["search", "contents"],
@@ -13858,6 +13859,7 @@ var PROVIDER_IDS = [
13858
13859
  "exa",
13859
13860
  "firecrawl",
13860
13861
  "gemini",
13862
+ "linkup",
13861
13863
  "perplexity",
13862
13864
  "parallel",
13863
13865
  "tavily",
@@ -13933,6 +13935,16 @@ var geminiProviderSchema = external_exports.object({
13933
13935
  options: geminiOptionsSchema.optional(),
13934
13936
  settings: executionSettingsSchema.optional()
13935
13937
  }).strict();
13938
+ var linkupOptionsSchema = external_exports.object({
13939
+ search: jsonObjectSchema.optional(),
13940
+ fetch: jsonObjectSchema.optional()
13941
+ }).strict();
13942
+ var linkupProviderSchema = external_exports.object({
13943
+ apiKey: stringSchema.optional(),
13944
+ baseUrl: stringSchema.optional(),
13945
+ options: linkupOptionsSchema.optional(),
13946
+ settings: executionSettingsSchema.optional()
13947
+ }).strict();
13936
13948
  var perplexityOptionsSchema = external_exports.object({
13937
13949
  search: jsonObjectSchema.optional(),
13938
13950
  answer: jsonObjectSchema.optional(),
@@ -14110,6 +14122,7 @@ function normalizeConfig(raw, source) {
14110
14122
  exa: normalizeExaProvider,
14111
14123
  firecrawl: normalizeFirecrawlProvider,
14112
14124
  gemini: normalizeGeminiProvider,
14125
+ linkup: normalizeLinkupProvider,
14113
14126
  perplexity: normalizePerplexityProvider,
14114
14127
  parallel: normalizeParallelProvider,
14115
14128
  tavily: normalizeTavilyProvider,
@@ -14160,6 +14173,9 @@ function normalizeValyuProvider(raw, source) {
14160
14173
  function normalizeGeminiProvider(raw, source) {
14161
14174
  return parseProviderWithSchema(raw, source, "gemini", geminiProviderSchema);
14162
14175
  }
14176
+ function normalizeLinkupProvider(raw, source) {
14177
+ return parseProviderWithSchema(raw, source, "linkup", linkupProviderSchema);
14178
+ }
14163
14179
  function normalizePerplexityProvider(raw, source) {
14164
14180
  return parseProviderWithSchema(
14165
14181
  raw,
@@ -16965,6 +16981,185 @@ function readNonEmptyString3(value) {
16965
16981
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
16966
16982
  }
16967
16983
 
16984
+ // src/providers/linkup.ts
16985
+ import {
16986
+ LinkupClient
16987
+ } from "linkup-sdk";
16988
+ var linkupAdapter = {
16989
+ id: "linkup",
16990
+ label: "Linkup",
16991
+ docsUrl: "https://docs.linkup.so/pages/sdk/js/js",
16992
+ tools: ["search", "contents"],
16993
+ createTemplate() {
16994
+ return {
16995
+ apiKey: "LINKUP_API_KEY"
16996
+ };
16997
+ },
16998
+ getCapabilityStatus(config2) {
16999
+ return getApiKeyStatus(config2?.apiKey);
17000
+ },
17001
+ buildPlan(request, config2) {
17002
+ return buildProviderPlan({
17003
+ request,
17004
+ config: config2,
17005
+ providerId: linkupAdapter.id,
17006
+ providerLabel: linkupAdapter.label,
17007
+ handlers: {
17008
+ search: {
17009
+ execute: (searchRequest, providerConfig, context) => linkupAdapter.search(
17010
+ searchRequest.query,
17011
+ searchRequest.maxResults,
17012
+ providerConfig,
17013
+ context,
17014
+ searchRequest.options
17015
+ )
17016
+ },
17017
+ contents: {
17018
+ execute: (contentsRequest, providerConfig, context) => linkupAdapter.contents(
17019
+ contentsRequest.urls,
17020
+ providerConfig,
17021
+ context,
17022
+ contentsRequest.options
17023
+ )
17024
+ }
17025
+ }
17026
+ });
17027
+ },
17028
+ async search(query2, maxResults, config2, _context, options) {
17029
+ const client = createClient4(config2);
17030
+ const defaults = stripLocalExecutionOptions(asJsonObject(config2.options?.search)) ?? {};
17031
+ const response = await client.search(
17032
+ buildSearchParams(query2, maxResults, {
17033
+ ...defaults,
17034
+ ...stripLocalExecutionOptions(options) ?? {}
17035
+ })
17036
+ );
17037
+ return {
17038
+ provider: linkupAdapter.id,
17039
+ results: (response.results ?? []).map(toSearchResult2).filter((result) => result !== null).slice(0, maxResults)
17040
+ };
17041
+ },
17042
+ async contents(urls, config2, _context, options) {
17043
+ const client = createClient4(config2);
17044
+ const defaults = stripLocalExecutionOptions(asJsonObject(config2.options?.fetch)) ?? {};
17045
+ return {
17046
+ provider: linkupAdapter.id,
17047
+ answers: await Promise.all(
17048
+ urls.map(async (url2) => {
17049
+ try {
17050
+ const response = await client.fetch(
17051
+ buildFetchParams(url2, {
17052
+ ...defaults,
17053
+ ...stripLocalExecutionOptions(options) ?? {}
17054
+ })
17055
+ );
17056
+ return response.markdown ? {
17057
+ url: url2,
17058
+ content: response.markdown
17059
+ } : {
17060
+ url: url2,
17061
+ error: "No content returned for this URL."
17062
+ };
17063
+ } catch (error48) {
17064
+ return {
17065
+ url: url2,
17066
+ error: error48 instanceof Error ? error48.message : String(error48)
17067
+ };
17068
+ }
17069
+ })
17070
+ )
17071
+ };
17072
+ }
17073
+ };
17074
+ function buildSearchParams(query2, maxResults, options) {
17075
+ const searchOptions = options;
17076
+ if (searchOptions.query !== void 0) {
17077
+ throw new Error("Linkup search options cannot override the managed query.");
17078
+ }
17079
+ if (searchOptions.maxResults !== void 0) {
17080
+ throw new Error(
17081
+ "Linkup search options cannot override the managed maxResults."
17082
+ );
17083
+ }
17084
+ if (searchOptions.outputType !== void 0 && searchOptions.outputType !== "searchResults") {
17085
+ throw new Error("Linkup search only supports outputType 'searchResults'.");
17086
+ }
17087
+ if (searchOptions.includeInlineCitations !== void 0 || searchOptions.includeSources !== void 0 || searchOptions.structuredOutputSchema !== void 0) {
17088
+ throw new Error(
17089
+ "Linkup search only supports search-results mode for managed web_search."
17090
+ );
17091
+ }
17092
+ return {
17093
+ query: query2,
17094
+ depth: searchOptions.depth ?? "standard",
17095
+ outputType: "searchResults",
17096
+ maxResults,
17097
+ ...searchOptions.includeImages !== void 0 ? { includeImages: searchOptions.includeImages } : {},
17098
+ ...searchOptions.includeDomains !== void 0 ? { includeDomains: searchOptions.includeDomains } : {},
17099
+ ...searchOptions.excludeDomains !== void 0 ? { excludeDomains: searchOptions.excludeDomains } : {},
17100
+ ...searchOptions.fromDate !== void 0 ? { fromDate: toDate(searchOptions.fromDate, "fromDate") } : {},
17101
+ ...searchOptions.toDate !== void 0 ? { toDate: toDate(searchOptions.toDate, "toDate") } : {}
17102
+ };
17103
+ }
17104
+ function buildFetchParams(url2, options) {
17105
+ const fetchOptions = options;
17106
+ if (fetchOptions.url !== void 0) {
17107
+ throw new Error("Linkup fetch options cannot override the managed URL.");
17108
+ }
17109
+ return {
17110
+ url: url2,
17111
+ ...fetchOptions.renderJs !== void 0 ? { renderJs: fetchOptions.renderJs } : {},
17112
+ ...fetchOptions.includeRawHtml !== void 0 ? { includeRawHtml: fetchOptions.includeRawHtml } : {},
17113
+ ...fetchOptions.extractImages !== void 0 ? { extractImages: fetchOptions.extractImages } : {}
17114
+ };
17115
+ }
17116
+ function createClient4(config2) {
17117
+ const apiKey = resolveConfigValue(config2.apiKey);
17118
+ if (!apiKey) {
17119
+ throw new Error("is missing an API key");
17120
+ }
17121
+ return new LinkupClient({
17122
+ apiKey,
17123
+ baseUrl: resolveConfigValue(config2.baseUrl)
17124
+ });
17125
+ }
17126
+ function toSearchResult2(value) {
17127
+ const entry = asRecord2(value);
17128
+ if (!entry) {
17129
+ return null;
17130
+ }
17131
+ const url2 = readString3(entry.url) ?? "";
17132
+ const title = readString3(entry.name) ?? (url2 || "Untitled");
17133
+ const type = readString3(entry.type);
17134
+ const favicon = readString3(entry.favicon);
17135
+ const snippet = type === "text" ? trimSnippet(readString3(entry.content) ?? "") : "";
17136
+ const metadata = {
17137
+ ...type ? { type } : {},
17138
+ ...favicon ? { favicon } : {}
17139
+ };
17140
+ return {
17141
+ title,
17142
+ url: url2,
17143
+ snippet,
17144
+ metadata: Object.keys(metadata).length > 0 ? metadata : void 0
17145
+ };
17146
+ }
17147
+ function asRecord2(value) {
17148
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
17149
+ }
17150
+ function readString3(value) {
17151
+ return typeof value === "string" ? value : void 0;
17152
+ }
17153
+ function toDate(value, name) {
17154
+ const date5 = value instanceof Date ? value : new Date(value);
17155
+ if (Number.isNaN(date5.getTime())) {
17156
+ throw new Error(
17157
+ `Linkup option '${name}' must be a valid date string, timestamp, or Date.`
17158
+ );
17159
+ }
17160
+ return date5;
17161
+ }
17162
+
16968
17163
  // src/providers/parallel.ts
16969
17164
  import ParallelClient from "parallel-web";
16970
17165
  var parallelAdapter = {
@@ -17017,7 +17212,7 @@ var parallelAdapter = {
17017
17212
  });
17018
17213
  },
17019
17214
  async search(query2, maxResults, config2, context, options) {
17020
- const client = createClient4(config2);
17215
+ const client = createClient5(config2);
17021
17216
  const defaults = stripLocalExecutionOptions(asJsonObject(config2.options?.search)) ?? {};
17022
17217
  const response = await client.beta.search(
17023
17218
  {
@@ -17038,7 +17233,7 @@ var parallelAdapter = {
17038
17233
  };
17039
17234
  },
17040
17235
  async contents(urls, config2, context, options) {
17041
- const client = createClient4(config2);
17236
+ const client = createClient5(config2);
17042
17237
  const defaults = stripLocalExecutionOptions(asJsonObject(config2.options?.extract)) ?? {};
17043
17238
  const response = await client.beta.extract(
17044
17239
  {
@@ -17077,7 +17272,7 @@ var parallelAdapter = {
17077
17272
  };
17078
17273
  }
17079
17274
  };
17080
- function createClient4(config2) {
17275
+ function createClient5(config2) {
17081
17276
  const apiKey = resolveConfigValue(config2.apiKey);
17082
17277
  if (!apiKey) {
17083
17278
  throw new Error("is missing an API key");
@@ -17152,7 +17347,7 @@ var perplexityAdapter = {
17152
17347
  });
17153
17348
  },
17154
17349
  async search(query2, maxResults, config2, context, options) {
17155
- const client = createClient5(config2);
17350
+ const client = createClient6(config2);
17156
17351
  const request = {
17157
17352
  ...stripLocalExecutionOptions(asJsonObject(config2.options?.search)) ?? {},
17158
17353
  ...options ?? {},
@@ -17198,7 +17393,7 @@ var perplexityAdapter = {
17198
17393
  }
17199
17394
  };
17200
17395
  async function runSilentForegroundChatTool(input, config2, context, fallbackModel, label, options, isResearch = false) {
17201
- const client = createClient5(config2);
17396
+ const client = createClient6(config2);
17202
17397
  const defaults = stripLocalExecutionOptions(
17203
17398
  isResearch ? asJsonObject(config2.options?.research) : asJsonObject(config2.options?.answer)
17204
17399
  ) ?? {};
@@ -17232,7 +17427,7 @@ async function runSilentForegroundChatTool(input, config2, context, fallbackMode
17232
17427
  };
17233
17428
  }
17234
17429
  async function runStreamingForegroundChatTool(input, config2, context, fallbackModel, label, options) {
17235
- const client = createClient5(config2);
17430
+ const client = createClient6(config2);
17236
17431
  const defaults = stripLocalExecutionOptions(asJsonObject(config2.options?.research)) ?? {};
17237
17432
  const request = {
17238
17433
  ...defaults,
@@ -17273,7 +17468,7 @@ async function runStreamingForegroundChatTool(input, config2, context, fallbackM
17273
17468
  itemCount: dedupedSources.length
17274
17469
  };
17275
17470
  }
17276
- function createClient5(config2) {
17471
+ function createClient6(config2) {
17277
17472
  const apiKey = resolveConfigValue(config2.apiKey);
17278
17473
  if (!apiKey) {
17279
17474
  throw new Error("is missing an API key");
@@ -17408,7 +17603,7 @@ var tavilyAdapter = {
17408
17603
  });
17409
17604
  },
17410
17605
  async search(query2, maxResults, config2, _context, options) {
17411
- const client = createClient6(config2);
17606
+ const client = createClient7(config2);
17412
17607
  const defaults = stripLocalExecutionOptions(asJsonObject(config2.options?.search)) ?? {};
17413
17608
  const response = await client.search(query2, {
17414
17609
  ...defaults,
@@ -17427,7 +17622,7 @@ var tavilyAdapter = {
17427
17622
  };
17428
17623
  },
17429
17624
  async contents(urls, config2, _context, options) {
17430
- const client = createClient6(config2);
17625
+ const client = createClient7(config2);
17431
17626
  const defaults = stripLocalExecutionOptions(asJsonObject(config2.options?.extract)) ?? {};
17432
17627
  const response = await client.extract(urls, {
17433
17628
  ...defaults,
@@ -17465,7 +17660,7 @@ var tavilyAdapter = {
17465
17660
  };
17466
17661
  }
17467
17662
  };
17468
- function createClient6(config2) {
17663
+ function createClient7(config2) {
17469
17664
  const apiKey = resolveConfigValue(config2.apiKey);
17470
17665
  if (!apiKey) {
17471
17666
  throw new Error("is missing an API key");
@@ -17559,7 +17754,7 @@ var valyuAdapter = {
17559
17754
  });
17560
17755
  },
17561
17756
  async search(query2, maxResults, config2, _context, searchOptions) {
17562
- const client = createClient7(config2);
17757
+ const client = createClient8(config2);
17563
17758
  const options = {
17564
17759
  ...stripLocalExecutionOptions(asJsonObject(config2.options)) ?? {},
17565
17760
  ...searchOptions ?? {},
@@ -17582,7 +17777,7 @@ var valyuAdapter = {
17582
17777
  };
17583
17778
  },
17584
17779
  async contents(urls, config2, _context, options) {
17585
- const client = createClient7(config2);
17780
+ const client = createClient8(config2);
17586
17781
  const response = await client.contents(urls, options);
17587
17782
  const finalResponse = "jobId" in response ? await client.waitForJob(response.jobId, {}) : response;
17588
17783
  if (!finalResponse.success) {
@@ -17616,7 +17811,7 @@ var valyuAdapter = {
17616
17811
  };
17617
17812
  },
17618
17813
  async answer(query2, config2, _context, options) {
17619
- const client = createClient7(config2);
17814
+ const client = createClient8(config2);
17620
17815
  const response = await client.answer(query2, {
17621
17816
  ...options ?? {},
17622
17817
  streaming: false
@@ -17654,7 +17849,7 @@ var valyuAdapter = {
17654
17849
  });
17655
17850
  },
17656
17851
  async startResearch(input, config2, _context, options) {
17657
- const client = createClient7(config2);
17852
+ const client = createClient8(config2);
17658
17853
  const task = await client.deepresearch.create({
17659
17854
  input,
17660
17855
  ...options ?? {}
@@ -17665,7 +17860,7 @@ var valyuAdapter = {
17665
17860
  return { id: task.deepresearch_id };
17666
17861
  },
17667
17862
  async pollResearch(id, config2, _context, _options) {
17668
- const client = createClient7(config2);
17863
+ const client = createClient8(config2);
17669
17864
  const result = await client.deepresearch.status(id);
17670
17865
  if (!result.success) {
17671
17866
  throw new Error(result.error || "deep research failed");
@@ -17708,7 +17903,7 @@ var valyuAdapter = {
17708
17903
  return { status: "in_progress" };
17709
17904
  }
17710
17905
  };
17711
- function createClient7(config2) {
17906
+ function createClient8(config2) {
17712
17907
  const apiKey = resolveConfigValue(config2.apiKey);
17713
17908
  if (!apiKey) {
17714
17909
  throw new Error("is missing an API key");
@@ -17725,6 +17920,7 @@ var ADAPTERS_BY_ID = {
17725
17920
  exa: exaAdapter,
17726
17921
  firecrawl: firecrawlAdapter,
17727
17922
  gemini: geminiAdapter,
17923
+ linkup: linkupAdapter,
17728
17924
  perplexity: perplexityAdapter,
17729
17925
  parallel: parallelAdapter,
17730
17926
  tavily: tavilyAdapter,
@@ -18846,7 +19042,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
18846
19042
  "deep-reasoning",
18847
19043
  "deep-max"
18848
19044
  ],
18849
- getValue: (config2) => readString3(getExaOptions(config2)?.type) ?? "default",
19045
+ getValue: (config2) => readString4(getExaOptions(config2)?.type) ?? "default",
18850
19046
  setValue: (config2, value) => {
18851
19047
  const options = ensureExaOptions(config2);
18852
19048
  if (value === "default") {
@@ -18950,6 +19146,13 @@ var PROVIDER_CONFIG_MANIFESTS = {
18950
19146
  })
18951
19147
  ]
18952
19148
  },
19149
+ linkup: {
19150
+ settings: [
19151
+ apiKeySetting(),
19152
+ baseUrlSetting(),
19153
+ ...requestSettings("linkup")
19154
+ ]
19155
+ },
18953
19156
  perplexity: {
18954
19157
  settings: [apiKeySetting(), baseUrlSetting()]
18955
19158
  },
@@ -18962,7 +19165,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
18962
19165
  label: "Search mode",
18963
19166
  help: "Parallel search mode. 'default' uses the SDK default.",
18964
19167
  values: ["default", "agentic", "one-shot"],
18965
- getValue: (config2) => readString3(getParallelOptions(config2)?.search?.mode) ?? "default",
19168
+ getValue: (config2) => readString4(getParallelOptions(config2)?.search?.mode) ?? "default",
18966
19169
  setValue: (config2, value) => {
18967
19170
  const options = ensureParallelOptions(config2);
18968
19171
  options.search = asJsonObject2(options.search) ?? {};
@@ -19032,7 +19235,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
19032
19235
  label: "Search type",
19033
19236
  help: "Valyu search type. 'default' uses the SDK default.",
19034
19237
  values: ["default", "all", "web", "proprietary", "news"],
19035
- getValue: (config2) => readString3(getValyuOptions(config2)?.searchType) ?? "default",
19238
+ getValue: (config2) => readString4(getValyuOptions(config2)?.searchType) ?? "default",
19036
19239
  setValue: (config2, value) => {
19037
19240
  const options = ensureValyuOptions(config2);
19038
19241
  if (value === "default") {
@@ -19048,7 +19251,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
19048
19251
  label: "Response length",
19049
19252
  help: "Valyu response length. 'default' uses the SDK default.",
19050
19253
  values: ["default", "short", "medium", "large", "max"],
19051
- getValue: (config2) => readString3(getValyuOptions(config2)?.responseLength) ?? "default",
19254
+ getValue: (config2) => readString4(getValyuOptions(config2)?.responseLength) ?? "default",
19052
19255
  setValue: (config2, value) => {
19053
19256
  const options = ensureValyuOptions(config2);
19054
19257
  if (value === "default") {
@@ -19212,7 +19415,7 @@ function getIntegerString(value) {
19212
19415
  function getBooleanValue(value) {
19213
19416
  return typeof value === "boolean" ? String(value) : "default";
19214
19417
  }
19215
- function readString3(value) {
19418
+ function readString4(value) {
19216
19419
  return typeof value === "string" ? value : void 0;
19217
19420
  }
19218
19421
  function asJsonObject2(value) {
@@ -19477,7 +19680,7 @@ function webProvidersExtension(pi) {
19477
19680
  latestWidgetContext = ctx;
19478
19681
  resetContentStore();
19479
19682
  updateWebResearchWidget(ctx);
19480
- await refreshManagedTools(
19683
+ await refreshManagedToolsOnStartup(
19481
19684
  pi,
19482
19685
  { activeWebResearchRequests, updateWebResearchWidget },
19483
19686
  ctx.cwd,
@@ -19492,7 +19695,7 @@ function webProvidersExtension(pi) {
19492
19695
  latestWidgetContext = ctx;
19493
19696
  await cleanupContentStore();
19494
19697
  updateWebResearchWidget(ctx);
19495
- await refreshManagedTools(
19698
+ await refreshManagedToolsOnStartup(
19496
19699
  pi,
19497
19700
  { activeWebResearchRequests, updateWebResearchWidget },
19498
19701
  ctx.cwd,
@@ -19722,6 +19925,10 @@ async function runWebProvidersConfig(pi, webResearchLifecycle, ctx) {
19722
19925
  addAvailable: true
19723
19926
  });
19724
19927
  }
19928
+ function formatStartupConfigError(error48) {
19929
+ const detail = error48 instanceof Error ? error48.message : String(error48);
19930
+ return `web-providers config error: ${detail.replace(getConfigPath(), "~/.pi/agent/web-providers.json")}`;
19931
+ }
19725
19932
  function getAvailableProviderIdsForCapability(config2, cwd, capability) {
19726
19933
  const providerId = getMappedProviderIdForTool(config2, capability);
19727
19934
  if (!providerId) {
@@ -19772,6 +19979,24 @@ async function refreshManagedTools(pi, webResearchLifecycle, cwd, options) {
19772
19979
  });
19773
19980
  await syncManagedToolAvailability(pi, nextActiveTools);
19774
19981
  }
19982
+ async function refreshManagedToolsOnStartup(pi, webResearchLifecycle, cwd, options) {
19983
+ try {
19984
+ await refreshManagedTools(pi, webResearchLifecycle, cwd, options);
19985
+ } catch (error48) {
19986
+ const message = formatStartupConfigError(error48);
19987
+ pi.sendMessage({
19988
+ customType: "web-providers-config-error",
19989
+ content: message,
19990
+ display: true
19991
+ });
19992
+ await syncManagedToolAvailability(
19993
+ pi,
19994
+ new Set(
19995
+ pi.getActiveTools().filter((toolName) => !MANAGED_TOOL_NAMES.includes(toolName))
19996
+ )
19997
+ );
19998
+ }
19999
+ }
19775
20000
  async function syncManagedToolAvailability(pi, nextActiveTools) {
19776
20001
  const activeTools = pi.getActiveTools();
19777
20002
  const changed = activeTools.length !== nextActiveTools.size || activeTools.some((toolName) => !nextActiveTools.has(toolName));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-providers",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Configurable web access extension for pi with per-tool provider routing for search, contents, answers, and research.",
5
5
  "type": "module",
6
6
  "files": [
@@ -22,6 +22,7 @@
22
22
  "exa",
23
23
  "firecrawl",
24
24
  "gemini",
25
+ "linkup",
25
26
  "perplexity",
26
27
  "parallel",
27
28
  "tavily",
@@ -45,7 +46,7 @@
45
46
  ]
46
47
  },
47
48
  "scripts": {
48
- "build": "rm -rf dist && esbuild src/index.ts --bundle --format=esm --platform=node --outfile=dist/index.js --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-ai --external:@mariozechner/pi-tui --external:@sinclair/typebox --external:@anthropic-ai/claude-agent-sdk --external:@google/genai --external:@mendable/firecrawl-js --external:@openai/codex-sdk --external:@perplexity-ai/perplexity_ai --external:@tavily/core --external:cloudflare --external:exa-js --external:parallel-web --external:valyu-js",
49
+ "build": "rm -rf dist && esbuild src/index.ts --bundle --format=esm --platform=node --outfile=dist/index.js --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-ai --external:@mariozechner/pi-tui --external:@sinclair/typebox --external:@anthropic-ai/claude-agent-sdk --external:@google/genai --external:@mendable/firecrawl-js --external:@openai/codex-sdk --external:@perplexity-ai/perplexity_ai --external:@tavily/core --external:cloudflare --external:exa-js --external:linkup-sdk --external:parallel-web --external:valyu-js",
49
50
  "prepare": "npm run build",
50
51
  "prepack": "npm run build",
51
52
  "check": "tsc --noEmit",
@@ -56,26 +57,27 @@
56
57
  "test:watch": "vitest"
57
58
  },
58
59
  "dependencies": {
59
- "@anthropic-ai/claude-agent-sdk": "^0.2.71",
60
- "@google/genai": "^1.44.0",
60
+ "@anthropic-ai/claude-agent-sdk": "^0.2.89",
61
+ "@google/genai": "^1.47.0",
61
62
  "@mendable/firecrawl-js": "^4.18.1",
62
- "@openai/codex-sdk": "^0.111.0",
63
- "@perplexity-ai/perplexity_ai": "^0.26.1",
63
+ "@openai/codex-sdk": "^0.118.0",
64
+ "@perplexity-ai/perplexity_ai": "^0.26.5",
64
65
  "@tavily/core": "^0.7.2",
65
66
  "cloudflare": "^5.2.0",
66
- "exa-js": "^2.7.0",
67
- "parallel-web": "^0.3.1",
68
- "valyu-js": "^2.5.9",
69
- "zod": "^4.1.11"
67
+ "exa-js": "^2.10.2",
68
+ "linkup-sdk": "^2.7.0",
69
+ "parallel-web": "^0.3.2",
70
+ "valyu-js": "^2.7.6",
71
+ "zod": "^4.3.6"
70
72
  },
71
73
  "devDependencies": {
72
- "@biomejs/biome": "^2.4.6",
73
- "@mariozechner/pi-ai": "*",
74
- "@mariozechner/pi-coding-agent": "*",
75
- "@sinclair/typebox": "*",
76
- "@types/node": "^24.3.0",
77
- "esbuild": "^0.25.10",
78
- "typescript": "^5.9.3",
79
- "vitest": "^4.0.18"
74
+ "@biomejs/biome": "^2.4.10",
75
+ "@mariozechner/pi-ai": "^0.64.0",
76
+ "@mariozechner/pi-coding-agent": "^0.64.0",
77
+ "@sinclair/typebox": "^0.34.49",
78
+ "@types/node": "^25.5.0",
79
+ "esbuild": "^0.27.4",
80
+ "typescript": "^6.0.2",
81
+ "vitest": "^4.1.2"
80
82
  }
81
83
  }