pi-web-providers 2.2.0 → 2.3.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 +77 -11
  2. package/dist/index.js +792 -207
  3. package/package.json +5 -3
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  visibleWidth,
24
24
  wrapTextWithAnsi
25
25
  } from "@mariozechner/pi-tui";
26
- import { Type as Type14 } from "@sinclair/typebox";
26
+ import { Type as Type15 } from "@sinclair/typebox";
27
27
 
28
28
  // src/config.ts
29
29
  import { execSync } from "node:child_process";
@@ -41,8 +41,9 @@ var PROVIDER_TOOLS_BY_ID = {
41
41
  firecrawl: ["search", "contents"],
42
42
  gemini: ["search", "answer", "research"],
43
43
  linkup: ["search", "contents"],
44
- perplexity: ["search", "answer", "research"],
44
+ openai: ["search", "answer", "research"],
45
45
  parallel: ["search", "contents"],
46
+ perplexity: ["search", "answer", "research"],
46
47
  tavily: ["search", "contents"],
47
48
  valyu: ["search", "contents", "answer", "research"]
48
49
  };
@@ -86,8 +87,9 @@ var PROVIDER_IDS = [
86
87
  "firecrawl",
87
88
  "gemini",
88
89
  "linkup",
89
- "perplexity",
90
+ "openai",
90
91
  "parallel",
92
+ "perplexity",
91
93
  "tavily",
92
94
  "valyu"
93
95
  ];
@@ -271,6 +273,18 @@ function normalizeProvider(providerId, raw, source) {
271
273
  options: readOptionalObject,
272
274
  settings: parseOptionalExecutionSettings
273
275
  });
276
+ case "openai":
277
+ return parseProviderWithShape(raw, source, providerId, {
278
+ apiKey: readOptionalString,
279
+ baseUrl: readOptionalString,
280
+ options: (value, innerSource, field) => parseOptionalCapabilityOptions(
281
+ value,
282
+ innerSource,
283
+ field,
284
+ ["search", "answer", "research"]
285
+ ),
286
+ settings: parseOptionalExecutionSettings
287
+ });
274
288
  case "firecrawl":
275
289
  case "linkup":
276
290
  case "parallel":
@@ -4082,12 +4096,528 @@ function toDate(value, name) {
4082
4096
  return date;
4083
4097
  }
4084
4098
 
4085
- // src/providers/parallel.ts
4099
+ // src/providers/openai.ts
4086
4100
  import { Type as Type10 } from "@sinclair/typebox";
4101
+ import OpenAI from "openai";
4102
+ var DEFAULT_SEARCH_MODEL2 = "gpt-4.1";
4103
+ var DEFAULT_ANSWER_MODEL2 = "gpt-4.1";
4104
+ var DEFAULT_RESEARCH_MODEL = "o4-mini-deep-research";
4105
+ var openaiSearchOptionsSchema = Type10.Object(
4106
+ {
4107
+ model: Type10.Optional(
4108
+ Type10.String({
4109
+ description: "OpenAI model to use for web search (for example 'gpt-4.1')."
4110
+ })
4111
+ ),
4112
+ instructions: Type10.Optional(
4113
+ Type10.String({
4114
+ description: "Optional instructions that shape source selection and result style."
4115
+ })
4116
+ )
4117
+ },
4118
+ { description: "OpenAI search options." }
4119
+ );
4120
+ var openaiAnswerOptionsSchema = Type10.Object(
4121
+ {
4122
+ model: Type10.Optional(
4123
+ Type10.String({
4124
+ description: "OpenAI model to use for grounded answers (for example 'gpt-4.1')."
4125
+ })
4126
+ ),
4127
+ instructions: Type10.Optional(
4128
+ Type10.String({
4129
+ description: "Optional instructions that shape the answer structure, tone, and source selection."
4130
+ })
4131
+ )
4132
+ },
4133
+ { description: "OpenAI answer options." }
4134
+ );
4135
+ var openaiResearchOptionsSchema = Type10.Object(
4136
+ {
4137
+ model: Type10.Optional(
4138
+ Type10.String({
4139
+ description: "OpenAI deep research model to use (for example 'o4-mini-deep-research')."
4140
+ })
4141
+ ),
4142
+ instructions: Type10.Optional(
4143
+ Type10.String({
4144
+ description: "Optional instructions that shape the report structure, tone, and source selection."
4145
+ })
4146
+ ),
4147
+ max_tool_calls: Type10.Optional(
4148
+ Type10.Integer({
4149
+ minimum: 1,
4150
+ description: "Maximum number of built-in tool calls the model may make during the research run."
4151
+ })
4152
+ )
4153
+ },
4154
+ { description: "OpenAI deep research options." }
4155
+ );
4156
+ var searchResultSchema = {
4157
+ type: "object",
4158
+ additionalProperties: false,
4159
+ required: ["sources"],
4160
+ properties: {
4161
+ sources: {
4162
+ type: "array",
4163
+ items: {
4164
+ type: "object",
4165
+ additionalProperties: false,
4166
+ required: ["title", "url", "snippet"],
4167
+ properties: {
4168
+ title: { type: "string" },
4169
+ url: { type: "string" },
4170
+ snippet: { type: "string" }
4171
+ }
4172
+ }
4173
+ }
4174
+ }
4175
+ };
4176
+ var openaiAdapter = {
4177
+ id: "openai",
4178
+ label: "OpenAI",
4179
+ docsUrl: "https://platform.openai.com/docs/guides/deep-research",
4180
+ tools: ["search", "answer", "research"],
4181
+ getToolOptionsSchema(capability) {
4182
+ switch (capability) {
4183
+ case "search":
4184
+ return openaiSearchOptionsSchema;
4185
+ case "answer":
4186
+ return openaiAnswerOptionsSchema;
4187
+ case "research":
4188
+ return openaiResearchOptionsSchema;
4189
+ default:
4190
+ return void 0;
4191
+ }
4192
+ },
4193
+ createTemplate() {
4194
+ return {
4195
+ apiKey: "OPENAI_API_KEY",
4196
+ options: {
4197
+ search: {
4198
+ model: DEFAULT_SEARCH_MODEL2
4199
+ },
4200
+ answer: {
4201
+ model: DEFAULT_ANSWER_MODEL2
4202
+ },
4203
+ research: {
4204
+ model: DEFAULT_RESEARCH_MODEL
4205
+ }
4206
+ }
4207
+ };
4208
+ },
4209
+ getConfigForCapability(capability, config) {
4210
+ switch (capability) {
4211
+ case "search":
4212
+ return {
4213
+ apiKey: config.apiKey,
4214
+ baseUrl: config.baseUrl,
4215
+ options: config.options?.search,
4216
+ settings: config.settings
4217
+ };
4218
+ case "answer":
4219
+ return {
4220
+ apiKey: config.apiKey,
4221
+ baseUrl: config.baseUrl,
4222
+ options: config.options?.answer,
4223
+ settings: config.settings
4224
+ };
4225
+ case "research":
4226
+ return {
4227
+ apiKey: config.apiKey,
4228
+ baseUrl: config.baseUrl,
4229
+ options: config.options?.research,
4230
+ settings: config.settings
4231
+ };
4232
+ default:
4233
+ return config;
4234
+ }
4235
+ },
4236
+ getCapabilityStatus(config) {
4237
+ return getApiKeyStatus(config?.apiKey);
4238
+ },
4239
+ buildPlan(request, config) {
4240
+ return buildProviderPlan({
4241
+ request,
4242
+ config,
4243
+ providerId: openaiAdapter.id,
4244
+ providerLabel: openaiAdapter.label,
4245
+ handlers: {
4246
+ search: {
4247
+ execute: (searchRequest, providerConfig, context) => openaiAdapter.search(
4248
+ searchRequest.query,
4249
+ searchRequest.maxResults,
4250
+ providerConfig,
4251
+ context,
4252
+ searchRequest.options
4253
+ )
4254
+ },
4255
+ answer: {
4256
+ execute: (answerRequest, providerConfig, context) => openaiAdapter.answer(
4257
+ answerRequest.query,
4258
+ providerConfig,
4259
+ context,
4260
+ answerRequest.options
4261
+ )
4262
+ },
4263
+ research: {
4264
+ execute: (researchRequest, providerConfig, context) => openaiAdapter.research(
4265
+ researchRequest.input,
4266
+ providerConfig,
4267
+ context,
4268
+ researchRequest.options
4269
+ )
4270
+ }
4271
+ }
4272
+ });
4273
+ },
4274
+ async search(query2, maxResults, config, context, options) {
4275
+ const client = createClient5(config);
4276
+ const response = await client.responses.create(
4277
+ buildOpenAISearchRequest(query2, maxResults, config, options),
4278
+ buildRequestOptions2(context.signal, context.idempotencyKey)
4279
+ );
4280
+ return parseSearchResponse2(response, maxResults);
4281
+ },
4282
+ async answer(query2, config, context, options) {
4283
+ const client = createClient5(config);
4284
+ const response = await client.responses.create(
4285
+ buildOpenAIAnswerRequest(query2, config, options),
4286
+ buildRequestOptions2(context.signal, context.idempotencyKey)
4287
+ );
4288
+ return ensureCompletedResponse(response, "answer");
4289
+ },
4290
+ async research(input, config, context, options) {
4291
+ return await executeAsyncResearch({
4292
+ providerLabel: openaiAdapter.label,
4293
+ providerId: openaiAdapter.id,
4294
+ context,
4295
+ start: (researchContext) => openaiAdapter.startResearch(input, config, researchContext, options),
4296
+ poll: (id, researchContext) => openaiAdapter.pollResearch(id, config, researchContext, options)
4297
+ });
4298
+ },
4299
+ async startResearch(input, config, context, options) {
4300
+ const client = createClient5(config);
4301
+ const response = await client.responses.create(
4302
+ buildOpenAIResearchRequest(input, config, options),
4303
+ buildRequestOptions2(context.signal, context.idempotencyKey)
4304
+ );
4305
+ return { id: response.id };
4306
+ },
4307
+ async pollResearch(id, config, context, _options) {
4308
+ const client = createClient5(config);
4309
+ const response = await client.responses.retrieve(
4310
+ id,
4311
+ void 0,
4312
+ buildRequestOptions2(context.signal)
4313
+ );
4314
+ const status = response.status ?? "completed";
4315
+ if (status === "completed") {
4316
+ return {
4317
+ status: "completed",
4318
+ output: formatResponseOutput(response, "research")
4319
+ };
4320
+ }
4321
+ if (status === "failed") {
4322
+ return {
4323
+ status: "failed",
4324
+ error: response.error?.message ?? "research failed"
4325
+ };
4326
+ }
4327
+ if (status === "cancelled") {
4328
+ return {
4329
+ status: "cancelled",
4330
+ error: "research was canceled"
4331
+ };
4332
+ }
4333
+ if (status === "incomplete") {
4334
+ return {
4335
+ status: "failed",
4336
+ error: formatIncompleteError(response, "research")
4337
+ };
4338
+ }
4339
+ return {
4340
+ status: "in_progress",
4341
+ statusText: status
4342
+ };
4343
+ }
4344
+ };
4345
+ function createClient5(config) {
4346
+ const apiKey = resolveConfigValue(config.apiKey);
4347
+ if (!apiKey) {
4348
+ throw new Error("is missing an API key");
4349
+ }
4350
+ const baseUrl = resolveConfigValue(config.baseUrl);
4351
+ return new OpenAI({
4352
+ apiKey,
4353
+ ...baseUrl ? { baseURL: baseUrl } : {}
4354
+ });
4355
+ }
4356
+ function buildOpenAISearchRequest(query2, maxResults, config, options) {
4357
+ const mergedOptions = resolveOpenAISearchOptions(config, options);
4358
+ const model = mergedOptions.model ?? DEFAULT_SEARCH_MODEL2;
4359
+ const instructions = mergedOptions.instructions;
4360
+ return {
4361
+ model,
4362
+ input: [
4363
+ "Search the public web and return only the most relevant sources for the user's query.",
4364
+ `Return at most ${maxResults} sources.`,
4365
+ "Prefer official, primary, or highly reputable sources when available.",
4366
+ "Each snippet should be short, specific, and grounded in the retrieved source.",
4367
+ "Return only data matching the provided JSON schema.",
4368
+ "",
4369
+ `User query: ${query2}`
4370
+ ].join("\n"),
4371
+ tools: [{ type: "web_search_preview" }],
4372
+ text: {
4373
+ format: {
4374
+ type: "json_schema",
4375
+ name: "openai_web_search_results",
4376
+ schema: searchResultSchema,
4377
+ strict: true
4378
+ }
4379
+ },
4380
+ ...instructions ? { instructions } : {}
4381
+ };
4382
+ }
4383
+ function buildOpenAIAnswerRequest(query2, config, options) {
4384
+ const mergedOptions = resolveOpenAIAnswerOptions(config, options);
4385
+ const model = mergedOptions.model ?? DEFAULT_ANSWER_MODEL2;
4386
+ const instructions = mergedOptions.instructions;
4387
+ return {
4388
+ model,
4389
+ input: query2,
4390
+ tools: [{ type: "web_search_preview" }],
4391
+ ...instructions ? { instructions } : {}
4392
+ };
4393
+ }
4394
+ function buildOpenAIResearchRequest(input, config, options) {
4395
+ const mergedOptions = resolveOpenAIResearchOptions(config, options);
4396
+ const model = mergedOptions.model ?? DEFAULT_RESEARCH_MODEL;
4397
+ const instructions = mergedOptions.instructions;
4398
+ const maxToolCalls = mergedOptions.max_tool_calls;
4399
+ return {
4400
+ model,
4401
+ input,
4402
+ background: true,
4403
+ tools: [{ type: "web_search_preview" }],
4404
+ ...instructions ? { instructions } : {},
4405
+ ...maxToolCalls ? { max_tool_calls: maxToolCalls } : {}
4406
+ };
4407
+ }
4408
+ function resolveOpenAISearchOptions(config, options) {
4409
+ const mergedOptions = {
4410
+ ...config.options?.search ?? {},
4411
+ ...options ?? {}
4412
+ };
4413
+ const model = readNonEmptyString4(mergedOptions.model);
4414
+ const instructions = readNonEmptyString4(mergedOptions.instructions);
4415
+ return {
4416
+ ...model ? { model } : {},
4417
+ ...instructions ? { instructions } : {}
4418
+ };
4419
+ }
4420
+ function resolveOpenAIAnswerOptions(config, options) {
4421
+ const mergedOptions = {
4422
+ ...config.options?.answer ?? {},
4423
+ ...options ?? {}
4424
+ };
4425
+ const model = readNonEmptyString4(mergedOptions.model);
4426
+ const instructions = readNonEmptyString4(mergedOptions.instructions);
4427
+ return {
4428
+ ...model ? { model } : {},
4429
+ ...instructions ? { instructions } : {}
4430
+ };
4431
+ }
4432
+ function resolveOpenAIResearchOptions(config, options) {
4433
+ const mergedOptions = {
4434
+ ...config.options?.research ?? {},
4435
+ ...options ?? {}
4436
+ };
4437
+ const model = readNonEmptyString4(mergedOptions.model);
4438
+ const instructions = readNonEmptyString4(mergedOptions.instructions);
4439
+ const maxToolCalls = readPositiveInteger2(mergedOptions.max_tool_calls);
4440
+ return {
4441
+ ...model ? { model } : {},
4442
+ ...instructions ? { instructions } : {},
4443
+ ...maxToolCalls ? { max_tool_calls: maxToolCalls } : {}
4444
+ };
4445
+ }
4446
+ function buildRequestOptions2(signal, idempotencyKey) {
4447
+ if (!signal && !idempotencyKey) {
4448
+ return void 0;
4449
+ }
4450
+ return {
4451
+ ...signal ? { signal } : {},
4452
+ ...idempotencyKey ? { idempotencyKey } : {}
4453
+ };
4454
+ }
4455
+ function parseSearchResponse2(response, maxResults) {
4456
+ const status = response.status ?? "completed";
4457
+ if (status === "failed") {
4458
+ throw new Error(response.error?.message ?? "search failed");
4459
+ }
4460
+ if (status === "cancelled") {
4461
+ throw new Error("search was canceled");
4462
+ }
4463
+ if (status === "incomplete") {
4464
+ throw new Error(formatIncompleteError(response, "search"));
4465
+ }
4466
+ if (status !== "completed") {
4467
+ throw new Error(`search did not complete (status: ${status})`);
4468
+ }
4469
+ const payload = parseSearchPayload(response.output_text);
4470
+ return {
4471
+ provider: openaiAdapter.id,
4472
+ results: payload.sources.slice(0, maxResults).map((source) => ({
4473
+ title: source.title.trim(),
4474
+ url: source.url.trim(),
4475
+ snippet: trimSnippet(source.snippet)
4476
+ }))
4477
+ };
4478
+ }
4479
+ function ensureCompletedResponse(response, operation) {
4480
+ const status = response.status ?? "completed";
4481
+ if (status === "completed") {
4482
+ return formatResponseOutput(response, operation);
4483
+ }
4484
+ if (status === "failed") {
4485
+ throw new Error(response.error?.message ?? `${operation} failed`);
4486
+ }
4487
+ if (status === "cancelled") {
4488
+ throw new Error(`${operation} was canceled`);
4489
+ }
4490
+ if (status === "incomplete") {
4491
+ throw new Error(formatIncompleteError(response, operation));
4492
+ }
4493
+ throw new Error(`${operation} did not complete (status: ${status})`);
4494
+ }
4495
+ function formatResponseOutput(response, operation) {
4496
+ const lines = [];
4497
+ lines.push(
4498
+ response.output_text?.trim() || `OpenAI ${operation} completed without textual output.`
4499
+ );
4500
+ const citations = extractUrlCitations(response);
4501
+ if (citations.length > 0) {
4502
+ lines.push("");
4503
+ lines.push("Sources:");
4504
+ for (const [index, citation] of citations.entries()) {
4505
+ lines.push(`${index + 1}. ${citation.title}`);
4506
+ lines.push(` ${citation.url}`);
4507
+ }
4508
+ }
4509
+ return {
4510
+ provider: openaiAdapter.id,
4511
+ text: lines.join("\n").trimEnd(),
4512
+ itemCount: citations.length,
4513
+ metadata: {
4514
+ responseId: response.id,
4515
+ model: response.model,
4516
+ citations
4517
+ }
4518
+ };
4519
+ }
4520
+ function extractUrlCitations(response) {
4521
+ const citations = [];
4522
+ const seen = /* @__PURE__ */ new Set();
4523
+ for (const item of response.output) {
4524
+ if (item.type !== "message" || !item.content) {
4525
+ continue;
4526
+ }
4527
+ for (const content of item.content) {
4528
+ if (content.type !== "output_text" || !content.annotations) {
4529
+ continue;
4530
+ }
4531
+ for (const annotation of content.annotations) {
4532
+ if (annotation.type !== "url_citation") {
4533
+ continue;
4534
+ }
4535
+ const title = readNonEmptyString4(annotation.title);
4536
+ const url = readNonEmptyString4(annotation.url);
4537
+ const startIndex = readInteger(annotation.start_index);
4538
+ const endIndex = readInteger(annotation.end_index);
4539
+ if (!title || !url || startIndex === void 0 || endIndex === void 0) {
4540
+ continue;
4541
+ }
4542
+ const citation = {
4543
+ title,
4544
+ url,
4545
+ startIndex,
4546
+ endIndex
4547
+ };
4548
+ const key = [
4549
+ citation.title,
4550
+ citation.url,
4551
+ String(citation.startIndex),
4552
+ String(citation.endIndex)
4553
+ ].join("::");
4554
+ if (seen.has(key)) {
4555
+ continue;
4556
+ }
4557
+ seen.add(key);
4558
+ citations.push(citation);
4559
+ }
4560
+ }
4561
+ }
4562
+ return citations;
4563
+ }
4564
+ function parseSearchPayload(text) {
4565
+ let parsed;
4566
+ try {
4567
+ parsed = JSON.parse(text ?? "");
4568
+ } catch (error) {
4569
+ throw new Error(
4570
+ `search returned invalid JSON: ${error.message}`
4571
+ );
4572
+ }
4573
+ if (typeof parsed !== "object" || parsed === null || !("sources" in parsed) || !Array.isArray(parsed.sources)) {
4574
+ throw new Error("search output must include a 'sources' array");
4575
+ }
4576
+ return {
4577
+ sources: parsed.sources.map((source, index) => {
4578
+ if (typeof source !== "object" || source === null) {
4579
+ throw new Error(`search source at index ${index} must be an object`);
4580
+ }
4581
+ const entry = source;
4582
+ const title = readNonEmptyString4(entry.title);
4583
+ const url = readNonEmptyString4(entry.url);
4584
+ const snippet = readNonEmptyString4(entry.snippet);
4585
+ if (!title) {
4586
+ throw new Error(`search source at index ${index} is missing title`);
4587
+ }
4588
+ if (!url) {
4589
+ throw new Error(`search source at index ${index} is missing url`);
4590
+ }
4591
+ if (!snippet) {
4592
+ throw new Error(`search source at index ${index} is missing snippet`);
4593
+ }
4594
+ return { title, url, snippet };
4595
+ })
4596
+ };
4597
+ }
4598
+ function formatIncompleteError(response, operation) {
4599
+ const reason = response.incomplete_details?.reason;
4600
+ if (reason) {
4601
+ return `${operation} ended incomplete (${reason})`;
4602
+ }
4603
+ return `${operation} ended incomplete`;
4604
+ }
4605
+ function readNonEmptyString4(value) {
4606
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
4607
+ }
4608
+ function readPositiveInteger2(value) {
4609
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
4610
+ }
4611
+ function readInteger(value) {
4612
+ return typeof value === "number" && Number.isInteger(value) ? value : void 0;
4613
+ }
4614
+
4615
+ // src/providers/parallel.ts
4616
+ import { Type as Type11 } from "@sinclair/typebox";
4087
4617
  import ParallelClient from "parallel-web";
4088
- var parallelSearchOptionsSchema = Type10.Object(
4618
+ var parallelSearchOptionsSchema = Type11.Object(
4089
4619
  {
4090
- mode: Type10.Optional(
4620
+ mode: Type11.Optional(
4091
4621
  literalUnion(["agentic", "one-shot"], {
4092
4622
  description: "Parallel search mode."
4093
4623
  })
@@ -4095,13 +4625,13 @@ var parallelSearchOptionsSchema = Type10.Object(
4095
4625
  },
4096
4626
  { description: "Parallel search options." }
4097
4627
  );
4098
- var parallelExtractOptionsSchema = Type10.Object(
4628
+ var parallelExtractOptionsSchema = Type11.Object(
4099
4629
  {
4100
- excerpts: Type10.Optional(
4101
- Type10.Boolean({ description: "Include excerpts in extraction results." })
4630
+ excerpts: Type11.Optional(
4631
+ Type11.Boolean({ description: "Include excerpts in extraction results." })
4102
4632
  ),
4103
- full_content: Type10.Optional(
4104
- Type10.Boolean({
4633
+ full_content: Type11.Optional(
4634
+ Type11.Boolean({
4105
4635
  description: "Include full page content in extraction results."
4106
4636
  })
4107
4637
  )
@@ -4188,7 +4718,7 @@ var parallelAdapter = {
4188
4718
  });
4189
4719
  },
4190
4720
  async search(query2, maxResults, config, context, options) {
4191
- const client = createClient5(config);
4721
+ const client = createClient6(config);
4192
4722
  const defaults = stripLocalExecutionOptions(asJsonObject(config.options?.search)) ?? {};
4193
4723
  const response = await client.beta.search(
4194
4724
  {
@@ -4197,7 +4727,7 @@ var parallelAdapter = {
4197
4727
  objective: query2,
4198
4728
  max_results: maxResults
4199
4729
  },
4200
- buildRequestOptions2(context)
4730
+ buildRequestOptions3(context)
4201
4731
  );
4202
4732
  return {
4203
4733
  provider: parallelAdapter.id,
@@ -4209,7 +4739,7 @@ var parallelAdapter = {
4209
4739
  };
4210
4740
  },
4211
4741
  async contents(urls, config, context, options) {
4212
- const client = createClient5(config);
4742
+ const client = createClient6(config);
4213
4743
  const defaults = stripLocalExecutionOptions(asJsonObject(config.options?.extract)) ?? {};
4214
4744
  const response = await client.beta.extract(
4215
4745
  {
@@ -4217,7 +4747,7 @@ var parallelAdapter = {
4217
4747
  ...options ?? {},
4218
4748
  urls
4219
4749
  },
4220
- buildRequestOptions2(context)
4750
+ buildRequestOptions3(context)
4221
4751
  );
4222
4752
  const resultsByUrl = new Map(
4223
4753
  response.results.map((result) => [result.url, result])
@@ -4248,7 +4778,7 @@ var parallelAdapter = {
4248
4778
  };
4249
4779
  }
4250
4780
  };
4251
- function createClient5(config) {
4781
+ function createClient6(config) {
4252
4782
  const apiKey = resolveConfigValue(config.apiKey);
4253
4783
  if (!apiKey) {
4254
4784
  throw new Error("is missing an API key");
@@ -4258,48 +4788,48 @@ function createClient5(config) {
4258
4788
  baseURL: resolveConfigValue(config.baseUrl)
4259
4789
  });
4260
4790
  }
4261
- function buildRequestOptions2(context) {
4791
+ function buildRequestOptions3(context) {
4262
4792
  return context.signal ? { signal: context.signal } : void 0;
4263
4793
  }
4264
4794
 
4265
4795
  // src/providers/perplexity.ts
4266
4796
  import PerplexityClient from "@perplexity-ai/perplexity_ai";
4267
- import { Type as Type11 } from "@sinclair/typebox";
4268
- var DEFAULT_ANSWER_MODEL2 = "sonar";
4269
- var DEFAULT_RESEARCH_MODEL = "sonar-deep-research";
4270
- var perplexitySearchOptionsSchema = Type11.Object(
4797
+ import { Type as Type12 } from "@sinclair/typebox";
4798
+ var DEFAULT_ANSWER_MODEL3 = "sonar";
4799
+ var DEFAULT_RESEARCH_MODEL2 = "sonar-deep-research";
4800
+ var perplexitySearchOptionsSchema = Type12.Object(
4271
4801
  {
4272
- country: Type11.Optional(
4273
- Type11.String({ description: "Country hint for search results." })
4802
+ country: Type12.Optional(
4803
+ Type12.String({ description: "Country hint for search results." })
4274
4804
  ),
4275
- search_mode: Type11.Optional(
4276
- Type11.String({ description: "Perplexity search mode." })
4805
+ search_mode: Type12.Optional(
4806
+ Type12.String({ description: "Perplexity search mode." })
4277
4807
  ),
4278
- search_domain_filter: Type11.Optional(
4279
- Type11.Array(Type11.String(), {
4808
+ search_domain_filter: Type12.Optional(
4809
+ Type12.Array(Type12.String(), {
4280
4810
  description: "Restrict search results to these domains."
4281
4811
  })
4282
4812
  ),
4283
- search_recency_filter: Type11.Optional(
4284
- Type11.String({ description: "Recency filter for search results." })
4813
+ search_recency_filter: Type12.Optional(
4814
+ Type12.String({ description: "Recency filter for search results." })
4285
4815
  )
4286
4816
  },
4287
4817
  { description: "Perplexity search options." }
4288
4818
  );
4289
- var perplexityAnswerOptionsSchema = Type11.Object(
4819
+ var perplexityAnswerOptionsSchema = Type12.Object(
4290
4820
  {
4291
- model: Type11.Optional(
4292
- Type11.String({
4821
+ model: Type12.Optional(
4822
+ Type12.String({
4293
4823
  description: "Perplexity model to use (for example 'sonar' or 'sonar-pro')."
4294
4824
  })
4295
4825
  )
4296
4826
  },
4297
4827
  { description: "Perplexity answer options." }
4298
4828
  );
4299
- var perplexityResearchOptionsSchema = Type11.Object(
4829
+ var perplexityResearchOptionsSchema = Type12.Object(
4300
4830
  {
4301
- model: Type11.Optional(
4302
- Type11.String({
4831
+ model: Type12.Optional(
4832
+ Type12.String({
4303
4833
  description: "Perplexity model to use (for example 'sonar-deep-research')."
4304
4834
  })
4305
4835
  )
@@ -4328,10 +4858,10 @@ var perplexityAdapter = {
4328
4858
  apiKey: "PERPLEXITY_API_KEY",
4329
4859
  options: {
4330
4860
  answer: {
4331
- model: DEFAULT_ANSWER_MODEL2
4861
+ model: DEFAULT_ANSWER_MODEL3
4332
4862
  },
4333
4863
  research: {
4334
- model: DEFAULT_RESEARCH_MODEL
4864
+ model: DEFAULT_RESEARCH_MODEL2
4335
4865
  }
4336
4866
  }
4337
4867
  };
@@ -4375,7 +4905,7 @@ var perplexityAdapter = {
4375
4905
  });
4376
4906
  },
4377
4907
  async search(query2, maxResults, config, context, options) {
4378
- const client = createClient6(config);
4908
+ const client = createClient7(config);
4379
4909
  const request = {
4380
4910
  ...stripLocalExecutionOptions(asJsonObject(config.options?.search)) ?? {},
4381
4911
  ...options ?? {},
@@ -4384,7 +4914,7 @@ var perplexityAdapter = {
4384
4914
  };
4385
4915
  const response = await client.search.create(
4386
4916
  request,
4387
- buildRequestOptions3(context)
4917
+ buildRequestOptions4(context)
4388
4918
  );
4389
4919
  return {
4390
4920
  provider: perplexityAdapter.id,
@@ -4404,7 +4934,7 @@ var perplexityAdapter = {
4404
4934
  query2,
4405
4935
  config,
4406
4936
  context,
4407
- DEFAULT_ANSWER_MODEL2,
4937
+ DEFAULT_ANSWER_MODEL3,
4408
4938
  "Answer",
4409
4939
  options
4410
4940
  );
@@ -4414,14 +4944,14 @@ var perplexityAdapter = {
4414
4944
  input,
4415
4945
  config,
4416
4946
  context,
4417
- DEFAULT_RESEARCH_MODEL,
4947
+ DEFAULT_RESEARCH_MODEL2,
4418
4948
  "Research",
4419
4949
  options
4420
4950
  );
4421
4951
  }
4422
4952
  };
4423
4953
  async function runSilentForegroundChatTool(input, config, context, fallbackModel, label, options, isResearch = false) {
4424
- const client = createClient6(config);
4954
+ const client = createClient7(config);
4425
4955
  const defaults = stripLocalExecutionOptions(
4426
4956
  isResearch ? asJsonObject(config.options?.research) : asJsonObject(config.options?.answer)
4427
4957
  ) ?? {};
@@ -4434,7 +4964,7 @@ async function runSilentForegroundChatTool(input, config, context, fallbackModel
4434
4964
  };
4435
4965
  const response = await client.chat.completions.create(
4436
4966
  request,
4437
- buildRequestOptions3(context)
4967
+ buildRequestOptions4(context)
4438
4968
  );
4439
4969
  const content = extractMessageText(response.choices[0]?.message?.content);
4440
4970
  const sources = dedupeSources(extractSources(response));
@@ -4455,7 +4985,7 @@ async function runSilentForegroundChatTool(input, config, context, fallbackModel
4455
4985
  };
4456
4986
  }
4457
4987
  async function runStreamingForegroundChatTool(input, config, context, fallbackModel, label, options) {
4458
- const client = createClient6(config);
4988
+ const client = createClient7(config);
4459
4989
  const defaults = stripLocalExecutionOptions(asJsonObject(config.options?.research)) ?? {};
4460
4990
  const request = {
4461
4991
  ...defaults,
@@ -4466,7 +4996,7 @@ async function runStreamingForegroundChatTool(input, config, context, fallbackMo
4466
4996
  };
4467
4997
  const stream = await client.chat.completions.create(
4468
4998
  request,
4469
- buildRequestOptions3(context)
4999
+ buildRequestOptions4(context)
4470
5000
  );
4471
5001
  let partialText = "";
4472
5002
  let lastChunk;
@@ -4496,7 +5026,7 @@ async function runStreamingForegroundChatTool(input, config, context, fallbackMo
4496
5026
  itemCount: dedupedSources.length
4497
5027
  };
4498
5028
  }
4499
- function createClient6(config) {
5029
+ function createClient7(config) {
4500
5030
  const apiKey = resolveConfigValue(config.apiKey);
4501
5031
  if (!apiKey) {
4502
5032
  throw new Error("is missing an API key");
@@ -4573,60 +5103,60 @@ function extractSources(response) {
4573
5103
  return url ? [{ title: url, url }] : [];
4574
5104
  }) ?? [];
4575
5105
  }
4576
- function buildRequestOptions3(context) {
5106
+ function buildRequestOptions4(context) {
4577
5107
  return context.signal ? { signal: context.signal } : void 0;
4578
5108
  }
4579
5109
 
4580
5110
  // src/providers/tavily.ts
4581
- import { Type as Type12 } from "@sinclair/typebox";
5111
+ import { Type as Type13 } from "@sinclair/typebox";
4582
5112
  import {
4583
5113
  tavily
4584
5114
  } from "@tavily/core";
4585
- var tavilySearchOptionsSchema = Type12.Object(
5115
+ var tavilySearchOptionsSchema = Type13.Object(
4586
5116
  {
4587
- topic: Type12.Optional(
5117
+ topic: Type13.Optional(
4588
5118
  literalUnion(["general", "news", "finance"], {
4589
5119
  description: "Category of the search query."
4590
5120
  })
4591
5121
  ),
4592
- searchDepth: Type12.Optional(
5122
+ searchDepth: Type13.Optional(
4593
5123
  literalUnion(["basic", "advanced"], {
4594
5124
  description: "Depth of the search. 'advanced' is slower but more thorough."
4595
5125
  })
4596
5126
  ),
4597
- timeRange: Type12.Optional(
4598
- Type12.String({ description: "Named time range filter." })
5127
+ timeRange: Type13.Optional(
5128
+ Type13.String({ description: "Named time range filter." })
4599
5129
  ),
4600
- country: Type12.Optional(
4601
- Type12.String({ description: "Country hint for search results." })
5130
+ country: Type13.Optional(
5131
+ Type13.String({ description: "Country hint for search results." })
4602
5132
  ),
4603
- exactMatch: Type12.Optional(
4604
- Type12.Boolean({ description: "Prefer exact matches." })
5133
+ exactMatch: Type13.Optional(
5134
+ Type13.Boolean({ description: "Prefer exact matches." })
4605
5135
  ),
4606
- includeAnswer: Type12.Optional(
4607
- Type12.Boolean({ description: "Include a short AI-generated answer." })
5136
+ includeAnswer: Type13.Optional(
5137
+ Type13.Boolean({ description: "Include a short AI-generated answer." })
4608
5138
  ),
4609
- includeRawContent: Type12.Optional(
4610
- Type12.Boolean({ description: "Include raw page content in results." })
5139
+ includeRawContent: Type13.Optional(
5140
+ Type13.Boolean({ description: "Include raw page content in results." })
4611
5141
  ),
4612
- includeImages: Type12.Optional(
4613
- Type12.Boolean({ description: "Include related images." })
5142
+ includeImages: Type13.Optional(
5143
+ Type13.Boolean({ description: "Include related images." })
4614
5144
  ),
4615
- includeFavicon: Type12.Optional(
4616
- Type12.Boolean({ description: "Include favicon URLs." })
5145
+ includeFavicon: Type13.Optional(
5146
+ Type13.Boolean({ description: "Include favicon URLs." })
4617
5147
  ),
4618
- includeDomains: Type12.Optional(
4619
- Type12.Array(Type12.String(), {
5148
+ includeDomains: Type13.Optional(
5149
+ Type13.Array(Type13.String(), {
4620
5150
  description: "Restrict results to these domains."
4621
5151
  })
4622
5152
  ),
4623
- excludeDomains: Type12.Optional(
4624
- Type12.Array(Type12.String(), {
5153
+ excludeDomains: Type13.Optional(
5154
+ Type13.Array(Type13.String(), {
4625
5155
  description: "Exclude these domains from results."
4626
5156
  })
4627
5157
  ),
4628
- days: Type12.Optional(
4629
- Type12.Integer({
5158
+ days: Type13.Optional(
5159
+ Type13.Integer({
4630
5160
  minimum: 1,
4631
5161
  description: "Limit results to the last N days."
4632
5162
  })
@@ -4634,27 +5164,27 @@ var tavilySearchOptionsSchema = Type12.Object(
4634
5164
  },
4635
5165
  { description: "Tavily search options." }
4636
5166
  );
4637
- var tavilyExtractOptionsSchema = Type12.Object(
5167
+ var tavilyExtractOptionsSchema = Type13.Object(
4638
5168
  {
4639
- extractDepth: Type12.Optional(
4640
- Type12.String({ description: "Depth setting for extraction." })
5169
+ extractDepth: Type13.Optional(
5170
+ Type13.String({ description: "Depth setting for extraction." })
4641
5171
  ),
4642
- format: Type12.Optional(
5172
+ format: Type13.Optional(
4643
5173
  literalUnion(["markdown", "text"], {
4644
5174
  description: "Output format for extracted content."
4645
5175
  })
4646
5176
  ),
4647
- includeImages: Type12.Optional(
4648
- Type12.Boolean({ description: "Include extracted images." })
5177
+ includeImages: Type13.Optional(
5178
+ Type13.Boolean({ description: "Include extracted images." })
4649
5179
  ),
4650
- query: Type12.Optional(
4651
- Type12.String({ description: "Optional query to focus extraction." })
5180
+ query: Type13.Optional(
5181
+ Type13.String({ description: "Optional query to focus extraction." })
4652
5182
  ),
4653
- chunksPerSource: Type12.Optional(
4654
- Type12.Integer({ minimum: 1, description: "Maximum chunks per source." })
5183
+ chunksPerSource: Type13.Optional(
5184
+ Type13.Integer({ minimum: 1, description: "Maximum chunks per source." })
4655
5185
  ),
4656
- includeFavicon: Type12.Optional(
4657
- Type12.Boolean({ description: "Include favicon URLs." })
5186
+ includeFavicon: Type13.Optional(
5187
+ Type13.Boolean({ description: "Include favicon URLs." })
4658
5188
  )
4659
5189
  },
4660
5190
  { description: "Tavily extract options." }
@@ -4739,7 +5269,7 @@ var tavilyAdapter = {
4739
5269
  });
4740
5270
  },
4741
5271
  async search(query2, maxResults, config, _context, options) {
4742
- const client = createClient7(config);
5272
+ const client = createClient8(config);
4743
5273
  const defaults = stripLocalExecutionOptions(asJsonObject(config.options?.search)) ?? {};
4744
5274
  const response = await client.search(query2, {
4745
5275
  ...defaults,
@@ -4758,7 +5288,7 @@ var tavilyAdapter = {
4758
5288
  };
4759
5289
  },
4760
5290
  async contents(urls, config, _context, options) {
4761
- const client = createClient7(config);
5291
+ const client = createClient8(config);
4762
5292
  const defaults = stripLocalExecutionOptions(asJsonObject(config.options?.extract)) ?? {};
4763
5293
  const response = await client.extract(urls, {
4764
5294
  ...defaults,
@@ -4796,7 +5326,7 @@ var tavilyAdapter = {
4796
5326
  };
4797
5327
  }
4798
5328
  };
4799
- function createClient7(config) {
5329
+ function createClient8(config) {
4800
5330
  const apiKey = resolveConfigValue(config.apiKey);
4801
5331
  if (!apiKey) {
4802
5332
  throw new Error("is missing an API key");
@@ -4828,48 +5358,48 @@ function buildExtractMetadata(response, result) {
4828
5358
  }
4829
5359
 
4830
5360
  // src/providers/valyu.ts
4831
- import { Type as Type13 } from "@sinclair/typebox";
5361
+ import { Type as Type14 } from "@sinclair/typebox";
4832
5362
  import { Valyu as ValyuClient } from "valyu-js";
4833
- var valyuSearchOptionsSchema = Type13.Object(
5363
+ var valyuSearchOptionsSchema = Type14.Object(
4834
5364
  {
4835
- searchType: Type13.Optional(
5365
+ searchType: Type14.Optional(
4836
5366
  literalUnion(["all", "web", "proprietary", "news"], {
4837
5367
  description: "Valyu search type."
4838
5368
  })
4839
5369
  ),
4840
- responseLength: Type13.Optional(
5370
+ responseLength: Type14.Optional(
4841
5371
  literalUnion(["short", "medium", "large", "max"], {
4842
5372
  description: "Response length."
4843
5373
  })
4844
5374
  ),
4845
- countryCode: Type13.Optional(
4846
- Type13.String({ description: "Country code to scope search results." })
5375
+ countryCode: Type14.Optional(
5376
+ Type14.String({ description: "Country code to scope search results." })
4847
5377
  )
4848
5378
  },
4849
5379
  { description: "Valyu search options." }
4850
5380
  );
4851
- var valyuAnswerOptionsSchema = Type13.Object(
5381
+ var valyuAnswerOptionsSchema = Type14.Object(
4852
5382
  {
4853
- responseLength: Type13.Optional(
5383
+ responseLength: Type14.Optional(
4854
5384
  literalUnion(["short", "medium", "large", "max"], {
4855
5385
  description: "Response length for answers."
4856
5386
  })
4857
5387
  ),
4858
- countryCode: Type13.Optional(
4859
- Type13.String({ description: "Country code to scope answer results." })
5388
+ countryCode: Type14.Optional(
5389
+ Type14.String({ description: "Country code to scope answer results." })
4860
5390
  )
4861
5391
  },
4862
5392
  { description: "Valyu answer options." }
4863
5393
  );
4864
- var valyuResearchOptionsSchema = Type13.Object(
5394
+ var valyuResearchOptionsSchema = Type14.Object(
4865
5395
  {
4866
- responseLength: Type13.Optional(
5396
+ responseLength: Type14.Optional(
4867
5397
  literalUnion(["short", "medium", "large", "max"], {
4868
5398
  description: "Response length for research."
4869
5399
  })
4870
5400
  ),
4871
- countryCode: Type13.Optional(
4872
- Type13.String({ description: "Country code to scope research results." })
5401
+ countryCode: Type14.Optional(
5402
+ Type14.String({ description: "Country code to scope research results." })
4873
5403
  )
4874
5404
  },
4875
5405
  { description: "Valyu research options." }
@@ -4982,7 +5512,7 @@ var valyuAdapter = {
4982
5512
  });
4983
5513
  },
4984
5514
  async search(query2, maxResults, config, _context, searchOptions) {
4985
- const client = createClient8(config);
5515
+ const client = createClient9(config);
4986
5516
  const options = {
4987
5517
  ...stripLocalExecutionOptions(asJsonObject(config.options?.search)) ?? {},
4988
5518
  ...searchOptions ?? {},
@@ -5005,7 +5535,7 @@ var valyuAdapter = {
5005
5535
  };
5006
5536
  },
5007
5537
  async contents(urls, config, _context, options) {
5008
- const client = createClient8(config);
5538
+ const client = createClient9(config);
5009
5539
  const response = await client.contents(urls, options);
5010
5540
  const finalResponse = "jobId" in response ? await client.waitForJob(response.jobId, {}) : response;
5011
5541
  if (!finalResponse.success) {
@@ -5039,7 +5569,7 @@ var valyuAdapter = {
5039
5569
  };
5040
5570
  },
5041
5571
  async answer(query2, config, _context, options) {
5042
- const client = createClient8(config);
5572
+ const client = createClient9(config);
5043
5573
  const response = await client.answer(query2, {
5044
5574
  ...stripLocalExecutionOptions(asJsonObject(config.options?.answer)) ?? {},
5045
5575
  ...options ?? {},
@@ -5078,7 +5608,7 @@ var valyuAdapter = {
5078
5608
  });
5079
5609
  },
5080
5610
  async startResearch(input, config, _context, options) {
5081
- const client = createClient8(config);
5611
+ const client = createClient9(config);
5082
5612
  const task = await client.deepresearch.create({
5083
5613
  input,
5084
5614
  ...stripLocalExecutionOptions(asJsonObject(config.options?.research)) ?? {},
@@ -5090,7 +5620,7 @@ var valyuAdapter = {
5090
5620
  return { id: task.deepresearch_id };
5091
5621
  },
5092
5622
  async pollResearch(id, config, _context, _options) {
5093
- const client = createClient8(config);
5623
+ const client = createClient9(config);
5094
5624
  const result = await client.deepresearch.status(id);
5095
5625
  if (!result.success) {
5096
5626
  throw new Error(result.error || "deep research failed");
@@ -5133,7 +5663,7 @@ var valyuAdapter = {
5133
5663
  return { status: "in_progress" };
5134
5664
  }
5135
5665
  };
5136
- function createClient8(config) {
5666
+ function createClient9(config) {
5137
5667
  const apiKey = resolveConfigValue(config.apiKey);
5138
5668
  if (!apiKey) {
5139
5669
  throw new Error("is missing an API key");
@@ -5144,15 +5674,16 @@ function createClient8(config) {
5144
5674
  // src/providers/index.ts
5145
5675
  var ADAPTERS_BY_ID = {
5146
5676
  claude: claudeAdapter,
5147
- cloudflare: cloudflareAdapter,
5148
5677
  codex: codexAdapter,
5678
+ cloudflare: cloudflareAdapter,
5149
5679
  custom: customAdapter,
5150
5680
  exa: exaAdapter,
5151
5681
  firecrawl: firecrawlAdapter,
5152
5682
  gemini: geminiAdapter,
5153
5683
  linkup: linkupAdapter,
5154
- perplexity: perplexityAdapter,
5684
+ openai: openaiAdapter,
5155
5685
  parallel: parallelAdapter,
5686
+ perplexity: perplexityAdapter,
5156
5687
  tavily: tavilyAdapter,
5157
5688
  valyu: valyuAdapter
5158
5689
  };
@@ -6226,8 +6757,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
6226
6757
  setValue: (config, value) => {
6227
6758
  setCustomEnv(config, "research", value);
6228
6759
  }
6229
- }),
6230
- ...requestSettings("custom")
6760
+ })
6231
6761
  ]
6232
6762
  },
6233
6763
  exa: {
@@ -6355,10 +6885,111 @@ var PROVIDER_CONFIG_MANIFESTS = {
6355
6885
  ]
6356
6886
  },
6357
6887
  linkup: {
6888
+ settings: [apiKeySetting(), baseUrlSetting()]
6889
+ },
6890
+ openai: {
6358
6891
  settings: [
6359
6892
  apiKeySetting(),
6360
6893
  baseUrlSetting(),
6361
- ...requestSettings("linkup")
6894
+ stringSetting({
6895
+ id: "openaiSearchModel",
6896
+ label: "Search model",
6897
+ help: "Model used for OpenAI web search runs.",
6898
+ getValue: (config) => getOpenAISearchOptions(config)?.model,
6899
+ setValue: (config, value) => {
6900
+ assignOptionalString(
6901
+ ensureOpenAISearchOptions(config),
6902
+ "model",
6903
+ value
6904
+ );
6905
+ cleanupCapabilityOptions(config, ["search", "answer", "research"]);
6906
+ }
6907
+ }),
6908
+ stringSetting({
6909
+ id: "openaiSearchInstructions",
6910
+ label: "Search instructions",
6911
+ help: "Optional default instructions for OpenAI web search runs.",
6912
+ getValue: (config) => getOpenAISearchOptions(config)?.instructions,
6913
+ setValue: (config, value) => {
6914
+ assignOptionalString(
6915
+ ensureOpenAISearchOptions(config),
6916
+ "instructions",
6917
+ value
6918
+ );
6919
+ cleanupCapabilityOptions(config, ["search", "answer", "research"]);
6920
+ }
6921
+ }),
6922
+ stringSetting({
6923
+ id: "openaiAnswerModel",
6924
+ label: "Answer model",
6925
+ help: "Model used for OpenAI grounded answers.",
6926
+ getValue: (config) => getOpenAIAnswerOptions(config)?.model,
6927
+ setValue: (config, value) => {
6928
+ assignOptionalString(
6929
+ ensureOpenAIAnswerOptions(config),
6930
+ "model",
6931
+ value
6932
+ );
6933
+ cleanupCapabilityOptions(config, ["search", "answer", "research"]);
6934
+ }
6935
+ }),
6936
+ stringSetting({
6937
+ id: "openaiAnswerInstructions",
6938
+ label: "Answer instructions",
6939
+ help: "Optional default instructions for OpenAI grounded answers.",
6940
+ getValue: (config) => getOpenAIAnswerOptions(config)?.instructions,
6941
+ setValue: (config, value) => {
6942
+ assignOptionalString(
6943
+ ensureOpenAIAnswerOptions(config),
6944
+ "instructions",
6945
+ value
6946
+ );
6947
+ cleanupCapabilityOptions(config, ["search", "answer", "research"]);
6948
+ }
6949
+ }),
6950
+ stringSetting({
6951
+ id: "openaiResearchModel",
6952
+ label: "Research model",
6953
+ help: "Model used for OpenAI deep research runs.",
6954
+ getValue: (config) => getOpenAIResearchOptions(config)?.model,
6955
+ setValue: (config, value) => {
6956
+ assignOptionalString(
6957
+ ensureOpenAIResearchOptions(config),
6958
+ "model",
6959
+ value
6960
+ );
6961
+ cleanupCapabilityOptions(config, ["search", "answer", "research"]);
6962
+ }
6963
+ }),
6964
+ stringSetting({
6965
+ id: "openaiResearchInstructions",
6966
+ label: "Research instructions",
6967
+ help: "Optional default instructions for OpenAI deep research runs.",
6968
+ getValue: (config) => getOpenAIResearchOptions(config)?.instructions,
6969
+ setValue: (config, value) => {
6970
+ assignOptionalString(
6971
+ ensureOpenAIResearchOptions(config),
6972
+ "instructions",
6973
+ value
6974
+ );
6975
+ cleanupCapabilityOptions(config, ["search", "answer", "research"]);
6976
+ }
6977
+ }),
6978
+ stringSetting({
6979
+ id: "openaiResearchMaxToolCalls",
6980
+ label: "Research max tool calls",
6981
+ help: "Optional default maximum number of built-in tool calls for OpenAI deep research runs.",
6982
+ getValue: (config) => getIntegerString(getOpenAIResearchOptions(config)?.max_tool_calls),
6983
+ setValue: (config, value) => {
6984
+ assignOptionalInteger(
6985
+ ensureOpenAIResearchOptions(config),
6986
+ "max_tool_calls",
6987
+ value,
6988
+ "OpenAI research max tool calls must be a positive integer."
6989
+ );
6990
+ cleanupCapabilityOptions(config, ["search", "answer", "research"]);
6991
+ }
6992
+ })
6362
6993
  ]
6363
6994
  },
6364
6995
  perplexity: {
@@ -6428,11 +7059,7 @@ var PROVIDER_CONFIG_MANIFESTS = {
6428
7059
  ]
6429
7060
  },
6430
7061
  tavily: {
6431
- settings: [
6432
- apiKeySetting(),
6433
- baseUrlSetting(),
6434
- ...requestSettings("tavily")
6435
- ]
7062
+ settings: [apiKeySetting(), baseUrlSetting()]
6436
7063
  },
6437
7064
  valyu: {
6438
7065
  settings: [
@@ -6539,88 +7166,20 @@ function baseUrlSetting() {
6539
7166
  }
6540
7167
  });
6541
7168
  }
6542
- function requestSettings(providerId) {
6543
- const settings = [
6544
- stringSetting({
6545
- id: "requestTimeoutMs",
6546
- label: "Request timeout (ms)",
6547
- help: "Maximum time to wait for each command before failing that attempt for this provider. Leave empty to inherit the shared setting.",
6548
- getValue: (config) => getIntegerString(config?.settings?.requestTimeoutMs),
6549
- setValue: (config, value) => {
6550
- assignOptionalInteger(
6551
- ensureSettings(config),
6552
- "requestTimeoutMs",
6553
- value,
6554
- "Request timeout must be a positive integer."
6555
- );
6556
- cleanupEmpty(config, "settings");
6557
- }
6558
- }),
6559
- stringSetting({
6560
- id: "retryCount",
6561
- label: "Retry count",
6562
- help: "How many times to retry transient command failures for this provider. Leave empty to inherit the shared setting.",
6563
- getValue: (config) => getIntegerString(config?.settings?.retryCount),
6564
- setValue: (config, value) => {
6565
- assignOptionalInteger(
6566
- ensureSettings(config),
6567
- "retryCount",
6568
- value,
6569
- "Retry count must be a non-negative integer.",
6570
- { allowZero: true }
6571
- );
6572
- cleanupEmpty(config, "settings");
6573
- }
6574
- }),
6575
- stringSetting({
6576
- id: "retryDelayMs",
6577
- label: "Retry delay (ms)",
6578
- help: "Initial delay before retrying command failures for this provider. Leave empty to inherit the shared setting.",
6579
- getValue: (config) => getIntegerString(config?.settings?.retryDelayMs),
6580
- setValue: (config, value) => {
6581
- assignOptionalInteger(
6582
- ensureSettings(config),
6583
- "retryDelayMs",
6584
- value,
6585
- "Retry delay must be a positive integer."
6586
- );
6587
- cleanupEmpty(config, "settings");
6588
- }
6589
- })
6590
- ];
6591
- if (supportsTool(providerId, "research")) {
6592
- settings.push(
6593
- stringSetting({
6594
- id: "researchTimeoutMs",
6595
- label: "Research timeout (ms)",
6596
- help: "Maximum total time to allow long-running web research for this provider before aborting it. Leave empty to inherit the shared setting.",
6597
- getValue: (config) => getIntegerString(config?.settings?.researchTimeoutMs),
6598
- setValue: (config, value) => {
6599
- assignOptionalInteger(
6600
- ensureSettings(config),
6601
- "researchTimeoutMs",
6602
- value,
6603
- "Research timeout must be a positive integer."
6604
- );
6605
- cleanupEmpty(config, "settings");
6606
- }
6607
- })
6608
- );
6609
- }
6610
- return settings;
6611
- }
6612
7169
  function assignOptionalString(target, key, value) {
7170
+ const record = target;
6613
7171
  const trimmed = value.trim();
6614
7172
  if (!trimmed) {
6615
- delete target[key];
7173
+ delete record[key];
6616
7174
  } else {
6617
- target[key] = trimmed;
7175
+ record[key] = trimmed;
6618
7176
  }
6619
7177
  }
6620
7178
  function assignOptionalInteger(target, key, value, errorMessage, options) {
7179
+ const record = target;
6621
7180
  const trimmed = value.trim();
6622
7181
  if (!trimmed) {
6623
- delete target[key];
7182
+ delete record[key];
6624
7183
  return;
6625
7184
  }
6626
7185
  const parsed = Number(trimmed);
@@ -6628,7 +7187,7 @@ function assignOptionalInteger(target, key, value, errorMessage, options) {
6628
7187
  if (!Number.isInteger(parsed) || parsed < minimum) {
6629
7188
  throw new Error(errorMessage);
6630
7189
  }
6631
- target[key] = parsed;
7190
+ record[key] = parsed;
6632
7191
  }
6633
7192
  function assignOptionalBoolean(target, key, value) {
6634
7193
  if (value === "default") {
@@ -6649,10 +7208,6 @@ function readString4(value) {
6649
7208
  function asJsonObject2(value) {
6650
7209
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
6651
7210
  }
6652
- function ensureSettings(config) {
6653
- config.settings = { ...config.settings ?? {} };
6654
- return config.settings;
6655
- }
6656
7211
  function cleanupEmpty(config, key) {
6657
7212
  const value = asJsonObject2(config[key]);
6658
7213
  if (value && Object.keys(value).length === 0) {
@@ -6817,6 +7372,36 @@ function ensureExaSearchOptions(config) {
6817
7372
  };
6818
7373
  return config.options.search;
6819
7374
  }
7375
+ function getOpenAISearchOptions(config) {
7376
+ return config?.options?.search;
7377
+ }
7378
+ function ensureOpenAISearchOptions(config) {
7379
+ config.options = {
7380
+ ...config.options ?? {},
7381
+ search: { ...config.options?.search ?? {} }
7382
+ };
7383
+ return config.options.search ?? (config.options.search = {});
7384
+ }
7385
+ function getOpenAIAnswerOptions(config) {
7386
+ return config?.options?.answer;
7387
+ }
7388
+ function ensureOpenAIAnswerOptions(config) {
7389
+ config.options = {
7390
+ ...config.options ?? {},
7391
+ answer: { ...config.options?.answer ?? {} }
7392
+ };
7393
+ return config.options.answer ?? (config.options.answer = {});
7394
+ }
7395
+ function getOpenAIResearchOptions(config) {
7396
+ return config?.options?.research;
7397
+ }
7398
+ function ensureOpenAIResearchOptions(config) {
7399
+ config.options = {
7400
+ ...config.options ?? {},
7401
+ research: { ...config.options?.research ?? {} }
7402
+ };
7403
+ return config.options.research ?? (config.options.research = {});
7404
+ }
6820
7405
  function getValyuCapabilityOptions(config, capability) {
6821
7406
  return config?.options?.[capability];
6822
7407
  }
@@ -6992,14 +7577,14 @@ function registerWebSearchTool(pi, providerIds) {
6992
7577
  promptGuidelines: [
6993
7578
  "Batch related searches when grouped comparison matters; use separate sibling web_search calls when independent results should surface as soon as they are ready."
6994
7579
  ],
6995
- parameters: Type14.Object({
6996
- queries: Type14.Array(Type14.String({ minLength: 1 }), {
7580
+ parameters: Type15.Object({
7581
+ queries: Type15.Array(Type15.String({ minLength: 1 }), {
6997
7582
  minItems: 1,
6998
7583
  maxItems: MAX_SEARCH_QUERIES,
6999
7584
  description: `One or more search queries to run in one call (max ${MAX_SEARCH_QUERIES})`
7000
7585
  }),
7001
- maxResults: Type14.Optional(
7002
- Type14.Integer({
7586
+ maxResults: Type15.Optional(
7587
+ Type15.Integer({
7003
7588
  minimum: 1,
7004
7589
  maximum: MAX_ALLOWED_RESULTS,
7005
7590
  description: `Maximum number of results to return (default: ${DEFAULT_MAX_RESULTS})`
@@ -7048,8 +7633,8 @@ function registerWebContentsTool(pi, providerIds) {
7048
7633
  name: "web_contents",
7049
7634
  label: "Web Contents",
7050
7635
  description: "Read and extract the main contents of one or more web pages. Batch related pages together, or use separate sibling calls when each page can be acted on independently.",
7051
- parameters: Type14.Object({
7052
- urls: Type14.Array(Type14.String({ minLength: 1 }), {
7636
+ parameters: Type15.Object({
7637
+ urls: Type15.Array(Type15.String({ minLength: 1 }), {
7053
7638
  minItems: 1,
7054
7639
  description: "One or more URLs to extract"
7055
7640
  }),
@@ -7099,8 +7684,8 @@ function registerWebAnswerTool(pi, providerIds) {
7099
7684
  name: "web_answer",
7100
7685
  label: "Web Answer",
7101
7686
  description: `Answer one or more questions using web-grounded evidence (up to ${MAX_SEARCH_QUERIES} per call).`,
7102
- parameters: Type14.Object({
7103
- queries: Type14.Array(Type14.String({ minLength: 1 }), {
7687
+ parameters: Type15.Object({
7688
+ queries: Type15.Array(Type15.String({ minLength: 1 }), {
7104
7689
  minItems: 1,
7105
7690
  maxItems: MAX_SEARCH_QUERIES,
7106
7691
  description: `One or more questions to answer in one call (max ${MAX_SEARCH_QUERIES})`
@@ -7154,8 +7739,8 @@ function registerWebResearchTool(pi, webResearchLifecycle, providerIds) {
7154
7739
  name: "web_research",
7155
7740
  label: "Web Research",
7156
7741
  description: "Start a long-running web research job. Returns immediately with a dispatch notice; the final report is saved to a file and posted later as a custom message.",
7157
- parameters: Type14.Object({
7158
- input: Type14.String({ description: "Research brief or question" }),
7742
+ parameters: Type15.Object({
7743
+ input: Type15.String({ description: "Research brief or question" }),
7159
7744
  ...optionalField(
7160
7745
  "options",
7161
7746
  buildStructuredOptionsSchema("research", selectedProviderId)
@@ -7298,7 +7883,7 @@ function optionalField(name, schema) {
7298
7883
  function buildStructuredOptionsSchema(capability, providerId) {
7299
7884
  const providerSchema = resolveProviderOptionsSchema(capability, providerId);
7300
7885
  const schema = buildToolOptionsSchema(capability, providerSchema);
7301
- return schema ? Type14.Optional(schema) : void 0;
7886
+ return schema ? Type15.Optional(schema) : void 0;
7302
7887
  }
7303
7888
  function resolveProviderOptionsSchema(capability, providerId) {
7304
7889
  if (!providerId) {
@@ -8606,7 +9191,7 @@ function getSharedSettingRawValue(config, id) {
8606
9191
  const value = config.settings?.[id];
8607
9192
  return typeof value === "number" ? String(value) : "";
8608
9193
  }
8609
- function ensureSettings2(config) {
9194
+ function ensureSettings(config) {
8610
9195
  config.settings = { ...config.settings ?? {} };
8611
9196
  return config.settings;
8612
9197
  }
@@ -8951,7 +9536,7 @@ var WebProvidersSettingsView = class {
8951
9536
  async handleSharedSettingChange(id, value) {
8952
9537
  await this.persist((config) => {
8953
9538
  const parsed = SETTING_META[id].parse(value);
8954
- const settings = ensureSettings2(config);
9539
+ const settings = ensureSettings(config);
8955
9540
  if (parsed === void 0) {
8956
9541
  delete settings[id];
8957
9542
  } else {