@oh-my-pi/pi-coding-agent 3.14.0 → 3.15.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.
- package/CHANGELOG.md +79 -0
- package/docs/theme.md +38 -5
- package/examples/sdk/11-sessions.ts +2 -2
- package/package.json +7 -4
- package/src/cli/file-processor.ts +51 -2
- package/src/cli/plugin-cli.ts +25 -19
- package/src/cli/update-cli.ts +4 -3
- package/src/core/agent-session.ts +31 -4
- package/src/core/compaction/branch-summarization.ts +4 -32
- package/src/core/compaction/compaction.ts +6 -84
- package/src/core/compaction/utils.ts +2 -3
- package/src/core/custom-tools/types.ts +2 -0
- package/src/core/export-html/index.ts +1 -1
- package/src/core/hooks/tool-wrapper.ts +0 -1
- package/src/core/hooks/types.ts +2 -2
- package/src/core/plugins/doctor.ts +9 -1
- package/src/core/sdk.ts +2 -1
- package/src/core/session-manager.ts +518 -40
- package/src/core/settings-manager.ts +174 -0
- package/src/core/system-prompt.ts +9 -14
- package/src/core/title-generator.ts +2 -8
- package/src/core/tools/ask.ts +19 -37
- package/src/core/tools/bash.ts +2 -37
- package/src/core/tools/edit.ts +2 -9
- package/src/core/tools/exa/render.ts +52 -48
- package/src/core/tools/find.ts +10 -8
- package/src/core/tools/grep.ts +45 -17
- package/src/core/tools/ls.ts +22 -2
- package/src/core/tools/lsp/clients/biome-client.ts +207 -0
- package/src/core/tools/lsp/clients/index.ts +49 -0
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
- package/src/core/tools/lsp/config.ts +3 -0
- package/src/core/tools/lsp/index.ts +107 -55
- package/src/core/tools/lsp/render.ts +192 -79
- package/src/core/tools/lsp/types.ts +27 -0
- package/src/core/tools/lsp/utils.ts +62 -22
- package/src/core/tools/notebook.ts +9 -1
- package/src/core/tools/output.ts +37 -14
- package/src/core/tools/read.ts +349 -34
- package/src/core/tools/renderers.ts +290 -89
- package/src/core/tools/review.ts +12 -5
- package/src/core/tools/task/agents.ts +5 -5
- package/src/core/tools/task/commands.ts +3 -3
- package/src/core/tools/task/executor.ts +33 -1
- package/src/core/tools/task/index.ts +93 -6
- package/src/core/tools/task/render.ts +147 -66
- package/src/core/tools/task/types.ts +14 -9
- package/src/core/tools/web-fetch.ts +242 -103
- package/src/core/tools/web-search/index.ts +64 -20
- package/src/core/tools/web-search/providers/exa.ts +68 -172
- package/src/core/tools/web-search/render.ts +264 -74
- package/src/core/tools/write.ts +2 -8
- package/src/main.ts +10 -6
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +9 -4
- package/src/modes/interactive/components/bash-execution.ts +6 -3
- package/src/modes/interactive/components/branch-summary-message.ts +1 -1
- package/src/modes/interactive/components/compaction-summary-message.ts +1 -1
- package/src/modes/interactive/components/dynamic-border.ts +1 -1
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +4 -5
- package/src/modes/interactive/components/extensions/extension-list.ts +18 -16
- package/src/modes/interactive/components/extensions/inspector-panel.ts +8 -8
- package/src/modes/interactive/components/hook-message.ts +2 -2
- package/src/modes/interactive/components/hook-selector.ts +1 -1
- package/src/modes/interactive/components/model-selector.ts +22 -9
- package/src/modes/interactive/components/oauth-selector.ts +20 -4
- package/src/modes/interactive/components/plugin-settings.ts +4 -2
- package/src/modes/interactive/components/session-selector.ts +9 -6
- package/src/modes/interactive/components/settings-defs.ts +285 -1
- package/src/modes/interactive/components/settings-selector.ts +176 -3
- package/src/modes/interactive/components/status-line/index.ts +4 -0
- package/src/modes/interactive/components/status-line/presets.ts +94 -0
- package/src/modes/interactive/components/status-line/segments.ts +350 -0
- package/src/modes/interactive/components/status-line/separators.ts +55 -0
- package/src/modes/interactive/components/status-line/types.ts +81 -0
- package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
- package/src/modes/interactive/components/status-line.ts +170 -223
- package/src/modes/interactive/components/tool-execution.ts +446 -211
- package/src/modes/interactive/components/tree-selector.ts +17 -6
- package/src/modes/interactive/components/ttsr-notification.ts +4 -4
- package/src/modes/interactive/components/welcome.ts +27 -19
- package/src/modes/interactive/interactive-mode.ts +98 -13
- package/src/modes/interactive/theme/dark.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
- package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
- package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
- package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
- package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/index.ts +67 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
- package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
- package/src/modes/interactive/theme/defaults/light-one.json +105 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
- package/src/modes/interactive/theme/light.json +3 -2
- package/src/modes/interactive/theme/theme-schema.json +120 -4
- package/src/modes/interactive/theme/theme.ts +1228 -14
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -0
- package/src/prompts/compaction-summary.md +34 -0
- package/src/prompts/compaction-turn-prefix.md +16 -0
- package/src/prompts/compaction-update-summary.md +41 -0
- package/src/prompts/init.md +30 -0
- package/src/{core/tools/task/bundled-agents → prompts}/reviewer.md +6 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/{core/tools/task/bundled-agents → prompts}/task.md +2 -0
- package/src/prompts/title-system.md +8 -0
- package/src/prompts/tools/ask.md +24 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/edit.md +9 -0
- package/src/prompts/tools/find.md +6 -0
- package/src/prompts/tools/grep.md +12 -0
- package/src/prompts/tools/lsp.md +14 -0
- package/src/prompts/tools/output.md +23 -0
- package/src/prompts/tools/read.md +25 -0
- package/src/prompts/tools/web-fetch.md +8 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +10 -0
- package/src/commands/init.md +0 -20
- /package/src/{core/tools/task/bundled-commands → prompts}/architect-plan.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/browser.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/explore.md +0 -0
- /package/src/{core/tools/task/bundled-commands → prompts}/implement-with-critic.md +0 -0
- /package/src/{core/tools/task/bundled-commands → prompts}/implement.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/plan.md +0 -0
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Exa Web Search Provider
|
|
3
3
|
*
|
|
4
|
-
* High-quality neural search via Exa
|
|
4
|
+
* High-quality neural search via Exa Search API.
|
|
5
5
|
* Returns structured search results with optional content extraction.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import * as os from "node:os";
|
|
9
8
|
import type { WebSearchResponse, WebSearchSource } from "../types";
|
|
10
9
|
|
|
11
|
-
const
|
|
10
|
+
const EXA_API_URL = "https://api.exa.ai/search";
|
|
11
|
+
|
|
12
|
+
type ExaSearchType = "neural" | "fast" | "auto" | "deep";
|
|
13
|
+
|
|
14
|
+
type ExaSearchParamType = ExaSearchType | "keyword";
|
|
12
15
|
|
|
13
16
|
export interface ExaSearchParams {
|
|
14
17
|
query: string;
|
|
15
18
|
num_results?: number;
|
|
16
|
-
type?:
|
|
19
|
+
type?: ExaSearchParamType;
|
|
17
20
|
include_domains?: string[];
|
|
18
21
|
exclude_domains?: string[];
|
|
19
22
|
start_published_date?: string;
|
|
@@ -29,9 +32,13 @@ async function parseEnvFile(filePath: string): Promise<Record<string, string>> {
|
|
|
29
32
|
|
|
30
33
|
const content = await file.text();
|
|
31
34
|
for (const line of content.split("\n")) {
|
|
32
|
-
|
|
35
|
+
let trimmed = line.trim();
|
|
33
36
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
34
37
|
|
|
38
|
+
if (trimmed.startsWith("export ")) {
|
|
39
|
+
trimmed = trimmed.slice("export ".length).trim();
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
const eqIndex = trimmed.indexOf("=");
|
|
36
43
|
if (eqIndex === -1) continue;
|
|
37
44
|
|
|
@@ -50,6 +57,10 @@ async function parseEnvFile(filePath: string): Promise<Record<string, string>> {
|
|
|
50
57
|
return result;
|
|
51
58
|
}
|
|
52
59
|
|
|
60
|
+
function getHomeDir(): string | null {
|
|
61
|
+
return process.env.HOME ?? process.env.USERPROFILE ?? null;
|
|
62
|
+
}
|
|
63
|
+
|
|
53
64
|
/** Find EXA_API_KEY from environment or .env files */
|
|
54
65
|
export async function findApiKey(): Promise<string | null> {
|
|
55
66
|
// 1. Check environment variable
|
|
@@ -64,171 +75,80 @@ export async function findApiKey(): Promise<string | null> {
|
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
// 3. Check ~/.env
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Parse SSE response format */
|
|
76
|
-
function parseSSE(text: string): unknown {
|
|
77
|
-
const lines = text.split("\n");
|
|
78
|
-
for (const line of lines) {
|
|
79
|
-
if (line.startsWith("data: ")) {
|
|
80
|
-
const data = line.slice(6).trim();
|
|
81
|
-
if (data === "[DONE]") continue;
|
|
82
|
-
try {
|
|
83
|
-
return JSON.parse(data);
|
|
84
|
-
} catch {
|
|
85
|
-
// Try next line
|
|
86
|
-
}
|
|
78
|
+
const homeDir = getHomeDir();
|
|
79
|
+
if (homeDir) {
|
|
80
|
+
const homeEnv = await parseEnvFile(`${homeDir}/.env`);
|
|
81
|
+
if (homeEnv.EXA_API_KEY) {
|
|
82
|
+
return homeEnv.EXA_API_KEY;
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
|
-
// Fallback: try parsing entire response as JSON
|
|
90
|
-
try {
|
|
91
|
-
return JSON.parse(text);
|
|
92
|
-
} catch {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
85
|
|
|
97
|
-
|
|
98
|
-
result?: {
|
|
99
|
-
content?: Array<{ type: string; text?: string }>;
|
|
100
|
-
};
|
|
101
|
-
error?: {
|
|
102
|
-
code: number;
|
|
103
|
-
message: string;
|
|
104
|
-
};
|
|
86
|
+
return null;
|
|
105
87
|
}
|
|
106
88
|
|
|
107
89
|
interface ExaSearchResult {
|
|
108
|
-
title?: string;
|
|
109
|
-
url?: string;
|
|
110
|
-
author?: string;
|
|
111
|
-
publishedDate?: string;
|
|
112
|
-
text?: string;
|
|
113
|
-
highlights?: string[];
|
|
90
|
+
title?: string | null;
|
|
91
|
+
url?: string | null;
|
|
92
|
+
author?: string | null;
|
|
93
|
+
publishedDate?: string | null;
|
|
94
|
+
text?: string | null;
|
|
95
|
+
highlights?: string[] | null;
|
|
114
96
|
}
|
|
115
97
|
|
|
116
98
|
interface ExaSearchResponse {
|
|
99
|
+
requestId?: string;
|
|
100
|
+
resolvedSearchType?: string;
|
|
117
101
|
results?: ExaSearchResult[];
|
|
118
102
|
costDollars?: { total: number };
|
|
119
103
|
searchTime?: number;
|
|
120
104
|
}
|
|
121
105
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
106
|
+
function normalizeSearchType(type: ExaSearchParamType | undefined): ExaSearchType {
|
|
107
|
+
if (!type) return "auto";
|
|
108
|
+
if (type === "keyword") return "fast";
|
|
109
|
+
return type;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Call Exa Search API */
|
|
113
|
+
async function callExaSearch(apiKey: string, params: ExaSearchParams): Promise<ExaSearchResponse> {
|
|
114
|
+
const body: Record<string, unknown> = {
|
|
115
|
+
query: params.query,
|
|
116
|
+
numResults: params.num_results ?? 10,
|
|
117
|
+
type: normalizeSearchType(params.type),
|
|
134
118
|
};
|
|
135
119
|
|
|
136
|
-
|
|
120
|
+
if (params.include_domains?.length) {
|
|
121
|
+
body.includeDomains = params.include_domains;
|
|
122
|
+
}
|
|
123
|
+
if (params.exclude_domains?.length) {
|
|
124
|
+
body.excludeDomains = params.exclude_domains;
|
|
125
|
+
}
|
|
126
|
+
if (params.start_published_date) {
|
|
127
|
+
body.startPublishedDate = params.start_published_date;
|
|
128
|
+
}
|
|
129
|
+
if (params.end_published_date) {
|
|
130
|
+
body.endPublishedDate = params.end_published_date;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const response = await fetch(EXA_API_URL, {
|
|
137
134
|
method: "POST",
|
|
138
135
|
headers: {
|
|
139
136
|
"Content-Type": "application/json",
|
|
140
|
-
|
|
137
|
+
"x-api-key": apiKey,
|
|
141
138
|
},
|
|
142
139
|
body: JSON.stringify(body),
|
|
143
140
|
});
|
|
144
141
|
|
|
145
142
|
if (!response.ok) {
|
|
146
143
|
const errorText = await response.text();
|
|
147
|
-
throw new Error(`Exa
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const text = await response.text();
|
|
151
|
-
const result = parseSSE(text);
|
|
152
|
-
|
|
153
|
-
if (!result) {
|
|
154
|
-
throw new Error("Failed to parse Exa MCP response");
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return result as MCPCallResponse;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** Parse MCP response content into ExaSearchResponse */
|
|
161
|
-
function parseMCPContent(content: Array<{ type: string; text?: string }>): ExaSearchResponse | null {
|
|
162
|
-
for (const block of content) {
|
|
163
|
-
if (block.type === "text" && block.text) {
|
|
164
|
-
// Try to parse as JSON first
|
|
165
|
-
try {
|
|
166
|
-
return JSON.parse(block.text) as ExaSearchResponse;
|
|
167
|
-
} catch {
|
|
168
|
-
// Parse markdown format
|
|
169
|
-
return parseExaMarkdown(block.text);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** Parse Exa markdown format into ExaSearchResponse */
|
|
177
|
-
function parseExaMarkdown(text: string): ExaSearchResponse | null {
|
|
178
|
-
const results: ExaSearchResult[] = [];
|
|
179
|
-
const lines = text.split("\n");
|
|
180
|
-
let currentResult: Partial<ExaSearchResult> | null = null;
|
|
181
|
-
|
|
182
|
-
for (const line of lines) {
|
|
183
|
-
const trimmed = line.trim();
|
|
184
|
-
|
|
185
|
-
// Match result header: ## Title
|
|
186
|
-
if (trimmed.startsWith("## ")) {
|
|
187
|
-
if (currentResult?.title) {
|
|
188
|
-
results.push(currentResult as ExaSearchResult);
|
|
189
|
-
}
|
|
190
|
-
currentResult = { title: trimmed.slice(3).trim() };
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (!currentResult) continue;
|
|
195
|
-
|
|
196
|
-
// Match URL: **URL:** ...
|
|
197
|
-
if (trimmed.startsWith("**URL:**")) {
|
|
198
|
-
currentResult.url = trimmed.slice(8).trim();
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Match Author: **Author:** ...
|
|
203
|
-
if (trimmed.startsWith("**Author:**")) {
|
|
204
|
-
currentResult.author = trimmed.slice(11).trim();
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Match Published Date: **Published Date:** ...
|
|
209
|
-
if (trimmed.startsWith("**Published Date:**")) {
|
|
210
|
-
currentResult.publishedDate = trimmed.slice(19).trim();
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Match Text: **Text:** ...
|
|
215
|
-
if (trimmed.startsWith("**Text:**")) {
|
|
216
|
-
currentResult.text = trimmed.slice(9).trim();
|
|
217
|
-
}
|
|
144
|
+
throw new Error(`Exa API error (${response.status}): ${errorText}`);
|
|
218
145
|
}
|
|
219
146
|
|
|
220
|
-
|
|
221
|
-
if (currentResult?.title) {
|
|
222
|
-
results.push(currentResult as ExaSearchResult);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (results.length === 0) return null;
|
|
226
|
-
|
|
227
|
-
return { results };
|
|
147
|
+
return response.json() as Promise<ExaSearchResponse>;
|
|
228
148
|
}
|
|
229
149
|
|
|
230
150
|
/** Calculate age in seconds from ISO date string */
|
|
231
|
-
function dateToAgeSeconds(dateStr: string | undefined): number | undefined {
|
|
151
|
+
function dateToAgeSeconds(dateStr: string | null | undefined): number | undefined {
|
|
232
152
|
if (!dateStr) return undefined;
|
|
233
153
|
try {
|
|
234
154
|
const date = new Date(dateStr);
|
|
@@ -246,46 +166,21 @@ export async function searchExa(params: ExaSearchParams): Promise<WebSearchRespo
|
|
|
246
166
|
throw new Error("EXA_API_KEY not found. Set it in environment or .env file.");
|
|
247
167
|
}
|
|
248
168
|
|
|
249
|
-
const
|
|
250
|
-
query: params.query,
|
|
251
|
-
numResults: params.num_results ?? 10,
|
|
252
|
-
type: params.type ?? "auto",
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
if (params.include_domains?.length) {
|
|
256
|
-
args.include_domains = params.include_domains;
|
|
257
|
-
}
|
|
258
|
-
if (params.exclude_domains?.length) {
|
|
259
|
-
args.exclude_domains = params.exclude_domains;
|
|
260
|
-
}
|
|
261
|
-
if (params.start_published_date) {
|
|
262
|
-
args.start_published_date = params.start_published_date;
|
|
263
|
-
}
|
|
264
|
-
if (params.end_published_date) {
|
|
265
|
-
args.end_published_date = params.end_published_date;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const response = await callExaMCP(apiKey, "web_search_exa", args);
|
|
269
|
-
|
|
270
|
-
if (response.error) {
|
|
271
|
-
throw new Error(`Exa MCP error: ${response.error.message}`);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const exaResponse = response.result?.content ? parseMCPContent(response.result.content) : null;
|
|
169
|
+
const response = await callExaSearch(apiKey, params);
|
|
275
170
|
|
|
276
171
|
// Convert to unified WebSearchResponse
|
|
277
172
|
const sources: WebSearchSource[] = [];
|
|
278
173
|
|
|
279
|
-
if (
|
|
280
|
-
for (const result of
|
|
174
|
+
if (response.results) {
|
|
175
|
+
for (const result of response.results) {
|
|
281
176
|
if (!result.url) continue;
|
|
282
177
|
sources.push({
|
|
283
178
|
title: result.title ?? result.url,
|
|
284
179
|
url: result.url,
|
|
285
|
-
snippet: result.text ?? result.highlights?.join(" "),
|
|
286
|
-
publishedDate: result.publishedDate,
|
|
287
|
-
ageSeconds: dateToAgeSeconds(result.publishedDate),
|
|
288
|
-
author: result.author,
|
|
180
|
+
snippet: result.text ?? result.highlights?.join(" ") ?? undefined,
|
|
181
|
+
publishedDate: result.publishedDate ?? undefined,
|
|
182
|
+
ageSeconds: dateToAgeSeconds(result.publishedDate ?? undefined),
|
|
183
|
+
author: result.author ?? undefined,
|
|
289
184
|
});
|
|
290
185
|
}
|
|
291
186
|
}
|
|
@@ -296,5 +191,6 @@ export async function searchExa(params: ExaSearchParams): Promise<WebSearchRespo
|
|
|
296
191
|
return {
|
|
297
192
|
provider: "exa",
|
|
298
193
|
sources: limitedSources,
|
|
194
|
+
requestId: response.requestId,
|
|
299
195
|
};
|
|
300
196
|
}
|