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,230 @@
1
+ import {
2
+ cancelProcessGroup,
3
+ killProcessGroup
4
+ } from "./chunk-ZVTVERW5.js";
5
+ import {
6
+ WEBFOX_ERROR_CODES
7
+ } from "./chunk-G2YAQEET.js";
8
+ import {
9
+ WebfoxError
10
+ } from "./chunk-U4BLULLV.js";
11
+
12
+ // src/providers/cli-json.ts
13
+ import { spawn } from "node:child_process";
14
+ import { resolve } from "node:path";
15
+ async function runCliJsonCommand({
16
+ command,
17
+ payload,
18
+ context,
19
+ label
20
+ }) {
21
+ context.signal?.throwIfAborted();
22
+ if (!command.argv.length)
23
+ throw new WebfoxError("INVALID_CONFIG", `${label} requires argv.`);
24
+ return new Promise((resolvePromise, reject) => {
25
+ const child = spawn(command.argv[0], command.argv.slice(1), {
26
+ detached: process.platform !== "win32",
27
+ cwd: resolve(context.cwd, command.cwd ?? "."),
28
+ env: { ...context.env, ...command.env },
29
+ stdio: ["pipe", "pipe", "pipe"]
30
+ });
31
+ let stdout = "";
32
+ let stderr = "";
33
+ let progress = "";
34
+ const abort = () => {
35
+ cancelProcessGroup(child);
36
+ reject(context.signal?.reason);
37
+ };
38
+ context.signal?.addEventListener("abort", abort, { once: true });
39
+ child.stdout.setEncoding("utf8");
40
+ child.stderr.setEncoding("utf8");
41
+ child.stdout.on("data", (chunk) => {
42
+ stdout += chunk;
43
+ if (Buffer.byteLength(stdout) > 16 * 1024 * 1024) {
44
+ killProcessGroup(child, "SIGKILL");
45
+ reject(
46
+ new WebfoxError(
47
+ "PROVIDER_FAILURE",
48
+ `${label} output exceeded 16 MiB.`
49
+ )
50
+ );
51
+ }
52
+ });
53
+ child.stderr.on("data", (chunk) => {
54
+ stderr = (stderr + chunk).slice(-64 * 1024);
55
+ progress += chunk;
56
+ const lines = progress.split(/\r?\n/);
57
+ progress = lines.pop();
58
+ for (const line of lines)
59
+ if (line.trim()) context.onProgress?.(line.trim());
60
+ if (progress.length > 64 * 1024) {
61
+ progress = "";
62
+ killProcessGroup(child, "SIGKILL");
63
+ reject(
64
+ new WebfoxError(
65
+ "PROVIDER_FAILURE",
66
+ `${label} progress line exceeded 64 KiB.`
67
+ )
68
+ );
69
+ }
70
+ });
71
+ child.on(
72
+ "error",
73
+ () => reject(
74
+ new WebfoxError(
75
+ "PROVIDER_FAILURE",
76
+ `${label} could not start. Check the command executable and working directory.`
77
+ )
78
+ )
79
+ );
80
+ child.on("close", (code) => {
81
+ context.signal?.removeEventListener("abort", abort);
82
+ if (context.signal?.aborted) {
83
+ reject(context.signal.reason);
84
+ return;
85
+ }
86
+ if (progress.trim()) context.onProgress?.(progress.trim());
87
+ if (code !== 0) {
88
+ reject(
89
+ new WebfoxError(
90
+ "PROVIDER_FAILURE",
91
+ `${label} failed (exit ${code}): ${stderr.trim()}`,
92
+ { retryable: false }
93
+ )
94
+ );
95
+ return;
96
+ }
97
+ try {
98
+ resolvePromise(JSON.parse(stdout));
99
+ } catch {
100
+ reject(
101
+ new WebfoxError(
102
+ "PROVIDER_FAILURE",
103
+ `${label} must write one valid JSON object to stdout.`
104
+ )
105
+ );
106
+ }
107
+ });
108
+ child.stdin.on("error", () => {
109
+ });
110
+ child.stdin.end(`${JSON.stringify(payload)}
111
+ `);
112
+ });
113
+ }
114
+
115
+ // src/providers/custom/adapter.ts
116
+ async function run(request, config, context) {
117
+ const command = config.commands?.[request.capability];
118
+ if (!command)
119
+ throw new WebfoxError(
120
+ "PROVIDER_UNAVAILABLE",
121
+ `Configure a custom ${request.capability} command.`
122
+ );
123
+ const { capability, options, ...input } = request;
124
+ const output = await runCliJsonCommand({
125
+ command,
126
+ payload: {
127
+ schemaVersion: 1,
128
+ capability,
129
+ input,
130
+ options: options ?? {},
131
+ cwd: context.cwd
132
+ },
133
+ context,
134
+ label: `Custom ${capability}`
135
+ });
136
+ return object(output, "Custom output must be a JSON object.");
137
+ }
138
+ var adapter = {
139
+ async search(request, config, context) {
140
+ const output = await run(request, config, context);
141
+ if (!Array.isArray(output.results))
142
+ invalid("Custom search output requires a results array.");
143
+ return {
144
+ provider: "custom",
145
+ results: output.results.map((entry) => {
146
+ const result = object(entry, "Search results must be objects.");
147
+ return {
148
+ title: string(result.title, "title"),
149
+ url: string(result.url, "url"),
150
+ snippet: string(result.snippet, "snippet"),
151
+ ...typeof result.score === "number" ? { score: result.score } : {},
152
+ ...result.metadata ? {
153
+ metadata: object(result.metadata, "metadata must be an object")
154
+ } : {}
155
+ };
156
+ })
157
+ };
158
+ },
159
+ async contents(request, config, context) {
160
+ const output = await run(request, config, context);
161
+ if (!Array.isArray(output.answers))
162
+ invalid("Custom contents output requires an answers array.");
163
+ return {
164
+ provider: "custom",
165
+ answers: output.answers.map((entry) => {
166
+ const answer = object(entry, "Contents answers must be objects.");
167
+ if (!Number.isInteger(answer.inputIndex))
168
+ invalid(
169
+ "Every custom contents answer requires inputIndex (the zero-based requested URL index)."
170
+ );
171
+ const error = answer.error === void 0 ? void 0 : parseError(answer.error);
172
+ if (answer.content === void 0 && answer.summary === void 0 && error === void 0)
173
+ invalid("Contents answers require content, summary, or error.");
174
+ return {
175
+ inputIndex: answer.inputIndex,
176
+ url: string(answer.url, "url"),
177
+ ...answer.content === void 0 ? {} : { content: string(answer.content, "content") },
178
+ ...answer.summary === void 0 ? {} : { summary: answer.summary },
179
+ ...answer.metadata ? {
180
+ metadata: object(answer.metadata, "metadata must be an object")
181
+ } : {},
182
+ ...error ? { error } : {}
183
+ };
184
+ })
185
+ };
186
+ },
187
+ async answer(request, config, context) {
188
+ return textOutput(await run(request, config, context));
189
+ },
190
+ async research(request, config, context) {
191
+ return textOutput(await run(request, config, context));
192
+ }
193
+ };
194
+ function textOutput(output) {
195
+ return {
196
+ provider: "custom",
197
+ text: string(output.text, "text"),
198
+ ...typeof output.itemCount === "number" && Number.isSafeInteger(output.itemCount) && output.itemCount >= 0 ? { itemCount: output.itemCount } : {},
199
+ ...output.metadata ? { metadata: object(output.metadata, "metadata must be an object") } : {}
200
+ };
201
+ }
202
+ function parseError(value) {
203
+ const error = object(
204
+ value,
205
+ "Custom contents errors must be structured objects with code and message."
206
+ );
207
+ if (!WEBFOX_ERROR_CODES.includes(error.code))
208
+ invalid("Custom contents error has an unknown code.");
209
+ return {
210
+ code: error.code,
211
+ message: string(error.message, "error.message"),
212
+ ...typeof error.retryable === "boolean" ? { retryable: error.retryable } : {}
213
+ };
214
+ }
215
+ function object(value, message) {
216
+ if (!value || typeof value !== "object" || Array.isArray(value))
217
+ invalid(message);
218
+ return value;
219
+ }
220
+ function string(value, name) {
221
+ if (typeof value !== "string")
222
+ invalid(`Custom output field ${name} must be a string.`);
223
+ return value;
224
+ }
225
+ function invalid(message) {
226
+ throw new WebfoxError("PROVIDER_FAILURE", message, { retryable: false });
227
+ }
228
+ export {
229
+ adapter
230
+ };
@@ -0,0 +1,232 @@
1
+ import {
2
+ trimSnippet
3
+ } from "./chunk-X6ZBIHZC.js";
4
+
5
+ // src/providers/perplexity/adapter.ts
6
+ import PerplexityClient from "@perplexity-ai/perplexity_ai";
7
+ var DEFAULT_ANSWER_MODEL = "sonar";
8
+ var DEFAULT_RESEARCH_MODEL = "sonar-deep-research";
9
+ var perplexityImplementation = {
10
+ async search(query, maxResults, config, context, options) {
11
+ const client = createClient(config);
12
+ const request = {
13
+ ...options ?? {},
14
+ query,
15
+ max_results: maxResults
16
+ };
17
+ const response = await client.search.create(
18
+ request,
19
+ buildRequestOptions(context)
20
+ );
21
+ return {
22
+ provider: "perplexity",
23
+ results: response.results.slice(0, maxResults).map((result) => ({
24
+ title: result.title,
25
+ url: result.url,
26
+ snippet: trimSnippet(result.snippet),
27
+ metadata: result.date || result.last_updated ? {
28
+ ...result.date ? { date: result.date } : {},
29
+ ...result.last_updated ? { last_updated: result.last_updated } : {}
30
+ } : void 0
31
+ }))
32
+ };
33
+ },
34
+ async answer(query, config, context, options) {
35
+ return runSilentForegroundChatTool(
36
+ query,
37
+ config,
38
+ context,
39
+ DEFAULT_ANSWER_MODEL,
40
+ "Answer",
41
+ options
42
+ );
43
+ },
44
+ async research(input, config, context, options) {
45
+ return runStreamingForegroundChatTool(
46
+ input,
47
+ config,
48
+ context,
49
+ DEFAULT_RESEARCH_MODEL,
50
+ "Research",
51
+ options
52
+ );
53
+ }
54
+ };
55
+ async function runSilentForegroundChatTool(input, config, context, fallbackModel, label, options) {
56
+ const client = createClient(config);
57
+ const request = {
58
+ ...options ?? {},
59
+ messages: [{ role: "user", content: input }],
60
+ model: resolveModel(options?.model, fallbackModel),
61
+ stream: false
62
+ };
63
+ const response = await client.chat.completions.create(
64
+ request,
65
+ buildRequestOptions(context)
66
+ );
67
+ const content = extractMessageText(response.choices[0]?.message?.content);
68
+ const sources = dedupeSources(extractSources(response));
69
+ const lines = [];
70
+ lines.push(content || `No ${label.toLowerCase()} returned.`);
71
+ if (sources.length > 0) {
72
+ lines.push("");
73
+ lines.push("Sources:");
74
+ for (const [index, source] of sources.entries()) {
75
+ lines.push(`${index + 1}. ${source.title}`);
76
+ lines.push(` ${source.url}`);
77
+ }
78
+ }
79
+ return {
80
+ provider: "perplexity",
81
+ text: lines.join("\n").trimEnd(),
82
+ itemCount: sources.length
83
+ };
84
+ }
85
+ async function runStreamingForegroundChatTool(input, config, context, fallbackModel, label, options) {
86
+ const client = createClient(config);
87
+ const request = {
88
+ ...options ?? {},
89
+ messages: [{ role: "user", content: input }],
90
+ model: resolveModel(options?.model, fallbackModel),
91
+ stream: true
92
+ };
93
+ const stream = await client.chat.completions.create(
94
+ request,
95
+ buildRequestOptions(context)
96
+ );
97
+ let partialText = "";
98
+ let lastChunk;
99
+ const sources = [];
100
+ for await (const chunk of stream) {
101
+ lastChunk = chunk;
102
+ const deltaText = extractDeltaText(chunk.choices[0]?.delta?.content);
103
+ if (deltaText) {
104
+ partialText = `${partialText}${deltaText}`;
105
+ }
106
+ sources.push(...extractSources(chunk));
107
+ }
108
+ const finalText = partialText.trim() || extractMessageText(lastChunk?.choices?.[0]?.message?.content) || `No ${label.toLowerCase()} returned.`;
109
+ const dedupedSources = dedupeSources(sources);
110
+ const lines = [finalText];
111
+ if (dedupedSources.length > 0) {
112
+ lines.push("");
113
+ lines.push("Sources:");
114
+ for (const [index, source] of dedupedSources.entries()) {
115
+ lines.push(`${index + 1}. ${source.title}`);
116
+ lines.push(` ${source.url}`);
117
+ }
118
+ }
119
+ return {
120
+ provider: "perplexity",
121
+ text: lines.join("\n").trimEnd(),
122
+ itemCount: dedupedSources.length
123
+ };
124
+ }
125
+ function createClient(config) {
126
+ const apiKey = config.credentials?.api;
127
+ if (!apiKey) {
128
+ throw new Error("is missing an API key");
129
+ }
130
+ return new PerplexityClient({
131
+ maxRetries: 0,
132
+ apiKey,
133
+ baseURL: config.baseUrl
134
+ });
135
+ }
136
+ function resolveModel(optionModel, fallbackModel) {
137
+ if (typeof optionModel === "string" && optionModel.trim().length > 0) {
138
+ return optionModel;
139
+ }
140
+ return fallbackModel;
141
+ }
142
+ function extractMessageText(content) {
143
+ if (typeof content === "string") {
144
+ return content.trim();
145
+ }
146
+ if (!Array.isArray(content)) {
147
+ return "";
148
+ }
149
+ return content.flatMap((chunk) => {
150
+ if (typeof chunk === "object" && chunk !== null && "type" in chunk && chunk.type === "text" && "text" in chunk && typeof chunk.text === "string") {
151
+ return [chunk.text.trim()];
152
+ }
153
+ return [];
154
+ }).filter((text) => text.length > 0).join("\n\n").trim();
155
+ }
156
+ function extractDeltaText(content) {
157
+ if (typeof content === "string") {
158
+ return content;
159
+ }
160
+ if (!Array.isArray(content)) {
161
+ return "";
162
+ }
163
+ return content.flatMap((chunk) => {
164
+ if (typeof chunk === "object" && chunk !== null && "type" in chunk && chunk.type === "text" && "text" in chunk && typeof chunk.text === "string") {
165
+ return [chunk.text];
166
+ }
167
+ return [];
168
+ }).join("");
169
+ }
170
+ function dedupeSources(sources) {
171
+ const seen = /* @__PURE__ */ new Set();
172
+ const unique = [];
173
+ for (const source of sources) {
174
+ const title = source.title.trim() || source.url.trim() || "Untitled";
175
+ const url = source.url.trim();
176
+ if (!url) continue;
177
+ const key = `${title.toLowerCase()}::${url.toLowerCase()}`;
178
+ if (seen.has(key)) continue;
179
+ seen.add(key);
180
+ unique.push({ title, url });
181
+ }
182
+ return unique;
183
+ }
184
+ function extractSources(response) {
185
+ const searchResults = response.search_results?.flatMap((result) => {
186
+ const url = result.url?.trim() ?? "";
187
+ if (!url) {
188
+ return [];
189
+ }
190
+ return [{ title: result.title?.trim() ?? url, url }];
191
+ }) ?? [];
192
+ if (searchResults.length > 0) {
193
+ return searchResults;
194
+ }
195
+ return response.citations?.flatMap((citation) => {
196
+ const url = citation?.trim() ?? "";
197
+ return url ? [{ title: url, url }] : [];
198
+ }) ?? [];
199
+ }
200
+ function buildRequestOptions(context) {
201
+ return context.signal ? { signal: context.signal } : void 0;
202
+ }
203
+ var adapter = {
204
+ async search(input, config, context) {
205
+ return await perplexityImplementation.search(
206
+ input.query,
207
+ input.maxResults,
208
+ config,
209
+ context,
210
+ input.options
211
+ );
212
+ },
213
+ async answer(input, config, context) {
214
+ return await perplexityImplementation.answer(
215
+ input.query,
216
+ config,
217
+ context,
218
+ input.options
219
+ );
220
+ },
221
+ async research(input, config, context) {
222
+ return await perplexityImplementation.research(
223
+ input.input,
224
+ config,
225
+ context,
226
+ input.options
227
+ );
228
+ }
229
+ };
230
+ export {
231
+ adapter
232
+ };
@@ -0,0 +1,162 @@
1
+ import {
2
+ formatJson,
3
+ trimSnippet
4
+ } from "./chunk-X6ZBIHZC.js";
5
+ import {
6
+ orderedContents
7
+ } from "./chunk-YZXM4U2C.js";
8
+ import {
9
+ WebfoxError
10
+ } from "./chunk-U4BLULLV.js";
11
+
12
+ // src/providers/exa/adapter.ts
13
+ import { Exa as ExaClient } from "exa-js";
14
+ var exaImplementation = {
15
+ async search(query, maxResults, config, _context, searchOptions) {
16
+ const client = createClient(config);
17
+ const options = {
18
+ ...searchOptions ?? {},
19
+ numResults: maxResults
20
+ };
21
+ const response = await client.search(query, options);
22
+ return {
23
+ provider: "exa",
24
+ results: (response.results ?? []).slice(0, maxResults).map((result) => ({
25
+ title: String(result.title ?? result.url ?? "Untitled"),
26
+ url: String(result.url ?? ""),
27
+ snippet: trimSnippet(
28
+ typeof result.text === "string" ? result.text : Array.isArray(result.highlights) ? result.highlights.join(" ") : typeof result.summary === "string" ? result.summary : ""
29
+ ),
30
+ score: typeof result.score === "number" ? result.score : void 0
31
+ }))
32
+ };
33
+ },
34
+ async contents(urls, config, _context, options) {
35
+ const client = createClient(config);
36
+ const response = await client.getContents(urls, options);
37
+ const results = response.results ?? [];
38
+ return {
39
+ provider: "exa",
40
+ answers: urls.map((url, index) => {
41
+ const result = results[index];
42
+ if (!result) {
43
+ return {
44
+ url,
45
+ error: "No content returned for this URL."
46
+ };
47
+ }
48
+ return {
49
+ url: typeof result.url === "string" ? result.url : url,
50
+ ...typeof result.text === "string" ? { content: result.text } : {},
51
+ ...result.summary !== void 0 ? { summary: result.summary } : {},
52
+ metadata: result
53
+ };
54
+ })
55
+ };
56
+ },
57
+ async answer(query, config, _context, options) {
58
+ const client = createClient(config);
59
+ const response = await client.answer(query, options);
60
+ const lines = [];
61
+ lines.push(
62
+ typeof response.answer === "string" ? response.answer : formatJson(response.answer)
63
+ );
64
+ const citations = response.citations ?? [];
65
+ if (citations.length > 0) {
66
+ lines.push("");
67
+ lines.push("Sources:");
68
+ for (const [index, citation] of citations.entries()) {
69
+ lines.push(
70
+ `${index + 1}. ${String(citation.title ?? citation.url ?? "Untitled")}`
71
+ );
72
+ lines.push(` ${String(citation.url ?? "")}`);
73
+ }
74
+ }
75
+ return {
76
+ provider: "exa",
77
+ text: lines.join("\n").trimEnd(),
78
+ itemCount: citations.length
79
+ };
80
+ },
81
+ async research(input, config, context, options) {
82
+ context.signal?.throwIfAborted();
83
+ context.onProgress?.("Exa is researching and synthesizing the report.");
84
+ const client = createClient(config);
85
+ const result = await client.search(input, {
86
+ ...options ?? {},
87
+ type: "deep-reasoning",
88
+ outputSchema: {
89
+ type: "text",
90
+ description: "A detailed Markdown research report answering the query. Cite sources with links, distinguish evidence from uncertainty, and include a conclusion."
91
+ }
92
+ });
93
+ const content = result.output?.content;
94
+ if (typeof content !== "string" || !content.trim())
95
+ throw new WebfoxError(
96
+ "PROVIDER_FAILURE",
97
+ "Exa returned no research report."
98
+ );
99
+ const sources = result.results ?? [];
100
+ const lines = [content.trim()];
101
+ if (sources.length) {
102
+ lines.push("", "Sources:");
103
+ for (const [index, source] of sources.entries())
104
+ lines.push(
105
+ `${index + 1}. ${source.title || source.url}`,
106
+ ` ${source.url}`
107
+ );
108
+ }
109
+ return {
110
+ provider: "exa",
111
+ text: lines.join("\n"),
112
+ itemCount: sources.length
113
+ };
114
+ }
115
+ };
116
+ function createClient(config) {
117
+ const apiKey = config.credentials?.api;
118
+ if (!apiKey) {
119
+ throw new Error("is missing an API key");
120
+ }
121
+ return new ExaClient(apiKey, config.baseUrl);
122
+ }
123
+ var adapter = {
124
+ async search(input, config, context) {
125
+ return await exaImplementation.search(
126
+ input.query,
127
+ input.maxResults,
128
+ config,
129
+ context,
130
+ input.options
131
+ );
132
+ },
133
+ async contents(input, config, context) {
134
+ return orderedContents(
135
+ await exaImplementation.contents(
136
+ input.urls,
137
+ config,
138
+ context,
139
+ input.options
140
+ )
141
+ );
142
+ },
143
+ async answer(input, config, context) {
144
+ return await exaImplementation.answer(
145
+ input.query,
146
+ config,
147
+ context,
148
+ input.options
149
+ );
150
+ },
151
+ async research(input, config, context) {
152
+ return await exaImplementation.research(
153
+ input.input,
154
+ config,
155
+ context,
156
+ input.options
157
+ );
158
+ }
159
+ };
160
+ export {
161
+ adapter
162
+ };