pi-web-providers 2.5.0 → 3.0.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 +50 -5
  2. package/dist/index.js +1833 -645
  3. package/package.json +8 -7
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  visibleWidth,
23
23
  wrapTextWithAnsi
24
24
  } from "@mariozechner/pi-tui";
25
- import { Type as Type15 } from "typebox";
25
+ import { Type as Type16 } from "typebox";
26
26
 
27
27
  // src/config.ts
28
28
  import { mkdir, readFile, writeFile } from "node:fs/promises";
@@ -42,107 +42,1219 @@ function resolveConfigValue(reference) {
42
42
  }
43
43
  return cached.value;
44
44
  }
45
- try {
46
- const output = execSync(reference.slice(1), {
47
- encoding: "utf-8",
48
- stdio: ["ignore", "pipe", "pipe"]
49
- }).trim();
50
- const value = output.length > 0 ? output : void 0;
51
- commandValueCache.set(reference, { value });
52
- return value;
53
- } catch (error) {
54
- const errorMessage = error.message;
55
- commandValueCache.set(reference, { errorMessage });
56
- throw error;
45
+ try {
46
+ const output = execSync(reference.slice(1), {
47
+ encoding: "utf-8",
48
+ stdio: ["ignore", "pipe", "pipe"]
49
+ }).trim();
50
+ const value = output.length > 0 ? output : void 0;
51
+ commandValueCache.set(reference, { value });
52
+ return value;
53
+ } catch (error) {
54
+ const errorMessage = error.message;
55
+ commandValueCache.set(reference, { errorMessage });
56
+ throw error;
57
+ }
58
+ }
59
+ const envValue = process.env[reference];
60
+ if (envValue !== void 0) {
61
+ return envValue;
62
+ }
63
+ if (/^[A-Z][A-Z0-9_]*$/.test(reference)) {
64
+ return void 0;
65
+ }
66
+ return reference;
67
+ }
68
+ function resolveEnvMap(envMap) {
69
+ if (!envMap) return void 0;
70
+ const resolved = Object.fromEntries(
71
+ Object.entries(envMap).map(([key, value]) => [key, resolveConfigValue(value)]).filter(
72
+ (entry) => typeof entry[1] === "string"
73
+ )
74
+ );
75
+ return Object.keys(resolved).length > 0 ? resolved : void 0;
76
+ }
77
+
78
+ // src/providers/brave.ts
79
+ import { Type } from "typebox";
80
+
81
+ // src/providers/definition.ts
82
+ function defineCapability(definition) {
83
+ return definition;
84
+ }
85
+ function defineProvider(definition) {
86
+ return definition;
87
+ }
88
+ function defineProviders(providers) {
89
+ return providers;
90
+ }
91
+ async function executeProviderCapability(definition, capability, input, context) {
92
+ const handler = definition.capabilities[capability];
93
+ if (!handler) {
94
+ throw new Error(
95
+ `Provider '${definition.id}' does not support '${capability}'.`
96
+ );
97
+ }
98
+ return await handler.execute(input, context);
99
+ }
100
+
101
+ // src/providers/shared.ts
102
+ function trimSnippet(input, maxLength = 300) {
103
+ const text = (input ?? "").replace(/\s+/g, " ").trim();
104
+ if (text.length <= maxLength) return text;
105
+ return `${text.slice(0, maxLength - 1)}\u2026`;
106
+ }
107
+ function normalizeContentText(input) {
108
+ const text = (input ?? "").replace(/\r/g, "").trim();
109
+ if (!text) {
110
+ return "";
111
+ }
112
+ return text.split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n").replace(/\n{3,}/g, "\n\n");
113
+ }
114
+ function asJsonObject(value) {
115
+ return value ? { ...value } : {};
116
+ }
117
+ function formatJson(value) {
118
+ return JSON.stringify(value, null, 2);
119
+ }
120
+ function getApiKeyStatus(apiKeyReference) {
121
+ try {
122
+ return resolveConfigValue(apiKeyReference) ? { state: "ready" } : { state: "missing_api_key" };
123
+ } catch (error) {
124
+ return {
125
+ state: "invalid_config",
126
+ detail: formatConfigValueError(error)
127
+ };
128
+ }
129
+ }
130
+ function formatConfigValueError(error) {
131
+ const message = error instanceof Error ? error.message : String(error);
132
+ return message.replace(/\s+/g, " ").trim() || "Failed to resolve config value";
133
+ }
134
+
135
+ // src/providers/brave.ts
136
+ var DEFAULT_BASE_URL = "https://api.search.brave.com";
137
+ var BRAVE_API_VERSION = void 0;
138
+ var countryOption = Type.Optional(
139
+ Type.String({
140
+ description: "Country code used to localize Brave results, for example 'US'."
141
+ })
142
+ );
143
+ var searchLangOption = Type.Optional(
144
+ Type.String({
145
+ description: "Content language for Brave results, for example 'en'."
146
+ })
147
+ );
148
+ var uiLangOption = Type.Optional(
149
+ Type.String({
150
+ description: "UI language for response metadata, for example 'en-US'."
151
+ })
152
+ );
153
+ var freshnessOption = Type.Optional(
154
+ Type.String({
155
+ description: "Freshness filter such as 'pd' (24h), 'pw' (7d), 'pm' (31d), 'py' (year), or a Brave date range."
156
+ })
157
+ );
158
+ var safesearchOption = Type.Optional(
159
+ Type.Enum({ off: "off", moderate: "moderate", strict: "strict" }, {
160
+ description: "Safe-search filtering level."
161
+ })
162
+ );
163
+ var spellcheckOption = Type.Optional(
164
+ Type.Boolean({ description: "Whether Brave may spellcheck the query." })
165
+ );
166
+ var countOption = Type.Optional(
167
+ Type.Integer({
168
+ minimum: 1,
169
+ maximum: 50,
170
+ description: "Mode-specific result count override. Prefer top-level maxResults unless Brave-specific pagination is needed."
171
+ })
172
+ );
173
+ var offsetOption = Type.Optional(
174
+ Type.Integer({
175
+ minimum: 0,
176
+ description: "Brave result page offset for paginated requests."
177
+ })
178
+ );
179
+ var gogglesOption = Type.Optional(
180
+ Type.String({ description: "Brave Goggles URL or inline definition." })
181
+ );
182
+ var extraSnippetsOption = Type.Optional(
183
+ Type.Boolean({ description: "Whether to ask Brave for extra snippets." })
184
+ );
185
+ var braveSearchOptionsSchema = Type.Object(
186
+ {
187
+ mode: Type.Optional(
188
+ Type.Enum(
189
+ {
190
+ web: "web",
191
+ llm_context: "llm_context",
192
+ news: "news",
193
+ videos: "videos",
194
+ images: "images",
195
+ places: "places"
196
+ },
197
+ {
198
+ description: "Brave search mode. Use 'news' for recent journalism or current events, 'videos' for clips/tutorials, 'images' for visual references, 'places' for local businesses, venues, cafes, restaurants, hotels, shops, or near/in-location searches, and 'llm_context' for retrieval context."
199
+ }
200
+ )
201
+ ),
202
+ common: Type.Optional(
203
+ Type.Object(
204
+ {
205
+ country: countryOption,
206
+ search_lang: searchLangOption,
207
+ ui_lang: uiLangOption
208
+ },
209
+ {
210
+ description: "Common Brave query options merged into the selected mode's options."
211
+ }
212
+ )
213
+ ),
214
+ web: Type.Optional(
215
+ Type.Object(
216
+ {
217
+ country: countryOption,
218
+ search_lang: searchLangOption,
219
+ ui_lang: uiLangOption,
220
+ freshness: freshnessOption,
221
+ safesearch: safesearchOption,
222
+ spellcheck: spellcheckOption,
223
+ goggles: gogglesOption,
224
+ extra_snippets: extraSnippetsOption,
225
+ offset: offsetOption,
226
+ enable_rich_callback: Type.Optional(
227
+ Type.Boolean({
228
+ description: "Whether to enable Brave rich callback metadata."
229
+ })
230
+ )
231
+ },
232
+ { description: "Options for Brave Web Search mode." }
233
+ )
234
+ ),
235
+ llmContext: Type.Optional(
236
+ Type.Object(
237
+ {
238
+ count: countOption,
239
+ maximum_number_of_urls: Type.Optional(
240
+ Type.Integer({ minimum: 1, description: "Maximum source URLs." })
241
+ ),
242
+ maximum_number_of_tokens: Type.Optional(
243
+ Type.Integer({
244
+ minimum: 1,
245
+ description: "Maximum context tokens."
246
+ })
247
+ ),
248
+ maximum_number_of_snippets: Type.Optional(
249
+ Type.Integer({ minimum: 1, description: "Maximum snippets." })
250
+ ),
251
+ maximum_number_of_tokens_per_url: Type.Optional(
252
+ Type.Integer({
253
+ minimum: 1,
254
+ description: "Maximum context tokens per URL."
255
+ })
256
+ ),
257
+ maximum_number_of_snippets_per_url: Type.Optional(
258
+ Type.Integer({
259
+ minimum: 1,
260
+ description: "Maximum snippets per URL."
261
+ })
262
+ ),
263
+ context_threshold_mode: Type.Optional(
264
+ Type.String({ description: "Brave LLM Context threshold mode." })
265
+ ),
266
+ enable_local: Type.Optional(
267
+ Type.Boolean({ description: "Whether to include local results." })
268
+ ),
269
+ enable_source_metadata: Type.Optional(
270
+ Type.Boolean({
271
+ description: "Whether to include source metadata in grounding."
272
+ })
273
+ ),
274
+ country: countryOption,
275
+ search_lang: searchLangOption,
276
+ ui_lang: uiLangOption,
277
+ freshness: freshnessOption,
278
+ safesearch: safesearchOption,
279
+ spellcheck: spellcheckOption,
280
+ goggles: gogglesOption
281
+ },
282
+ { description: "Options for Brave LLM Context mode." }
283
+ )
284
+ ),
285
+ news: Type.Optional(
286
+ Type.Object(
287
+ {
288
+ country: countryOption,
289
+ search_lang: searchLangOption,
290
+ ui_lang: uiLangOption,
291
+ freshness: freshnessOption,
292
+ safesearch: safesearchOption,
293
+ spellcheck: spellcheckOption,
294
+ goggles: gogglesOption,
295
+ extra_snippets: extraSnippetsOption,
296
+ offset: offsetOption,
297
+ count: countOption
298
+ },
299
+ { description: "Options for Brave News Search mode." }
300
+ )
301
+ ),
302
+ videos: Type.Optional(
303
+ Type.Object(
304
+ {
305
+ country: countryOption,
306
+ search_lang: searchLangOption,
307
+ ui_lang: uiLangOption,
308
+ freshness: freshnessOption,
309
+ safesearch: safesearchOption,
310
+ spellcheck: spellcheckOption,
311
+ offset: offsetOption,
312
+ count: countOption
313
+ },
314
+ { description: "Options for Brave Video Search mode." }
315
+ )
316
+ ),
317
+ images: Type.Optional(
318
+ Type.Object(
319
+ {
320
+ country: countryOption,
321
+ search_lang: searchLangOption,
322
+ ui_lang: uiLangOption,
323
+ safesearch: safesearchOption,
324
+ spellcheck: spellcheckOption,
325
+ count: countOption
326
+ },
327
+ { description: "Options for Brave Image Search mode." }
328
+ )
329
+ ),
330
+ places: Type.Optional(
331
+ Type.Object(
332
+ {
333
+ country: countryOption,
334
+ search_lang: searchLangOption,
335
+ ui_lang: uiLangOption,
336
+ latitude: Type.Optional(
337
+ Type.Number({ description: "Latitude for local place search." })
338
+ ),
339
+ longitude: Type.Optional(
340
+ Type.Number({ description: "Longitude for local place search." })
341
+ ),
342
+ location: Type.Optional(
343
+ Type.String({
344
+ description: "Human-readable local search location, e.g. 'Eppendorf, Hamburg, Germany'. Use with mode='places' for neighborhood or near-me style searches."
345
+ })
346
+ ),
347
+ radius: Type.Optional(
348
+ Type.Number({ description: "Local search radius." })
349
+ ),
350
+ units: Type.Optional(
351
+ Type.String({ description: "Distance units for local search." })
352
+ ),
353
+ safesearch: safesearchOption,
354
+ spellcheck: spellcheckOption,
355
+ geoloc: Type.Optional(
356
+ Type.String({
357
+ description: "Optional geolocation token used to refine results."
358
+ })
359
+ ),
360
+ count: countOption,
361
+ includeDetails: Type.Optional(
362
+ Type.Boolean({
363
+ description: "Places mode only. Fetch detailed POI metadata when the task needs contact info, opening hours, ratings/review counts, photos, profiles, or richer address/distance data. Leave off for simple place listings to avoid extra latency and quota usage."
364
+ })
365
+ ),
366
+ includeDescriptions: Type.Optional(
367
+ Type.Boolean({
368
+ description: "Places mode only. Fetch AI-generated POI descriptions when the task needs qualitative summaries or short explanations of places. Leave off for simple nearby/place listing queries to avoid extra latency and quota usage."
369
+ })
370
+ )
371
+ },
372
+ { description: "Options for Brave Local Place Search mode." }
373
+ )
374
+ )
375
+ },
376
+ { description: "Brave search options." }
377
+ );
378
+ var braveSearchPromptGuidelines = [
379
+ "Use Brave places mode for direct point-of-interest listings such as restaurants, cafes, hotels, shops, landmarks, or venues.",
380
+ "Prefer Brave places mode over llm_context when the user asks for nearby businesses or wants names, addresses, ratings, opening hours, categories, or contact details.",
381
+ "In Brave places mode, set places.includeDetails when the task needs POI attributes beyond the basic result list, such as contact info, opening hours, ratings/review counts, photos, profiles, or richer address/distance metadata.",
382
+ "In Brave places mode, set places.includeDescriptions when the task needs qualitative summaries or short explanations of places. Leave it off for simple nearby/place listing queries to avoid extra latency and quota usage.",
383
+ "Use Brave llm_context mode when the agent needs extracted source context for reasoning, synthesis, RAG-style grounding, or source-material collection.",
384
+ "In Brave llm_context mode, set llmContext.enable_local=true for local or near-me queries where POI/map grounding may be useful."
385
+ ];
386
+ var braveAnswerOptionsSchema = Type.Object(
387
+ {
388
+ country: Type.Optional(Type.String()),
389
+ language: Type.Optional(Type.String()),
390
+ enable_citations: Type.Optional(Type.Boolean()),
391
+ enable_entities: Type.Optional(Type.Boolean()),
392
+ max_completion_tokens: Type.Optional(Type.Integer({ minimum: 1 }))
393
+ },
394
+ { description: "Brave answer options." }
395
+ );
396
+ var braveResearchOptionsSchema = Type.Object(
397
+ {
398
+ country: Type.Optional(Type.String()),
399
+ language: Type.Optional(Type.String()),
400
+ enable_entities: Type.Optional(Type.Boolean()),
401
+ enable_citations: Type.Optional(
402
+ Type.Boolean({
403
+ description: "Accepted for compatibility but forced to false for Brave research mode."
404
+ })
405
+ ),
406
+ max_completion_tokens: Type.Optional(Type.Integer({ minimum: 1 })),
407
+ research_allow_thinking: Type.Optional(Type.Boolean()),
408
+ research_maximum_number_of_tokens_per_query: Type.Optional(
409
+ Type.Integer({ minimum: 1 })
410
+ ),
411
+ research_maximum_number_of_queries: Type.Optional(
412
+ Type.Integer({ minimum: 1 })
413
+ ),
414
+ research_maximum_number_of_iterations: Type.Optional(
415
+ Type.Integer({ minimum: 1 })
416
+ ),
417
+ research_maximum_number_of_seconds: Type.Optional(
418
+ Type.Integer({ minimum: 1 })
419
+ ),
420
+ research_maximum_number_of_results_per_query: Type.Optional(
421
+ Type.Integer({ minimum: 1 })
422
+ )
423
+ },
424
+ { description: "Brave research options." }
425
+ );
426
+ var braveImplementation = {
427
+ id: "brave",
428
+ label: "Brave",
429
+ docsUrl: "https://api-dashboard.search.brave.com/app/documentation",
430
+ getToolOptionsSchema(capability) {
431
+ switch (capability) {
432
+ case "search":
433
+ return braveSearchOptionsSchema;
434
+ case "answer":
435
+ return braveAnswerOptionsSchema;
436
+ case "research":
437
+ return braveResearchOptionsSchema;
438
+ default:
439
+ return void 0;
440
+ }
441
+ },
442
+ createTemplate() {
443
+ return {
444
+ credentials: {
445
+ search: "BRAVE_SEARCH_API_KEY",
446
+ answers: "BRAVE_ANSWERS_API_KEY"
447
+ },
448
+ options: {}
449
+ };
450
+ },
451
+ getCapabilityStatus(config, _cwd, tool) {
452
+ const key = tool === "answer" || tool === "research" ? config?.credentials?.answers : config?.credentials?.search;
453
+ try {
454
+ if (tool)
455
+ return resolveConfigValue(key) ? { state: "ready" } : { state: "missing_api_key" };
456
+ return [
457
+ config?.credentials?.search,
458
+ config?.credentials?.answers,
459
+ config?.credentials?.autosuggest
460
+ ].some((v) => resolveConfigValue(v)) ? { state: "ready" } : { state: "missing_api_key" };
461
+ } catch (error) {
462
+ return { state: "invalid_config", detail: formatConfigValueError(error) };
463
+ }
464
+ },
465
+ async search(query2, maxResults, config, context, options) {
466
+ const apiKey = requireKey(config.credentials?.search, "Brave search");
467
+ const defaults = asJsonObject(
468
+ config.options?.search
469
+ );
470
+ const callOptions = { ...defaults, ...options ?? {} };
471
+ const mode = readMode(callOptions.mode);
472
+ if (mode === "llm_context")
473
+ return await llmContext(
474
+ query2,
475
+ maxResults,
476
+ config,
477
+ context,
478
+ apiKey,
479
+ callOptions
480
+ );
481
+ if (mode === "news")
482
+ return await news(
483
+ query2,
484
+ maxResults,
485
+ config,
486
+ context,
487
+ apiKey,
488
+ callOptions
489
+ );
490
+ if (mode === "videos")
491
+ return await videos(
492
+ query2,
493
+ maxResults,
494
+ config,
495
+ context,
496
+ apiKey,
497
+ callOptions
498
+ );
499
+ if (mode === "images")
500
+ return await images(
501
+ query2,
502
+ maxResults,
503
+ config,
504
+ context,
505
+ apiKey,
506
+ callOptions
507
+ );
508
+ if (mode === "places")
509
+ return await places(
510
+ query2,
511
+ maxResults,
512
+ config,
513
+ context,
514
+ apiKey,
515
+ callOptions
516
+ );
517
+ return await web(query2, maxResults, config, context, apiKey, callOptions);
518
+ },
519
+ async answer(query2, config, context, options) {
520
+ const raw = {
521
+ ...asJsonObject(
522
+ config.options?.answer
523
+ ),
524
+ ...options ?? {}
525
+ };
526
+ return await completion(query2, config, context, buildAnswerRequest(raw));
527
+ },
528
+ async research(input, config, context, options) {
529
+ const raw = {
530
+ ...asJsonObject(
531
+ config.options?.research
532
+ ),
533
+ ...options ?? {}
534
+ };
535
+ return await completion(input, config, context, buildResearchRequest(raw));
536
+ }
537
+ };
538
+ function requireKey(ref, label) {
539
+ const key = resolveConfigValue(ref);
540
+ if (!key) throw new Error(`${label} is missing an API key`);
541
+ return key;
542
+ }
543
+ function base(config) {
544
+ return (resolveConfigValue(config.baseUrl) ?? DEFAULT_BASE_URL).replace(
545
+ /\/+$/,
546
+ ""
547
+ );
548
+ }
549
+ function clamp(n, max = 20) {
550
+ return Math.max(1, Math.min(max, Math.trunc(n || 0)));
551
+ }
552
+ function readMode(v) {
553
+ return v === "llm_context" || v === "news" || v === "videos" || v === "images" || v === "places" ? v : "web";
554
+ }
555
+ function obj(v) {
556
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
557
+ }
558
+ function arr(v) {
559
+ return Array.isArray(v) ? v : [];
560
+ }
561
+ function str(v) {
562
+ return typeof v === "string" ? v : void 0;
563
+ }
564
+ function num(v) {
565
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
566
+ }
567
+ function pick(source, allowed) {
568
+ return Object.fromEntries(
569
+ allowed.filter((k) => source[k] !== void 0).map((k) => [k, source[k]])
570
+ );
571
+ }
572
+ function mergeOptions(options, key, allowed) {
573
+ return pick({ ...obj(options.common), ...obj(options[key]) }, allowed);
574
+ }
575
+ function headers(key, json = false) {
576
+ const result = { "X-Subscription-Token": key };
577
+ if (BRAVE_API_VERSION) {
578
+ result["Api-Version"] = BRAVE_API_VERSION;
579
+ }
580
+ if (json) {
581
+ result["content-type"] = "application/json";
582
+ }
583
+ return result;
584
+ }
585
+ function url(config, path, params) {
586
+ const u = new URL(`${base(config)}${path}`);
587
+ for (const [k, v] of Object.entries(params))
588
+ if (v !== void 0)
589
+ u.searchParams.set(k, Array.isArray(v) ? v.join(",") : String(v));
590
+ return u;
591
+ }
592
+ async function httpError(response) {
593
+ const text = (await response.text()).trim();
594
+ return `Brave API request failed (${response.status}${response.statusText ? ` ${response.statusText}` : ""})${text ? `: ${text}` : "."}`;
595
+ }
596
+ async function web(query2, maxResults, config, context, key, options) {
597
+ const params = {
598
+ q: query2,
599
+ count: clamp(maxResults),
600
+ text_decorations: false,
601
+ ...mergeOptions(options, "web", [
602
+ "country",
603
+ "search_lang",
604
+ "ui_lang",
605
+ "freshness",
606
+ "safesearch",
607
+ "spellcheck",
608
+ "goggles",
609
+ "extra_snippets",
610
+ "offset",
611
+ "enable_rich_callback"
612
+ ])
613
+ };
614
+ const r = await fetch(url(config, "/res/v1/web/search", params), {
615
+ headers: headers(key),
616
+ signal: context.signal
617
+ });
618
+ if (!r.ok) throw new Error(await httpError(r));
619
+ const p = obj(await r.json());
620
+ return {
621
+ provider: "brave",
622
+ results: arr(obj(p.web).results).map((e) => {
623
+ const x = obj(e);
624
+ const u = str(x.url) ?? "";
625
+ return {
626
+ title: str(x.title) || u || "Untitled",
627
+ url: u,
628
+ snippet: trimSnippet(
629
+ str(x.description) ?? arr(x.extra_snippets).join(" ")
630
+ ),
631
+ metadata: x
632
+ };
633
+ }).slice(0, clamp(maxResults))
634
+ };
635
+ }
636
+ async function llmContext(query2, maxResults, config, context, key, options) {
637
+ const params = {
638
+ q: query2,
639
+ count: clamp(maxResults),
640
+ maximum_number_of_urls: clamp(maxResults),
641
+ maximum_number_of_tokens: 8192,
642
+ enable_source_metadata: true,
643
+ ...mergeOptions(options, "llmContext", [
644
+ "count",
645
+ "maximum_number_of_urls",
646
+ "maximum_number_of_tokens",
647
+ "maximum_number_of_snippets",
648
+ "maximum_number_of_tokens_per_url",
649
+ "maximum_number_of_snippets_per_url",
650
+ "context_threshold_mode",
651
+ "enable_local",
652
+ "enable_source_metadata",
653
+ "country",
654
+ "search_lang",
655
+ "ui_lang",
656
+ "freshness",
657
+ "safesearch",
658
+ "spellcheck",
659
+ "goggles"
660
+ ])
661
+ };
662
+ const r = await fetch(url(config, "/res/v1/llm/context", params), {
663
+ headers: headers(key),
664
+ signal: context.signal
665
+ });
666
+ if (!r.ok) throw new Error(await httpError(r));
667
+ const p = obj(await r.json());
668
+ return {
669
+ provider: "brave",
670
+ results: collectLlmContextEntries(obj(p.grounding)).map((e) => {
671
+ const x = obj(e);
672
+ const snippets = arr(x.snippets).map(String);
673
+ const source = obj(x.source);
674
+ const u = str(x.url) ?? str(source.url) ?? "";
675
+ return {
676
+ title: str(x.title) ?? str(x.name) ?? str(source.title) ?? (u || "Untitled"),
677
+ url: u,
678
+ snippet: trimSnippet(snippets.join("\n\n"), 1200),
679
+ metadata: x
680
+ };
681
+ }).slice(0, clamp(maxResults))
682
+ };
683
+ }
684
+ function collectLlmContextEntries(grounding) {
685
+ const entries = [];
686
+ for (const value of [grounding.generic, grounding.map]) {
687
+ entries.push(...arr(value).map(obj));
688
+ }
689
+ const poiEntries = Array.isArray(grounding.poi) ? grounding.poi.map(obj) : [obj(grounding.poi)];
690
+ entries.push(...poiEntries.filter((entry) => Object.keys(entry).length > 0));
691
+ return entries;
692
+ }
693
+ async function news(query2, maxResults, config, context, key, options) {
694
+ const params = {
695
+ q: query2,
696
+ count: clamp(maxResults, 50),
697
+ ...mergeOptions(options, "news", [
698
+ "country",
699
+ "search_lang",
700
+ "ui_lang",
701
+ "freshness",
702
+ "safesearch",
703
+ "spellcheck",
704
+ "goggles",
705
+ "extra_snippets",
706
+ "offset",
707
+ "count"
708
+ ])
709
+ };
710
+ const r = await fetch(url(config, "/res/v1/news/search", params), {
711
+ headers: headers(key),
712
+ signal: context.signal
713
+ });
714
+ if (!r.ok) throw new Error(await httpError(r));
715
+ const p = obj(await r.json());
716
+ return {
717
+ provider: "brave",
718
+ results: arr(p.results).map((e) => {
719
+ const x = obj(e);
720
+ const u = str(x.url) ?? "";
721
+ const source = str(x.source) ?? str(x.source_name);
722
+ return {
723
+ title: str(x.title) || u || "Untitled",
724
+ url: u,
725
+ snippet: trimSnippet(
726
+ [
727
+ str(x.description) ?? arr(x.extra_snippets).join(" "),
728
+ source,
729
+ str(x.age) ?? str(x.page_age)
730
+ ].filter(Boolean).join(" \u2014 ")
731
+ ),
732
+ metadata: x
733
+ };
734
+ }).slice(0, clamp(maxResults, 50))
735
+ };
736
+ }
737
+ async function videos(query2, maxResults, config, context, key, options) {
738
+ const params = {
739
+ q: query2,
740
+ count: clamp(maxResults, 50),
741
+ ...mergeOptions(options, "videos", [
742
+ "country",
743
+ "search_lang",
744
+ "ui_lang",
745
+ "freshness",
746
+ "safesearch",
747
+ "spellcheck",
748
+ "offset",
749
+ "count"
750
+ ])
751
+ };
752
+ const r = await fetch(url(config, "/res/v1/videos/search", params), {
753
+ headers: headers(key),
754
+ signal: context.signal
755
+ });
756
+ if (!r.ok) throw new Error(await httpError(r));
757
+ const p = obj(await r.json());
758
+ return {
759
+ provider: "brave",
760
+ results: arr(p.results).map((e) => {
761
+ const x = obj(e);
762
+ const u = str(x.url) ?? "";
763
+ const video = obj(x.video);
764
+ return {
765
+ title: str(x.title) || u || "Untitled",
766
+ url: u,
767
+ snippet: trimSnippet(
768
+ [
769
+ str(x.description),
770
+ str(video.creator) ?? str(x.creator),
771
+ str(video.duration) ?? str(x.duration),
772
+ num(video.views) ? `${num(video.views)} views` : void 0,
773
+ str(x.age) ?? str(x.page_age)
774
+ ].filter(Boolean).join(" \u2014 ")
775
+ ),
776
+ metadata: x
777
+ };
778
+ }).slice(0, clamp(maxResults, 50))
779
+ };
780
+ }
781
+ async function images(query2, maxResults, config, context, key, options) {
782
+ const params = {
783
+ q: query2,
784
+ count: clamp(maxResults),
785
+ ...mergeOptions(options, "images", [
786
+ "country",
787
+ "search_lang",
788
+ "ui_lang",
789
+ "safesearch",
790
+ "spellcheck",
791
+ "count"
792
+ ])
793
+ };
794
+ const r = await fetch(url(config, "/res/v1/images/search", params), {
795
+ headers: headers(key),
796
+ signal: context.signal
797
+ });
798
+ if (!r.ok) throw new Error(await httpError(r));
799
+ const p = obj(await r.json());
800
+ return {
801
+ provider: "brave",
802
+ results: arr(p.results).map((e) => {
803
+ const x = obj(e);
804
+ const props = obj(x.properties);
805
+ const page = str(x.url) ?? str(x.source) ?? str(props.url) ?? "";
806
+ const image = str(props.url);
807
+ return {
808
+ title: str(x.title) || page || "Untitled",
809
+ url: page || image || "",
810
+ snippet: trimSnippet(
811
+ [str(x.description), str(x.publisher), image].filter(Boolean).join(" \u2014 ")
812
+ ),
813
+ metadata: x
814
+ };
815
+ }).slice(0, clamp(maxResults))
816
+ };
817
+ }
818
+ async function places(query2, maxResults, config, context, key, options) {
819
+ const placeOptions = obj(options.places);
820
+ const params = {
821
+ q: query2,
822
+ count: clamp(maxResults),
823
+ ...mergeOptions(options, "places", [
824
+ "country",
825
+ "search_lang",
826
+ "ui_lang",
827
+ "latitude",
828
+ "longitude",
829
+ "location",
830
+ "radius",
831
+ "units",
832
+ "safesearch",
833
+ "spellcheck",
834
+ "geoloc",
835
+ "count"
836
+ ])
837
+ };
838
+ const r = await fetch(url(config, "/res/v1/local/place_search", params), {
839
+ headers: headers(key),
840
+ signal: context.signal
841
+ });
842
+ if (!r.ok) throw new Error(await httpError(r));
843
+ const p = obj(await r.json());
844
+ const rows = arr(p.results).slice(0, clamp(maxResults));
845
+ const ids = rows.map((e) => str(obj(e).id)).filter((id) => id !== void 0);
846
+ const [detailsById, descriptionsById] = await Promise.all([
847
+ placeOptions.includeDetails && ids.length > 0 ? fetchPlaceDetails(
848
+ config,
849
+ context,
850
+ key,
851
+ ids,
852
+ pick(params, ["search_lang", "ui_lang", "units"])
853
+ ) : Promise.resolve(/* @__PURE__ */ new Map()),
854
+ placeOptions.includeDescriptions && ids.length > 0 ? fetchPlaceDescriptions(config, context, key, ids) : Promise.resolve(/* @__PURE__ */ new Map())
855
+ ]);
856
+ return {
857
+ provider: "brave",
858
+ results: rows.map((e) => {
859
+ const x = obj(e);
860
+ const id = str(x.id);
861
+ const details = id ? detailsById.get(id) : void 0;
862
+ const description = id ? descriptionsById.get(id) : void 0;
863
+ const u = str(x.url) ?? str(x.provider_url) ?? "";
864
+ return {
865
+ title: str(x.title) || u || "Untitled",
866
+ url: u,
867
+ snippet: trimSnippet(
868
+ [
869
+ placeDescriptionText(description) ?? str(x.description),
870
+ placeAddress(x, details),
871
+ arr(x.categories).join(", "),
872
+ placeRating(x, details)
873
+ ].filter(Boolean).join(" \u2014 ")
874
+ ),
875
+ metadata: {
876
+ ...x,
877
+ ...details ? { poiDetails: details } : {},
878
+ ...description ? { poiDescription: description } : {}
879
+ }
880
+ };
881
+ })
882
+ };
883
+ }
884
+ async function fetchPlaceDetails(config, context, key, ids, options) {
885
+ const r = await fetch(
886
+ urlWithRepeatedArrays(config, "/res/v1/local/pois", {
887
+ ids: ids.slice(0, 20),
888
+ ...options
889
+ }),
890
+ {
891
+ headers: headers(key),
892
+ signal: context.signal
893
+ }
894
+ );
895
+ if (!r.ok) throw new Error(await httpError(r));
896
+ return indexById(arr(obj(await r.json()).results));
897
+ }
898
+ async function fetchPlaceDescriptions(config, context, key, ids) {
899
+ const r = await fetch(
900
+ urlWithRepeatedArrays(config, "/res/v1/local/descriptions", {
901
+ ids: ids.slice(0, 20)
902
+ }),
903
+ {
904
+ headers: headers(key),
905
+ signal: context.signal
906
+ }
907
+ );
908
+ if (!r.ok) throw new Error(await httpError(r));
909
+ return indexById(arr(obj(await r.json()).results));
910
+ }
911
+ function urlWithRepeatedArrays(config, path, params) {
912
+ const u = new URL(`${base(config)}${path}`);
913
+ for (const [k, v] of Object.entries(params)) {
914
+ if (v === void 0) continue;
915
+ if (Array.isArray(v)) {
916
+ for (const item of v) {
917
+ u.searchParams.append(k, String(item));
918
+ }
919
+ } else {
920
+ u.searchParams.set(k, String(v));
921
+ }
922
+ }
923
+ return u;
924
+ }
925
+ function indexById(values) {
926
+ const result = /* @__PURE__ */ new Map();
927
+ for (const value of values) {
928
+ const entry = obj(value);
929
+ const id = str(entry.id);
930
+ if (id) {
931
+ result.set(id, entry);
932
+ }
933
+ }
934
+ return result;
935
+ }
936
+ function placeDescriptionText(description) {
937
+ if (!description) return void 0;
938
+ return str(description.description) ?? str(description.text) ?? str(description.summary) ?? str(obj(description.description).text);
939
+ }
940
+ function placeAddress(place, details) {
941
+ return str(place.address) ?? str(obj(place.postal_address).displayAddress) ?? str(details?.address) ?? str(obj(details?.postal_address).displayAddress);
942
+ }
943
+ function placeRating(place, details) {
944
+ const rating = num(place.rating) ?? num(obj(place.rating).ratingValue) ?? num(details?.rating) ?? num(obj(details?.rating).ratingValue);
945
+ return rating === void 0 ? void 0 : `Rating: ${rating}`;
946
+ }
947
+ function buildAnswerRequest(raw) {
948
+ const answerOptions = pick(raw, [
949
+ "country",
950
+ "language",
951
+ "safesearch",
952
+ "enable_entities",
953
+ "enable_citations"
954
+ ]);
955
+ if (answerOptions.enable_citations === void 0) {
956
+ answerOptions.enable_citations = true;
957
+ }
958
+ const stream = answerOptions.enable_citations === true || answerOptions.enable_entities === true;
959
+ return {
960
+ stream,
961
+ ...pick(raw, ["max_completion_tokens", "metadata", "seed"]),
962
+ ...answerOptions
963
+ };
964
+ }
965
+ function buildResearchRequest(raw) {
966
+ const researchOptions = pick(raw, [
967
+ "country",
968
+ "language",
969
+ "safesearch",
970
+ "enable_entities",
971
+ "research_allow_thinking",
972
+ "research_maximum_number_of_tokens_per_query",
973
+ "research_maximum_number_of_queries",
974
+ "research_maximum_number_of_iterations",
975
+ "research_maximum_number_of_seconds",
976
+ "research_maximum_number_of_results_per_query"
977
+ ]);
978
+ return {
979
+ stream: true,
980
+ ...pick(raw, ["max_completion_tokens", "metadata", "seed"]),
981
+ ...researchOptions,
982
+ enable_research: true,
983
+ enable_citations: false
984
+ };
985
+ }
986
+ async function completion(input, config, context, request) {
987
+ const key = requireKey(config.credentials?.answers, "Brave Answers");
988
+ const body = {
989
+ model: "brave",
990
+ messages: [{ role: "user", content: input }],
991
+ ...request
992
+ };
993
+ const r = await fetch(`${base(config)}/res/v1/chat/completions`, {
994
+ method: "POST",
995
+ headers: headers(key, true),
996
+ body: JSON.stringify(body),
997
+ signal: context.signal
998
+ });
999
+ if (!r.ok) throw new Error(await httpError(r));
1000
+ const text = await r.text();
1001
+ const parsed = body.stream === false ? parseAnswerJson(text) : parseAnswerStream(text);
1002
+ const lines = [parsed.answer.trim() || text.trim()];
1003
+ if (parsed.citations.length) {
1004
+ lines.push("", "Sources:");
1005
+ parsed.citations.forEach((c, i) => {
1006
+ lines.push(`${i + 1}. ${c.title ?? c.url ?? "Source"}`);
1007
+ if (c.url) lines.push(` ${c.url}`);
1008
+ });
1009
+ }
1010
+ const metadata = buildAnswerMetadata(parsed);
1011
+ return {
1012
+ provider: "brave",
1013
+ text: lines.join("\n").trimEnd(),
1014
+ itemCount: parsed.citations.length,
1015
+ ...metadata ? { metadata } : {}
1016
+ };
1017
+ }
1018
+ function buildAnswerMetadata(parsed) {
1019
+ const metadata = {};
1020
+ if (parsed.usage) {
1021
+ metadata.usage = parsed.usage;
1022
+ }
1023
+ if (parsed.entities.length) {
1024
+ metadata.entities = parsed.entities;
1025
+ }
1026
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
1027
+ }
1028
+ function parseAnswerJson(text) {
1029
+ const payload = obj(JSON.parse(text));
1030
+ const choice = obj(arr(payload.choices)[0]);
1031
+ const message = obj(choice.message);
1032
+ const content = str(message.content) ?? "";
1033
+ const tags = extractBraveTags(content);
1034
+ return {
1035
+ answer: tags.text,
1036
+ citations: dedupeCitations(tags.citations),
1037
+ entities: tags.entities,
1038
+ usage: payload.usage ?? tags.usage
1039
+ };
1040
+ }
1041
+ function parseAnswerStream(text) {
1042
+ let rawAnswer = "";
1043
+ for (const line of text.split(/\r?\n/)) {
1044
+ const data = line.startsWith("data:") ? line.slice(5).trim() : line.trim();
1045
+ if (!data || data === "[DONE]") continue;
1046
+ try {
1047
+ const parsed = obj(JSON.parse(data));
1048
+ const choice = obj(arr(parsed.choices)[0]);
1049
+ const delta = str(obj(choice.delta).content);
1050
+ if (delta) {
1051
+ rawAnswer += delta;
1052
+ }
1053
+ } catch {
1054
+ rawAnswer += data;
1055
+ }
1056
+ }
1057
+ const tags = extractBraveTags(rawAnswer);
1058
+ return {
1059
+ answer: tags.text,
1060
+ citations: dedupeCitations(tags.citations),
1061
+ entities: tags.entities,
1062
+ usage: tags.usage
1063
+ };
1064
+ }
1065
+ function extractBraveTags(text) {
1066
+ const citations = [];
1067
+ const entities = [];
1068
+ let usage;
1069
+ let cleaned = "";
1070
+ let offset = 0;
1071
+ while (offset < text.length) {
1072
+ const tagStart = text.indexOf("<", offset);
1073
+ if (tagStart === -1) {
1074
+ cleaned += text.slice(offset);
1075
+ break;
1076
+ }
1077
+ const parsedTag = readBraveTagStart(text, tagStart);
1078
+ if (!parsedTag) {
1079
+ cleaned += text.slice(offset, tagStart + 1);
1080
+ offset = tagStart + 1;
1081
+ continue;
1082
+ }
1083
+ const jsonEnd = findJsonValueEnd(text, parsedTag.jsonStart);
1084
+ if (jsonEnd === -1) {
1085
+ cleaned += text.slice(offset);
1086
+ break;
1087
+ }
1088
+ cleaned += text.slice(offset, tagStart);
1089
+ const json = text.slice(parsedTag.jsonStart, jsonEnd + 1);
1090
+ try {
1091
+ const parsed = JSON.parse(json);
1092
+ if (parsedTag.tag === "answer") {
1093
+ cleaned += str(parsed.answer) ?? str(parsed.text) ?? "";
1094
+ } else if (parsedTag.tag === "citation") {
1095
+ citations.push({
1096
+ title: str(parsed.title),
1097
+ url: str(parsed.url)
1098
+ });
1099
+ } else if (parsedTag.tag === "enum_item") {
1100
+ const item = obj(parsed);
1101
+ entities.push(item);
1102
+ cleaned += str(item.original_tokens) ?? str(item.name) ?? str(item.href) ?? "";
1103
+ } else if (parsedTag.tag === "usage") {
1104
+ usage = parsed;
1105
+ }
1106
+ } catch {
1107
+ }
1108
+ const closing = `</${parsedTag.tag}>`;
1109
+ const abbreviatedClosing = `</${parsedTag.tag}`;
1110
+ if (text.startsWith(closing, jsonEnd + 1)) {
1111
+ offset = jsonEnd + 1 + closing.length;
1112
+ } else if (text.startsWith(abbreviatedClosing, jsonEnd + 1)) {
1113
+ offset = jsonEnd + 1 + abbreviatedClosing.length;
1114
+ } else {
1115
+ offset = jsonEnd + 1;
1116
+ }
1117
+ }
1118
+ return { text: cleaned, citations, entities, usage };
1119
+ }
1120
+ var BRAVE_STRUCTURED_TAGS = /* @__PURE__ */ new Set([
1121
+ "analyzing",
1122
+ "answer",
1123
+ "blindspots",
1124
+ "citation",
1125
+ "enum_item",
1126
+ "progress",
1127
+ "queries",
1128
+ "thinking",
1129
+ "usage"
1130
+ ]);
1131
+ function readBraveTagStart(text, tagStart) {
1132
+ const match = /^<([A-Za-z_][A-Za-z0-9_-]*)(>)?[{[]/.exec(
1133
+ text.slice(tagStart)
1134
+ );
1135
+ if (!match) return void 0;
1136
+ const tag = match[1];
1137
+ if (!BRAVE_STRUCTURED_TAGS.has(tag)) return void 0;
1138
+ return {
1139
+ tag,
1140
+ jsonStart: tagStart + match[0].length - 1
1141
+ };
1142
+ }
1143
+ function findJsonValueEnd(text, start) {
1144
+ const first = text[start];
1145
+ const closing = first === "{" ? "}" : first === "[" ? "]" : void 0;
1146
+ if (!closing) return -1;
1147
+ const stack = [];
1148
+ let inString = false;
1149
+ let escaped = false;
1150
+ for (let index = start; index < text.length; index++) {
1151
+ const char = text[index];
1152
+ if (escaped) {
1153
+ escaped = false;
1154
+ continue;
1155
+ }
1156
+ if (char === "\\") {
1157
+ escaped = true;
1158
+ continue;
1159
+ }
1160
+ if (char === '"') {
1161
+ inString = !inString;
1162
+ continue;
1163
+ }
1164
+ if (inString) continue;
1165
+ if (char === "{") {
1166
+ stack.push("}");
1167
+ } else if (char === "[") {
1168
+ stack.push("]");
1169
+ } else if (char === "}" || char === "]") {
1170
+ if (stack.pop() !== char) return -1;
1171
+ if (stack.length === 0) return index;
57
1172
  }
58
1173
  }
59
- const envValue = process.env[reference];
60
- if (envValue !== void 0) {
61
- return envValue;
62
- }
63
- if (/^[A-Z][A-Z0-9_]*$/.test(reference)) {
64
- return void 0;
65
- }
66
- return reference;
1174
+ return -1;
67
1175
  }
68
- function resolveEnvMap(envMap) {
69
- if (!envMap) return void 0;
70
- const resolved = Object.fromEntries(
71
- Object.entries(envMap).map(([key, value]) => [key, resolveConfigValue(value)]).filter(
72
- (entry) => typeof entry[1] === "string"
73
- )
74
- );
75
- return Object.keys(resolved).length > 0 ? resolved : void 0;
1176
+ function dedupeCitations(citations) {
1177
+ const seen = /* @__PURE__ */ new Set();
1178
+ return citations.filter((citation) => {
1179
+ const key = citation.url ?? citation.title;
1180
+ if (!key) return false;
1181
+ if (seen.has(key)) return false;
1182
+ seen.add(key);
1183
+ return true;
1184
+ });
76
1185
  }
1186
+ var braveProvider = defineProvider({
1187
+ id: "brave",
1188
+ label: braveImplementation.label,
1189
+ docsUrl: braveImplementation.docsUrl,
1190
+ config: {
1191
+ createTemplate: () => braveImplementation.createTemplate(),
1192
+ fields: ["credentials", "baseUrl", "options", "settings"],
1193
+ credentials: {
1194
+ search: "BRAVE_SEARCH_API_KEY",
1195
+ answers: "BRAVE_ANSWERS_API_KEY",
1196
+ autosuggest: "BRAVE_AUTOSUGGEST_API_KEY"
1197
+ },
1198
+ optionCapabilities: ["search", "answer", "research"]
1199
+ },
1200
+ getCapabilityStatus: (config, cwd, tool) => braveImplementation.getCapabilityStatus(
1201
+ config,
1202
+ cwd,
1203
+ tool
1204
+ ),
1205
+ capabilities: {
1206
+ search: defineCapability({
1207
+ options: braveImplementation.getToolOptionsSchema("search"),
1208
+ promptGuidelines: braveSearchPromptGuidelines,
1209
+ async execute(input, ctx) {
1210
+ return await braveImplementation.search(
1211
+ input.query,
1212
+ input.maxResults,
1213
+ ctx.config,
1214
+ ctx,
1215
+ input.options
1216
+ );
1217
+ }
1218
+ }),
1219
+ answer: defineCapability({
1220
+ options: braveImplementation.getToolOptionsSchema("answer"),
1221
+ async execute(input, ctx) {
1222
+ return await braveImplementation.answer(
1223
+ input.query,
1224
+ ctx.config,
1225
+ ctx,
1226
+ input.options
1227
+ );
1228
+ }
1229
+ }),
1230
+ research: defineCapability({
1231
+ options: braveImplementation.getToolOptionsSchema("research"),
1232
+ async execute(input, ctx) {
1233
+ return await braveImplementation.research(
1234
+ input.input,
1235
+ ctx.config,
1236
+ ctx,
1237
+ input.options
1238
+ );
1239
+ }
1240
+ })
1241
+ }
1242
+ });
77
1243
 
78
1244
  // src/providers/claude.ts
79
1245
  import { existsSync } from "node:fs";
80
1246
  import { query } from "@anthropic-ai/claude-agent-sdk";
81
- import { Type as Type2 } from "typebox";
1247
+ import { Type as Type3 } from "typebox";
82
1248
 
83
1249
  // src/providers/schema.ts
84
- import { Type } from "typebox";
1250
+ import { Type as Type2 } from "typebox";
85
1251
  function literalUnion(values, options) {
86
- return Type.Union(
87
- values.map((value) => Type.Literal(value)),
1252
+ return Type2.Union(
1253
+ values.map((value) => Type2.Literal(value)),
88
1254
  options
89
1255
  );
90
1256
  }
91
1257
 
92
- // src/providers/shared.ts
93
- function trimSnippet(input, maxLength = 300) {
94
- const text = (input ?? "").replace(/\s+/g, " ").trim();
95
- if (text.length <= maxLength) return text;
96
- return `${text.slice(0, maxLength - 1)}\u2026`;
97
- }
98
- function normalizeContentText(input) {
99
- const text = (input ?? "").replace(/\r/g, "").trim();
100
- if (!text) {
101
- return "";
102
- }
103
- return text.split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n").replace(/\n{3,}/g, "\n\n");
104
- }
105
- function asJsonObject(value) {
106
- return value ? { ...value } : {};
107
- }
108
- function formatJson(value) {
109
- return JSON.stringify(value, null, 2);
110
- }
111
- function getApiKeyStatus(apiKeyReference) {
112
- try {
113
- return resolveConfigValue(apiKeyReference) ? { state: "ready" } : { state: "missing_api_key" };
114
- } catch (error) {
115
- return {
116
- state: "invalid_config",
117
- detail: formatConfigValueError(error)
118
- };
119
- }
120
- }
121
- function formatConfigValueError(error) {
122
- const message = error instanceof Error ? error.message : String(error);
123
- return message.replace(/\s+/g, " ").trim() || "Failed to resolve config value";
124
- }
125
-
126
- // src/providers/definition.ts
127
- function defineCapability(definition) {
128
- return definition;
129
- }
130
- function defineProvider(definition) {
131
- return definition;
132
- }
133
- function defineProviders(providers) {
134
- return providers;
135
- }
136
- async function executeProviderCapability(definition, capability, input, context) {
137
- const handler = definition.capabilities[capability];
138
- if (!handler) {
139
- throw new Error(
140
- `Provider '${definition.id}' does not support '${capability}'.`
141
- );
142
- }
143
- return await handler.execute(input, context);
144
- }
145
-
146
1258
  // src/providers/claude.ts
147
1259
  var SEARCH_OUTPUT_SCHEMA = {
148
1260
  type: "object",
@@ -184,36 +1296,36 @@ var ANSWER_OUTPUT_SCHEMA = {
184
1296
  },
185
1297
  required: ["answer", "sources"]
186
1298
  };
187
- var claudeOptionsSchema = Type2.Object(
1299
+ var claudeOptionsSchema = Type3.Object(
188
1300
  {
189
- model: Type2.Optional(
190
- Type2.String({ description: "Claude model override." })
1301
+ model: Type3.Optional(
1302
+ Type3.String({ description: "Claude model override." })
191
1303
  ),
192
- effort: Type2.Optional(
1304
+ effort: Type3.Optional(
193
1305
  literalUnion(["low", "medium", "high", "max"], {
194
1306
  description: "How much effort Claude should use."
195
1307
  })
196
1308
  ),
197
- maxTurns: Type2.Optional(
198
- Type2.Integer({
1309
+ maxTurns: Type3.Optional(
1310
+ Type3.Integer({
199
1311
  minimum: 1,
200
1312
  description: "Maximum number of Claude turns."
201
1313
  })
202
1314
  ),
203
- maxThinkingTokens: Type2.Optional(
204
- Type2.Integer({ minimum: 0, description: "Maximum thinking tokens." })
1315
+ maxThinkingTokens: Type3.Optional(
1316
+ Type3.Integer({ minimum: 0, description: "Maximum thinking tokens." })
205
1317
  ),
206
- maxBudgetUsd: Type2.Optional(
207
- Type2.Number({
1318
+ maxBudgetUsd: Type3.Optional(
1319
+ Type3.Number({
208
1320
  exclusiveMinimum: 0,
209
1321
  description: "Maximum budget in USD."
210
1322
  })
211
1323
  ),
212
- thinking: Type2.Optional(
213
- Type2.Object(
1324
+ thinking: Type3.Optional(
1325
+ Type3.Object(
214
1326
  {
215
- type: Type2.Optional(
216
- Type2.String({ description: "Claude thinking mode." })
1327
+ type: Type3.Optional(
1328
+ Type3.String({ description: "Claude thinking mode." })
217
1329
  )
218
1330
  },
219
1331
  {
@@ -508,13 +1620,13 @@ var claudeProvider = defineProvider({
508
1620
 
509
1621
  // src/providers/cloudflare.ts
510
1622
  import CloudflareClient from "cloudflare";
511
- import { Type as Type3 } from "typebox";
512
- var cloudflareContentsOptionsSchema = Type3.Object(
1623
+ import { Type as Type4 } from "typebox";
1624
+ var cloudflareContentsOptionsSchema = Type4.Object(
513
1625
  {
514
- gotoOptions: Type3.Optional(
515
- Type3.Object(
1626
+ gotoOptions: Type4.Optional(
1627
+ Type4.Object(
516
1628
  {
517
- waitUntil: Type3.Optional(
1629
+ waitUntil: Type4.Optional(
518
1630
  literalUnion(
519
1631
  ["load", "domcontentloaded", "networkidle0", "networkidle2"],
520
1632
  { description: "When to consider navigation complete." }
@@ -545,7 +1657,7 @@ var cloudflareImplementation = {
545
1657
  },
546
1658
  createTemplate() {
547
1659
  return {
548
- apiToken: "CLOUDFLARE_API_TOKEN",
1660
+ credentials: { api: "CLOUDFLARE_API_TOKEN" },
549
1661
  accountId: "CLOUDFLARE_ACCOUNT_ID",
550
1662
  options: {
551
1663
  gotoOptions: {
@@ -555,7 +1667,7 @@ var cloudflareImplementation = {
555
1667
  };
556
1668
  },
557
1669
  getCapabilityStatus(config) {
558
- const apiTokenStatus = getApiKeyStatus(config?.apiToken);
1670
+ const apiTokenStatus = getApiKeyStatus(config?.credentials?.api);
559
1671
  if (apiTokenStatus.state !== "ready") {
560
1672
  return apiTokenStatus;
561
1673
  }
@@ -579,24 +1691,24 @@ var cloudflareImplementation = {
579
1691
  }
580
1692
  const defaults = asJsonObject(config.options);
581
1693
  const answers = await Promise.all(
582
- urls.map(async (url) => {
1694
+ urls.map(async (url2) => {
583
1695
  try {
584
1696
  const markdown = await client.browserRendering.markdown.create(
585
1697
  {
586
1698
  ...defaults ?? {},
587
1699
  ...options ?? {},
588
1700
  account_id: accountId,
589
- url
1701
+ url: url2
590
1702
  },
591
1703
  buildRequestOptions(context)
592
1704
  );
593
1705
  return {
594
- url,
1706
+ url: url2,
595
1707
  content: markdown
596
1708
  };
597
1709
  } catch (error) {
598
1710
  return {
599
- url,
1711
+ url: url2,
600
1712
  error: error instanceof Error ? error.message : String(error)
601
1713
  };
602
1714
  }
@@ -609,7 +1721,7 @@ var cloudflareImplementation = {
609
1721
  }
610
1722
  };
611
1723
  function createClient(config) {
612
- const apiToken = resolveConfigValue(config.apiToken);
1724
+ const apiToken = resolveConfigValue(config.credentials?.api);
613
1725
  if (!apiToken) {
614
1726
  throw new Error("is missing an API token");
615
1727
  }
@@ -626,7 +1738,7 @@ var cloudflareProvider = defineProvider({
626
1738
  docsUrl: cloudflareImplementation.docsUrl,
627
1739
  config: {
628
1740
  createTemplate: () => cloudflareImplementation.createTemplate(),
629
- fields: ["apiToken", "accountId", "options", "settings"]
1741
+ fields: ["credentials", "accountId", "options", "settings"]
630
1742
  },
631
1743
  getCapabilityStatus: (config, cwd, tool) => cloudflareImplementation.getCapabilityStatus(
632
1744
  config,
@@ -650,15 +1762,15 @@ var cloudflareProvider = defineProvider({
650
1762
 
651
1763
  // src/providers/codex.ts
652
1764
  import { Codex as CodexClient } from "@openai/codex-sdk";
653
- import { Type as Type4 } from "typebox";
654
- var codexOutputSchema = Type4.Object(
1765
+ import { Type as Type5 } from "typebox";
1766
+ var codexOutputSchema = Type5.Object(
655
1767
  {
656
- sources: Type4.Array(
657
- Type4.Object(
1768
+ sources: Type5.Array(
1769
+ Type5.Object(
658
1770
  {
659
- title: Type4.String(),
660
- url: Type4.String(),
661
- snippet: Type4.String()
1771
+ title: Type5.String(),
1772
+ url: Type5.String(),
1773
+ snippet: Type5.String()
662
1774
  },
663
1775
  { additionalProperties: false }
664
1776
  )
@@ -666,29 +1778,31 @@ var codexOutputSchema = Type4.Object(
666
1778
  },
667
1779
  { additionalProperties: false }
668
1780
  );
669
- var codexSearchOptionsSchema = Type4.Object(
1781
+ var codexSearchOptionsSchema = Type5.Object(
670
1782
  {
671
- model: Type4.Optional(Type4.String({ description: "Codex model override." })),
672
- modelReasoningEffort: Type4.Optional(
673
- Type4.Union(
1783
+ model: Type5.Optional(Type5.String({ description: "Codex model override." })),
1784
+ modelReasoningEffort: Type5.Optional(
1785
+ Type5.Union(
674
1786
  [
675
- Type4.Literal("minimal"),
676
- Type4.Literal("low"),
677
- Type4.Literal("medium"),
678
- Type4.Literal("high"),
679
- Type4.Literal("xhigh")
1787
+ Type5.Literal("minimal"),
1788
+ Type5.Literal("low"),
1789
+ Type5.Literal("medium"),
1790
+ Type5.Literal("high"),
1791
+ Type5.Literal("xhigh")
680
1792
  ],
681
1793
  { description: "Reasoning depth for Codex." }
682
1794
  )
683
1795
  ),
684
- webSearchMode: Type4.Optional(
685
- Type4.Union(
1796
+ webSearchMode: Type5.Optional(
1797
+ Type5.Union(
686
1798
  [
687
- Type4.Literal("disabled"),
688
- Type4.Literal("cached"),
689
- Type4.Literal("live")
1799
+ Type5.Literal("disabled"),
1800
+ Type5.Literal("cached"),
1801
+ Type5.Literal("live")
690
1802
  ],
691
- { description: "How Codex should source web results." }
1803
+ {
1804
+ description: "How Codex should source web results. Use 'live' for current information, 'cached' when freshness is less important, and 'disabled' only when web access should not be used."
1805
+ }
692
1806
  )
693
1807
  )
694
1808
  },
@@ -734,7 +1848,7 @@ var codexImplementation = {
734
1848
  const codex = new CodexClient({
735
1849
  codexPathOverride: config.codexPath,
736
1850
  baseUrl: config.baseUrl,
737
- apiKey: resolveConfigValue(config.apiKey),
1851
+ apiKey: resolveConfigValue(config.credentials?.api),
738
1852
  config: config.config,
739
1853
  env: resolveEnvMap(config.env)
740
1854
  });
@@ -859,7 +1973,7 @@ var codexProvider = defineProvider({
859
1973
  fields: [
860
1974
  "codexPath",
861
1975
  "baseUrl",
862
- "apiKey",
1976
+ "credentials",
863
1977
  "env",
864
1978
  "config",
865
1979
  "options",
@@ -1159,7 +2273,7 @@ function parseContentsAnswer(entry, index) {
1159
2273
  entry,
1160
2274
  `contents answer at index ${index} must be a JSON object`
1161
2275
  );
1162
- const url = readRequiredString(value.url, `answers[${index}].url`);
2276
+ const url2 = readRequiredString(value.url, `answers[${index}].url`);
1163
2277
  const content = readOptionalString(
1164
2278
  value.content,
1165
2279
  `answers[${index}].content`
@@ -1176,7 +2290,7 @@ function parseContentsAnswer(entry, index) {
1176
2290
  );
1177
2291
  }
1178
2292
  return {
1179
- url,
2293
+ url: url2,
1180
2294
  ...content !== void 0 ? { content } : {},
1181
2295
  ...summary !== void 0 ? { summary } : {},
1182
2296
  ...metadata !== void 0 ? { metadata } : {},
@@ -1291,7 +2405,7 @@ var customProvider = defineProvider({
1291
2405
 
1292
2406
  // src/providers/exa.ts
1293
2407
  import { Exa as ExaClient } from "exa-js";
1294
- import { Type as Type5 } from "typebox";
2408
+ import { Type as Type6 } from "typebox";
1295
2409
 
1296
2410
  // src/execution-policy-defaults.ts
1297
2411
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
@@ -1656,9 +2770,9 @@ function normalizeError(error) {
1656
2770
  }
1657
2771
 
1658
2772
  // src/providers/exa.ts
1659
- var exaSearchOptionsSchema = Type5.Object(
2773
+ var exaSearchOptionsSchema = Type6.Object(
1660
2774
  {
1661
- type: Type5.Optional(
2775
+ type: Type6.Optional(
1662
2776
  literalUnion(
1663
2777
  [
1664
2778
  "keyword",
@@ -1674,41 +2788,41 @@ var exaSearchOptionsSchema = Type5.Object(
1674
2788
  { description: "Exa search mode." }
1675
2789
  )
1676
2790
  ),
1677
- category: Type5.Optional(
1678
- Type5.String({
2791
+ category: Type6.Optional(
2792
+ Type6.String({
1679
2793
  description: "Filter by category (e.g., 'company', 'research paper')."
1680
2794
  })
1681
2795
  ),
1682
- includeDomains: Type5.Optional(
1683
- Type5.Array(Type5.String(), {
2796
+ includeDomains: Type6.Optional(
2797
+ Type6.Array(Type6.String(), {
1684
2798
  description: "Restrict results to these domains."
1685
2799
  })
1686
2800
  ),
1687
- excludeDomains: Type5.Optional(
1688
- Type5.Array(Type5.String(), { description: "Exclude these domains." })
2801
+ excludeDomains: Type6.Optional(
2802
+ Type6.Array(Type6.String(), { description: "Exclude these domains." })
1689
2803
  ),
1690
- startPublishedDate: Type5.Optional(
1691
- Type5.String({
2804
+ startPublishedDate: Type6.Optional(
2805
+ Type6.String({
1692
2806
  description: "ISO date string for earliest publish date."
1693
2807
  })
1694
2808
  ),
1695
- endPublishedDate: Type5.Optional(
1696
- Type5.String({ description: "ISO date string for latest publish date." })
2809
+ endPublishedDate: Type6.Optional(
2810
+ Type6.String({ description: "ISO date string for latest publish date." })
1697
2811
  ),
1698
- userLocation: Type5.Optional(
1699
- Type5.Object(
2812
+ userLocation: Type6.Optional(
2813
+ Type6.Object(
1700
2814
  {
1701
- country: Type5.Optional(
1702
- Type5.String({ description: "Country hint for the user location." })
2815
+ country: Type6.Optional(
2816
+ Type6.String({ description: "Country hint for the user location." })
1703
2817
  ),
1704
- region: Type5.Optional(
1705
- Type5.String({ description: "Region hint for the user location." })
2818
+ region: Type6.Optional(
2819
+ Type6.String({ description: "Region hint for the user location." })
1706
2820
  ),
1707
- city: Type5.Optional(
1708
- Type5.String({ description: "City hint for the user location." })
2821
+ city: Type6.Optional(
2822
+ Type6.String({ description: "City hint for the user location." })
1709
2823
  ),
1710
- timezone: Type5.Optional(
1711
- Type5.String({
2824
+ timezone: Type6.Optional(
2825
+ Type6.String({
1712
2826
  description: "Timezone hint for the user location."
1713
2827
  })
1714
2828
  )
@@ -1718,17 +2832,17 @@ var exaSearchOptionsSchema = Type5.Object(
1718
2832
  }
1719
2833
  )
1720
2834
  ),
1721
- contents: Type5.Optional(
1722
- Type5.Object(
2835
+ contents: Type6.Optional(
2836
+ Type6.Object(
1723
2837
  {
1724
- text: Type5.Optional(
1725
- Type5.Boolean({ description: "Include text content." })
2838
+ text: Type6.Optional(
2839
+ Type6.Boolean({ description: "Include text content." })
1726
2840
  ),
1727
- highlights: Type5.Optional(
1728
- Type5.Boolean({ description: "Include highlighted excerpts." })
2841
+ highlights: Type6.Optional(
2842
+ Type6.Boolean({ description: "Include highlighted excerpts." })
1729
2843
  ),
1730
- summary: Type5.Optional(
1731
- Type5.Boolean({ description: "Include AI-generated summary." })
2844
+ summary: Type6.Optional(
2845
+ Type6.Boolean({ description: "Include AI-generated summary." })
1732
2846
  )
1733
2847
  },
1734
2848
  { description: "What content to include in results." }
@@ -1751,7 +2865,7 @@ var exaImplementation = {
1751
2865
  },
1752
2866
  createTemplate() {
1753
2867
  return {
1754
- apiKey: "EXA_API_KEY",
2868
+ credentials: { api: "EXA_API_KEY" },
1755
2869
  options: {
1756
2870
  search: {
1757
2871
  type: "auto",
@@ -1763,7 +2877,7 @@ var exaImplementation = {
1763
2877
  };
1764
2878
  },
1765
2879
  getCapabilityStatus(config) {
1766
- return getApiKeyStatus(config?.apiKey);
2880
+ return getApiKeyStatus(config?.credentials?.api);
1767
2881
  },
1768
2882
  async search(query2, maxResults, config, _context, searchOptions) {
1769
2883
  const client = createClient2(config);
@@ -1791,16 +2905,16 @@ var exaImplementation = {
1791
2905
  const results = response.results ?? [];
1792
2906
  return {
1793
2907
  provider: exaImplementation.id,
1794
- answers: urls.map((url, index) => {
2908
+ answers: urls.map((url2, index) => {
1795
2909
  const result = results[index];
1796
2910
  if (!result) {
1797
2911
  return {
1798
- url,
2912
+ url: url2,
1799
2913
  error: "No content returned for this URL."
1800
2914
  };
1801
2915
  }
1802
2916
  return {
1803
- url,
2917
+ url: url2,
1804
2918
  ...typeof result.text === "string" ? { content: result.text } : {},
1805
2919
  ...result.summary !== void 0 ? { summary: result.summary } : {},
1806
2920
  metadata: result
@@ -1883,7 +2997,7 @@ var exaImplementation = {
1883
2997
  }
1884
2998
  };
1885
2999
  function createClient2(config) {
1886
- const apiKey = resolveConfigValue(config.apiKey);
3000
+ const apiKey = resolveConfigValue(config.credentials?.api);
1887
3001
  if (!apiKey) {
1888
3002
  throw new Error("is missing an API key");
1889
3003
  }
@@ -1895,7 +3009,7 @@ var exaProvider = defineProvider({
1895
3009
  docsUrl: exaImplementation.docsUrl,
1896
3010
  config: {
1897
3011
  createTemplate: () => exaImplementation.createTemplate(),
1898
- fields: ["apiKey", "baseUrl", "options", "settings"],
3012
+ fields: ["credentials", "baseUrl", "options", "settings"],
1899
3013
  optionCapabilities: ["search"]
1900
3014
  },
1901
3015
  getCapabilityStatus: (config, cwd, tool) => exaImplementation.getCapabilityStatus(
@@ -1955,55 +3069,55 @@ var exaProvider = defineProvider({
1955
3069
 
1956
3070
  // src/providers/firecrawl.ts
1957
3071
  import FirecrawlClient from "@mendable/firecrawl-js";
1958
- import { Type as Type6 } from "typebox";
1959
- var firecrawlSearchOptionsSchema = Type6.Object(
3072
+ import { Type as Type7 } from "typebox";
3073
+ var firecrawlSearchOptionsSchema = Type7.Object(
1960
3074
  {
1961
- lang: Type6.Optional(
1962
- Type6.String({
3075
+ lang: Type7.Optional(
3076
+ Type7.String({
1963
3077
  description: "Language code for search results (for example 'en')."
1964
3078
  })
1965
3079
  ),
1966
- country: Type6.Optional(
1967
- Type6.String({
3080
+ country: Type7.Optional(
3081
+ Type7.String({
1968
3082
  description: "Country code for search results (for example 'us')."
1969
3083
  })
1970
3084
  ),
1971
- sources: Type6.Optional(
1972
- Type6.Array(Type6.String(), {
3085
+ sources: Type7.Optional(
3086
+ Type7.Array(Type7.String(), {
1973
3087
  description: "Search source groups to include."
1974
3088
  })
1975
3089
  ),
1976
- categories: Type6.Optional(
1977
- Type6.Array(Type6.String(), {
3090
+ categories: Type7.Optional(
3091
+ Type7.Array(Type7.String(), {
1978
3092
  description: "Search categories to include."
1979
3093
  })
1980
3094
  ),
1981
- location: Type6.Optional(
1982
- Type6.Object(
3095
+ location: Type7.Optional(
3096
+ Type7.Object(
1983
3097
  {
1984
- country: Type6.Optional(Type6.String({ description: "Country hint." })),
1985
- region: Type6.Optional(Type6.String({ description: "Region hint." })),
1986
- city: Type6.Optional(Type6.String({ description: "City hint." }))
3098
+ country: Type7.Optional(Type7.String({ description: "Country hint." })),
3099
+ region: Type7.Optional(Type7.String({ description: "Region hint." })),
3100
+ city: Type7.Optional(Type7.String({ description: "City hint." }))
1987
3101
  },
1988
3102
  { description: "Location hint for search." }
1989
3103
  )
1990
3104
  ),
1991
- timeout: Type6.Optional(
1992
- Type6.Integer({
3105
+ timeout: Type7.Optional(
3106
+ Type7.Integer({
1993
3107
  minimum: 0,
1994
3108
  description: "Request timeout in milliseconds."
1995
3109
  })
1996
3110
  ),
1997
- scrapeOptions: Type6.Optional(
1998
- Type6.Object(
3111
+ scrapeOptions: Type7.Optional(
3112
+ Type7.Object(
1999
3113
  {
2000
- formats: Type6.Optional(
2001
- Type6.Array(literalUnion(["markdown", "html", "rawHtml"]), {
3114
+ formats: Type7.Optional(
3115
+ Type7.Array(literalUnion(["markdown", "html", "rawHtml"]), {
2002
3116
  description: "Output formats."
2003
3117
  })
2004
3118
  ),
2005
- onlyMainContent: Type6.Optional(
2006
- Type6.Boolean({ description: "Extract only the main content." })
3119
+ onlyMainContent: Type7.Optional(
3120
+ Type7.Boolean({ description: "Extract only the main content." })
2007
3121
  )
2008
3122
  },
2009
3123
  {
@@ -2014,48 +3128,48 @@ var firecrawlSearchOptionsSchema = Type6.Object(
2014
3128
  },
2015
3129
  { description: "Firecrawl search options." }
2016
3130
  );
2017
- var firecrawlScrapeOptionsSchema = Type6.Object(
3131
+ var firecrawlScrapeOptionsSchema = Type7.Object(
2018
3132
  {
2019
- formats: Type6.Optional(
2020
- Type6.Array(literalUnion(["markdown", "html", "rawHtml"]), {
3133
+ formats: Type7.Optional(
3134
+ Type7.Array(literalUnion(["markdown", "html", "rawHtml"]), {
2021
3135
  description: "Output formats for scraping."
2022
3136
  })
2023
3137
  ),
2024
- onlyMainContent: Type6.Optional(
2025
- Type6.Boolean({ description: "Extract only the main content." })
3138
+ onlyMainContent: Type7.Optional(
3139
+ Type7.Boolean({ description: "Extract only the main content." })
2026
3140
  ),
2027
- includeTags: Type6.Optional(
2028
- Type6.Array(Type6.String(), { description: "CSS selectors to include." })
3141
+ includeTags: Type7.Optional(
3142
+ Type7.Array(Type7.String(), { description: "CSS selectors to include." })
2029
3143
  ),
2030
- excludeTags: Type6.Optional(
2031
- Type6.Array(Type6.String(), { description: "CSS selectors to exclude." })
3144
+ excludeTags: Type7.Optional(
3145
+ Type7.Array(Type7.String(), { description: "CSS selectors to exclude." })
2032
3146
  ),
2033
- waitFor: Type6.Optional(
2034
- Type6.Integer({
3147
+ waitFor: Type7.Optional(
3148
+ Type7.Integer({
2035
3149
  minimum: 0,
2036
3150
  description: "Milliseconds to wait before scraping."
2037
3151
  })
2038
3152
  ),
2039
- headers: Type6.Optional(
2040
- Type6.Record(Type6.String(), Type6.String(), {
3153
+ headers: Type7.Optional(
3154
+ Type7.Record(Type7.String(), Type7.String(), {
2041
3155
  description: "Headers to send when scraping."
2042
3156
  })
2043
3157
  ),
2044
- location: Type6.Optional(
2045
- Type6.Object(
3158
+ location: Type7.Optional(
3159
+ Type7.Object(
2046
3160
  {
2047
- country: Type6.Optional(Type6.String({ description: "Country hint." })),
2048
- region: Type6.Optional(Type6.String({ description: "Region hint." })),
2049
- city: Type6.Optional(Type6.String({ description: "City hint." }))
3161
+ country: Type7.Optional(Type7.String({ description: "Country hint." })),
3162
+ region: Type7.Optional(Type7.String({ description: "Region hint." })),
3163
+ city: Type7.Optional(Type7.String({ description: "City hint." }))
2050
3164
  },
2051
3165
  { description: "Location hint for scraping." }
2052
3166
  )
2053
3167
  ),
2054
- mobile: Type6.Optional(
2055
- Type6.Boolean({ description: "Use a mobile browser profile." })
3168
+ mobile: Type7.Optional(
3169
+ Type7.Boolean({ description: "Use a mobile browser profile." })
2056
3170
  ),
2057
- proxy: Type6.Optional(
2058
- Type6.String({
3171
+ proxy: Type7.Optional(
3172
+ Type7.String({
2059
3173
  description: "Proxy mode passed through to the Firecrawl SDK."
2060
3174
  })
2061
3175
  )
@@ -2078,7 +3192,7 @@ var firecrawlImplementation = {
2078
3192
  },
2079
3193
  createTemplate() {
2080
3194
  return {
2081
- apiKey: "FIRECRAWL_API_KEY",
3195
+ credentials: { api: "FIRECRAWL_API_KEY" },
2082
3196
  options: {
2083
3197
  scrape: {
2084
3198
  formats: ["markdown"],
@@ -2088,7 +3202,7 @@ var firecrawlImplementation = {
2088
3202
  };
2089
3203
  },
2090
3204
  getCapabilityStatus(config) {
2091
- return getApiKeyStatus(config?.apiKey);
3205
+ return getApiKeyStatus(config?.credentials?.api);
2092
3206
  },
2093
3207
  async search(query2, maxResults, config, _context, options) {
2094
3208
  const client = createClient3(config);
@@ -2115,23 +3229,23 @@ var firecrawlImplementation = {
2115
3229
  return {
2116
3230
  provider: firecrawlImplementation.id,
2117
3231
  answers: await Promise.all(
2118
- urls.map(async (url) => {
3232
+ urls.map(async (url2) => {
2119
3233
  try {
2120
- const document = await client.scrape(url, scrapeOptions);
3234
+ const document = await client.scrape(url2, scrapeOptions);
2121
3235
  const content = getDocumentContent(document);
2122
3236
  return content ? {
2123
- url,
3237
+ url: url2,
2124
3238
  content,
2125
3239
  ...document.metadata ? {
2126
3240
  metadata: document.metadata
2127
3241
  } : {}
2128
3242
  } : {
2129
- url,
3243
+ url: url2,
2130
3244
  error: "No content returned for this URL."
2131
3245
  };
2132
3246
  } catch (error) {
2133
3247
  return {
2134
- url,
3248
+ url: url2,
2135
3249
  error: error instanceof Error ? error.message : String(error)
2136
3250
  };
2137
3251
  }
@@ -2141,7 +3255,7 @@ var firecrawlImplementation = {
2141
3255
  }
2142
3256
  };
2143
3257
  function createClient3(config) {
2144
- const apiKey = resolveConfigValue(config.apiKey);
3258
+ const apiKey = resolveConfigValue(config.credentials?.api);
2145
3259
  if (!apiKey) {
2146
3260
  throw new Error("is missing an API key");
2147
3261
  }
@@ -2161,8 +3275,8 @@ function toSearchResult(source, value) {
2161
3275
  return null;
2162
3276
  }
2163
3277
  const metadata = asRecord(entry.metadata);
2164
- const url = readString2(entry.url) ?? readString2(metadata?.sourceURL) ?? readString2(entry.imageUrl) ?? "";
2165
- const title = readString2(entry.title) ?? readString2(metadata?.title) ?? url;
3278
+ const url2 = readString2(entry.url) ?? readString2(metadata?.sourceURL) ?? readString2(entry.imageUrl) ?? "";
3279
+ const title = readString2(entry.title) ?? readString2(metadata?.title) ?? url2;
2166
3280
  const snippet = trimSnippet(
2167
3281
  readString2(entry.description) ?? readString2(entry.snippet) ?? readString2(entry.markdown) ?? readString2(metadata?.description) ?? ""
2168
3282
  );
@@ -2176,7 +3290,7 @@ function toSearchResult(source, value) {
2176
3290
  };
2177
3291
  return {
2178
3292
  title: title || "Untitled",
2179
- url,
3293
+ url: url2,
2180
3294
  snippet,
2181
3295
  metadata: Object.keys(resultMetadata).length > 1 ? resultMetadata : void 0
2182
3296
  };
@@ -2205,7 +3319,7 @@ var firecrawlProvider = defineProvider({
2205
3319
  docsUrl: firecrawlImplementation.docsUrl,
2206
3320
  config: {
2207
3321
  createTemplate: () => firecrawlImplementation.createTemplate(),
2208
- fields: ["apiKey", "baseUrl", "options", "settings"]
3322
+ fields: ["credentials", "baseUrl", "options", "settings"]
2209
3323
  },
2210
3324
  getCapabilityStatus: (config, cwd, tool) => firecrawlImplementation.getCapabilityStatus(
2211
3325
  config,
@@ -2242,29 +3356,29 @@ var firecrawlProvider = defineProvider({
2242
3356
 
2243
3357
  // src/providers/gemini.ts
2244
3358
  import { GoogleGenAI } from "@google/genai";
2245
- import { Type as Type7 } from "typebox";
3359
+ import { Type as Type8 } from "typebox";
2246
3360
  var DEFAULT_SEARCH_MODEL = "gemini-2.5-flash";
2247
3361
  var DEFAULT_ANSWER_MODEL = "gemini-2.5-flash";
2248
3362
  var DEFAULT_RESEARCH_AGENT = "deep-research-pro-preview-12-2025";
2249
- var geminiGenerationConfigSchema = Type7.Object(
3363
+ var geminiGenerationConfigSchema = Type8.Object(
2250
3364
  {
2251
- temperature: Type7.Optional(
2252
- Type7.Number({ description: "Sampling temperature." })
3365
+ temperature: Type8.Optional(
3366
+ Type8.Number({ description: "Sampling temperature." })
2253
3367
  ),
2254
- topP: Type7.Optional(Type7.Number({ description: "Top-p sampling value." })),
2255
- topK: Type7.Optional(
2256
- Type7.Integer({ minimum: 0, description: "Top-k sampling value." })
3368
+ topP: Type8.Optional(Type8.Number({ description: "Top-p sampling value." })),
3369
+ topK: Type8.Optional(
3370
+ Type8.Integer({ minimum: 0, description: "Top-k sampling value." })
2257
3371
  ),
2258
- candidateCount: Type7.Optional(
2259
- Type7.Integer({
3372
+ candidateCount: Type8.Optional(
3373
+ Type8.Integer({
2260
3374
  minimum: 1,
2261
3375
  description: "Number of candidates to generate."
2262
3376
  })
2263
3377
  ),
2264
- maxOutputTokens: Type7.Optional(
2265
- Type7.Integer({ minimum: 1, description: "Maximum output tokens." })
3378
+ maxOutputTokens: Type8.Optional(
3379
+ Type8.Integer({ minimum: 1, description: "Maximum output tokens." })
2266
3380
  ),
2267
- tool_choice: Type7.Optional(
3381
+ tool_choice: Type8.Optional(
2268
3382
  literalUnion(["auto", "any", "none"], {
2269
3383
  description: "Tool choice mode for Gemini search interactions."
2270
3384
  })
@@ -2272,35 +3386,35 @@ var geminiGenerationConfigSchema = Type7.Object(
2272
3386
  },
2273
3387
  { description: "Gemini generation configuration." }
2274
3388
  );
2275
- var geminiAnswerConfigSchema = Type7.Object(
3389
+ var geminiAnswerConfigSchema = Type8.Object(
2276
3390
  {
2277
- labels: Type7.Optional(
2278
- Type7.Record(Type7.String(), Type7.String(), {
3391
+ labels: Type8.Optional(
3392
+ Type8.Record(Type8.String(), Type8.String(), {
2279
3393
  description: "Request labels to attach to the Gemini call."
2280
3394
  })
2281
3395
  ),
2282
- temperature: Type7.Optional(
2283
- Type7.Number({ description: "Sampling temperature." })
3396
+ temperature: Type8.Optional(
3397
+ Type8.Number({ description: "Sampling temperature." })
2284
3398
  ),
2285
- topP: Type7.Optional(Type7.Number({ description: "Top-p sampling value." })),
2286
- topK: Type7.Optional(
2287
- Type7.Integer({ minimum: 0, description: "Top-k sampling value." })
3399
+ topP: Type8.Optional(Type8.Number({ description: "Top-p sampling value." })),
3400
+ topK: Type8.Optional(
3401
+ Type8.Integer({ minimum: 0, description: "Top-k sampling value." })
2288
3402
  ),
2289
- candidateCount: Type7.Optional(
2290
- Type7.Integer({
3403
+ candidateCount: Type8.Optional(
3404
+ Type8.Integer({
2291
3405
  minimum: 1,
2292
3406
  description: "Number of candidates to generate."
2293
3407
  })
2294
3408
  ),
2295
- maxOutputTokens: Type7.Optional(
2296
- Type7.Integer({ minimum: 1, description: "Maximum output tokens." })
3409
+ maxOutputTokens: Type8.Optional(
3410
+ Type8.Integer({ minimum: 1, description: "Maximum output tokens." })
2297
3411
  )
2298
3412
  },
2299
3413
  { description: "Gemini generate-content config overrides." }
2300
3414
  );
2301
- var geminiAgentConfigSchema = Type7.Object(
3415
+ var geminiAgentConfigSchema = Type8.Object(
2302
3416
  {
2303
- thinking_summaries: Type7.Optional(
3417
+ thinking_summaries: Type8.Optional(
2304
3418
  literalUnion(["auto", "none"], {
2305
3419
  description: "Whether to include thought summaries in the response."
2306
3420
  })
@@ -2311,31 +3425,31 @@ var geminiAgentConfigSchema = Type7.Object(
2311
3425
  description: "Safe Gemini deep-research agent configuration. The provider adds the required type field."
2312
3426
  }
2313
3427
  );
2314
- var geminiSearchOptionsSchema = Type7.Object(
3428
+ var geminiSearchOptionsSchema = Type8.Object(
2315
3429
  {
2316
- model: Type7.Optional(
2317
- Type7.String({
3430
+ model: Type8.Optional(
3431
+ Type8.String({
2318
3432
  description: "Gemini model for search (for example 'gemini-2.5-flash')."
2319
3433
  })
2320
3434
  ),
2321
- generation_config: Type7.Optional(geminiGenerationConfigSchema)
3435
+ generation_config: Type8.Optional(geminiGenerationConfigSchema)
2322
3436
  },
2323
3437
  { description: "Gemini search options." }
2324
3438
  );
2325
- var geminiAnswerOptionsSchema = Type7.Object(
3439
+ var geminiAnswerOptionsSchema = Type8.Object(
2326
3440
  {
2327
- model: Type7.Optional(
2328
- Type7.String({
3441
+ model: Type8.Optional(
3442
+ Type8.String({
2329
3443
  description: "Gemini model for answers (for example 'gemini-2.5-flash')."
2330
3444
  })
2331
3445
  ),
2332
- config: Type7.Optional(geminiAnswerConfigSchema)
3446
+ config: Type8.Optional(geminiAnswerConfigSchema)
2333
3447
  },
2334
3448
  { description: "Gemini answer options." }
2335
3449
  );
2336
- var geminiResearchOptionsSchema = Type7.Object(
3450
+ var geminiResearchOptionsSchema = Type8.Object(
2337
3451
  {
2338
- agent_config: Type7.Optional(geminiAgentConfigSchema)
3452
+ agent_config: Type8.Optional(geminiAgentConfigSchema)
2339
3453
  },
2340
3454
  { additionalProperties: false, description: "Gemini research options." }
2341
3455
  );
@@ -2357,7 +3471,7 @@ var geminiImplementation = {
2357
3471
  },
2358
3472
  createTemplate() {
2359
3473
  return {
2360
- apiKey: "GOOGLE_API_KEY",
3474
+ credentials: { api: "GOOGLE_API_KEY" },
2361
3475
  options: {
2362
3476
  searchModel: DEFAULT_SEARCH_MODEL,
2363
3477
  answerModel: DEFAULT_ANSWER_MODEL,
@@ -2366,7 +3480,7 @@ var geminiImplementation = {
2366
3480
  };
2367
3481
  },
2368
3482
  getCapabilityStatus(config) {
2369
- return getApiKeyStatus(config?.apiKey);
3483
+ return getApiKeyStatus(config?.credentials?.api);
2370
3484
  },
2371
3485
  async search(query2, maxResults, config, context, options) {
2372
3486
  const ai = this.createClient(config);
@@ -2505,7 +3619,7 @@ var geminiImplementation = {
2505
3619
  return status === "in_progress" ? { status: "in_progress" } : { status: "in_progress", statusText: status };
2506
3620
  },
2507
3621
  createClient(config) {
2508
- const apiKey = resolveConfigValue(config.apiKey);
3622
+ const apiKey = resolveConfigValue(config.credentials?.api);
2509
3623
  if (!apiKey) {
2510
3624
  throw new Error("is missing an API key");
2511
3625
  }
@@ -2649,11 +3763,11 @@ function normalizeHtmlAttributeValue(value) {
2649
3763
  return readNonEmptyString3(value);
2650
3764
  }
2651
3765
  function normalizeSearchUrl(value) {
2652
- const url = normalizeHtmlAttributeValue(value);
2653
- if (!url || url.startsWith("#") || /^javascript:/i.test(url)) {
3766
+ const url2 = normalizeHtmlAttributeValue(value);
3767
+ if (!url2 || url2.startsWith("#") || /^javascript:/i.test(url2)) {
2654
3768
  return void 0;
2655
3769
  }
2656
- return url;
3770
+ return url2;
2657
3771
  }
2658
3772
  function decodeHtmlEntities(text) {
2659
3773
  return text.replace(
@@ -2688,20 +3802,20 @@ function extractGroundingSources(chunks) {
2688
3802
  return sources;
2689
3803
  }
2690
3804
  for (const chunk of chunks) {
2691
- const web = typeof chunk === "object" && chunk !== null && "web" in chunk && typeof chunk.web === "object" && chunk.web !== null ? chunk.web : void 0;
2692
- if (!web) continue;
2693
- const rawUrl = typeof web.uri === "string" ? web.uri : "";
3805
+ const web2 = typeof chunk === "object" && chunk !== null && "web" in chunk && typeof chunk.web === "object" && chunk.web !== null ? chunk.web : void 0;
3806
+ if (!web2) continue;
3807
+ const rawUrl = typeof web2.uri === "string" ? web2.uri : "";
2694
3808
  const title = formatGroundingSourceTitle(
2695
- typeof web.title === "string" ? web.title : rawUrl,
3809
+ typeof web2.title === "string" ? web2.title : rawUrl,
2696
3810
  rawUrl
2697
3811
  );
2698
- const url = formatGroundingSourceUrl(rawUrl);
2699
- const key = [title.toLowerCase(), url.toLowerCase()].join("::");
3812
+ const url2 = formatGroundingSourceUrl(rawUrl);
3813
+ const key = [title.toLowerCase(), url2.toLowerCase()].join("::");
2700
3814
  if (seen.has(key)) continue;
2701
3815
  seen.add(key);
2702
3816
  sources.push({
2703
3817
  title,
2704
- url
3818
+ url: url2
2705
3819
  });
2706
3820
  if (sources.length >= maxSources) {
2707
3821
  break;
@@ -2724,32 +3838,32 @@ function formatInteractionOutputs(outputs) {
2724
3838
  }
2725
3839
  return lines.join("\n\n").trim();
2726
3840
  }
2727
- function formatGroundingSourceTitle(title, url) {
3841
+ function formatGroundingSourceTitle(title, url2) {
2728
3842
  const trimmedTitle = title?.trim();
2729
3843
  if (trimmedTitle) {
2730
3844
  return trimmedTitle;
2731
3845
  }
2732
- if (url) {
3846
+ if (url2) {
2733
3847
  try {
2734
- return new URL(url).hostname;
3848
+ return new URL(url2).hostname;
2735
3849
  } catch {
2736
- return url;
3850
+ return url2;
2737
3851
  }
2738
3852
  }
2739
3853
  return "Untitled";
2740
3854
  }
2741
- function formatGroundingSourceUrl(url) {
2742
- if (!url) {
3855
+ function formatGroundingSourceUrl(url2) {
3856
+ if (!url2) {
2743
3857
  return "";
2744
3858
  }
2745
- if (isGoogleGroundingRedirect(url)) {
3859
+ if (isGoogleGroundingRedirect(url2)) {
2746
3860
  return "";
2747
3861
  }
2748
- return url;
3862
+ return url2;
2749
3863
  }
2750
- function isGoogleGroundingRedirect(url) {
3864
+ function isGoogleGroundingRedirect(url2) {
2751
3865
  try {
2752
- const parsed = new URL(url);
3866
+ const parsed = new URL(url2);
2753
3867
  return parsed.hostname === "vertexaisearch.cloud.google.com" && parsed.pathname.startsWith("/grounding-api-redirect/");
2754
3868
  } catch {
2755
3869
  return false;
@@ -2870,22 +3984,22 @@ function isBuiltInToolChoiceError(error) {
2870
3984
  }
2871
3985
  return false;
2872
3986
  }
2873
- async function resolveGoogleSearchUrl(url, signal) {
2874
- if (!url) {
3987
+ async function resolveGoogleSearchUrl(url2, signal) {
3988
+ if (!url2) {
2875
3989
  return void 0;
2876
3990
  }
2877
- if (!isGoogleGroundingRedirect(url)) {
2878
- return url;
3991
+ if (!isGoogleGroundingRedirect(url2)) {
3992
+ return url2;
2879
3993
  }
2880
3994
  try {
2881
- const response = await fetch(url, {
3995
+ const response = await fetch(url2, {
2882
3996
  method: "HEAD",
2883
3997
  redirect: "manual",
2884
3998
  signal
2885
3999
  });
2886
- return response.headers.get("location") || url;
4000
+ return response.headers.get("location") || url2;
2887
4001
  } catch {
2888
- return url;
4002
+ return url2;
2889
4003
  }
2890
4004
  }
2891
4005
  function buildGeminiSearchRequest(query2, defaultModel, options) {
@@ -2993,7 +4107,7 @@ var geminiProvider = defineProvider({
2993
4107
  docsUrl: geminiImplementation.docsUrl,
2994
4108
  config: {
2995
4109
  createTemplate: () => geminiImplementation.createTemplate(),
2996
- fields: ["apiKey", "options", "settings"]
4110
+ fields: ["credentials", "options", "settings"]
2997
4111
  },
2998
4112
  getCapabilityStatus: (config, cwd, tool) => geminiImplementation.getCapabilityStatus(
2999
4113
  config,
@@ -3043,46 +4157,46 @@ var geminiProvider = defineProvider({
3043
4157
  import {
3044
4158
  LinkupClient
3045
4159
  } from "linkup-sdk";
3046
- import { Type as Type8 } from "typebox";
3047
- var linkupSearchOptionsSchema = Type8.Object(
4160
+ import { Type as Type9 } from "typebox";
4161
+ var linkupSearchOptionsSchema = Type9.Object(
3048
4162
  {
3049
- depth: Type8.Optional(
4163
+ depth: Type9.Optional(
3050
4164
  literalUnion(["standard", "deep"], {
3051
4165
  description: "Search depth. 'deep' is slower but more thorough."
3052
4166
  })
3053
4167
  ),
3054
- includeImages: Type8.Optional(
3055
- Type8.Boolean({ description: "Include images in search results." })
4168
+ includeImages: Type9.Optional(
4169
+ Type9.Boolean({ description: "Include images in search results." })
3056
4170
  ),
3057
- includeDomains: Type8.Optional(
3058
- Type8.Array(Type8.String(), {
4171
+ includeDomains: Type9.Optional(
4172
+ Type9.Array(Type9.String(), {
3059
4173
  description: "Restrict results to these domains."
3060
4174
  })
3061
4175
  ),
3062
- excludeDomains: Type8.Optional(
3063
- Type8.Array(Type8.String(), { description: "Exclude these domains." })
4176
+ excludeDomains: Type9.Optional(
4177
+ Type9.Array(Type9.String(), { description: "Exclude these domains." })
3064
4178
  ),
3065
- fromDate: Type8.Optional(
3066
- Type8.String({ description: "ISO date string for earliest result date." })
4179
+ fromDate: Type9.Optional(
4180
+ Type9.String({ description: "ISO date string for earliest result date." })
3067
4181
  ),
3068
- toDate: Type8.Optional(
3069
- Type8.String({ description: "ISO date string for latest result date." })
4182
+ toDate: Type9.Optional(
4183
+ Type9.String({ description: "ISO date string for latest result date." })
3070
4184
  )
3071
4185
  },
3072
4186
  { description: "Linkup search options." }
3073
4187
  );
3074
- var linkupContentsOptionsSchema = Type8.Object(
4188
+ var linkupContentsOptionsSchema = Type9.Object(
3075
4189
  {
3076
- renderJs: Type8.Optional(
3077
- Type8.Boolean({
4190
+ renderJs: Type9.Optional(
4191
+ Type9.Boolean({
3078
4192
  description: "Render JavaScript before extracting content."
3079
4193
  })
3080
4194
  ),
3081
- includeRawHtml: Type8.Optional(
3082
- Type8.Boolean({ description: "Include raw HTML in the response." })
4195
+ includeRawHtml: Type9.Optional(
4196
+ Type9.Boolean({ description: "Include raw HTML in the response." })
3083
4197
  ),
3084
- extractImages: Type8.Optional(
3085
- Type8.Boolean({ description: "Extract images from the page." })
4198
+ extractImages: Type9.Optional(
4199
+ Type9.Boolean({ description: "Extract images from the page." })
3086
4200
  )
3087
4201
  },
3088
4202
  { description: "Linkup fetch options." }
@@ -3103,11 +4217,11 @@ var linkupImplementation = {
3103
4217
  },
3104
4218
  createTemplate() {
3105
4219
  return {
3106
- apiKey: "LINKUP_API_KEY"
4220
+ credentials: { api: "LINKUP_API_KEY" }
3107
4221
  };
3108
4222
  },
3109
4223
  getCapabilityStatus(config) {
3110
- return getApiKeyStatus(config?.apiKey);
4224
+ return getApiKeyStatus(config?.credentials?.api);
3111
4225
  },
3112
4226
  async search(query2, maxResults, config, _context, options) {
3113
4227
  const client = createClient4(config);
@@ -3129,24 +4243,24 @@ var linkupImplementation = {
3129
4243
  return {
3130
4244
  provider: linkupImplementation.id,
3131
4245
  answers: await Promise.all(
3132
- urls.map(async (url) => {
4246
+ urls.map(async (url2) => {
3133
4247
  try {
3134
4248
  const response = await client.fetch(
3135
- buildFetchParams(url, {
4249
+ buildFetchParams(url2, {
3136
4250
  ...defaults,
3137
4251
  ...options ?? {}
3138
4252
  })
3139
4253
  );
3140
4254
  return response.markdown ? {
3141
- url,
4255
+ url: url2,
3142
4256
  content: response.markdown
3143
4257
  } : {
3144
- url,
4258
+ url: url2,
3145
4259
  error: "No content returned for this URL."
3146
4260
  };
3147
4261
  } catch (error) {
3148
4262
  return {
3149
- url,
4263
+ url: url2,
3150
4264
  error: error instanceof Error ? error.message : String(error)
3151
4265
  };
3152
4266
  }
@@ -3185,20 +4299,20 @@ function buildSearchParams(query2, maxResults, options) {
3185
4299
  ...searchOptions.toDate !== void 0 ? { toDate: toDate(searchOptions.toDate, "toDate") } : {}
3186
4300
  };
3187
4301
  }
3188
- function buildFetchParams(url, options) {
4302
+ function buildFetchParams(url2, options) {
3189
4303
  const fetchOptions = options;
3190
4304
  if (fetchOptions.url !== void 0) {
3191
4305
  throw new Error("Linkup fetch options cannot override the managed URL.");
3192
4306
  }
3193
4307
  return {
3194
- url,
4308
+ url: url2,
3195
4309
  ...fetchOptions.renderJs !== void 0 ? { renderJs: fetchOptions.renderJs } : {},
3196
4310
  ...fetchOptions.includeRawHtml !== void 0 ? { includeRawHtml: fetchOptions.includeRawHtml } : {},
3197
4311
  ...fetchOptions.extractImages !== void 0 ? { extractImages: fetchOptions.extractImages } : {}
3198
4312
  };
3199
4313
  }
3200
4314
  function createClient4(config) {
3201
- const apiKey = resolveConfigValue(config.apiKey);
4315
+ const apiKey = resolveConfigValue(config.credentials?.api);
3202
4316
  if (!apiKey) {
3203
4317
  throw new Error("is missing an API key");
3204
4318
  }
@@ -3212,8 +4326,8 @@ function toSearchResult2(value) {
3212
4326
  if (!entry) {
3213
4327
  return null;
3214
4328
  }
3215
- const url = readString3(entry.url) ?? "";
3216
- const title = readString3(entry.name) ?? (url || "Untitled");
4329
+ const url2 = readString3(entry.url) ?? "";
4330
+ const title = readString3(entry.name) ?? (url2 || "Untitled");
3217
4331
  const type = readString3(entry.type);
3218
4332
  const favicon = readString3(entry.favicon);
3219
4333
  const snippet = type === "text" ? trimSnippet(readString3(entry.content) ?? "") : "";
@@ -3223,7 +4337,7 @@ function toSearchResult2(value) {
3223
4337
  };
3224
4338
  return {
3225
4339
  title,
3226
- url,
4340
+ url: url2,
3227
4341
  snippet,
3228
4342
  metadata: Object.keys(metadata).length > 0 ? metadata : void 0
3229
4343
  };
@@ -3249,7 +4363,7 @@ var linkupProvider = defineProvider({
3249
4363
  docsUrl: linkupImplementation.docsUrl,
3250
4364
  config: {
3251
4365
  createTemplate: () => linkupImplementation.createTemplate(),
3252
- fields: ["apiKey", "baseUrl", "options", "settings"]
4366
+ fields: ["credentials", "baseUrl", "options", "settings"]
3253
4367
  },
3254
4368
  getCapabilityStatus: (config, cwd, tool) => linkupImplementation.getCapabilityStatus(
3255
4369
  config,
@@ -3285,7 +4399,7 @@ var linkupProvider = defineProvider({
3285
4399
  });
3286
4400
 
3287
4401
  // src/providers/ollama.ts
3288
- var DEFAULT_BASE_URL = "https://ollama.com";
4402
+ var DEFAULT_BASE_URL2 = "https://ollama.com";
3289
4403
  var WEB_SEARCH_PATH = "/api/web_search";
3290
4404
  var WEB_FETCH_PATH = "/api/web_fetch";
3291
4405
  var ollamaProvider = defineProvider({
@@ -3295,13 +4409,13 @@ var ollamaProvider = defineProvider({
3295
4409
  config: {
3296
4410
  createTemplate() {
3297
4411
  return {
3298
- apiKey: "OLLAMA_API_KEY"
4412
+ credentials: { api: "OLLAMA_API_KEY" }
3299
4413
  };
3300
4414
  },
3301
- fields: ["apiKey", "baseUrl", "settings"]
4415
+ fields: ["credentials", "baseUrl", "settings"]
3302
4416
  },
3303
4417
  getCapabilityStatus(config) {
3304
- return getApiKeyStatus(config?.apiKey);
4418
+ return getApiKeyStatus(config?.credentials?.api);
3305
4419
  },
3306
4420
  capabilities: {
3307
4421
  search: defineCapability({
@@ -3355,19 +4469,19 @@ async function fetchOllamaContents(urls, config, context) {
3355
4469
  return {
3356
4470
  provider: ollamaProvider.id,
3357
4471
  answers: await Promise.all(
3358
- urls.map(async (url) => {
4472
+ urls.map(async (url2) => {
3359
4473
  try {
3360
4474
  const response = await fetch(endpoint, {
3361
4475
  method: "POST",
3362
4476
  headers: buildHeaders(apiKey),
3363
4477
  body: JSON.stringify({
3364
- url
4478
+ url: url2
3365
4479
  }),
3366
4480
  signal: context.signal
3367
4481
  });
3368
4482
  if (!response.ok) {
3369
4483
  return {
3370
- url,
4484
+ url: url2,
3371
4485
  error: await buildHttpError(response)
3372
4486
  };
3373
4487
  }
@@ -3375,19 +4489,19 @@ async function fetchOllamaContents(urls, config, context) {
3375
4489
  const content = normalizeContentText(data.content);
3376
4490
  if (!content) {
3377
4491
  return {
3378
- url,
4492
+ url: url2,
3379
4493
  error: "No content returned for this URL."
3380
4494
  };
3381
4495
  }
3382
4496
  const metadata = buildFetchMetadata(data);
3383
4497
  return {
3384
- url,
4498
+ url: url2,
3385
4499
  content,
3386
4500
  ...metadata ? { metadata } : {}
3387
4501
  };
3388
4502
  } catch (error) {
3389
4503
  return {
3390
- url,
4504
+ url: url2,
3391
4505
  error: error instanceof Error ? error.message : String(error)
3392
4506
  };
3393
4507
  }
@@ -3396,7 +4510,7 @@ async function fetchOllamaContents(urls, config, context) {
3396
4510
  };
3397
4511
  }
3398
4512
  function resolveApiKey(config) {
3399
- const apiKey = resolveConfigValue(config.apiKey);
4513
+ const apiKey = resolveConfigValue(config.credentials?.api);
3400
4514
  if (!apiKey) {
3401
4515
  throw new Error("is missing an API key");
3402
4516
  }
@@ -3409,16 +4523,16 @@ function buildHeaders(apiKey) {
3409
4523
  };
3410
4524
  }
3411
4525
  function resolveEndpoint(baseUrlReference, endpointPath) {
3412
- const baseUrl = resolveConfigValue(baseUrlReference) ?? DEFAULT_BASE_URL;
3413
- const base = baseUrl.replace(/\/+$/, "");
4526
+ const baseUrl = resolveConfigValue(baseUrlReference) ?? DEFAULT_BASE_URL2;
4527
+ const base2 = baseUrl.replace(/\/+$/, "");
3414
4528
  const apiPath = endpointPath.replace(/^\/api\//, "");
3415
- if (base.endsWith(endpointPath)) {
3416
- return base;
4529
+ if (base2.endsWith(endpointPath)) {
4530
+ return base2;
3417
4531
  }
3418
- if (base.endsWith("/api")) {
3419
- return `${base}/${apiPath}`;
4532
+ if (base2.endsWith("/api")) {
4533
+ return `${base2}/${apiPath}`;
3420
4534
  }
3421
- return `${base}${endpointPath}`;
4535
+ return `${base2}${endpointPath}`;
3422
4536
  }
3423
4537
  function clampMaxResults(value) {
3424
4538
  return Math.max(1, Math.min(10, Math.trunc(value || 0)));
@@ -3457,55 +4571,55 @@ function buildFetchMetadata(data) {
3457
4571
  }
3458
4572
 
3459
4573
  // src/providers/openai.ts
3460
- import { Type as Type9 } from "typebox";
4574
+ import { Type as Type10 } from "typebox";
3461
4575
  import OpenAI from "openai";
3462
4576
  var DEFAULT_SEARCH_MODEL2 = "gpt-4.1";
3463
4577
  var DEFAULT_ANSWER_MODEL2 = "gpt-4.1";
3464
4578
  var DEFAULT_RESEARCH_MODEL = "o4-mini-deep-research";
3465
- var openaiSearchOptionsSchema = Type9.Object(
4579
+ var openaiSearchOptionsSchema = Type10.Object(
3466
4580
  {
3467
- model: Type9.Optional(
3468
- Type9.String({
4581
+ model: Type10.Optional(
4582
+ Type10.String({
3469
4583
  description: "OpenAI model to use for web search (for example 'gpt-4.1')."
3470
4584
  })
3471
4585
  ),
3472
- instructions: Type9.Optional(
3473
- Type9.String({
4586
+ instructions: Type10.Optional(
4587
+ Type10.String({
3474
4588
  description: "Optional instructions that shape source selection and result style."
3475
4589
  })
3476
4590
  )
3477
4591
  },
3478
4592
  { description: "OpenAI search options." }
3479
4593
  );
3480
- var openaiAnswerOptionsSchema = Type9.Object(
4594
+ var openaiAnswerOptionsSchema = Type10.Object(
3481
4595
  {
3482
- model: Type9.Optional(
3483
- Type9.String({
4596
+ model: Type10.Optional(
4597
+ Type10.String({
3484
4598
  description: "OpenAI model to use for grounded answers (for example 'gpt-4.1')."
3485
4599
  })
3486
4600
  ),
3487
- instructions: Type9.Optional(
3488
- Type9.String({
4601
+ instructions: Type10.Optional(
4602
+ Type10.String({
3489
4603
  description: "Optional instructions that shape the answer structure, tone, and source selection."
3490
4604
  })
3491
4605
  )
3492
4606
  },
3493
4607
  { description: "OpenAI answer options." }
3494
4608
  );
3495
- var openaiResearchOptionsSchema = Type9.Object(
4609
+ var openaiResearchOptionsSchema = Type10.Object(
3496
4610
  {
3497
- model: Type9.Optional(
3498
- Type9.String({
4611
+ model: Type10.Optional(
4612
+ Type10.String({
3499
4613
  description: "OpenAI deep research model to use (for example 'o4-mini-deep-research')."
3500
4614
  })
3501
4615
  ),
3502
- instructions: Type9.Optional(
3503
- Type9.String({
4616
+ instructions: Type10.Optional(
4617
+ Type10.String({
3504
4618
  description: "Optional instructions that shape the report structure, tone, and source selection."
3505
4619
  })
3506
4620
  ),
3507
- max_tool_calls: Type9.Optional(
3508
- Type9.Integer({
4621
+ max_tool_calls: Type10.Optional(
4622
+ Type10.Integer({
3509
4623
  minimum: 1,
3510
4624
  description: "Maximum number of built-in tool calls the model may make during the research run."
3511
4625
  })
@@ -3551,7 +4665,7 @@ var openaiImplementation = {
3551
4665
  },
3552
4666
  createTemplate() {
3553
4667
  return {
3554
- apiKey: "OPENAI_API_KEY",
4668
+ credentials: { api: "OPENAI_API_KEY" },
3555
4669
  options: {
3556
4670
  search: {
3557
4671
  model: DEFAULT_SEARCH_MODEL2
@@ -3566,7 +4680,7 @@ var openaiImplementation = {
3566
4680
  };
3567
4681
  },
3568
4682
  getCapabilityStatus(config) {
3569
- return getApiKeyStatus(config?.apiKey);
4683
+ return getApiKeyStatus(config?.credentials?.api);
3570
4684
  },
3571
4685
  async search(query2, maxResults, config, context, options) {
3572
4686
  const client = createClient5(config);
@@ -3645,7 +4759,7 @@ var openaiImplementation = {
3645
4759
  }
3646
4760
  };
3647
4761
  function createClient5(config) {
3648
- const apiKey = resolveConfigValue(config.apiKey);
4762
+ const apiKey = resolveConfigValue(config.credentials?.api);
3649
4763
  if (!apiKey) {
3650
4764
  throw new Error("is missing an API key");
3651
4765
  }
@@ -3835,15 +4949,15 @@ function extractUrlCitations(response) {
3835
4949
  continue;
3836
4950
  }
3837
4951
  const title = readNonEmptyString4(annotation.title);
3838
- const url = readNonEmptyString4(annotation.url);
4952
+ const url2 = readNonEmptyString4(annotation.url);
3839
4953
  const startIndex = readInteger(annotation.start_index);
3840
4954
  const endIndex = readInteger(annotation.end_index);
3841
- if (!title || !url || startIndex === void 0 || endIndex === void 0) {
4955
+ if (!title || !url2 || startIndex === void 0 || endIndex === void 0) {
3842
4956
  continue;
3843
4957
  }
3844
4958
  const citation = {
3845
4959
  title,
3846
- url,
4960
+ url: url2,
3847
4961
  startIndex,
3848
4962
  endIndex
3849
4963
  };
@@ -3882,18 +4996,18 @@ function parseSearchPayload(text) {
3882
4996
  }
3883
4997
  const entry = source;
3884
4998
  const title = readNonEmptyString4(entry.title);
3885
- const url = readNonEmptyString4(entry.url);
4999
+ const url2 = readNonEmptyString4(entry.url);
3886
5000
  const snippet = readNonEmptyString4(entry.snippet);
3887
5001
  if (!title) {
3888
5002
  throw new Error(`search source at index ${index} is missing title`);
3889
5003
  }
3890
- if (!url) {
5004
+ if (!url2) {
3891
5005
  throw new Error(`search source at index ${index} is missing url`);
3892
5006
  }
3893
5007
  if (!snippet) {
3894
5008
  throw new Error(`search source at index ${index} is missing snippet`);
3895
5009
  }
3896
- return { title, url, snippet };
5010
+ return { title, url: url2, snippet };
3897
5011
  })
3898
5012
  };
3899
5013
  }
@@ -3919,7 +5033,7 @@ var openaiProvider = defineProvider({
3919
5033
  docsUrl: openaiImplementation.docsUrl,
3920
5034
  config: {
3921
5035
  createTemplate: () => openaiImplementation.createTemplate(),
3922
- fields: ["apiKey", "baseUrl", "options", "settings"],
5036
+ fields: ["credentials", "baseUrl", "options", "settings"],
3923
5037
  optionCapabilities: ["search", "answer", "research"]
3924
5038
  },
3925
5039
  getCapabilityStatus: (config, cwd, tool) => openaiImplementation.getCapabilityStatus(
@@ -3967,25 +5081,25 @@ var openaiProvider = defineProvider({
3967
5081
  });
3968
5082
 
3969
5083
  // src/providers/parallel.ts
3970
- import { Type as Type10 } from "typebox";
3971
5084
  import ParallelClient from "parallel-web";
3972
- var parallelSearchOptionsSchema = Type10.Object(
5085
+ import { Type as Type11 } from "typebox";
5086
+ var parallelSearchOptionsSchema = Type11.Object(
3973
5087
  {
3974
- mode: Type10.Optional(
5088
+ mode: Type11.Optional(
3975
5089
  literalUnion(["agentic", "one-shot"], {
3976
- description: "Parallel search mode."
5090
+ description: "Parallel search mode. Use 'agentic' for exploratory or multi-step source discovery and 'one-shot' for direct, simple searches."
3977
5091
  })
3978
5092
  )
3979
5093
  },
3980
5094
  { description: "Parallel search options." }
3981
5095
  );
3982
- var parallelExtractOptionsSchema = Type10.Object(
5096
+ var parallelExtractOptionsSchema = Type11.Object(
3983
5097
  {
3984
- excerpts: Type10.Optional(
3985
- Type10.Boolean({ description: "Include excerpts in extraction results." })
5098
+ excerpts: Type11.Optional(
5099
+ Type11.Boolean({ description: "Include excerpts in extraction results." })
3986
5100
  ),
3987
- full_content: Type10.Optional(
3988
- Type10.Boolean({
5101
+ full_content: Type11.Optional(
5102
+ Type11.Boolean({
3989
5103
  description: "Include full page content in extraction results."
3990
5104
  })
3991
5105
  )
@@ -4008,7 +5122,7 @@ var parallelImplementation = {
4008
5122
  },
4009
5123
  createTemplate() {
4010
5124
  return {
4011
- apiKey: "PARALLEL_API_KEY",
5125
+ credentials: { api: "PARALLEL_API_KEY" },
4012
5126
  options: {
4013
5127
  search: {
4014
5128
  mode: "agentic"
@@ -4021,7 +5135,7 @@ var parallelImplementation = {
4021
5135
  };
4022
5136
  },
4023
5137
  getCapabilityStatus(config) {
4024
- return getApiKeyStatus(config?.apiKey);
5138
+ return getApiKeyStatus(config?.credentials?.api);
4025
5139
  },
4026
5140
  async search(query2, maxResults, config, context, options) {
4027
5141
  const client = createClient6(config);
@@ -4063,21 +5177,21 @@ var parallelImplementation = {
4063
5177
  );
4064
5178
  return {
4065
5179
  provider: parallelImplementation.id,
4066
- answers: urls.map((url) => {
4067
- const result = resultsByUrl.get(url);
5180
+ answers: urls.map((url2) => {
5181
+ const result = resultsByUrl.get(url2);
4068
5182
  if (result) {
4069
5183
  return {
4070
- url,
5184
+ url: url2,
4071
5185
  content: result.full_content ?? result.excerpts?.join("\n\n") ?? void 0,
4072
5186
  metadata: result
4073
5187
  };
4074
5188
  }
4075
- const error = errorsByUrl.get(url);
5189
+ const error = errorsByUrl.get(url2);
4076
5190
  return error ? {
4077
- url,
5191
+ url: url2,
4078
5192
  error: formatJson(error)
4079
5193
  } : {
4080
- url,
5194
+ url: url2,
4081
5195
  error: "No content returned for this URL."
4082
5196
  };
4083
5197
  })
@@ -4085,7 +5199,7 @@ var parallelImplementation = {
4085
5199
  }
4086
5200
  };
4087
5201
  function createClient6(config) {
4088
- const apiKey = resolveConfigValue(config.apiKey);
5202
+ const apiKey = resolveConfigValue(config.credentials?.api);
4089
5203
  if (!apiKey) {
4090
5204
  throw new Error("is missing an API key");
4091
5205
  }
@@ -4103,7 +5217,7 @@ var parallelProvider = defineProvider({
4103
5217
  docsUrl: parallelImplementation.docsUrl,
4104
5218
  config: {
4105
5219
  createTemplate: () => parallelImplementation.createTemplate(),
4106
- fields: ["apiKey", "baseUrl", "options", "settings"]
5220
+ fields: ["credentials", "baseUrl", "options", "settings"]
4107
5221
  },
4108
5222
  getCapabilityStatus: (config, cwd, tool) => parallelImplementation.getCapabilityStatus(
4109
5223
  config,
@@ -4140,42 +5254,44 @@ var parallelProvider = defineProvider({
4140
5254
 
4141
5255
  // src/providers/perplexity.ts
4142
5256
  import PerplexityClient from "@perplexity-ai/perplexity_ai";
4143
- import { Type as Type11 } from "typebox";
5257
+ import { Type as Type12 } from "typebox";
4144
5258
  var DEFAULT_ANSWER_MODEL3 = "sonar";
4145
5259
  var DEFAULT_RESEARCH_MODEL2 = "sonar-deep-research";
4146
- var perplexitySearchOptionsSchema = Type11.Object(
5260
+ var perplexitySearchOptionsSchema = Type12.Object(
4147
5261
  {
4148
- country: Type11.Optional(
4149
- Type11.String({ description: "Country hint for search results." })
5262
+ country: Type12.Optional(
5263
+ Type12.String({ description: "Country hint for search results." })
4150
5264
  ),
4151
- search_mode: Type11.Optional(
4152
- Type11.String({ description: "Perplexity search mode." })
5265
+ search_mode: Type12.Optional(
5266
+ Type12.String({
5267
+ description: "Perplexity search mode. Choose the provider mode that best matches the user's intent, such as broad web search versus academic or other specialized retrieval modes supported by Perplexity."
5268
+ })
4153
5269
  ),
4154
- search_domain_filter: Type11.Optional(
4155
- Type11.Array(Type11.String(), {
5270
+ search_domain_filter: Type12.Optional(
5271
+ Type12.Array(Type12.String(), {
4156
5272
  description: "Restrict search results to these domains."
4157
5273
  })
4158
5274
  ),
4159
- search_recency_filter: Type11.Optional(
4160
- Type11.String({ description: "Recency filter for search results." })
5275
+ search_recency_filter: Type12.Optional(
5276
+ Type12.String({ description: "Recency filter for search results." })
4161
5277
  )
4162
5278
  },
4163
5279
  { description: "Perplexity search options." }
4164
5280
  );
4165
- var perplexityAnswerOptionsSchema = Type11.Object(
5281
+ var perplexityAnswerOptionsSchema = Type12.Object(
4166
5282
  {
4167
- model: Type11.Optional(
4168
- Type11.String({
5283
+ model: Type12.Optional(
5284
+ Type12.String({
4169
5285
  description: "Perplexity model to use (for example 'sonar' or 'sonar-pro')."
4170
5286
  })
4171
5287
  )
4172
5288
  },
4173
5289
  { description: "Perplexity answer options." }
4174
5290
  );
4175
- var perplexityResearchOptionsSchema = Type11.Object(
5291
+ var perplexityResearchOptionsSchema = Type12.Object(
4176
5292
  {
4177
- model: Type11.Optional(
4178
- Type11.String({
5293
+ model: Type12.Optional(
5294
+ Type12.String({
4179
5295
  description: "Perplexity model to use (for example 'sonar-deep-research')."
4180
5296
  })
4181
5297
  )
@@ -4200,7 +5316,7 @@ var perplexityImplementation = {
4200
5316
  },
4201
5317
  createTemplate() {
4202
5318
  return {
4203
- apiKey: "PERPLEXITY_API_KEY",
5319
+ credentials: { api: "PERPLEXITY_API_KEY" },
4204
5320
  options: {
4205
5321
  answer: {
4206
5322
  model: DEFAULT_ANSWER_MODEL3
@@ -4212,7 +5328,7 @@ var perplexityImplementation = {
4212
5328
  };
4213
5329
  },
4214
5330
  getCapabilityStatus(config) {
4215
- return getApiKeyStatus(config?.apiKey);
5331
+ return getApiKeyStatus(config?.credentials?.api);
4216
5332
  },
4217
5333
  async search(query2, maxResults, config, context, options) {
4218
5334
  const client = createClient7(config);
@@ -4335,7 +5451,7 @@ async function runStreamingForegroundChatTool(input, config, context, fallbackMo
4335
5451
  };
4336
5452
  }
4337
5453
  function createClient7(config) {
4338
- const apiKey = resolveConfigValue(config.apiKey);
5454
+ const apiKey = resolveConfigValue(config.credentials?.api);
4339
5455
  if (!apiKey) {
4340
5456
  throw new Error("is missing an API key");
4341
5457
  }
@@ -4386,29 +5502,29 @@ function dedupeSources(sources) {
4386
5502
  const unique = [];
4387
5503
  for (const source of sources) {
4388
5504
  const title = source.title.trim() || source.url.trim() || "Untitled";
4389
- const url = source.url.trim();
4390
- if (!url) continue;
4391
- const key = `${title.toLowerCase()}::${url.toLowerCase()}`;
5505
+ const url2 = source.url.trim();
5506
+ if (!url2) continue;
5507
+ const key = `${title.toLowerCase()}::${url2.toLowerCase()}`;
4392
5508
  if (seen.has(key)) continue;
4393
5509
  seen.add(key);
4394
- unique.push({ title, url });
5510
+ unique.push({ title, url: url2 });
4395
5511
  }
4396
5512
  return unique;
4397
5513
  }
4398
5514
  function extractSources(response) {
4399
5515
  const searchResults = response.search_results?.flatMap((result) => {
4400
- const url = result.url?.trim() ?? "";
4401
- if (!url) {
5516
+ const url2 = result.url?.trim() ?? "";
5517
+ if (!url2) {
4402
5518
  return [];
4403
5519
  }
4404
- return [{ title: result.title?.trim() ?? url, url }];
5520
+ return [{ title: result.title?.trim() ?? url2, url: url2 }];
4405
5521
  }) ?? [];
4406
5522
  if (searchResults.length > 0) {
4407
5523
  return searchResults;
4408
5524
  }
4409
5525
  return response.citations?.flatMap((citation) => {
4410
- const url = citation?.trim() ?? "";
4411
- return url ? [{ title: url, url }] : [];
5526
+ const url2 = citation?.trim() ?? "";
5527
+ return url2 ? [{ title: url2, url: url2 }] : [];
4412
5528
  }) ?? [];
4413
5529
  }
4414
5530
  function buildRequestOptions4(context) {
@@ -4420,7 +5536,7 @@ var perplexityProvider = defineProvider({
4420
5536
  docsUrl: perplexityImplementation.docsUrl,
4421
5537
  config: {
4422
5538
  createTemplate: () => perplexityImplementation.createTemplate(),
4423
- fields: ["apiKey", "baseUrl", "options", "settings"]
5539
+ fields: ["credentials", "baseUrl", "options", "settings"]
4424
5540
  },
4425
5541
  getCapabilityStatus: (config, cwd, tool) => perplexityImplementation.getCapabilityStatus(
4426
5542
  config,
@@ -4467,33 +5583,33 @@ var perplexityProvider = defineProvider({
4467
5583
  });
4468
5584
 
4469
5585
  // src/providers/serper.ts
4470
- import { Type as Type12 } from "typebox";
4471
- var DEFAULT_BASE_URL2 = "https://google.serper.dev";
4472
- var serperSearchOptionsSchema = Type12.Object(
5586
+ import { Type as Type13 } from "typebox";
5587
+ var DEFAULT_BASE_URL3 = "https://google.serper.dev";
5588
+ var serperSearchOptionsSchema = Type13.Object(
4473
5589
  {
4474
- gl: Type12.Optional(
4475
- Type12.String({
5590
+ gl: Type13.Optional(
5591
+ Type13.String({
4476
5592
  description: "Country code hint for Google results (for example 'us')."
4477
5593
  })
4478
5594
  ),
4479
- hl: Type12.Optional(
4480
- Type12.String({
5595
+ hl: Type13.Optional(
5596
+ Type13.String({
4481
5597
  description: "Language code hint for Google results (for example 'en')."
4482
5598
  })
4483
5599
  ),
4484
- location: Type12.Optional(
4485
- Type12.String({
5600
+ location: Type13.Optional(
5601
+ Type13.String({
4486
5602
  description: "Geographic location hint for Google results."
4487
5603
  })
4488
5604
  ),
4489
- page: Type12.Optional(
4490
- Type12.Integer({
5605
+ page: Type13.Optional(
5606
+ Type13.Integer({
4491
5607
  minimum: 1,
4492
5608
  description: "1-based results page to request from Serper."
4493
5609
  })
4494
5610
  ),
4495
- autocorrect: Type12.Optional(
4496
- Type12.Boolean({
5611
+ autocorrect: Type13.Optional(
5612
+ Type13.Boolean({
4497
5613
  description: "Enable or disable Serper query autocorrection."
4498
5614
  })
4499
5615
  )
@@ -4514,15 +5630,15 @@ var serperImplementation = {
4514
5630
  },
4515
5631
  createTemplate() {
4516
5632
  return {
4517
- apiKey: "SERPER_API_KEY",
5633
+ credentials: { api: "SERPER_API_KEY" },
4518
5634
  options: {}
4519
5635
  };
4520
5636
  },
4521
5637
  getCapabilityStatus(config) {
4522
- return getApiKeyStatus(config?.apiKey);
5638
+ return getApiKeyStatus(config?.credentials?.api);
4523
5639
  },
4524
5640
  async search(query2, maxResults, config, context, options) {
4525
- const apiKey = resolveConfigValue(config.apiKey);
5641
+ const apiKey = resolveConfigValue(config.credentials?.api);
4526
5642
  if (!apiKey) {
4527
5643
  throw new Error("is missing an API key");
4528
5644
  }
@@ -4565,8 +5681,8 @@ var serperImplementation = {
4565
5681
  }
4566
5682
  };
4567
5683
  function joinUrl(baseUrl) {
4568
- const base = (baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
4569
- return `${base}/search`;
5684
+ const base2 = (baseUrl ?? DEFAULT_BASE_URL3).replace(/\/+$/, "");
5685
+ return `${base2}/search`;
4570
5686
  }
4571
5687
  function clampMaxResults2(value) {
4572
5688
  return Math.max(1, Math.min(20, Math.trunc(value || 0)));
@@ -4598,8 +5714,8 @@ function toSearchResult3(entry, searchContext) {
4598
5714
  if (!record) {
4599
5715
  return null;
4600
5716
  }
4601
- const url = readString4(record.link) ?? "";
4602
- const title = readString4(record.title) || url || "Untitled";
5717
+ const url2 = readString4(record.link) ?? "";
5718
+ const title = readString4(record.title) || url2 || "Untitled";
4603
5719
  const snippet = trimSnippet(
4604
5720
  readString4(record.snippet) ?? readString4(record.richSnippet) ?? readString4(record.date) ?? ""
4605
5721
  );
@@ -4617,7 +5733,7 @@ function toSearchResult3(entry, searchContext) {
4617
5733
  });
4618
5734
  return {
4619
5735
  title,
4620
- url,
5736
+ url: url2,
4621
5737
  snippet,
4622
5738
  ...Object.keys(metadata).length > 0 ? { metadata } : {}
4623
5739
  };
@@ -4669,7 +5785,7 @@ var serperProvider = defineProvider({
4669
5785
  docsUrl: serperImplementation.docsUrl,
4670
5786
  config: {
4671
5787
  createTemplate: () => serperImplementation.createTemplate(),
4672
- fields: ["apiKey", "baseUrl", "options", "settings"],
5788
+ fields: ["credentials", "baseUrl", "options", "settings"],
4673
5789
  optionCapabilities: ["search"]
4674
5790
  },
4675
5791
  getCapabilityStatus: (config, cwd, tool) => serperImplementation.getCapabilityStatus(
@@ -4695,55 +5811,55 @@ var serperProvider = defineProvider({
4695
5811
  });
4696
5812
 
4697
5813
  // src/providers/tavily.ts
4698
- import { Type as Type13 } from "typebox";
4699
5814
  import {
4700
5815
  tavily
4701
5816
  } from "@tavily/core";
4702
- var tavilySearchOptionsSchema = Type13.Object(
5817
+ import { Type as Type14 } from "typebox";
5818
+ var tavilySearchOptionsSchema = Type14.Object(
4703
5819
  {
4704
- topic: Type13.Optional(
5820
+ topic: Type14.Optional(
4705
5821
  literalUnion(["general", "news", "finance"], {
4706
- description: "Category of the search query."
5822
+ description: "Category of the search query. Use 'news' for recent journalism or current events, 'finance' for markets or company financial data, and 'general' for broad web search."
4707
5823
  })
4708
5824
  ),
4709
- searchDepth: Type13.Optional(
5825
+ searchDepth: Type14.Optional(
4710
5826
  literalUnion(["basic", "advanced"], {
4711
5827
  description: "Depth of the search. 'advanced' is slower but more thorough."
4712
5828
  })
4713
5829
  ),
4714
- timeRange: Type13.Optional(
4715
- Type13.String({ description: "Named time range filter." })
5830
+ timeRange: Type14.Optional(
5831
+ Type14.String({ description: "Named time range filter." })
4716
5832
  ),
4717
- country: Type13.Optional(
4718
- Type13.String({ description: "Country hint for search results." })
5833
+ country: Type14.Optional(
5834
+ Type14.String({ description: "Country hint for search results." })
4719
5835
  ),
4720
- exactMatch: Type13.Optional(
4721
- Type13.Boolean({ description: "Prefer exact matches." })
5836
+ exactMatch: Type14.Optional(
5837
+ Type14.Boolean({ description: "Prefer exact matches." })
4722
5838
  ),
4723
- includeAnswer: Type13.Optional(
4724
- Type13.Boolean({ description: "Include a short AI-generated answer." })
5839
+ includeAnswer: Type14.Optional(
5840
+ Type14.Boolean({ description: "Include a short AI-generated answer." })
4725
5841
  ),
4726
- includeRawContent: Type13.Optional(
4727
- Type13.Boolean({ description: "Include raw page content in results." })
5842
+ includeRawContent: Type14.Optional(
5843
+ Type14.Boolean({ description: "Include raw page content in results." })
4728
5844
  ),
4729
- includeImages: Type13.Optional(
4730
- Type13.Boolean({ description: "Include related images." })
5845
+ includeImages: Type14.Optional(
5846
+ Type14.Boolean({ description: "Include related images." })
4731
5847
  ),
4732
- includeFavicon: Type13.Optional(
4733
- Type13.Boolean({ description: "Include favicon URLs." })
5848
+ includeFavicon: Type14.Optional(
5849
+ Type14.Boolean({ description: "Include favicon URLs." })
4734
5850
  ),
4735
- includeDomains: Type13.Optional(
4736
- Type13.Array(Type13.String(), {
5851
+ includeDomains: Type14.Optional(
5852
+ Type14.Array(Type14.String(), {
4737
5853
  description: "Restrict results to these domains."
4738
5854
  })
4739
5855
  ),
4740
- excludeDomains: Type13.Optional(
4741
- Type13.Array(Type13.String(), {
5856
+ excludeDomains: Type14.Optional(
5857
+ Type14.Array(Type14.String(), {
4742
5858
  description: "Exclude these domains from results."
4743
5859
  })
4744
5860
  ),
4745
- days: Type13.Optional(
4746
- Type13.Integer({
5861
+ days: Type14.Optional(
5862
+ Type14.Integer({
4747
5863
  minimum: 1,
4748
5864
  description: "Limit results to the last N days."
4749
5865
  })
@@ -4751,27 +5867,27 @@ var tavilySearchOptionsSchema = Type13.Object(
4751
5867
  },
4752
5868
  { description: "Tavily search options." }
4753
5869
  );
4754
- var tavilyExtractOptionsSchema = Type13.Object(
5870
+ var tavilyExtractOptionsSchema = Type14.Object(
4755
5871
  {
4756
- extractDepth: Type13.Optional(
4757
- Type13.String({ description: "Depth setting for extraction." })
5872
+ extractDepth: Type14.Optional(
5873
+ Type14.String({ description: "Depth setting for extraction." })
4758
5874
  ),
4759
- format: Type13.Optional(
5875
+ format: Type14.Optional(
4760
5876
  literalUnion(["markdown", "text"], {
4761
5877
  description: "Output format for extracted content."
4762
5878
  })
4763
5879
  ),
4764
- includeImages: Type13.Optional(
4765
- Type13.Boolean({ description: "Include extracted images." })
5880
+ includeImages: Type14.Optional(
5881
+ Type14.Boolean({ description: "Include extracted images." })
4766
5882
  ),
4767
- query: Type13.Optional(
4768
- Type13.String({ description: "Optional query to focus extraction." })
5883
+ query: Type14.Optional(
5884
+ Type14.String({ description: "Optional query to focus extraction." })
4769
5885
  ),
4770
- chunksPerSource: Type13.Optional(
4771
- Type13.Integer({ minimum: 1, description: "Maximum chunks per source." })
5886
+ chunksPerSource: Type14.Optional(
5887
+ Type14.Integer({ minimum: 1, description: "Maximum chunks per source." })
4772
5888
  ),
4773
- includeFavicon: Type13.Optional(
4774
- Type13.Boolean({ description: "Include favicon URLs." })
5889
+ includeFavicon: Type14.Optional(
5890
+ Type14.Boolean({ description: "Include favicon URLs." })
4775
5891
  )
4776
5892
  },
4777
5893
  { description: "Tavily extract options." }
@@ -4792,7 +5908,7 @@ var tavilyImplementation = {
4792
5908
  },
4793
5909
  createTemplate() {
4794
5910
  return {
4795
- apiKey: "TAVILY_API_KEY",
5911
+ credentials: { api: "TAVILY_API_KEY" },
4796
5912
  options: {
4797
5913
  search: {
4798
5914
  includeFavicon: true
@@ -4805,7 +5921,7 @@ var tavilyImplementation = {
4805
5921
  };
4806
5922
  },
4807
5923
  getCapabilityStatus(config) {
4808
- return getApiKeyStatus(config?.apiKey);
5924
+ return getApiKeyStatus(config?.credentials?.api);
4809
5925
  },
4810
5926
  async search(query2, maxResults, config, _context, options) {
4811
5927
  const client = createClient8(config);
@@ -4841,24 +5957,24 @@ var tavilyImplementation = {
4841
5957
  );
4842
5958
  return {
4843
5959
  provider: tavilyImplementation.id,
4844
- answers: urls.map((url) => {
4845
- const result = resultsByUrl.get(url);
5960
+ answers: urls.map((url2) => {
5961
+ const result = resultsByUrl.get(url2);
4846
5962
  if (result) {
4847
5963
  return {
4848
- url,
5964
+ url: url2,
4849
5965
  ...typeof result.rawContent === "string" ? { content: result.rawContent } : {},
4850
5966
  metadata: buildExtractMetadata(response, result)
4851
5967
  };
4852
5968
  }
4853
- const failedResult = failedResultsByUrl.get(url);
5969
+ const failedResult = failedResultsByUrl.get(url2);
4854
5970
  if (failedResult) {
4855
5971
  return {
4856
- url,
5972
+ url: url2,
4857
5973
  error: failedResult.error || "Content extraction failed."
4858
5974
  };
4859
5975
  }
4860
5976
  return {
4861
- url,
5977
+ url: url2,
4862
5978
  error: "No content returned for this URL."
4863
5979
  };
4864
5980
  })
@@ -4866,7 +5982,7 @@ var tavilyImplementation = {
4866
5982
  }
4867
5983
  };
4868
5984
  function createClient8(config) {
4869
- const apiKey = resolveConfigValue(config.apiKey);
5985
+ const apiKey = resolveConfigValue(config.credentials?.api);
4870
5986
  if (!apiKey) {
4871
5987
  throw new Error("is missing an API key");
4872
5988
  }
@@ -4901,7 +6017,7 @@ var tavilyProvider = defineProvider({
4901
6017
  docsUrl: tavilyImplementation.docsUrl,
4902
6018
  config: {
4903
6019
  createTemplate: () => tavilyImplementation.createTemplate(),
4904
- fields: ["apiKey", "baseUrl", "options", "settings"]
6020
+ fields: ["credentials", "baseUrl", "options", "settings"]
4905
6021
  },
4906
6022
  getCapabilityStatus: (config, cwd, tool) => tavilyImplementation.getCapabilityStatus(
4907
6023
  config,
@@ -4937,48 +6053,48 @@ var tavilyProvider = defineProvider({
4937
6053
  });
4938
6054
 
4939
6055
  // src/providers/valyu.ts
4940
- import { Type as Type14 } from "typebox";
6056
+ import { Type as Type15 } from "typebox";
4941
6057
  import { Valyu as ValyuClient } from "valyu-js";
4942
- var valyuSearchOptionsSchema = Type14.Object(
6058
+ var valyuSearchOptionsSchema = Type15.Object(
4943
6059
  {
4944
- searchType: Type14.Optional(
6060
+ searchType: Type15.Optional(
4945
6061
  literalUnion(["all", "web", "proprietary", "news"], {
4946
- description: "Valyu search type."
6062
+ description: "Valyu search type. Use 'news' for recent journalism or current events, 'web' for public web results, 'proprietary' for Valyu proprietary sources, and 'all' when both public and proprietary sources are useful."
4947
6063
  })
4948
6064
  ),
4949
- responseLength: Type14.Optional(
6065
+ responseLength: Type15.Optional(
4950
6066
  literalUnion(["short", "medium", "large", "max"], {
4951
6067
  description: "Response length."
4952
6068
  })
4953
6069
  ),
4954
- countryCode: Type14.Optional(
4955
- Type14.String({ description: "Country code to scope search results." })
6070
+ countryCode: Type15.Optional(
6071
+ Type15.String({ description: "Country code to scope search results." })
4956
6072
  )
4957
6073
  },
4958
6074
  { description: "Valyu search options." }
4959
6075
  );
4960
- var valyuAnswerOptionsSchema = Type14.Object(
6076
+ var valyuAnswerOptionsSchema = Type15.Object(
4961
6077
  {
4962
- responseLength: Type14.Optional(
6078
+ responseLength: Type15.Optional(
4963
6079
  literalUnion(["short", "medium", "large", "max"], {
4964
6080
  description: "Response length for answers."
4965
6081
  })
4966
6082
  ),
4967
- countryCode: Type14.Optional(
4968
- Type14.String({ description: "Country code to scope answer results." })
6083
+ countryCode: Type15.Optional(
6084
+ Type15.String({ description: "Country code to scope answer results." })
4969
6085
  )
4970
6086
  },
4971
6087
  { description: "Valyu answer options." }
4972
6088
  );
4973
- var valyuResearchOptionsSchema = Type14.Object(
6089
+ var valyuResearchOptionsSchema = Type15.Object(
4974
6090
  {
4975
- responseLength: Type14.Optional(
6091
+ responseLength: Type15.Optional(
4976
6092
  literalUnion(["short", "medium", "large", "max"], {
4977
6093
  description: "Response length for research."
4978
6094
  })
4979
6095
  ),
4980
- countryCode: Type14.Optional(
4981
- Type14.String({ description: "Country code to scope research results." })
6096
+ countryCode: Type15.Optional(
6097
+ Type15.String({ description: "Country code to scope research results." })
4982
6098
  )
4983
6099
  },
4984
6100
  { description: "Valyu research options." }
@@ -5001,7 +6117,7 @@ var valyuImplementation = {
5001
6117
  },
5002
6118
  createTemplate() {
5003
6119
  return {
5004
- apiKey: "VALYU_API_KEY",
6120
+ credentials: { api: "VALYU_API_KEY" },
5005
6121
  options: {
5006
6122
  search: {
5007
6123
  searchType: "all",
@@ -5011,7 +6127,7 @@ var valyuImplementation = {
5011
6127
  };
5012
6128
  },
5013
6129
  getCapabilityStatus(config) {
5014
- return getApiKeyStatus(config?.apiKey);
6130
+ return getApiKeyStatus(config?.credentials?.api);
5015
6131
  },
5016
6132
  async search(query2, maxResults, config, _context, searchOptions) {
5017
6133
  const client = createClient9(config);
@@ -5050,19 +6166,19 @@ var valyuImplementation = {
5050
6166
  );
5051
6167
  return {
5052
6168
  provider: valyuImplementation.id,
5053
- answers: urls.map((url) => {
5054
- const result = resultsByUrl.get(url);
6169
+ answers: urls.map((url2) => {
6170
+ const result = resultsByUrl.get(url2);
5055
6171
  if (!result) {
5056
6172
  return {
5057
- url,
6173
+ url: url2,
5058
6174
  error: "No content returned for this URL."
5059
6175
  };
5060
6176
  }
5061
6177
  return result.status === "failed" ? {
5062
- url,
6178
+ url: url2,
5063
6179
  error: result.error ?? formatJson(result)
5064
6180
  } : {
5065
- url,
6181
+ url: url2,
5066
6182
  ...typeof result.content === "string" || typeof result.content === "number" ? { content: String(result.content) } : {},
5067
6183
  ...result.summary !== void 0 ? { summary: result.summary } : {},
5068
6184
  metadata: result
@@ -5171,7 +6287,7 @@ var valyuImplementation = {
5171
6287
  }
5172
6288
  };
5173
6289
  function createClient9(config) {
5174
- const apiKey = resolveConfigValue(config.apiKey);
6290
+ const apiKey = resolveConfigValue(config.credentials?.api);
5175
6291
  if (!apiKey) {
5176
6292
  throw new Error("is missing an API key");
5177
6293
  }
@@ -5183,7 +6299,7 @@ var valyuProvider = defineProvider({
5183
6299
  docsUrl: valyuImplementation.docsUrl,
5184
6300
  config: {
5185
6301
  createTemplate: () => valyuImplementation.createTemplate(),
5186
- fields: ["apiKey", "baseUrl", "options", "settings"],
6302
+ fields: ["credentials", "baseUrl", "options", "settings"],
5187
6303
  optionCapabilities: ["search", "answer", "research"]
5188
6304
  },
5189
6305
  getCapabilityStatus: (config, cwd, tool) => valyuImplementation.getCapabilityStatus(
@@ -5243,6 +6359,7 @@ var valyuProvider = defineProvider({
5243
6359
 
5244
6360
  // src/providers/index.ts
5245
6361
  var PROVIDERS = defineProviders({
6362
+ brave: braveProvider,
5246
6363
  claude: claudeProvider,
5247
6364
  codex: codexProvider,
5248
6365
  cloudflare: cloudflareProvider,
@@ -5312,7 +6429,13 @@ async function loadConfig() {
5312
6429
  async function readConfigFile(path) {
5313
6430
  try {
5314
6431
  const content = await readFile(path, "utf-8");
5315
- return parseConfig(content, path);
6432
+ const raw = parseJson(content, path);
6433
+ const migrated = migrateLegacyCredentialConfig(raw);
6434
+ const config = normalizeConfig(migrated.config, path);
6435
+ if (migrated.changed) {
6436
+ await writeFile(path, serializeConfig(config), "utf-8");
6437
+ }
6438
+ return config;
5316
6439
  } catch (error) {
5317
6440
  if (error.code === "ENOENT") {
5318
6441
  return {};
@@ -5328,9 +6451,6 @@ async function writeConfigFile(config) {
5328
6451
  await writeFile(path, serializeConfig(cleaned), "utf-8");
5329
6452
  return path;
5330
6453
  }
5331
- function parseConfig(text, source = CONFIG_FILE_NAME) {
5332
- return normalizeConfig(parseJson(text, source), source);
5333
- }
5334
6454
  function serializeConfig(config) {
5335
6455
  return `${JSON.stringify(toPublicConfig(config), null, 2)}
5336
6456
  `;
@@ -5342,6 +6462,32 @@ function parseJson(text, source) {
5342
6462
  throw new Error(`Invalid JSON in ${source}: ${error.message}`);
5343
6463
  }
5344
6464
  }
6465
+ function migrateLegacyCredentialConfig(raw) {
6466
+ if (!isPlainObject3(raw) || !isPlainObject3(raw.providers)) {
6467
+ return { config: raw, changed: false };
6468
+ }
6469
+ let changed = false;
6470
+ const config = structuredClone(raw);
6471
+ const providers = config.providers;
6472
+ for (const [providerId, provider] of Object.entries(providers)) {
6473
+ if (!isPlainObject3(provider)) {
6474
+ continue;
6475
+ }
6476
+ const legacyKey = providerId === "cloudflare" ? "apiToken" : "apiKey";
6477
+ const legacyValue = provider[legacyKey];
6478
+ if (legacyValue === void 0) {
6479
+ continue;
6480
+ }
6481
+ const credentials = isPlainObject3(provider.credentials) ? { ...provider.credentials } : {};
6482
+ if (credentials.api === void 0) {
6483
+ credentials.api = legacyValue;
6484
+ }
6485
+ provider.credentials = credentials;
6486
+ delete provider[legacyKey];
6487
+ changed = true;
6488
+ }
6489
+ return { config, changed };
6490
+ }
5345
6491
  function normalizeConfig(raw, source) {
5346
6492
  const configObject = requireObject2(
5347
6493
  raw,
@@ -5407,8 +6553,6 @@ function toProviderConfigKey(field) {
5407
6553
  function getProviderConfigFieldParser(field, optionCapabilities) {
5408
6554
  switch (field) {
5409
6555
  case "accountId":
5410
- case "apiKey":
5411
- case "apiToken":
5412
6556
  case "baseUrl":
5413
6557
  case "codexPath":
5414
6558
  case "pathToClaudeCodeExecutable":
@@ -5417,6 +6561,7 @@ function getProviderConfigFieldParser(field, optionCapabilities) {
5417
6561
  return readOptionalObject;
5418
6562
  case "customOptions":
5419
6563
  return parseOptionalCustomProviderOptions;
6564
+ case "credentials":
5420
6565
  case "env":
5421
6566
  return readOptionalStringMap;
5422
6567
  case "options":
@@ -5701,8 +6846,7 @@ function toPublicProviderConfig(provider) {
5701
6846
  } : {},
5702
6847
  ..."codexPath" in provider && provider.codexPath !== void 0 ? { codexPath: provider.codexPath } : {},
5703
6848
  ..."baseUrl" in provider && provider.baseUrl !== void 0 ? { baseUrl: provider.baseUrl } : {},
5704
- ..."apiKey" in provider && provider.apiKey !== void 0 ? { apiKey: provider.apiKey } : {},
5705
- ..."apiToken" in provider && provider.apiToken !== void 0 ? { apiToken: provider.apiToken } : {},
6849
+ ...provider.credentials ? { credentials: provider.credentials } : {},
5706
6850
  ..."accountId" in provider && provider.accountId !== void 0 ? { accountId: provider.accountId } : {},
5707
6851
  ..."env" in provider && provider.env !== void 0 ? { env: provider.env } : {},
5708
6852
  ..."config" in provider && provider.config !== void 0 ? { config: provider.config } : {},
@@ -5945,6 +7089,10 @@ function getEffectiveProviderConfig(config, providerId) {
5945
7089
  const resolved = {
5946
7090
  ...defaults,
5947
7091
  ...overrides,
7092
+ credentials: mergeNestedObjects(
7093
+ defaults.credentials,
7094
+ overrides.credentials
7095
+ ),
5948
7096
  options: mergeNestedObjects(defaults.options, overrides.options)
5949
7097
  };
5950
7098
  const effectiveSettings = mergeExecutionSettings(
@@ -5958,26 +7106,26 @@ function getEffectiveProviderConfig(config, providerId) {
5958
7106
  }
5959
7107
  return resolved;
5960
7108
  }
5961
- function mergeExecutionSettings(base, overrides) {
7109
+ function mergeExecutionSettings(base2, overrides) {
5962
7110
  const merged = {
5963
- requestTimeoutMs: overrides?.requestTimeoutMs ?? base?.requestTimeoutMs,
5964
- retryCount: overrides?.retryCount ?? base?.retryCount,
5965
- retryDelayMs: overrides?.retryDelayMs ?? base?.retryDelayMs,
5966
- researchTimeoutMs: overrides?.researchTimeoutMs ?? base?.researchTimeoutMs
7111
+ requestTimeoutMs: overrides?.requestTimeoutMs ?? base2?.requestTimeoutMs,
7112
+ retryCount: overrides?.retryCount ?? base2?.retryCount,
7113
+ retryDelayMs: overrides?.retryDelayMs ?? base2?.retryDelayMs,
7114
+ researchTimeoutMs: overrides?.researchTimeoutMs ?? base2?.researchTimeoutMs
5967
7115
  };
5968
7116
  return Object.values(merged).some((value) => value !== void 0) ? merged : void 0;
5969
7117
  }
5970
- function mergeNestedObjects(base, overrides) {
5971
- if (base === void 0) {
7118
+ function mergeNestedObjects(base2, overrides) {
7119
+ if (base2 === void 0) {
5972
7120
  return overrides;
5973
7121
  }
5974
7122
  if (overrides === void 0) {
5975
- return base;
7123
+ return base2;
5976
7124
  }
5977
- if (!isPlainObject4(base) || !isPlainObject4(overrides)) {
7125
+ if (!isPlainObject4(base2) || !isPlainObject4(overrides)) {
5978
7126
  return overrides;
5979
7127
  }
5980
- const result = { ...base };
7128
+ const result = { ...base2 };
5981
7129
  for (const [key, value] of Object.entries(overrides)) {
5982
7130
  const baseValue = result[key];
5983
7131
  result[key] = isPlainObject4(baseValue) && isPlainObject4(value) ? mergeNestedObjects(baseValue, value) : value;
@@ -6013,9 +7161,9 @@ function getProviderSetupState(config, providerId) {
6013
7161
  return Object.keys(providerConfig).length > 0 ? "configured" : "none";
6014
7162
  }
6015
7163
  if (providerId === "cloudflare") {
6016
- return providerConfig.apiToken !== void 0 || providerConfig.accountId !== void 0 ? "configured" : "none";
7164
+ return providerConfig.credentials !== void 0 || providerConfig.accountId !== void 0 ? "configured" : "none";
6017
7165
  }
6018
- return providerConfig.apiKey !== void 0 ? "configured" : "none";
7166
+ return providerConfig.credentials !== void 0 ? "configured" : "none";
6019
7167
  }
6020
7168
  function formatProviderCapabilityStatus(status, providerId, tool) {
6021
7169
  switch (status.state) {
@@ -6242,8 +7390,8 @@ async function startContentsPrefetch({
6242
7390
  const ttlMs = clampTtlMs(options.ttlMs);
6243
7391
  void Promise.allSettled(
6244
7392
  selectedUrls.map(
6245
- (url) => ensureContentsStored({
6246
- url,
7393
+ (url2) => ensureContentsStored({
7394
+ url: url2,
6247
7395
  providerId: provider.id,
6248
7396
  config,
6249
7397
  cwd,
@@ -6269,7 +7417,7 @@ async function resolveContentsFromStore({
6269
7417
  onProgress
6270
7418
  }) {
6271
7419
  cleanupExpiredEntries();
6272
- if (urls.length <= 1 || urls.some((url) => hasReusableContents(url, providerId, options))) {
7420
+ if (urls.length <= 1 || urls.some((url2) => hasReusableContents(url2, providerId, options))) {
6273
7421
  return await resolvePerUrlContents({
6274
7422
  urls,
6275
7423
  providerId,
@@ -6316,8 +7464,8 @@ async function resolvePerUrlContents({
6316
7464
  }) {
6317
7465
  const settled = await Promise.allSettled(
6318
7466
  urls.map(
6319
- (url) => ensureContentsStored({
6320
- url,
7467
+ (url2) => ensureContentsStored({
7468
+ url: url2,
6321
7469
  providerId,
6322
7470
  config,
6323
7471
  cwd,
@@ -6408,7 +7556,7 @@ async function fetchBatchContents({
6408
7556
  };
6409
7557
  }
6410
7558
  async function ensureContentsStored({
6411
- url,
7559
+ url: url2,
6412
7560
  providerId,
6413
7561
  config,
6414
7562
  cwd,
@@ -6418,7 +7566,7 @@ async function ensureContentsStored({
6418
7566
  ttlMs = DEFAULT_CONTENT_TTL_MS,
6419
7567
  generation = contentStoreGeneration
6420
7568
  }) {
6421
- const canonicalUrl = canonicalizeUrl(url);
7569
+ const canonicalUrl = canonicalizeUrl(url2);
6422
7570
  const key = buildContentsCacheKey(canonicalUrl, providerId, options);
6423
7571
  const cached = getCachedContents(key);
6424
7572
  if (cached) {
@@ -6501,8 +7649,8 @@ function cleanupExpiredEntries(now = Date.now()) {
6501
7649
  }
6502
7650
  }
6503
7651
  }
6504
- function hasReusableContents(url, providerId, options) {
6505
- const key = buildContentsCacheKey(canonicalizeUrl(url), providerId, options);
7652
+ function hasReusableContents(url2, providerId, options) {
7653
+ const key = buildContentsCacheKey(canonicalizeUrl(url2), providerId, options);
6506
7654
  return getCachedContents(key) !== void 0 || inFlightContents.has(key);
6507
7655
  }
6508
7656
  function getCachedContents(key) {
@@ -6521,8 +7669,8 @@ function setCachedContents(key, value, generation) {
6521
7669
  contentCache.set(key, value);
6522
7670
  }
6523
7671
  }
6524
- function findAnswerForUrl(answers, url) {
6525
- return answers.find((answer) => canonicalizeUrl(answer.url) === url);
7672
+ function findAnswerForUrl(answers, url2) {
7673
+ return answers.find((answer) => canonicalizeUrl(answer.url) === url2);
6526
7674
  }
6527
7675
  function toStoredContentItem(answer) {
6528
7676
  return {
@@ -6550,8 +7698,8 @@ function orderContentsForRequest(answers, urls) {
6550
7698
  }
6551
7699
  }
6552
7700
  const ordered = [];
6553
- for (const url of urls) {
6554
- const bucket = byUrl.get(canonicalizeUrl(url));
7701
+ for (const url2 of urls) {
7702
+ const bucket = byUrl.get(canonicalizeUrl(url2));
6555
7703
  const next = bucket?.shift();
6556
7704
  if (next) {
6557
7705
  ordered.push(next);
@@ -6563,12 +7711,12 @@ function orderContentsForRequest(answers, urls) {
6563
7711
  ordered.push(...extras);
6564
7712
  return ordered.length > 0 ? ordered : answers;
6565
7713
  }
6566
- function buildContentsCacheKey(url, providerId, options) {
7714
+ function buildContentsCacheKey(url2, providerId, options) {
6567
7715
  return [
6568
7716
  "web-contents",
6569
7717
  `v${CONTENT_CACHE_VERSION}`,
6570
7718
  providerId,
6571
- hashString(url),
7719
+ hashString(url2),
6572
7720
  hashOptions(options)
6573
7721
  ].join(":");
6574
7722
  }
@@ -6594,24 +7742,24 @@ function resolveContentsProvider(config, cwd, explicitProvider) {
6594
7742
  );
6595
7743
  return isProviderCapabilityReady(status) ? provider : void 0;
6596
7744
  }
6597
- function canonicalizeUrl(url) {
7745
+ function canonicalizeUrl(url2) {
6598
7746
  try {
6599
- const parsed = new URL(url);
7747
+ const parsed = new URL(url2);
6600
7748
  parsed.hash = "";
6601
7749
  return parsed.toString();
6602
7750
  } catch {
6603
- return url.trim();
7751
+ return url2.trim();
6604
7752
  }
6605
7753
  }
6606
7754
  function normalizeBatchUrls(urls) {
6607
- return [...new Set(urls.map((url) => canonicalizeUrl(url)).filter(Boolean))].filter((url) => /^https?:\/\//i.test(url)).sort();
7755
+ return [...new Set(urls.map((url2) => canonicalizeUrl(url2)).filter(Boolean))].filter((url2) => /^https?:\/\//i.test(url2)).sort();
6608
7756
  }
6609
7757
  function selectPrefetchUrls(urls, maxUrls) {
6610
7758
  const limit = clampPrefetchUrlCount(maxUrls);
6611
7759
  const seen = /* @__PURE__ */ new Set();
6612
7760
  const selected = [];
6613
- for (const url of urls) {
6614
- const canonical = canonicalizeUrl(url);
7761
+ for (const url2 of urls) {
7762
+ const canonical = canonicalizeUrl(url2);
6615
7763
  if (!/^https?:\/\//i.test(canonical) || seen.has(canonical)) {
6616
7764
  continue;
6617
7765
  }
@@ -6664,6 +7812,23 @@ function isContentsAnswer(value) {
6664
7812
 
6665
7813
  // src/provider-config-manifests.ts
6666
7814
  var PROVIDER_SETTINGS = {
7815
+ brave: {
7816
+ settings: [
7817
+ credentialSetting({
7818
+ id: "credentials.search",
7819
+ name: "search",
7820
+ label: "Search API key",
7821
+ help: "Brave Search API key. You can use a literal value, an env var name like BRAVE_SEARCH_API_KEY, or !command."
7822
+ }),
7823
+ credentialSetting({
7824
+ id: "credentials.answers",
7825
+ name: "answers",
7826
+ label: "Answers API key",
7827
+ help: "Brave Answers API key. You can use a literal value, an env var name like BRAVE_ANSWERS_API_KEY, or !command."
7828
+ }),
7829
+ baseUrlSetting()
7830
+ ]
7831
+ },
6667
7832
  claude: {
6668
7833
  settings: [
6669
7834
  stringSetting({
@@ -6724,19 +7889,10 @@ var PROVIDER_SETTINGS = {
6724
7889
  },
6725
7890
  cloudflare: {
6726
7891
  settings: [
6727
- stringSetting({
6728
- id: "apiToken",
7892
+ credentialSetting({
7893
+ id: "credentials.api",
6729
7894
  label: "API token",
6730
- help: "Cloudflare API token for Browser Rendering. The token needs the permission `Account | Browser Rendering | Edit`. You can use a literal value, an env var name like CLOUDFLARE_API_TOKEN, or !command.",
6731
- secret: true,
6732
- getValue: (config) => config?.apiToken,
6733
- setValue: (config, value) => {
6734
- assignOptionalString(
6735
- config,
6736
- "apiToken",
6737
- value
6738
- );
6739
- }
7895
+ help: "Cloudflare API token for Browser Rendering. The token needs the permission `Account | Browser Rendering | Edit`. You can use a literal value, an env var name like CLOUDFLARE_API_TOKEN, or !command."
6740
7896
  }),
6741
7897
  stringSetting({
6742
7898
  id: "accountId",
@@ -6963,7 +8119,7 @@ var PROVIDER_SETTINGS = {
6963
8119
  },
6964
8120
  exa: {
6965
8121
  settings: [
6966
- apiKeySetting(),
8122
+ credentialSetting(),
6967
8123
  baseUrlSetting(),
6968
8124
  valuesSetting({
6969
8125
  id: "exaSearchType",
@@ -7020,11 +8176,11 @@ var PROVIDER_SETTINGS = {
7020
8176
  ]
7021
8177
  },
7022
8178
  firecrawl: {
7023
- settings: [apiKeySetting(), baseUrlSetting()]
8179
+ settings: [credentialSetting(), baseUrlSetting()]
7024
8180
  },
7025
8181
  gemini: {
7026
8182
  settings: [
7027
- apiKeySetting(),
8183
+ credentialSetting(),
7028
8184
  valuesSetting({
7029
8185
  id: "geminiApiVersion",
7030
8186
  label: "API version",
@@ -7086,14 +8242,14 @@ var PROVIDER_SETTINGS = {
7086
8242
  ]
7087
8243
  },
7088
8244
  linkup: {
7089
- settings: [apiKeySetting(), baseUrlSetting()]
8245
+ settings: [credentialSetting(), baseUrlSetting()]
7090
8246
  },
7091
8247
  ollama: {
7092
- settings: [apiKeySetting(), baseUrlSetting()]
8248
+ settings: [credentialSetting(), baseUrlSetting()]
7093
8249
  },
7094
8250
  openai: {
7095
8251
  settings: [
7096
- apiKeySetting(),
8252
+ credentialSetting(),
7097
8253
  baseUrlSetting(),
7098
8254
  stringSetting({
7099
8255
  id: "openaiSearchModel",
@@ -7197,11 +8353,11 @@ var PROVIDER_SETTINGS = {
7197
8353
  ]
7198
8354
  },
7199
8355
  perplexity: {
7200
- settings: [apiKeySetting(), baseUrlSetting()]
8356
+ settings: [credentialSetting(), baseUrlSetting()]
7201
8357
  },
7202
8358
  parallel: {
7203
8359
  settings: [
7204
- apiKeySetting(),
8360
+ credentialSetting(),
7205
8361
  baseUrlSetting(),
7206
8362
  valuesSetting({
7207
8363
  id: "parallelSearchMode",
@@ -7263,14 +8419,14 @@ var PROVIDER_SETTINGS = {
7263
8419
  ]
7264
8420
  },
7265
8421
  serper: {
7266
- settings: [apiKeySetting(), baseUrlSetting()]
8422
+ settings: [credentialSetting(), baseUrlSetting()]
7267
8423
  },
7268
8424
  tavily: {
7269
- settings: [apiKeySetting(), baseUrlSetting()]
8425
+ settings: [credentialSetting(), baseUrlSetting()]
7270
8426
  },
7271
8427
  valyu: {
7272
8428
  settings: [
7273
- apiKeySetting(),
8429
+ credentialSetting(),
7274
8430
  baseUrlSetting(),
7275
8431
  valuesSetting({
7276
8432
  id: "valyuSearchType",
@@ -7353,19 +8509,22 @@ function valuesSetting(setting) {
7353
8509
  ...setting
7354
8510
  };
7355
8511
  }
7356
- function apiKeySetting() {
8512
+ function credentialSetting(options = {}) {
8513
+ const name = options.name ?? "api";
7357
8514
  return stringSetting({
7358
- id: "apiKey",
7359
- label: "API key",
7360
- help: "Provider API key. You can use a literal value, an env var name like EXA_API_KEY, or !command.",
8515
+ id: options.id ?? `credentials.${name}`,
8516
+ label: options.label ?? "API key",
8517
+ help: options.help ?? "Provider credential. You can use a literal value, an env var name like EXA_API_KEY, or !command.",
7361
8518
  secret: true,
7362
- getValue: (config) => config?.apiKey,
8519
+ getValue: (config) => config?.credentials?.[name],
7363
8520
  setValue: (config, value) => {
7364
- assignOptionalString(
7365
- config,
7366
- "apiKey",
7367
- value
7368
- );
8521
+ const trimmed = value.trim();
8522
+ if (!trimmed) {
8523
+ delete config.credentials?.[name];
8524
+ } else {
8525
+ config.credentials = { ...config.credentials ?? {}, [name]: trimmed };
8526
+ }
8527
+ cleanupEmpty(config, "credentials");
7369
8528
  }
7370
8529
  });
7371
8530
  }
@@ -7776,18 +8935,18 @@ function registerWebSearchTool(pi, providerIds) {
7776
8935
  name: "web_search",
7777
8936
  label: "Web Search",
7778
8937
  description: `Find likely sources on the public web for up to ${MAX_SEARCH_QUERIES} queries in a single call and return titles, URLs, and snippets grouped by query. Output is truncated to ${DEFAULT_MAX_LINES} lines or ${formatSize(DEFAULT_MAX_BYTES)} when needed.`,
7779
- promptGuidelines: [
8938
+ promptGuidelines: buildPromptGuidelines("search", selectedProviderId, [
7780
8939
  "Batch related searches when grouped comparison matters; use separate sibling web_search calls when independent results should surface as soon as they are ready."
7781
- ],
7782
- parameters: Type15.Object(
8940
+ ]),
8941
+ parameters: Type16.Object(
7783
8942
  {
7784
- queries: Type15.Array(Type15.String({ minLength: 1 }), {
8943
+ queries: Type16.Array(Type16.String({ minLength: 1 }), {
7785
8944
  minItems: 1,
7786
8945
  maxItems: MAX_SEARCH_QUERIES,
7787
8946
  description: `One or more search queries to run in one call (max ${MAX_SEARCH_QUERIES})`
7788
8947
  }),
7789
- maxResults: Type15.Optional(
7790
- Type15.Integer({
8948
+ maxResults: Type16.Optional(
8949
+ Type16.Integer({
7791
8950
  minimum: 1,
7792
8951
  maximum: maxAllowedResults,
7793
8952
  description: `Maximum number of results to return (default: ${DEFAULT_MAX_RESULTS})`
@@ -7838,9 +8997,9 @@ function registerWebContentsTool(pi, providerIds) {
7838
8997
  name: "web_contents",
7839
8998
  label: "Web Contents",
7840
8999
  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.",
7841
- parameters: Type15.Object(
9000
+ parameters: Type16.Object(
7842
9001
  {
7843
- urls: Type15.Array(Type15.String({ minLength: 1 }), {
9002
+ urls: Type16.Array(Type16.String({ minLength: 1 }), {
7844
9003
  minItems: 1,
7845
9004
  description: "One or more URLs to extract"
7846
9005
  }),
@@ -7851,6 +9010,7 @@ function registerWebContentsTool(pi, providerIds) {
7851
9010
  },
7852
9011
  { additionalProperties: false }
7853
9012
  ),
9013
+ promptGuidelines: buildPromptGuidelines("contents", selectedProviderId, []),
7854
9014
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
7855
9015
  return executeProviderTool({
7856
9016
  config: await loadConfig(),
@@ -7892,9 +9052,9 @@ function registerWebAnswerTool(pi, providerIds) {
7892
9052
  name: "web_answer",
7893
9053
  label: "Web Answer",
7894
9054
  description: `Answer one or more simple factual questions using web-grounded evidence (up to ${MAX_SEARCH_QUERIES} per call). Prefer web_search plus web_contents when source selection matters, and web_research for multi-step investigations.`,
7895
- parameters: Type15.Object(
9055
+ parameters: Type16.Object(
7896
9056
  {
7897
- queries: Type15.Array(Type15.String({ minLength: 1 }), {
9057
+ queries: Type16.Array(Type16.String({ minLength: 1 }), {
7898
9058
  minItems: 1,
7899
9059
  maxItems: MAX_SEARCH_QUERIES,
7900
9060
  description: `One or more simple factual questions to answer in one call (max ${MAX_SEARCH_QUERIES})`
@@ -7906,11 +9066,11 @@ function registerWebAnswerTool(pi, providerIds) {
7906
9066
  },
7907
9067
  { additionalProperties: false }
7908
9068
  ),
7909
- promptGuidelines: [
9069
+ promptGuidelines: buildPromptGuidelines("answer", selectedProviderId, [
7910
9070
  "Use web_answer as a quick grounded-answer shortcut for simple factual questions, not as a replacement for inspecting sources or doing deeper research.",
7911
9071
  "Prefer web_search plus web_contents when source selection matters or primary sources need direct inspection; prefer web_research for open-ended, controversial, or multi-step investigations.",
7912
9072
  "Batch related questions when the answers belong together; use separate sibling web_answer calls when earlier independent answers can unblock the next step."
7913
- ],
9073
+ ]),
7914
9074
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
7915
9075
  return executeAnswerTool({
7916
9076
  config: await loadConfig(),
@@ -7952,9 +9112,9 @@ function registerWebResearchTool(pi, webResearchLifecycle, providerIds) {
7952
9112
  name: "web_research",
7953
9113
  label: "Web Research",
7954
9114
  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.",
7955
- parameters: Type15.Object(
9115
+ parameters: Type16.Object(
7956
9116
  {
7957
- input: Type15.String({ description: "Research brief or question" }),
9117
+ input: Type16.String({ description: "Research brief or question" }),
7958
9118
  ...optionalField(
7959
9119
  "options",
7960
9120
  buildStructuredOptionsSchema("research", selectedProviderId)
@@ -7962,10 +9122,11 @@ function registerWebResearchTool(pi, webResearchLifecycle, providerIds) {
7962
9122
  },
7963
9123
  { additionalProperties: false }
7964
9124
  ),
7965
- promptGuidelines: [
9125
+ promptGuidelines: buildPromptGuidelines("research", selectedProviderId, [
7966
9126
  "Use this tool for deep investigations that can finish asynchronously.",
9127
+ "Pass only input unless the user explicitly requests provider options.",
7967
9128
  "Do not expect the final report in the same turn; tell the user that web research has started and wait for the completion message with the saved report path."
7968
- ],
9129
+ ]),
7969
9130
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
7970
9131
  return dispatchWebResearch({
7971
9132
  pi,
@@ -8092,13 +9253,23 @@ function getSearchMaxResultsLimit(providerId) {
8092
9253
  const capabilities = PROVIDERS_BY_ID[providerId].capabilities;
8093
9254
  return capabilities.search?.limits?.maxResults ?? MAX_ALLOWED_RESULTS;
8094
9255
  }
9256
+ function buildPromptGuidelines(capability, providerId, baseGuidelines) {
9257
+ return [
9258
+ ...baseGuidelines,
9259
+ ...getProviderCapabilityPromptGuidelines(capability, providerId)
9260
+ ];
9261
+ }
9262
+ function getProviderCapabilityPromptGuidelines(capability, providerId) {
9263
+ const capabilities = PROVIDERS_BY_ID[providerId].capabilities;
9264
+ return capabilities[capability]?.promptGuidelines ?? [];
9265
+ }
8095
9266
  function optionalField(name, schema) {
8096
9267
  return schema ? { [name]: schema } : {};
8097
9268
  }
8098
9269
  function buildStructuredOptionsSchema(capability, providerId) {
8099
9270
  const providerSchema = resolveProviderOptionsSchema(capability, providerId);
8100
9271
  const schema = buildToolOptionsSchema(capability, providerSchema);
8101
- return schema ? Type15.Optional(schema) : void 0;
9272
+ return schema ? Type16.Optional(schema) : void 0;
8102
9273
  }
8103
9274
  function resolveProviderOptionsSchema(capability, providerId) {
8104
9275
  if (!providerId) {
@@ -8447,8 +9618,11 @@ function formatAnswerResponses(outcomes) {
8447
9618
  ).join("\n\n");
8448
9619
  }
8449
9620
  function formatAnswerOutcomeSection(outcome, index, total) {
8450
- const heading = total > 1 ? `## Question ${index + 1}: ${formatAnswerHeading(outcome.query)}` : `## ${formatAnswerHeading(outcome.query)}`;
8451
9621
  const body = outcome.response ? outcome.response.text : `Answer failed: ${outcome.error ?? "Unknown error."}`;
9622
+ if (total === 1) {
9623
+ return body;
9624
+ }
9625
+ const heading = `## Question ${index + 1}: ${formatAnswerHeading(outcome.query)}`;
8452
9626
  return `${heading}
8453
9627
 
8454
9628
  ${body}`;
@@ -8501,7 +9675,7 @@ async function executeProviderOperation({
8501
9675
  `Fetching contents via ${provider.label} for ${(urls ?? []).length} URL(s)`
8502
9676
  );
8503
9677
  } else if (capability === "answer") {
8504
- onProgress?.(`Answering via ${provider.label}: ${query2 ?? ""}`);
9678
+ onProgress?.(`Answering via ${provider.label}`);
8505
9679
  } else if (capability === "research") {
8506
9680
  onProgress?.(`Researching via ${provider.label}`);
8507
9681
  }
@@ -8922,7 +10096,7 @@ async function executeBatchedContentsTool({
8922
10096
  batchProgress.start();
8923
10097
  const settled = await Promise.allSettled(
8924
10098
  urls.map(
8925
- (url, index) => executeProviderOperation({
10099
+ (url2, index) => executeProviderOperation({
8926
10100
  capability: "contents",
8927
10101
  config,
8928
10102
  provider,
@@ -8930,7 +10104,7 @@ async function executeBatchedContentsTool({
8930
10104
  ctx,
8931
10105
  signal,
8932
10106
  options,
8933
- urls: [url],
10107
+ urls: [url2],
8934
10108
  onProgress: void 0,
8935
10109
  executionOverride: executionOverrides?.[index]
8936
10110
  }).then(
@@ -8989,9 +10163,9 @@ async function executeBatchedContentsTool({
8989
10163
  }
8990
10164
  return {
8991
10165
  provider: successful[0]?.response.provider ?? provider.id,
8992
- answers: urls.map((url) => {
8993
- return answersByUrl.get(url) ?? {
8994
- url,
10166
+ answers: urls.map((url2) => {
10167
+ return answersByUrl.get(url2) ?? {
10168
+ url: url2,
8995
10169
  error: "No content returned for this URL."
8996
10170
  };
8997
10171
  })
@@ -9094,13 +10268,21 @@ function renderListCallHeader(toolName, items, theme, suffix, options = {}) {
9094
10268
  headerLine + " ".repeat(Math.max(0, width - visibleWidth(headerLine)))
9095
10269
  );
9096
10270
  for (const item of normalizedItems) {
9097
- const itemLine = truncateToWidth(
9098
- ` ${theme.fg("accent", truncateInline(item, 120))}`,
9099
- width
9100
- );
9101
- lines.push(
9102
- itemLine + " ".repeat(Math.max(0, width - visibleWidth(itemLine)))
9103
- );
10271
+ const itemLines = options.forceMultiline ? wrapTextWithAnsi(
10272
+ theme.fg("accent", item),
10273
+ Math.max(1, width - 2)
10274
+ ).map((line) => ` ${line}`) : [
10275
+ truncateToWidth(
10276
+ ` ${theme.fg("accent", truncateInline(item, 120))}`,
10277
+ width
10278
+ )
10279
+ ];
10280
+ for (const itemLine of itemLines) {
10281
+ const line = truncateToWidth(itemLine, width);
10282
+ lines.push(
10283
+ line + " ".repeat(Math.max(0, width - visibleWidth(line)))
10284
+ );
10285
+ }
9104
10286
  }
9105
10287
  return lines;
9106
10288
  }
@@ -9124,15 +10306,7 @@ function renderQuestionCallHeader(params, theme) {
9124
10306
  );
9125
10307
  }
9126
10308
  function renderResearchCallHeader(params, theme) {
9127
- return renderListCallHeader(
9128
- "web_research",
9129
- [params.input],
9130
- theme,
9131
- void 0,
9132
- {
9133
- forceMultiline: true
9134
- }
9135
- );
10309
+ return renderListCallHeader("web_research", [params.input], theme);
9136
10310
  }
9137
10311
  function renderSearchToolResult(result, expanded, isPartial, theme) {
9138
10312
  const text = extractTextContent(result.content);
@@ -9153,7 +10327,18 @@ function renderWebResearchDispatchResult(result, expanded, theme) {
9153
10327
  const text = extractTextContent(result.content) ?? "Started web research.";
9154
10328
  const details = isWebResearchRequest(result.details) ? result.details : void 0;
9155
10329
  if (expanded) {
9156
- return renderBlockText(details?.input ?? text, theme, "toolOutput");
10330
+ const expandedText = details ? [
10331
+ text,
10332
+ "",
10333
+ "## Research brief",
10334
+ "",
10335
+ details.input,
10336
+ "",
10337
+ "## Report path",
10338
+ "",
10339
+ `\`${details.outputPath}\``
10340
+ ].join("\n") : text;
10341
+ return renderMarkdownBlock(expandedText);
9157
10342
  }
9158
10343
  const summary = details ? `Started web research via ${PROVIDERS_BY_ID[details.provider]?.label ?? details.provider}` : text;
9159
10344
  let summaryText = theme.fg("success", summary);
@@ -10410,17 +11595,17 @@ function renderCollapsedSearchSummary(details, text, theme) {
10410
11595
  const resultCount = typeof details?.resultCount === "number" ? details.resultCount : inferSearchResultCount(text);
10411
11596
  const failedQueryCount = typeof details?.failedQueryCount === "number" ? details.failedQueryCount : inferSearchFailureCount(text);
10412
11597
  const providerLabel = typeof details?.provider === "string" ? PROVIDERS_BY_ID[details.provider]?.label ?? details.provider : void 0;
10413
- let base = buildSearchSummaryText({
11598
+ let base2 = buildSearchSummaryText({
10414
11599
  queryCount,
10415
11600
  resultCount
10416
11601
  });
10417
11602
  if (providerLabel) {
10418
- base = `${base} via ${providerLabel}`;
11603
+ base2 = `${base2} via ${providerLabel}`;
10419
11604
  }
10420
11605
  if (failedQueryCount && failedQueryCount > 0) {
10421
- base += `, ${failedQueryCount} failed`;
11606
+ base2 += `, ${failedQueryCount} failed`;
10422
11607
  }
10423
- let summary = theme.fg("success", base);
11608
+ let summary = theme.fg("success", base2);
10424
11609
  summary += theme.fg("muted", ` (${getExpandHint()})`);
10425
11610
  return new Text(summary, 0, 0);
10426
11611
  }
@@ -10500,8 +11685,11 @@ function formatSearchResponses(outcomes, prefetch) {
10500
11685
  Background contents prefetch started via ${prefetch.provider} for ${prefetch.urlCount} URL(s).`;
10501
11686
  }
10502
11687
  function formatSearchOutcomeSection(outcome, index, total) {
10503
- const heading = total > 1 ? `## Query ${index + 1}: ${formatSearchHeading(outcome.query)}` : `## ${formatSearchHeading(outcome.query)}`;
10504
11688
  const body = outcome.response ? formatSearchResponseMarkdown(outcome.response) : `Search failed: ${outcome.error ?? "Unknown error."}`;
11689
+ if (total === 1) {
11690
+ return body;
11691
+ }
11692
+ const heading = `## Query ${index + 1}: ${formatSearchHeading(outcome.query)}`;
10505
11693
  return `${heading}
10506
11694
 
10507
11695
  ${body}`;
@@ -10531,8 +11719,8 @@ function formatSearchResponseMarkdown(response) {
10531
11719
  return lines.join("\n");
10532
11720
  }).join("\n\n");
10533
11721
  }
10534
- function formatMarkdownLink(label, url) {
10535
- return `[${escapeMarkdownLinkLabel(label)}](<${url}>)`;
11722
+ function formatMarkdownLink(label, url2) {
11723
+ return `[${escapeMarkdownLinkLabel(label)}](<${url2}>)`;
10536
11724
  }
10537
11725
  function escapeMarkdownLinkLabel(text) {
10538
11726
  return cleanSingleLine(text).replaceAll("\\", "\\\\").replaceAll("]", "\\]");