@alasano/pi-exa 0.0.1
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.
- package/README.md +109 -0
- package/assets/screenshot.png +0 -0
- package/assets/web_search_exa.png +0 -0
- package/extensions/agent-tracker.ts +258 -0
- package/extensions/agent-widget.ts +136 -0
- package/extensions/agent.ts +289 -0
- package/extensions/auth.ts +66 -0
- package/extensions/client.ts +146 -0
- package/extensions/format.ts +436 -0
- package/extensions/index.ts +69 -0
- package/extensions/output.ts +52 -0
- package/extensions/render.ts +394 -0
- package/extensions/schemas.ts +239 -0
- package/extensions/settings.ts +263 -0
- package/extensions/tools/helpers.ts +26 -0
- package/extensions/tools/index.ts +28 -0
- package/extensions/tools/web-agent-runs.ts +377 -0
- package/extensions/tools/web-agent.ts +328 -0
- package/extensions/tools/web-answer.ts +92 -0
- package/extensions/tools/web-fetch.ts +94 -0
- package/extensions/tools/web-search-advanced.ts +164 -0
- package/extensions/tools/web-search.ts +112 -0
- package/extensions/types.ts +238 -0
- package/extensions/util.ts +26 -0
- package/package.json +46 -0
- package/skills/exa-search/SKILL.md +81 -0
- package/skills/exa-search/references/web-agent-exa.md +277 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentToolResult,
|
|
3
|
+
AgentToolUpdateCallback,
|
|
4
|
+
Theme,
|
|
5
|
+
ToolRenderResultOptions,
|
|
6
|
+
} from '@earendil-works/pi-coding-agent';
|
|
7
|
+
import { Text } from '@earendil-works/pi-tui';
|
|
8
|
+
import type {
|
|
9
|
+
ExaAgentEventListResponse,
|
|
10
|
+
ExaAgentRun,
|
|
11
|
+
ExaAgentRunListResponse,
|
|
12
|
+
ExaDeletedAgentRun,
|
|
13
|
+
ExaAnswerResponse,
|
|
14
|
+
ExaContentsResponse,
|
|
15
|
+
ExaSearchResponse,
|
|
16
|
+
ExaSearchResult,
|
|
17
|
+
PreviewDetails,
|
|
18
|
+
} from './types';
|
|
19
|
+
import { asString } from './util';
|
|
20
|
+
import type { TruncatedToolOutput } from './output';
|
|
21
|
+
|
|
22
|
+
const DEFAULT_UI_PREVIEW_RESULT_COUNT = 3;
|
|
23
|
+
const DEFAULT_UI_PREVIEW_EXCERPT_CHARS = 220;
|
|
24
|
+
|
|
25
|
+
type ToolArgs = Record<string, unknown>;
|
|
26
|
+
|
|
27
|
+
function normalizeWhitespace(text: string): string {
|
|
28
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function stringifyValue(value: unknown): string | undefined {
|
|
32
|
+
if (typeof value === 'string') return value.trim() || undefined;
|
|
33
|
+
if (value === undefined || value === null) return undefined;
|
|
34
|
+
try {
|
|
35
|
+
return JSON.stringify(value, null, 2);
|
|
36
|
+
} catch {
|
|
37
|
+
return String(value);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function toExcerpt(text: string | undefined, maxChars = DEFAULT_UI_PREVIEW_EXCERPT_CHARS) {
|
|
42
|
+
if (!text) return undefined;
|
|
43
|
+
const normalized = normalizeWhitespace(text);
|
|
44
|
+
if (!normalized) return undefined;
|
|
45
|
+
return normalized.length > maxChars
|
|
46
|
+
? `${normalized.slice(0, Math.max(0, maxChars - 1)).trimEnd()}...`
|
|
47
|
+
: normalized;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function resultExcerpt(result: ExaSearchResult): string | undefined {
|
|
51
|
+
return (
|
|
52
|
+
toExcerpt(result.highlights?.join(' ')) ||
|
|
53
|
+
toExcerpt(stringifyValue(result.summary)) ||
|
|
54
|
+
toExcerpt(result.text)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function previewLinesFromResults(results: ExaSearchResult[]): string[] {
|
|
59
|
+
return results.slice(0, DEFAULT_UI_PREVIEW_RESULT_COUNT).flatMap((result, index) => {
|
|
60
|
+
const title =
|
|
61
|
+
asString(result.title) || asString(result.url) || asString(result.id) || 'Untitled';
|
|
62
|
+
const lines = [`${index + 1}. ${title}`];
|
|
63
|
+
if (result.url) lines.push(` ${result.url}`);
|
|
64
|
+
const excerpt = resultExcerpt(result);
|
|
65
|
+
if (excerpt) lines.push(` ${excerpt}`);
|
|
66
|
+
return lines;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildStatusSummary(statuses: ExaSearchResponse['statuses']): string | undefined {
|
|
71
|
+
if (!statuses?.length) return undefined;
|
|
72
|
+
const counts = new Map<string, number>();
|
|
73
|
+
for (const status of statuses) {
|
|
74
|
+
const key = asString(status.status) || 'unknown';
|
|
75
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
76
|
+
}
|
|
77
|
+
return [...counts.entries()].map(([status, count]) => `${status}: ${count}`).join(' | ');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function previewFromOutput(output: TruncatedToolOutput) {
|
|
81
|
+
return {
|
|
82
|
+
truncated: output.truncation.truncated,
|
|
83
|
+
fullOutputPath: output.fullOutputPath,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function buildSearchPreview(
|
|
88
|
+
response: ExaSearchResponse,
|
|
89
|
+
output: TruncatedToolOutput,
|
|
90
|
+
): PreviewDetails {
|
|
91
|
+
const results = response.results || [];
|
|
92
|
+
const lines = previewLinesFromResults(results);
|
|
93
|
+
const synthesizedOutput = toExcerpt(stringifyValue(response.output?.content));
|
|
94
|
+
if (synthesizedOutput) lines.unshift(`Output: ${synthesizedOutput}`);
|
|
95
|
+
|
|
96
|
+
const statusSummary = buildStatusSummary(response.statuses);
|
|
97
|
+
if (statusSummary) lines.unshift(`Statuses: ${statusSummary}`);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
kind: 'search',
|
|
101
|
+
summary: `${results.length} result(s)`,
|
|
102
|
+
lines,
|
|
103
|
+
expandedLines: lines,
|
|
104
|
+
...previewFromOutput(output),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function buildContentsPreview(
|
|
109
|
+
response: ExaContentsResponse,
|
|
110
|
+
urlCount: number,
|
|
111
|
+
output: TruncatedToolOutput,
|
|
112
|
+
): PreviewDetails {
|
|
113
|
+
const results = response.results || [];
|
|
114
|
+
const lines = previewLinesFromResults(results);
|
|
115
|
+
const statusSummary = buildStatusSummary(response.statuses);
|
|
116
|
+
if (statusSummary) lines.unshift(`Statuses: ${statusSummary}`);
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
kind: 'contents',
|
|
120
|
+
summary: `${results.length} item(s) from ${urlCount} URL(s)`,
|
|
121
|
+
lines,
|
|
122
|
+
expandedLines: lines,
|
|
123
|
+
...previewFromOutput(output),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function buildAnswerPreview(
|
|
128
|
+
response: ExaAnswerResponse,
|
|
129
|
+
output: TruncatedToolOutput,
|
|
130
|
+
): PreviewDetails {
|
|
131
|
+
const answer = stringifyValue(response.answer);
|
|
132
|
+
const citations = response.citations || [];
|
|
133
|
+
const lines = [
|
|
134
|
+
...(answer ? [`1. Answer`, ` ${toExcerpt(answer)}`] : ['1. No answer returned']),
|
|
135
|
+
...citations
|
|
136
|
+
.slice(0, Math.max(0, DEFAULT_UI_PREVIEW_RESULT_COUNT - 1))
|
|
137
|
+
.flatMap((citation, index) => {
|
|
138
|
+
const title = asString(citation.title) || asString(citation.url) || 'Source';
|
|
139
|
+
const entry = [`${index + 2}. ${title}`];
|
|
140
|
+
if (citation.url) entry.push(` ${citation.url}`);
|
|
141
|
+
const excerpt = toExcerpt(citation.text);
|
|
142
|
+
if (excerpt) entry.push(` ${excerpt}`);
|
|
143
|
+
return entry;
|
|
144
|
+
}),
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
kind: 'answer',
|
|
149
|
+
summary: `${citations.length} source(s)`,
|
|
150
|
+
lines,
|
|
151
|
+
expandedLines: lines,
|
|
152
|
+
...previewFromOutput(output),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function agentRunExcerpt(run: ExaAgentRun): string | undefined {
|
|
157
|
+
return (
|
|
158
|
+
toExcerpt(run.output?.text || undefined) ||
|
|
159
|
+
toExcerpt(stringifyValue(run.output?.structured)) ||
|
|
160
|
+
toExcerpt(run.request?.query)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function agentStructuredSummary(run: ExaAgentRun): string | undefined {
|
|
165
|
+
const structured = run.output?.structured;
|
|
166
|
+
if (structured === undefined || structured === null) return undefined;
|
|
167
|
+
if (Array.isArray(structured)) return `Array with ${structured.length} item(s)`;
|
|
168
|
+
if (typeof structured === 'object') {
|
|
169
|
+
const entries = Object.entries(structured as Record<string, unknown>);
|
|
170
|
+
const arrayEntry = entries.find(([, value]) => Array.isArray(value));
|
|
171
|
+
if (arrayEntry && Array.isArray(arrayEntry[1])) {
|
|
172
|
+
return `${arrayEntry[0]}: ${arrayEntry[1].length} item(s)`;
|
|
173
|
+
}
|
|
174
|
+
return `Object with ${entries.length} field(s)`;
|
|
175
|
+
}
|
|
176
|
+
return toExcerpt(String(structured));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function agentCostSummary(run: ExaAgentRun): string | undefined {
|
|
180
|
+
const total = run.costDollars?.total;
|
|
181
|
+
return total === undefined ? undefined : `$${total}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function buildAgentRunPreview(
|
|
185
|
+
run: ExaAgentRun,
|
|
186
|
+
output: TruncatedToolOutput,
|
|
187
|
+
options?: { timedOut?: boolean; background?: boolean },
|
|
188
|
+
): PreviewDetails {
|
|
189
|
+
const lines: string[] = [];
|
|
190
|
+
const excerpt = agentRunExcerpt(run);
|
|
191
|
+
const structuredSummary = agentStructuredSummary(run);
|
|
192
|
+
if (run.output?.text && excerpt) lines.push(`Answer: ${excerpt}`);
|
|
193
|
+
if (structuredSummary) lines.push(`Structured: ${structuredSummary}`);
|
|
194
|
+
if (!run.output?.text && !structuredSummary && excerpt) lines.push(`Preview: ${excerpt}`);
|
|
195
|
+
if (options?.timedOut) lines.push('Timed out waiting; run can be inspected later.');
|
|
196
|
+
if (options?.background) {
|
|
197
|
+
lines.push('Background tracking enabled; the extension will notify on completion.');
|
|
198
|
+
}
|
|
199
|
+
if (lines.length === 0) lines.push(`Status: ${run.status}`);
|
|
200
|
+
|
|
201
|
+
const cost = agentCostSummary(run);
|
|
202
|
+
const summary = [run.status, cost].filter(Boolean).join(' | ');
|
|
203
|
+
const expandedLines = [
|
|
204
|
+
`Run ID: ${run.id}`,
|
|
205
|
+
...(run.request?.query ? [`Query: ${toExcerpt(run.request.query, 500)}`] : []),
|
|
206
|
+
...lines,
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
kind: 'agent',
|
|
211
|
+
summary,
|
|
212
|
+
lines,
|
|
213
|
+
expandedLines,
|
|
214
|
+
...previewFromOutput(output),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function buildAgentRunListPreview(
|
|
219
|
+
response: ExaAgentRunListResponse,
|
|
220
|
+
output: TruncatedToolOutput,
|
|
221
|
+
): PreviewDetails {
|
|
222
|
+
const runs = response.data || [];
|
|
223
|
+
const lines = runs.slice(0, DEFAULT_UI_PREVIEW_RESULT_COUNT).flatMap((run, index) => {
|
|
224
|
+
const entry = [`${index + 1}. ${run.id} | ${run.status}`];
|
|
225
|
+
if (run.request?.query) entry.push(` ${toExcerpt(run.request.query)}`);
|
|
226
|
+
return entry;
|
|
227
|
+
});
|
|
228
|
+
if (response.hasMore)
|
|
229
|
+
lines.push(`More available: ${response.nextCursor || 'next cursor omitted'}`);
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
kind: 'agent',
|
|
233
|
+
summary: `${runs.length} run(s)`,
|
|
234
|
+
lines,
|
|
235
|
+
expandedLines: lines,
|
|
236
|
+
...previewFromOutput(output),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function buildAgentEventListPreview(
|
|
241
|
+
response: ExaAgentEventListResponse,
|
|
242
|
+
output: TruncatedToolOutput,
|
|
243
|
+
): PreviewDetails {
|
|
244
|
+
const events = response.data || [];
|
|
245
|
+
const lines = events.slice(0, DEFAULT_UI_PREVIEW_RESULT_COUNT).map((event, index) => {
|
|
246
|
+
const runId = asString(event.data?.id) || asString(event.data?.runId);
|
|
247
|
+
return `${index + 1}. ${event.event}${runId ? ` | ${runId}` : ''}`;
|
|
248
|
+
});
|
|
249
|
+
if (response.hasMore)
|
|
250
|
+
lines.push(`More available: ${response.nextCursor || 'next cursor omitted'}`);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
kind: 'agent',
|
|
254
|
+
summary: `${events.length} event(s)`,
|
|
255
|
+
lines,
|
|
256
|
+
expandedLines: lines,
|
|
257
|
+
...previewFromOutput(output),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function buildDeletedAgentRunPreview(
|
|
262
|
+
response: ExaDeletedAgentRun,
|
|
263
|
+
output: TruncatedToolOutput,
|
|
264
|
+
): PreviewDetails {
|
|
265
|
+
const lines = [`Run: ${response.id}`, `Deleted: ${response.deleted ? 'yes' : 'no'}`];
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
kind: 'agent',
|
|
269
|
+
summary: response.deleted ? 'deleted' : 'not deleted',
|
|
270
|
+
lines,
|
|
271
|
+
expandedLines: lines,
|
|
272
|
+
...previewFromOutput(output),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function sendProgress<TDetails>(
|
|
277
|
+
onUpdate: AgentToolUpdateCallback<TDetails> | undefined,
|
|
278
|
+
text: string,
|
|
279
|
+
) {
|
|
280
|
+
onUpdate?.({
|
|
281
|
+
content: [{ type: 'text', text }],
|
|
282
|
+
details: { status: 'pending' } as TDetails,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function renderExaCall(
|
|
287
|
+
toolName: string,
|
|
288
|
+
primary: string,
|
|
289
|
+
theme: Theme,
|
|
290
|
+
metadata: string[] = [],
|
|
291
|
+
): Text {
|
|
292
|
+
let text = theme.fg('toolTitle', theme.bold(`${toolName} `));
|
|
293
|
+
text += theme.fg('accent', primary);
|
|
294
|
+
if (metadata.length > 0) text += theme.fg('dim', ` | ${metadata.join(' | ')}`);
|
|
295
|
+
return new Text(text, 0, 0);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function renderPreviewText(
|
|
299
|
+
preview: PreviewDetails | undefined,
|
|
300
|
+
expanded: boolean,
|
|
301
|
+
theme: Theme,
|
|
302
|
+
options?: { partial?: boolean; partialLabel?: string },
|
|
303
|
+
) {
|
|
304
|
+
if (!preview) {
|
|
305
|
+
return theme.fg('dim', 'No preview available');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const lines = expanded ? (preview.expandedLines ?? preview.lines) : preview.lines;
|
|
309
|
+
|
|
310
|
+
let text = theme.fg('success', preview.summary);
|
|
311
|
+
if (preview.truncated) text += theme.fg('warning', ' | agent output truncated');
|
|
312
|
+
|
|
313
|
+
if (lines.length > 0) {
|
|
314
|
+
const label =
|
|
315
|
+
preview.kind === 'agent'
|
|
316
|
+
? options?.partial
|
|
317
|
+
? (options.partialLabel ?? 'Streaming events:')
|
|
318
|
+
: expanded
|
|
319
|
+
? 'Expanded UI preview; Ctrl+O to hide:'
|
|
320
|
+
: 'Preview only; Ctrl+O to show full response:'
|
|
321
|
+
: 'UI preview only; the agent received a fuller payload:';
|
|
322
|
+
text += `\n${theme.fg('muted', label)}`;
|
|
323
|
+
for (const line of lines) {
|
|
324
|
+
text += `\n${theme.fg('dim', line)}`;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (preview.fullOutputPath) {
|
|
329
|
+
text += `\n${theme.fg('muted', `Full output saved to ${preview.fullOutputPath}`)}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return text;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function renderPreviewResult(
|
|
336
|
+
preview: PreviewDetails | undefined,
|
|
337
|
+
expanded: boolean,
|
|
338
|
+
theme: Theme,
|
|
339
|
+
): Text {
|
|
340
|
+
return new Text(renderPreviewText(preview, expanded, theme), 0, 0);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function rawResponsePreview(details: unknown): string | undefined {
|
|
344
|
+
if (!details || typeof details !== 'object' || !('response' in details)) return undefined;
|
|
345
|
+
try {
|
|
346
|
+
return JSON.stringify((details as { response: unknown }).response, null, 2);
|
|
347
|
+
} catch {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export function renderExaResult<TDetails extends { preview?: PreviewDetails } | undefined>(
|
|
353
|
+
result: AgentToolResult<TDetails>,
|
|
354
|
+
options: ToolRenderResultOptions,
|
|
355
|
+
theme: Theme,
|
|
356
|
+
pendingText: string,
|
|
357
|
+
): Text {
|
|
358
|
+
if (options.isPartial) {
|
|
359
|
+
if (result.details?.preview) {
|
|
360
|
+
const details = result.details as { monitor?: unknown; preview?: PreviewDetails };
|
|
361
|
+
const partialLabel =
|
|
362
|
+
details.preview?.kind === 'agent' && details.monitor === 'poll'
|
|
363
|
+
? 'Polling status:'
|
|
364
|
+
: undefined;
|
|
365
|
+
return new Text(
|
|
366
|
+
renderPreviewText(result.details.preview, true, theme, { partial: true, partialLabel }),
|
|
367
|
+
0,
|
|
368
|
+
0,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const textContent = result.content?.find((item) => item.type === 'text')?.text;
|
|
373
|
+
return new Text(theme.fg('warning', textContent || pendingText), 0, 0);
|
|
374
|
+
}
|
|
375
|
+
let text = renderPreviewText(result.details?.preview, options.expanded, theme);
|
|
376
|
+
|
|
377
|
+
if (options.expanded && result.details?.preview?.kind === 'agent') {
|
|
378
|
+
const raw = rawResponsePreview(result.details);
|
|
379
|
+
if (raw) {
|
|
380
|
+
text += `\n${theme.fg('muted', 'UI-only raw API response:')}\n${theme.fg('dim', raw)}`;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return new Text(text, 0, 0);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function metadataFromArgs(args: ToolArgs, keys: string[]): string[] {
|
|
388
|
+
return keys.flatMap((key) => {
|
|
389
|
+
const value = args[key];
|
|
390
|
+
if (value === undefined || value === null || value === false) return [];
|
|
391
|
+
if (value === true) return [key];
|
|
392
|
+
return [`${key}=${String(value)}`];
|
|
393
|
+
});
|
|
394
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { Type } from '@sinclair/typebox';
|
|
2
|
+
|
|
3
|
+
const JsonObjectSchema = Type.Object({}, { additionalProperties: true });
|
|
4
|
+
|
|
5
|
+
export const SearchTypeSchema = Type.Union([
|
|
6
|
+
Type.Literal('auto'),
|
|
7
|
+
Type.Literal('fast'),
|
|
8
|
+
Type.Literal('instant'),
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
export const CategorySchema = Type.Union([
|
|
12
|
+
Type.Literal('company'),
|
|
13
|
+
Type.Literal('research paper'),
|
|
14
|
+
Type.Literal('news'),
|
|
15
|
+
Type.Literal('pdf'),
|
|
16
|
+
Type.Literal('github'),
|
|
17
|
+
Type.Literal('personal site'),
|
|
18
|
+
Type.Literal('people'),
|
|
19
|
+
Type.Literal('financial report'),
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
export const WebSearchParamsSchema = Type.Object({
|
|
23
|
+
query: Type.String({
|
|
24
|
+
description:
|
|
25
|
+
'Natural language search query. Prefer a specific description of the desired page over short keywords. Optional category:<type> hints are supported for company, people, news, research paper, and personal site.',
|
|
26
|
+
}),
|
|
27
|
+
numResults: Type.Optional(
|
|
28
|
+
Type.Integer({ minimum: 1, maximum: 100, description: 'Number of search results to return.' }),
|
|
29
|
+
),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const WebSearchAdvancedParamsSchema = Type.Object({
|
|
33
|
+
query: Type.String({ description: 'Search query.' }),
|
|
34
|
+
numResults: Type.Optional(
|
|
35
|
+
Type.Integer({ minimum: 1, maximum: 100, description: 'Number of search results to return.' }),
|
|
36
|
+
),
|
|
37
|
+
type: Type.Optional(SearchTypeSchema),
|
|
38
|
+
category: Type.Optional(CategorySchema),
|
|
39
|
+
includeDomains: Type.Optional(Type.Array(Type.String())),
|
|
40
|
+
excludeDomains: Type.Optional(Type.Array(Type.String())),
|
|
41
|
+
startPublishedDate: Type.Optional(Type.String({ description: 'YYYY-MM-DD or ISO date.' })),
|
|
42
|
+
endPublishedDate: Type.Optional(Type.String({ description: 'YYYY-MM-DD or ISO date.' })),
|
|
43
|
+
startCrawlDate: Type.Optional(Type.String({ description: 'YYYY-MM-DD or ISO date.' })),
|
|
44
|
+
endCrawlDate: Type.Optional(Type.String({ description: 'YYYY-MM-DD or ISO date.' })),
|
|
45
|
+
includeText: Type.Optional(Type.Array(Type.String())),
|
|
46
|
+
excludeText: Type.Optional(Type.Array(Type.String())),
|
|
47
|
+
userLocation: Type.Optional(Type.String({ description: 'ISO country code, for example US.' })),
|
|
48
|
+
moderation: Type.Optional(Type.Boolean()),
|
|
49
|
+
additionalQueries: Type.Optional(Type.Array(Type.String())),
|
|
50
|
+
textMaxCharacters: Type.Optional(
|
|
51
|
+
Type.Integer({
|
|
52
|
+
minimum: 1,
|
|
53
|
+
description:
|
|
54
|
+
'Maximum text characters per result. Omit to request uncapped text from advanced search.',
|
|
55
|
+
}),
|
|
56
|
+
),
|
|
57
|
+
contextMaxCharacters: Type.Optional(
|
|
58
|
+
Type.Integer({
|
|
59
|
+
minimum: 1,
|
|
60
|
+
description: 'Maximum characters for Exa response-level context string.',
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
63
|
+
enableSummary: Type.Optional(Type.Boolean()),
|
|
64
|
+
summaryQuery: Type.Optional(Type.String()),
|
|
65
|
+
enableHighlights: Type.Optional(
|
|
66
|
+
Type.Boolean({
|
|
67
|
+
description: 'Set true to request highlights in addition to text. Defaults to false.',
|
|
68
|
+
}),
|
|
69
|
+
),
|
|
70
|
+
highlightsMaxCharacters: Type.Optional(
|
|
71
|
+
Type.Integer({ minimum: 1, description: 'Maximum total highlight characters per URL.' }),
|
|
72
|
+
),
|
|
73
|
+
highlightsQuery: Type.Optional(Type.String()),
|
|
74
|
+
maxAgeHours: Type.Optional(
|
|
75
|
+
Type.Integer({
|
|
76
|
+
minimum: -1,
|
|
77
|
+
description:
|
|
78
|
+
'Maximum cache age in hours. Use 0 to force fresh crawl where supported, -1 for cache-only where supported.',
|
|
79
|
+
}),
|
|
80
|
+
),
|
|
81
|
+
livecrawlTimeout: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
82
|
+
subpages: Type.Optional(Type.Integer({ minimum: 1, maximum: 10 })),
|
|
83
|
+
subpageTarget: Type.Optional(Type.Array(Type.String())),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const WebFetchParamsSchema = Type.Object({
|
|
87
|
+
urls: Type.Array(Type.String({ minLength: 1 }), {
|
|
88
|
+
description: 'URLs to read. Batch multiple URLs in one call.',
|
|
89
|
+
}),
|
|
90
|
+
maxCharacters: Type.Optional(
|
|
91
|
+
Type.Integer({
|
|
92
|
+
minimum: 1,
|
|
93
|
+
description: 'Maximum characters to extract per page. Defaults to 3000.',
|
|
94
|
+
}),
|
|
95
|
+
),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export const WebAnswerParamsSchema = Type.Object({
|
|
99
|
+
query: Type.String({ minLength: 1, description: 'Natural-language question to answer.' }),
|
|
100
|
+
text: Type.Optional(
|
|
101
|
+
Type.Boolean({
|
|
102
|
+
description: 'Whether to include full text for citations. Defaults to false.',
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
|
+
outputSchema: Type.Optional(
|
|
106
|
+
Type.Object({}, { additionalProperties: true, description: 'JSON Schema Draft 7 object.' }),
|
|
107
|
+
),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export const AgentEffortSchema = Type.Union([
|
|
111
|
+
Type.Literal('minimal'),
|
|
112
|
+
Type.Literal('low'),
|
|
113
|
+
Type.Literal('medium'),
|
|
114
|
+
Type.Literal('high'),
|
|
115
|
+
Type.Literal('xhigh'),
|
|
116
|
+
Type.Literal('auto'),
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
export const AgentRunIdSchema = Type.String({
|
|
120
|
+
minLength: 1,
|
|
121
|
+
maxLength: 200,
|
|
122
|
+
pattern: '^[A-Za-z0-9_.:-]+$',
|
|
123
|
+
description: 'Exa Agent run ID, usually prefixed with agent_run_.',
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
export const WebAgentParamsSchema = Type.Object({
|
|
127
|
+
query: Type.String({
|
|
128
|
+
minLength: 1,
|
|
129
|
+
description: 'Natural-language research, list-building, or enrichment task for Exa Agent.',
|
|
130
|
+
}),
|
|
131
|
+
systemPrompt: Type.Optional(
|
|
132
|
+
Type.String({
|
|
133
|
+
description:
|
|
134
|
+
'Additional behavior guidance, source preferences, disambiguation rules, novelty/deduping constraints, or output constraints.',
|
|
135
|
+
}),
|
|
136
|
+
),
|
|
137
|
+
effort: Type.Optional(
|
|
138
|
+
Type.Union([AgentEffortSchema], {
|
|
139
|
+
description:
|
|
140
|
+
'Cost/quality/runtime preference. Defaults to auto. Use medium for standard single-entity research, auto for variable-scope list building.',
|
|
141
|
+
}),
|
|
142
|
+
),
|
|
143
|
+
input: Type.Optional(
|
|
144
|
+
Type.Object(
|
|
145
|
+
{
|
|
146
|
+
data: Type.Optional(
|
|
147
|
+
Type.Array(JsonObjectSchema, {
|
|
148
|
+
description: 'Records the agent should process or enrich.',
|
|
149
|
+
}),
|
|
150
|
+
),
|
|
151
|
+
exclusion: Type.Optional(
|
|
152
|
+
Type.Array(JsonObjectSchema, {
|
|
153
|
+
description: 'Records or entities the agent should avoid returning.',
|
|
154
|
+
}),
|
|
155
|
+
),
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
additionalProperties: false,
|
|
159
|
+
description: 'Structured input rows or exclusions for the run.',
|
|
160
|
+
},
|
|
161
|
+
),
|
|
162
|
+
),
|
|
163
|
+
outputSchema: Type.Optional(
|
|
164
|
+
Type.Object(
|
|
165
|
+
{},
|
|
166
|
+
{
|
|
167
|
+
additionalProperties: true,
|
|
168
|
+
description:
|
|
169
|
+
'JSON Schema for validated structured output in output.structured. Supports draft-07, 2019-09, and 2020-12 via $schema. Bound arrays with maxItems when possible.',
|
|
170
|
+
},
|
|
171
|
+
),
|
|
172
|
+
),
|
|
173
|
+
previousRunId: Type.Optional(
|
|
174
|
+
Type.String({
|
|
175
|
+
minLength: 1,
|
|
176
|
+
description:
|
|
177
|
+
'Completed same-team Agent run ID to continue from. Exa documents continuations as sharing the supplied previousRunId.',
|
|
178
|
+
}),
|
|
179
|
+
),
|
|
180
|
+
metadata: Type.Optional(
|
|
181
|
+
Type.Record(Type.String(), Type.String(), {
|
|
182
|
+
description: 'Caller-provided key-value metadata for tracking.',
|
|
183
|
+
}),
|
|
184
|
+
),
|
|
185
|
+
mode: Type.Optional(
|
|
186
|
+
Type.Union([Type.Literal('wait'), Type.Literal('background')], {
|
|
187
|
+
description:
|
|
188
|
+
'wait streams or polls until completion; background returns the run ID immediately and watches it in the session. Do not manually poll background runs unless the user asks.',
|
|
189
|
+
}),
|
|
190
|
+
),
|
|
191
|
+
monitor: Type.Optional(
|
|
192
|
+
Type.Union([Type.Literal('stream'), Type.Literal('poll')], {
|
|
193
|
+
description:
|
|
194
|
+
'Only applies when mode=wait. stream uses server-sent events; poll uses GET /agent/runs/{id}. Ignored for mode=background.',
|
|
195
|
+
}),
|
|
196
|
+
),
|
|
197
|
+
pollIntervalMs: Type.Optional(
|
|
198
|
+
Type.Integer({
|
|
199
|
+
minimum: 1000,
|
|
200
|
+
maximum: 60000,
|
|
201
|
+
description: 'Polling interval for poll mode or background tracking.',
|
|
202
|
+
}),
|
|
203
|
+
),
|
|
204
|
+
timeoutMs: Type.Optional(
|
|
205
|
+
Type.Integer({
|
|
206
|
+
minimum: 1000,
|
|
207
|
+
maximum: 3600000,
|
|
208
|
+
description:
|
|
209
|
+
'Foreground wait timeout. If reached, the run keeps going and can be inspected later.',
|
|
210
|
+
}),
|
|
211
|
+
),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
export const WebAgentRunParamsSchema = Type.Object({
|
|
215
|
+
runId: AgentRunIdSchema,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
export const WebAgentListParamsSchema = Type.Object({
|
|
219
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
|
|
220
|
+
cursor: Type.Optional(Type.String({ minLength: 1 })),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
export const WebAgentEventsParamsSchema = Type.Object({
|
|
224
|
+
runId: AgentRunIdSchema,
|
|
225
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
|
|
226
|
+
cursor: Type.Optional(Type.String({ minLength: 1 })),
|
|
227
|
+
replay: Type.Optional(
|
|
228
|
+
Type.Boolean({
|
|
229
|
+
description:
|
|
230
|
+
'Replay stored events as server-sent events instead of listing JSON events. Use only for progress/history inspection.',
|
|
231
|
+
}),
|
|
232
|
+
),
|
|
233
|
+
lastEventId: Type.Optional(
|
|
234
|
+
Type.String({
|
|
235
|
+
minLength: 1,
|
|
236
|
+
description: 'For replay=true, return only events after this Last-Event-ID value.',
|
|
237
|
+
}),
|
|
238
|
+
),
|
|
239
|
+
});
|