webfox 0.0.0-test.0 → 4.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 (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +256 -3
  3. package/assets/webfox-lockup-dark.png +0 -0
  4. package/assets/webfox-lockup.png +0 -0
  5. package/dist/adapter-2YBFCBCZ.js +161 -0
  6. package/dist/adapter-67TIISDW.js +753 -0
  7. package/dist/adapter-AB7LKV7N.js +324 -0
  8. package/dist/adapter-AUYLTDPM.js +73 -0
  9. package/dist/adapter-HITGYUXN.js +494 -0
  10. package/dist/adapter-HPTMLH75.js +334 -0
  11. package/dist/adapter-I6OUKBZQ.js +492 -0
  12. package/dist/adapter-ISZQTZDP.js +166 -0
  13. package/dist/adapter-MAG7X663.js +230 -0
  14. package/dist/adapter-NEXSOHGT.js +232 -0
  15. package/dist/adapter-PADXKW43.js +162 -0
  16. package/dist/adapter-SDQX2533.js +123 -0
  17. package/dist/adapter-UAZNLDSS.js +237 -0
  18. package/dist/adapter-XBAQSK7I.js +282 -0
  19. package/dist/application-types.d.ts +34 -0
  20. package/dist/application.d.ts +2 -0
  21. package/dist/chunk-EF4VMDZL.js +85 -0
  22. package/dist/chunk-G2YAQEET.js +38 -0
  23. package/dist/chunk-I2UIACY3.js +12 -0
  24. package/dist/chunk-L425U55P.js +3051 -0
  25. package/dist/chunk-QRHDXM2L.js +31 -0
  26. package/dist/chunk-SMF5ALX7.js +762 -0
  27. package/dist/chunk-U4BLULLV.js +52 -0
  28. package/dist/chunk-WCTOYZVB.js +85 -0
  29. package/dist/chunk-X6ZBIHZC.js +21 -0
  30. package/dist/chunk-YZXM4U2C.js +21 -0
  31. package/dist/chunk-ZVTVERW5.js +20 -0
  32. package/dist/cli.js +641 -0
  33. package/dist/config.schema.json +4270 -0
  34. package/dist/configuration/defaults.d.ts +3 -0
  35. package/dist/configuration/file.d.ts +17 -0
  36. package/dist/configuration/options-schema.d.ts +2 -0
  37. package/dist/configuration/planning.d.ts +10 -0
  38. package/dist/configuration/schema.d.ts +9 -0
  39. package/dist/configuration/types.d.ts +37 -0
  40. package/dist/contents.d.ts +19 -0
  41. package/dist/domain.d.ts +89 -0
  42. package/dist/errors.d.ts +17 -0
  43. package/dist/index.d.ts +8 -0
  44. package/dist/index.js +38 -0
  45. package/dist/package-metadata.d.ts +3 -0
  46. package/dist/pi-keybindings.d.ts +1 -0
  47. package/dist/pi-params.d.ts +3 -0
  48. package/dist/pi-render.d.ts +50 -0
  49. package/dist/pi-search-render.d.ts +4 -0
  50. package/dist/pi-text.d.ts +4 -0
  51. package/dist/pi-validation.d.ts +4 -0
  52. package/dist/pi.d.ts +2 -0
  53. package/dist/pi.js +563 -0
  54. package/dist/providers/brave/adapter.d.ts +7 -0
  55. package/dist/providers/brave/definition.d.ts +1 -0
  56. package/dist/providers/brave/types.d.ts +4 -0
  57. package/dist/providers/cli-json.d.ts +9 -0
  58. package/dist/providers/cloudflare/adapter.d.ts +5 -0
  59. package/dist/providers/cloudflare/definition.d.ts +1 -0
  60. package/dist/providers/cloudflare/types.d.ts +4 -0
  61. package/dist/providers/contract.d.ts +88 -0
  62. package/dist/providers/custom/adapter.d.ts +9 -0
  63. package/dist/providers/custom/definition.d.ts +1 -0
  64. package/dist/providers/custom/types.d.ts +9 -0
  65. package/dist/providers/definition.d.ts +32 -0
  66. package/dist/providers/exa/adapter.d.ts +8 -0
  67. package/dist/providers/exa/definition.d.ts +1 -0
  68. package/dist/providers/exa/types.d.ts +4 -0
  69. package/dist/providers/firecrawl/adapter.d.ts +7 -0
  70. package/dist/providers/firecrawl/definition.d.ts +1 -0
  71. package/dist/providers/firecrawl/types.d.ts +4 -0
  72. package/dist/providers/gemini/adapter.d.ts +14 -0
  73. package/dist/providers/gemini/definition.d.ts +1 -0
  74. package/dist/providers/gemini/types.d.ts +3 -0
  75. package/dist/providers/linkup/adapter.d.ts +7 -0
  76. package/dist/providers/linkup/definition.d.ts +1 -0
  77. package/dist/providers/linkup/types.d.ts +4 -0
  78. package/dist/providers/metadata.d.ts +15 -0
  79. package/dist/providers/ollama/adapter.d.ts +6 -0
  80. package/dist/providers/ollama/definition.d.ts +1 -0
  81. package/dist/providers/ollama/types.d.ts +4 -0
  82. package/dist/providers/openai/adapter.d.ts +7 -0
  83. package/dist/providers/openai/definition.d.ts +1 -0
  84. package/dist/providers/openai/types.d.ts +28 -0
  85. package/dist/providers/parallel/adapter.d.ts +6 -0
  86. package/dist/providers/parallel/definition.d.ts +1 -0
  87. package/dist/providers/parallel/types.d.ts +4 -0
  88. package/dist/providers/perplexity/adapter.d.ts +7 -0
  89. package/dist/providers/perplexity/definition.d.ts +1 -0
  90. package/dist/providers/perplexity/types.d.ts +4 -0
  91. package/dist/providers/registry.d.ts +16 -0
  92. package/dist/providers/serper/adapter.d.ts +5 -0
  93. package/dist/providers/serper/definition.d.ts +1 -0
  94. package/dist/providers/serper/types.d.ts +21 -0
  95. package/dist/providers/shared.d.ts +4 -0
  96. package/dist/providers/tavily/adapter.d.ts +6 -0
  97. package/dist/providers/tavily/definition.d.ts +1 -0
  98. package/dist/providers/tavily/types.d.ts +4 -0
  99. package/dist/providers/valyu/adapter.d.ts +8 -0
  100. package/dist/providers/valyu/definition.d.ts +1 -0
  101. package/dist/providers/valyu/types.d.ts +4 -0
  102. package/dist/render.d.ts +5 -0
  103. package/dist/runtime/credentials.d.ts +15 -0
  104. package/dist/runtime/duration.d.ts +2 -0
  105. package/dist/runtime/execute.d.ts +20 -0
  106. package/dist/runtime/lifecycle.d.ts +8 -0
  107. package/dist/runtime/outward.d.ts +11 -0
  108. package/dist/runtime/polling.d.ts +13 -0
  109. package/dist/runtime/process.d.ts +4 -0
  110. package/docs/cli-experience.md +177 -0
  111. package/docs/provider.md +177 -0
  112. package/docs/reference.md +181 -0
  113. package/example-config.yaml +36 -0
  114. package/examples/custom/README.md +57 -0
  115. package/examples/custom/provider.mjs +54 -0
  116. package/examples/custom/webfox.json +18 -0
  117. package/package.json +122 -8
  118. package/index.js +0 -2
@@ -0,0 +1,753 @@
1
+ import {
2
+ trimSnippet
3
+ } from "./chunk-X6ZBIHZC.js";
4
+ import {
5
+ httpError
6
+ } from "./chunk-U4BLULLV.js";
7
+
8
+ // src/providers/brave/adapter.ts
9
+ var DEFAULT_BASE_URL = "https://api.search.brave.com";
10
+ var BRAVE_API_VERSION = void 0;
11
+ var braveImplementation = {
12
+ async search(query, maxResults, config, context, options) {
13
+ const apiKey = requireKey(config.credentials?.search, "Brave search");
14
+ const callOptions = { ...options ?? {} };
15
+ const mode = readMode(callOptions.mode);
16
+ if (mode === "llm_context")
17
+ return await llmContext(
18
+ query,
19
+ maxResults,
20
+ config,
21
+ context,
22
+ apiKey,
23
+ callOptions
24
+ );
25
+ if (mode === "news")
26
+ return await news(
27
+ query,
28
+ maxResults,
29
+ config,
30
+ context,
31
+ apiKey,
32
+ callOptions
33
+ );
34
+ if (mode === "videos")
35
+ return await videos(
36
+ query,
37
+ maxResults,
38
+ config,
39
+ context,
40
+ apiKey,
41
+ callOptions
42
+ );
43
+ if (mode === "images")
44
+ return await images(
45
+ query,
46
+ maxResults,
47
+ config,
48
+ context,
49
+ apiKey,
50
+ callOptions
51
+ );
52
+ if (mode === "places")
53
+ return await places(
54
+ query,
55
+ maxResults,
56
+ config,
57
+ context,
58
+ apiKey,
59
+ callOptions
60
+ );
61
+ return await web(query, maxResults, config, context, apiKey, callOptions);
62
+ },
63
+ async answer(query, config, context, options) {
64
+ const raw = {
65
+ ...options ?? {}
66
+ };
67
+ return await completion(query, config, context, buildAnswerRequest(raw));
68
+ },
69
+ async research(input, config, context, options) {
70
+ const raw = {
71
+ ...options ?? {}
72
+ };
73
+ return await completion(input, config, context, buildResearchRequest(raw));
74
+ }
75
+ };
76
+ function requireKey(ref, label) {
77
+ const key = ref;
78
+ if (!key) throw new Error(`${label} is missing an API key`);
79
+ return key;
80
+ }
81
+ function base(config) {
82
+ return (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
83
+ }
84
+ function clamp(n, max = 20) {
85
+ return Math.max(1, Math.min(max, Math.trunc(n || 0)));
86
+ }
87
+ function readMode(v) {
88
+ return v === "llm_context" || v === "news" || v === "videos" || v === "images" || v === "places" ? v : "web";
89
+ }
90
+ function obj(v) {
91
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
92
+ }
93
+ function arr(v) {
94
+ return Array.isArray(v) ? v : [];
95
+ }
96
+ function str(v) {
97
+ return typeof v === "string" ? v : void 0;
98
+ }
99
+ function num(v) {
100
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
101
+ }
102
+ function pick(source, allowed) {
103
+ return Object.fromEntries(
104
+ allowed.filter((k) => source[k] !== void 0).map((k) => [k, source[k]])
105
+ );
106
+ }
107
+ function mergeOptions(options, key, allowed) {
108
+ return pick({ ...obj(options.common), ...obj(options[key]) }, allowed);
109
+ }
110
+ function headers(key, json = false) {
111
+ const result = { "X-Subscription-Token": key };
112
+ if (BRAVE_API_VERSION) {
113
+ result["Api-Version"] = BRAVE_API_VERSION;
114
+ }
115
+ if (json) {
116
+ result["content-type"] = "application/json";
117
+ }
118
+ return result;
119
+ }
120
+ function url(config, path, params) {
121
+ const u = new URL(`${base(config)}${path}`);
122
+ for (const [k, v] of Object.entries(params))
123
+ if (v !== void 0)
124
+ u.searchParams.set(k, Array.isArray(v) ? v.join(",") : String(v));
125
+ return u;
126
+ }
127
+ async function httpError2(response) {
128
+ const text = (await response.text()).trim();
129
+ return httpError(
130
+ response,
131
+ `Brave API request failed (${response.status}${response.statusText ? ` ${response.statusText}` : ""})${text ? `: ${text}` : "."}`
132
+ );
133
+ }
134
+ async function web(query, maxResults, config, context, key, options) {
135
+ const params = {
136
+ q: query,
137
+ count: clamp(maxResults),
138
+ text_decorations: false,
139
+ ...mergeOptions(options, "web", [
140
+ "country",
141
+ "search_lang",
142
+ "ui_lang",
143
+ "freshness",
144
+ "safesearch",
145
+ "spellcheck",
146
+ "goggles",
147
+ "extra_snippets",
148
+ "offset",
149
+ "enable_rich_callback"
150
+ ])
151
+ };
152
+ const r = await fetch(url(config, "/res/v1/web/search", params), {
153
+ headers: headers(key),
154
+ signal: context.signal
155
+ });
156
+ if (!r.ok) throw await httpError2(r);
157
+ const p = obj(await r.json());
158
+ return {
159
+ provider: "brave",
160
+ results: arr(obj(p.web).results).map((e) => {
161
+ const x = obj(e);
162
+ const u = str(x.url) ?? "";
163
+ return {
164
+ title: str(x.title) || u || "Untitled",
165
+ url: u,
166
+ snippet: trimSnippet(
167
+ str(x.description) ?? arr(x.extra_snippets).join(" ")
168
+ ),
169
+ metadata: x
170
+ };
171
+ }).slice(0, clamp(maxResults))
172
+ };
173
+ }
174
+ async function llmContext(query, maxResults, config, context, key, options) {
175
+ const params = {
176
+ q: query,
177
+ count: clamp(maxResults),
178
+ maximum_number_of_urls: clamp(maxResults),
179
+ maximum_number_of_tokens: 8192,
180
+ enable_source_metadata: true,
181
+ ...mergeOptions(options, "llmContext", [
182
+ "count",
183
+ "maximum_number_of_urls",
184
+ "maximum_number_of_tokens",
185
+ "maximum_number_of_snippets",
186
+ "maximum_number_of_tokens_per_url",
187
+ "maximum_number_of_snippets_per_url",
188
+ "context_threshold_mode",
189
+ "enable_local",
190
+ "enable_source_metadata",
191
+ "country",
192
+ "search_lang",
193
+ "ui_lang",
194
+ "freshness",
195
+ "safesearch",
196
+ "spellcheck",
197
+ "goggles"
198
+ ])
199
+ };
200
+ const r = await fetch(url(config, "/res/v1/llm/context", params), {
201
+ headers: headers(key),
202
+ signal: context.signal
203
+ });
204
+ if (!r.ok) throw await httpError2(r);
205
+ const p = obj(await r.json());
206
+ return {
207
+ provider: "brave",
208
+ results: collectLlmContextEntries(obj(p.grounding)).map((e) => {
209
+ const x = obj(e);
210
+ const snippets = arr(x.snippets).map(String);
211
+ const source = obj(x.source);
212
+ const u = str(x.url) ?? str(source.url) ?? "";
213
+ return {
214
+ title: str(x.title) ?? str(x.name) ?? str(source.title) ?? (u || "Untitled"),
215
+ url: u,
216
+ snippet: trimSnippet(snippets.join("\n\n"), 1200),
217
+ metadata: x
218
+ };
219
+ }).slice(0, clamp(maxResults))
220
+ };
221
+ }
222
+ function collectLlmContextEntries(grounding) {
223
+ const entries = [];
224
+ for (const value of [grounding.generic, grounding.map]) {
225
+ entries.push(...arr(value).map(obj));
226
+ }
227
+ const poiEntries = Array.isArray(grounding.poi) ? grounding.poi.map(obj) : [obj(grounding.poi)];
228
+ entries.push(...poiEntries.filter((entry) => Object.keys(entry).length > 0));
229
+ return entries;
230
+ }
231
+ async function news(query, maxResults, config, context, key, options) {
232
+ const params = {
233
+ q: query,
234
+ count: clamp(maxResults, 50),
235
+ ...mergeOptions(options, "news", [
236
+ "country",
237
+ "search_lang",
238
+ "ui_lang",
239
+ "freshness",
240
+ "safesearch",
241
+ "spellcheck",
242
+ "goggles",
243
+ "extra_snippets",
244
+ "offset",
245
+ "count"
246
+ ])
247
+ };
248
+ const r = await fetch(url(config, "/res/v1/news/search", params), {
249
+ headers: headers(key),
250
+ signal: context.signal
251
+ });
252
+ if (!r.ok) throw await httpError2(r);
253
+ const p = obj(await r.json());
254
+ return {
255
+ provider: "brave",
256
+ results: arr(p.results).map((e) => {
257
+ const x = obj(e);
258
+ const u = str(x.url) ?? "";
259
+ const source = str(x.source) ?? str(x.source_name);
260
+ return {
261
+ title: str(x.title) || u || "Untitled",
262
+ url: u,
263
+ snippet: trimSnippet(
264
+ [
265
+ str(x.description) ?? arr(x.extra_snippets).join(" "),
266
+ source,
267
+ str(x.age) ?? str(x.page_age)
268
+ ].filter(Boolean).join(" \u2014 ")
269
+ ),
270
+ metadata: x
271
+ };
272
+ }).slice(0, clamp(maxResults, 50))
273
+ };
274
+ }
275
+ async function videos(query, maxResults, config, context, key, options) {
276
+ const params = {
277
+ q: query,
278
+ count: clamp(maxResults, 50),
279
+ ...mergeOptions(options, "videos", [
280
+ "country",
281
+ "search_lang",
282
+ "ui_lang",
283
+ "freshness",
284
+ "safesearch",
285
+ "spellcheck",
286
+ "offset",
287
+ "count"
288
+ ])
289
+ };
290
+ const r = await fetch(url(config, "/res/v1/videos/search", params), {
291
+ headers: headers(key),
292
+ signal: context.signal
293
+ });
294
+ if (!r.ok) throw await httpError2(r);
295
+ const p = obj(await r.json());
296
+ return {
297
+ provider: "brave",
298
+ results: arr(p.results).map((e) => {
299
+ const x = obj(e);
300
+ const u = str(x.url) ?? "";
301
+ const video = obj(x.video);
302
+ return {
303
+ title: str(x.title) || u || "Untitled",
304
+ url: u,
305
+ snippet: trimSnippet(
306
+ [
307
+ str(x.description),
308
+ str(video.creator) ?? str(x.creator),
309
+ str(video.duration) ?? str(x.duration),
310
+ num(video.views) ? `${num(video.views)} views` : void 0,
311
+ str(x.age) ?? str(x.page_age)
312
+ ].filter(Boolean).join(" \u2014 ")
313
+ ),
314
+ metadata: x
315
+ };
316
+ }).slice(0, clamp(maxResults, 50))
317
+ };
318
+ }
319
+ async function images(query, maxResults, config, context, key, options) {
320
+ const params = {
321
+ q: query,
322
+ count: clamp(maxResults),
323
+ ...mergeOptions(options, "images", [
324
+ "country",
325
+ "search_lang",
326
+ "ui_lang",
327
+ "safesearch",
328
+ "spellcheck",
329
+ "count"
330
+ ])
331
+ };
332
+ const r = await fetch(url(config, "/res/v1/images/search", params), {
333
+ headers: headers(key),
334
+ signal: context.signal
335
+ });
336
+ if (!r.ok) throw await httpError2(r);
337
+ const p = obj(await r.json());
338
+ return {
339
+ provider: "brave",
340
+ results: arr(p.results).map((e) => {
341
+ const x = obj(e);
342
+ const props = obj(x.properties);
343
+ const page = str(x.url) ?? str(x.source) ?? str(props.url) ?? "";
344
+ const image = str(props.url);
345
+ return {
346
+ title: str(x.title) || page || "Untitled",
347
+ url: page || image || "",
348
+ snippet: trimSnippet(
349
+ [str(x.description), str(x.publisher), image].filter(Boolean).join(" \u2014 ")
350
+ ),
351
+ metadata: x
352
+ };
353
+ }).slice(0, clamp(maxResults))
354
+ };
355
+ }
356
+ async function places(query, maxResults, config, context, key, options) {
357
+ const placeOptions = obj(options.places);
358
+ const params = {
359
+ q: query,
360
+ count: clamp(maxResults),
361
+ ...mergeOptions(options, "places", [
362
+ "country",
363
+ "search_lang",
364
+ "ui_lang",
365
+ "latitude",
366
+ "longitude",
367
+ "location",
368
+ "radius",
369
+ "units",
370
+ "safesearch",
371
+ "spellcheck",
372
+ "geoloc",
373
+ "count"
374
+ ])
375
+ };
376
+ const r = await fetch(url(config, "/res/v1/local/place_search", params), {
377
+ headers: headers(key),
378
+ signal: context.signal
379
+ });
380
+ if (!r.ok) throw await httpError2(r);
381
+ const p = obj(await r.json());
382
+ const rows = arr(p.results).slice(0, clamp(maxResults));
383
+ const ids = rows.map((e) => str(obj(e).id)).filter((id) => id !== void 0);
384
+ const [detailsById, descriptionsById] = await Promise.all([
385
+ placeOptions.includeDetails && ids.length > 0 ? fetchPlaceDetails(
386
+ config,
387
+ context,
388
+ key,
389
+ ids,
390
+ pick(params, ["search_lang", "ui_lang", "units"])
391
+ ) : Promise.resolve(/* @__PURE__ */ new Map()),
392
+ placeOptions.includeDescriptions && ids.length > 0 ? fetchPlaceDescriptions(config, context, key, ids) : Promise.resolve(/* @__PURE__ */ new Map())
393
+ ]);
394
+ return {
395
+ provider: "brave",
396
+ results: rows.map((e) => {
397
+ const x = obj(e);
398
+ const id = str(x.id);
399
+ const details = id ? detailsById.get(id) : void 0;
400
+ const description = id ? descriptionsById.get(id) : void 0;
401
+ const u = str(x.url) ?? str(x.provider_url) ?? "";
402
+ return {
403
+ title: str(x.title) || u || "Untitled",
404
+ url: u,
405
+ snippet: trimSnippet(
406
+ [
407
+ placeDescriptionText(description) ?? str(x.description),
408
+ placeAddress(x, details),
409
+ arr(x.categories).join(", "),
410
+ placeRating(x, details)
411
+ ].filter(Boolean).join(" \u2014 ")
412
+ ),
413
+ metadata: {
414
+ ...x,
415
+ ...details ? { poiDetails: details } : {},
416
+ ...description ? { poiDescription: description } : {}
417
+ }
418
+ };
419
+ })
420
+ };
421
+ }
422
+ async function fetchPlaceDetails(config, context, key, ids, options) {
423
+ const r = await fetch(
424
+ urlWithRepeatedArrays(config, "/res/v1/local/pois", {
425
+ ids: ids.slice(0, 20),
426
+ ...options
427
+ }),
428
+ {
429
+ headers: headers(key),
430
+ signal: context.signal
431
+ }
432
+ );
433
+ if (!r.ok) throw await httpError2(r);
434
+ return indexById(arr(obj(await r.json()).results));
435
+ }
436
+ async function fetchPlaceDescriptions(config, context, key, ids) {
437
+ const r = await fetch(
438
+ urlWithRepeatedArrays(config, "/res/v1/local/descriptions", {
439
+ ids: ids.slice(0, 20)
440
+ }),
441
+ {
442
+ headers: headers(key),
443
+ signal: context.signal
444
+ }
445
+ );
446
+ if (!r.ok) throw await httpError2(r);
447
+ return indexById(arr(obj(await r.json()).results));
448
+ }
449
+ function urlWithRepeatedArrays(config, path, params) {
450
+ const u = new URL(`${base(config)}${path}`);
451
+ for (const [k, v] of Object.entries(params)) {
452
+ if (v === void 0) continue;
453
+ if (Array.isArray(v)) {
454
+ for (const item of v) {
455
+ u.searchParams.append(k, String(item));
456
+ }
457
+ } else {
458
+ u.searchParams.set(k, String(v));
459
+ }
460
+ }
461
+ return u;
462
+ }
463
+ function indexById(values) {
464
+ const result = /* @__PURE__ */ new Map();
465
+ for (const value of values) {
466
+ const entry = obj(value);
467
+ const id = str(entry.id);
468
+ if (id) {
469
+ result.set(id, entry);
470
+ }
471
+ }
472
+ return result;
473
+ }
474
+ function placeDescriptionText(description) {
475
+ if (!description) return void 0;
476
+ return str(description.description) ?? str(description.text) ?? str(description.summary) ?? str(obj(description.description).text);
477
+ }
478
+ function placeAddress(place, details) {
479
+ return str(place.address) ?? str(obj(place.postal_address).displayAddress) ?? str(details?.address) ?? str(obj(details?.postal_address).displayAddress);
480
+ }
481
+ function placeRating(place, details) {
482
+ const rating = num(place.rating) ?? num(obj(place.rating).ratingValue) ?? num(details?.rating) ?? num(obj(details?.rating).ratingValue);
483
+ return rating === void 0 ? void 0 : `Rating: ${rating}`;
484
+ }
485
+ function buildAnswerRequest(raw) {
486
+ const webSearchOptions = pick(raw, [
487
+ "country",
488
+ "language",
489
+ "safesearch",
490
+ "enable_entities",
491
+ "enable_citations"
492
+ ]);
493
+ if (webSearchOptions.enable_citations === void 0) {
494
+ webSearchOptions.enable_citations = true;
495
+ }
496
+ const stream = webSearchOptions.enable_citations === true || webSearchOptions.enable_entities === true;
497
+ return {
498
+ stream,
499
+ ...pick(raw, ["model", "max_completion_tokens", "metadata", "seed"]),
500
+ web_search_options: webSearchOptions
501
+ };
502
+ }
503
+ function buildResearchRequest(raw) {
504
+ const webSearchOptions = pick(raw, [
505
+ "country",
506
+ "language",
507
+ "safesearch",
508
+ "enable_entities",
509
+ "research_allow_thinking",
510
+ "research_maximum_number_of_tokens_per_query",
511
+ "research_maximum_number_of_queries",
512
+ "research_maximum_number_of_iterations",
513
+ "research_maximum_number_of_seconds",
514
+ "research_maximum_number_of_results_per_query"
515
+ ]);
516
+ webSearchOptions.enable_research = true;
517
+ webSearchOptions.enable_citations = false;
518
+ return {
519
+ stream: true,
520
+ ...pick(raw, ["model", "max_completion_tokens", "metadata", "seed"]),
521
+ web_search_options: webSearchOptions
522
+ };
523
+ }
524
+ async function completion(input, config, context, request) {
525
+ const key = requireKey(config.credentials?.answers, "Brave Answers");
526
+ const body = {
527
+ model: "brave",
528
+ messages: [{ role: "user", content: input }],
529
+ ...request
530
+ };
531
+ const r = await fetch(`${base(config)}/res/v1/chat/completions`, {
532
+ method: "POST",
533
+ headers: headers(key, true),
534
+ body: JSON.stringify(body),
535
+ signal: context.signal
536
+ });
537
+ if (!r.ok) throw await httpError2(r);
538
+ const text = await r.text();
539
+ const parsed = body.stream === false ? parseAnswerJson(text) : parseAnswerStream(text);
540
+ const lines = [parsed.answer.trim() || text.trim()];
541
+ if (parsed.citations.length) {
542
+ lines.push("", "Sources:");
543
+ parsed.citations.forEach((c, i) => {
544
+ lines.push(`${i + 1}. ${c.title ?? c.url ?? "Source"}`);
545
+ if (c.url) lines.push(` ${c.url}`);
546
+ });
547
+ }
548
+ const metadata = buildAnswerMetadata(parsed);
549
+ return {
550
+ provider: "brave",
551
+ text: lines.join("\n").trimEnd(),
552
+ itemCount: parsed.citations.length,
553
+ ...metadata ? { metadata } : {}
554
+ };
555
+ }
556
+ function buildAnswerMetadata(parsed) {
557
+ const metadata = {};
558
+ if (parsed.usage) {
559
+ metadata.usage = parsed.usage;
560
+ }
561
+ if (parsed.entities.length) {
562
+ metadata.entities = parsed.entities;
563
+ }
564
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
565
+ }
566
+ function parseAnswerJson(text) {
567
+ const payload = obj(JSON.parse(text));
568
+ const choice = obj(arr(payload.choices)[0]);
569
+ const message = obj(choice.message);
570
+ const content = str(message.content) ?? "";
571
+ const tags = extractBraveTags(content);
572
+ return {
573
+ answer: tags.text,
574
+ citations: dedupeCitations(tags.citations),
575
+ entities: tags.entities,
576
+ usage: payload.usage ?? tags.usage
577
+ };
578
+ }
579
+ function parseAnswerStream(text) {
580
+ let rawAnswer = "";
581
+ for (const line of text.split(/\r?\n/)) {
582
+ const data = line.startsWith("data:") ? line.slice(5).trim() : line.trim();
583
+ if (!data || data === "[DONE]") continue;
584
+ try {
585
+ const parsed = obj(JSON.parse(data));
586
+ const choice = obj(arr(parsed.choices)[0]);
587
+ const delta = str(obj(choice.delta).content);
588
+ if (delta) {
589
+ rawAnswer += delta;
590
+ }
591
+ } catch {
592
+ rawAnswer += data;
593
+ }
594
+ }
595
+ const tags = extractBraveTags(rawAnswer);
596
+ return {
597
+ answer: tags.text,
598
+ citations: dedupeCitations(tags.citations),
599
+ entities: tags.entities,
600
+ usage: tags.usage
601
+ };
602
+ }
603
+ function extractBraveTags(text) {
604
+ const citations = [];
605
+ const entities = [];
606
+ let usage;
607
+ let cleaned = "";
608
+ let offset = 0;
609
+ while (offset < text.length) {
610
+ const tagStart = text.indexOf("<", offset);
611
+ if (tagStart === -1) {
612
+ cleaned += text.slice(offset);
613
+ break;
614
+ }
615
+ const parsedTag = readBraveTagStart(text, tagStart);
616
+ if (!parsedTag) {
617
+ cleaned += text.slice(offset, tagStart + 1);
618
+ offset = tagStart + 1;
619
+ continue;
620
+ }
621
+ const jsonEnd = findJsonValueEnd(text, parsedTag.jsonStart);
622
+ if (jsonEnd === -1) {
623
+ cleaned += text.slice(offset);
624
+ break;
625
+ }
626
+ cleaned += text.slice(offset, tagStart);
627
+ const json = text.slice(parsedTag.jsonStart, jsonEnd + 1);
628
+ try {
629
+ const parsed = JSON.parse(json);
630
+ if (parsedTag.tag === "answer") {
631
+ cleaned += str(parsed.answer) ?? str(parsed.text) ?? "";
632
+ } else if (parsedTag.tag === "citation") {
633
+ citations.push({
634
+ title: str(parsed.title),
635
+ url: str(parsed.url)
636
+ });
637
+ } else if (parsedTag.tag === "enum_item") {
638
+ const item = obj(parsed);
639
+ entities.push(item);
640
+ cleaned += str(item.original_tokens) ?? str(item.name) ?? str(item.href) ?? "";
641
+ } else if (parsedTag.tag === "usage") {
642
+ usage = parsed;
643
+ }
644
+ } catch {
645
+ }
646
+ const closing = `</${parsedTag.tag}>`;
647
+ const abbreviatedClosing = `</${parsedTag.tag}`;
648
+ if (text.startsWith(closing, jsonEnd + 1)) {
649
+ offset = jsonEnd + 1 + closing.length;
650
+ } else if (text.startsWith(abbreviatedClosing, jsonEnd + 1)) {
651
+ offset = jsonEnd + 1 + abbreviatedClosing.length;
652
+ } else {
653
+ offset = jsonEnd + 1;
654
+ }
655
+ }
656
+ return { text: cleaned, citations, entities, usage };
657
+ }
658
+ var BRAVE_STRUCTURED_TAGS = /* @__PURE__ */ new Set([
659
+ "analyzing",
660
+ "answer",
661
+ "blindspots",
662
+ "citation",
663
+ "enum_item",
664
+ "progress",
665
+ "queries",
666
+ "thinking",
667
+ "usage"
668
+ ]);
669
+ function readBraveTagStart(text, tagStart) {
670
+ const match = /^<([A-Za-z_][A-Za-z0-9_-]*)(>)?[{[]/.exec(
671
+ text.slice(tagStart)
672
+ );
673
+ if (!match) return void 0;
674
+ const tag = match[1];
675
+ if (!BRAVE_STRUCTURED_TAGS.has(tag)) return void 0;
676
+ return {
677
+ tag,
678
+ jsonStart: tagStart + match[0].length - 1
679
+ };
680
+ }
681
+ function findJsonValueEnd(text, start) {
682
+ const first = text[start];
683
+ const closing = first === "{" ? "}" : first === "[" ? "]" : void 0;
684
+ if (!closing) return -1;
685
+ const stack = [];
686
+ let inString = false;
687
+ let escaped = false;
688
+ for (let index = start; index < text.length; index++) {
689
+ const char = text[index];
690
+ if (escaped) {
691
+ escaped = false;
692
+ continue;
693
+ }
694
+ if (char === "\\") {
695
+ escaped = true;
696
+ continue;
697
+ }
698
+ if (char === '"') {
699
+ inString = !inString;
700
+ continue;
701
+ }
702
+ if (inString) continue;
703
+ if (char === "{") {
704
+ stack.push("}");
705
+ } else if (char === "[") {
706
+ stack.push("]");
707
+ } else if (char === "}" || char === "]") {
708
+ if (stack.pop() !== char) return -1;
709
+ if (stack.length === 0) return index;
710
+ }
711
+ }
712
+ return -1;
713
+ }
714
+ function dedupeCitations(citations) {
715
+ const seen = /* @__PURE__ */ new Set();
716
+ return citations.filter((citation) => {
717
+ const key = citation.url ?? citation.title;
718
+ if (!key) return false;
719
+ if (seen.has(key)) return false;
720
+ seen.add(key);
721
+ return true;
722
+ });
723
+ }
724
+ var adapter = {
725
+ async search(input, config, context) {
726
+ return await braveImplementation.search(
727
+ input.query,
728
+ input.maxResults,
729
+ config,
730
+ context,
731
+ input.options
732
+ );
733
+ },
734
+ async answer(input, config, context) {
735
+ return await braveImplementation.answer(
736
+ input.query,
737
+ config,
738
+ context,
739
+ input.options
740
+ );
741
+ },
742
+ async research(input, config, context) {
743
+ return await braveImplementation.research(
744
+ input.input,
745
+ config,
746
+ context,
747
+ input.options
748
+ );
749
+ }
750
+ };
751
+ export {
752
+ adapter
753
+ };